diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | ext/x264/gstx264enc.c | 383 |
2 files changed, 200 insertions, 192 deletions
@@ -1,5 +1,14 @@ 2008-08-08 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * ext/x264/gstx264enc.c: (gst_x264_enc_log_callback), + (gst_x264_enc_finalize), (gst_x264_enc_header_buf), + (gst_x264_enc_set_src_caps), (gst_x264_enc_sink_set_caps), + (gst_x264_enc_flush_frames): + Coding style and layout; re-order some functions in more + typical and natural flow. + +2008-08-08 Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * ext/x264/Makefile.am: * ext/x264/gstx264enc.c: (gst_x264_enc_set_src_caps), (gst_x264_enc_sink_set_caps), (gst_x264_enc_init), diff --git a/ext/x264/gstx264enc.c b/ext/x264/gstx264enc.c index c539e24b..6b6c81fa 100644 --- a/ext/x264/gstx264enc.c +++ b/ext/x264/gstx264enc.c @@ -126,14 +126,13 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS ("video/x-h264") ); -GST_BOILERPLATE (GstX264Enc, gst_x264_enc, GstElement, GST_TYPE_ELEMENT); - static void gst_x264_enc_finalize (GObject * object); static void gst_x264_enc_reset (GstX264Enc * encoder); static gboolean gst_x264_enc_init_encoder (GstX264Enc * encoder); static void gst_x264_enc_close_encoder (GstX264Enc * encoder); +static gboolean gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps); static gboolean gst_x264_enc_sink_event (GstPad * pad, GstEvent * event); static GstFlowReturn gst_x264_enc_chain (GstPad * pad, GstBuffer * buf); static void gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send); @@ -147,171 +146,7 @@ static void gst_x264_enc_set_property (GObject * object, guint prop_id, static void gst_x264_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); - -/* - * Returns: Buffer with the stream headers. - */ -static GstBuffer * -gst_x264_enc_header_buf (GstX264Enc * encoder) -{ - GstBuffer *buf; - x264_nal_t *nal; - int i_nal; - int header_return; - int i_size; - int nal_size, i_data; - guint8 *buffer, *sps; - gulong buffer_size; - - if (G_UNLIKELY (encoder->x264enc == NULL)) - return NULL; - - /* Create avcC header. */ - - header_return = x264_encoder_headers (encoder->x264enc, &nal, &i_nal); - if (header_return < 0) { - GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x264 header failed."), - ("x264_encoder_headers return code=%d", header_return)); - return NULL; - } - - /* This should be enough for a header buffer. */ - buffer_size = 100000; - buffer = g_malloc (buffer_size); - - if (nal[1].i_type != 7 || nal[2].i_type != 8) { - GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Unexpected x264 header."), - ("TODO avcC header construction for high profiles needs some work")); - return NULL; - } - - sps = nal[1].p_payload; - - buffer[0] = 1; /* AVC Decoder Configuration Record ver. 1 */ - buffer[1] = sps[0]; /* profile_idc */ - buffer[2] = sps[1]; /* profile_compability */ - buffer[3] = sps[2]; /* level_idc */ - buffer[4] = 0xfc | (4 - 1); /* nal_length_size_minus1 */ - - i_size = 5; - - buffer[i_size++] = 0xe0 | 1; /* number of SPSs */ - - i_data = buffer_size - i_size - 2; - nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[1]); - GST_WRITE_UINT16_BE (buffer + i_size, nal_size); - i_size += nal_size + 2; - - buffer[i_size++] = 1; /* number of PPSs */ - - i_data = buffer_size - i_size - 2; - nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[2]); - GST_WRITE_UINT16_BE (buffer + i_size, nal_size); - i_size += nal_size + 2; - - buf = gst_buffer_new_and_alloc (i_size); - - memcpy (GST_BUFFER_DATA (buf), buffer, i_size); - - g_free (buffer); - - return buf; -} - -/* gst_x264_enc_set_src_caps - * Returns: TRUE on success. - */ -static gboolean -gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstPad * pad, GstCaps * caps) -{ - GstStructure *structure; - GstBuffer *buf; - GstCaps *outcaps; - gboolean res; - - structure = gst_caps_get_structure (caps, 0); - structure = gst_structure_copy (structure); - gst_structure_set_name (structure, "video/x-h264"); - outcaps = gst_caps_new_full (structure, NULL); - - if (!encoder->byte_stream) { - buf = gst_x264_enc_header_buf (encoder); - if (buf != NULL) { - gst_caps_set_simple (outcaps, "codec_data", GST_TYPE_BUFFER, buf, NULL); - gst_buffer_unref (buf); - } - } - - res = gst_pad_set_caps (pad, outcaps); - gst_caps_unref (outcaps); - - return res; -} - -static gboolean -gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps) -{ - GstX264Enc *encoder = GST_X264_ENC (GST_OBJECT_PARENT (pad)); - gint width, height; - gint fps_num, fps_den; - gint par_num, par_den; - gint i; - - /* get info from caps */ - /* only I420 supported for now; so apparently claims x264enc ? */ - if (!gst_video_format_parse_caps (caps, &encoder->format, &width, &height) || - encoder->format != GST_VIDEO_FORMAT_I420) - return FALSE; - if (!gst_video_parse_caps_framerate (caps, &fps_num, &fps_den)) - return FALSE; - if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_num, &par_den)) { - par_num = 1; - par_den = 1; - } - - /* If the encoder is initialized, do not - reinitialize it again if not necessary */ - if (encoder->x264enc) { - if (width == encoder->width && height == encoder->height - && fps_num == encoder->fps_num && fps_den == encoder->fps_den - && par_num == encoder->par_num && par_den == encoder->par_den) - return TRUE; - - /* clear out pending frames */ - gst_x264_enc_flush_frames (encoder, TRUE); - - encoder->sps_id++; - } - - /* store input description */ - encoder->width = width; - encoder->height = height; - encoder->fps_num = fps_num; - encoder->fps_den = fps_den; - encoder->par_num = par_num; - encoder->par_den = par_den; - - /* prepare a cached image description */ - encoder->image_size = gst_video_format_get_size (encoder->format, width, - height); - for (i = 0; i < 3; ++i) { - /* only offsets now, is shifted later */ - encoder->offset[i] = gst_video_format_get_component_offset (encoder->format, - i, width, height); - encoder->stride[i] = gst_video_format_get_row_stride (encoder->format, - i, width); - } - - if (!gst_x264_enc_init_encoder (encoder)) - return FALSE; - - if (!gst_x264_enc_set_src_caps (encoder, encoder->srcpad, caps)) { - gst_x264_enc_close_encoder (encoder); - return FALSE; - } - - return TRUE; -} +GST_BOILERPLATE (GstX264Enc, gst_x264_enc, GstElement, GST_TYPE_ELEMENT); static void gst_x264_enc_base_init (gpointer g_class) @@ -435,8 +270,8 @@ gst_x264_enc_log_callback (gpointer private, gint level, const char *format, case X264_LOG_INFO: gst_level = GST_LEVEL_INFO; break; - /* push x264enc debug down to our lower levels to avoid some clutter */ default: + /* push x264enc debug down to our lower levels to avoid some clutter */ gst_level = GST_LEVEL_LOG; break; } @@ -510,6 +345,23 @@ gst_x264_enc_reset (GstX264Enc * encoder) encoder->last_timestamp = GST_CLOCK_TIME_NONE; } +static void +gst_x264_enc_finalize (GObject * object) +{ + GstX264Enc *encoder = GST_X264_ENC (object); + + g_free (encoder->stats_file); + encoder->stats_file = NULL; + g_free (encoder->buffer); + encoder->buffer = NULL; + g_queue_free (encoder->delay); + encoder->delay = NULL; + + gst_x264_enc_close_encoder (encoder); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + /* * gst_x264_enc_init_encoder * @encoder: Encoder which should be initialized. @@ -629,38 +481,169 @@ gst_x264_enc_close_encoder (GstX264Enc * encoder) } } -static void -gst_x264_enc_finalize (GObject * object) +/* + * Returns: Buffer with the stream headers. + */ +static GstBuffer * +gst_x264_enc_header_buf (GstX264Enc * encoder) { - GstX264Enc *encoder = GST_X264_ENC (object); + GstBuffer *buf; + x264_nal_t *nal; + int i_nal; + int header_return; + int i_size; + int nal_size, i_data; + guint8 *buffer, *sps; + gulong buffer_size; - g_free (encoder->stats_file); - encoder->stats_file = NULL; - g_free (encoder->buffer); - encoder->buffer = NULL; - g_queue_free (encoder->delay); - encoder->delay = NULL; + if (G_UNLIKELY (encoder->x264enc == NULL)) + return NULL; - gst_x264_enc_close_encoder (encoder); + /* Create avcC header. */ - G_OBJECT_CLASS (parent_class)->finalize (object); + header_return = x264_encoder_headers (encoder->x264enc, &nal, &i_nal); + if (header_return < 0) { + GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x264 header failed."), + ("x264_encoder_headers return code=%d", header_return)); + return NULL; + } + + /* This should be enough for a header buffer. */ + buffer_size = 100000; + buffer = g_malloc (buffer_size); + + if (nal[1].i_type != 7 || nal[2].i_type != 8) { + GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Unexpected x264 header."), + ("TODO avcC header construction for high profiles needs some work")); + return NULL; + } + + sps = nal[1].p_payload; + + buffer[0] = 1; /* AVC Decoder Configuration Record ver. 1 */ + buffer[1] = sps[0]; /* profile_idc */ + buffer[2] = sps[1]; /* profile_compability */ + buffer[3] = sps[2]; /* level_idc */ + buffer[4] = 0xfc | (4 - 1); /* nal_length_size_minus1 */ + + i_size = 5; + + buffer[i_size++] = 0xe0 | 1; /* number of SPSs */ + + i_data = buffer_size - i_size - 2; + nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[1]); + GST_WRITE_UINT16_BE (buffer + i_size, nal_size); + i_size += nal_size + 2; + + buffer[i_size++] = 1; /* number of PPSs */ + + i_data = buffer_size - i_size - 2; + nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[2]); + GST_WRITE_UINT16_BE (buffer + i_size, nal_size); + i_size += nal_size + 2; + + buf = gst_buffer_new_and_alloc (i_size); + + memcpy (GST_BUFFER_DATA (buf), buffer, i_size); + + g_free (buffer); + + return buf; } -static void -gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send) +/* gst_x264_enc_set_src_caps + * Returns: TRUE on success. + */ +static gboolean +gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstPad * pad, GstCaps * caps) { - GstFlowReturn flow_ret; - gint i_nal; + GstStructure *structure; + GstBuffer *buf; + GstCaps *outcaps; + gboolean res; - /* first send the remaining frames */ - if (encoder->x264enc) - do { - flow_ret = gst_x264_enc_encode_frame (encoder, NULL, &i_nal, send); - } while (flow_ret == GST_FLOW_OK && i_nal > 0); + structure = gst_caps_get_structure (caps, 0); + structure = gst_structure_copy (structure); + gst_structure_set_name (structure, "video/x-h264"); + outcaps = gst_caps_new_full (structure, NULL); - /* in any case, make sure the delay queue is emptied */ - while (!g_queue_is_empty (encoder->delay)) - gst_buffer_unref (g_queue_pop_head (encoder->delay)); + if (!encoder->byte_stream) { + buf = gst_x264_enc_header_buf (encoder); + if (buf != NULL) { + gst_caps_set_simple (outcaps, "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + } + } + + res = gst_pad_set_caps (pad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static gboolean +gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps) +{ + GstX264Enc *encoder = GST_X264_ENC (GST_OBJECT_PARENT (pad)); + gint width, height; + gint fps_num, fps_den; + gint par_num, par_den; + gint i; + + /* get info from caps */ + /* only I420 supported for now; so apparently claims x264enc ? */ + if (!gst_video_format_parse_caps (caps, &encoder->format, &width, &height) || + encoder->format != GST_VIDEO_FORMAT_I420) + return FALSE; + if (!gst_video_parse_caps_framerate (caps, &fps_num, &fps_den)) + return FALSE; + if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_num, &par_den)) { + par_num = 1; + par_den = 1; + } + + /* If the encoder is initialized, do not + reinitialize it again if not necessary */ + if (encoder->x264enc) { + if (width == encoder->width && height == encoder->height + && fps_num == encoder->fps_num && fps_den == encoder->fps_den + && par_num == encoder->par_num && par_den == encoder->par_den) + return TRUE; + + /* clear out pending frames */ + gst_x264_enc_flush_frames (encoder, TRUE); + + encoder->sps_id++; + } + + /* store input description */ + encoder->width = width; + encoder->height = height; + encoder->fps_num = fps_num; + encoder->fps_den = fps_den; + encoder->par_num = par_num; + encoder->par_den = par_den; + + /* prepare a cached image description */ + encoder->image_size = gst_video_format_get_size (encoder->format, width, + height); + for (i = 0; i < 3; ++i) { + /* only offsets now, is shifted later */ + encoder->offset[i] = gst_video_format_get_component_offset (encoder->format, + i, width, height); + encoder->stride[i] = gst_video_format_get_row_stride (encoder->format, + i, width); + } + + if (!gst_x264_enc_init_encoder (encoder)) + return FALSE; + + if (!gst_x264_enc_set_src_caps (encoder, encoder->srcpad, caps)) { + gst_x264_enc_close_encoder (encoder); + return FALSE; + } + + return TRUE; } static gboolean @@ -690,7 +673,6 @@ gst_x264_enc_sink_event (GstPad * pad, GstEvent * event) /* chain function * this function does the actual processing */ - static GstFlowReturn gst_x264_enc_chain (GstPad * pad, GstBuffer * buf) { @@ -855,6 +837,23 @@ gst_x264_enc_encode_frame (GstX264Enc * encoder, x264_picture_t * pic_in, return gst_pad_push (encoder->srcpad, out_buf); } +static void +gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send) +{ + GstFlowReturn flow_ret; + gint i_nal; + + /* first send the remaining frames */ + if (encoder->x264enc) + do { + flow_ret = gst_x264_enc_encode_frame (encoder, NULL, &i_nal, send); + } while (flow_ret == GST_FLOW_OK && i_nal > 0); + + /* in any case, make sure the delay queue is emptied */ + while (!g_queue_is_empty (encoder->delay)) + gst_buffer_unref (g_queue_pop_head (encoder->delay)); +} + static GstStateChangeReturn gst_x264_enc_change_state (GstElement * element, GstStateChange transition) { |