diff --git a/ChangeLog b/ChangeLog index f883f773a7..653a5d4b7e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2007-10-27 Julien MOUTTE + + * gst/mpeg4videoparse/mpeg4videoparse.c: (gst_mpeg4vparse_align), + (gst_mpeg4vparse_drain), (gst_mpeg4vparse_chain), + (gst_mpeg4vparse_sink_setcaps), (gst_mpeg4vparse_sink_event), + (gst_mpeg4vparse_cleanup), (gst_mpeg4vparse_change_state), + (gst_mpeg4vparse_dispose), (gst_mpeg4vparse_base_init), + (gst_mpeg4vparse_class_init), (gst_mpeg4vparse_init), + (plugin_init): + * gst/mpeg4videoparse/mpeg4videoparse.h: Improved version not + damaging headers using a simple state machine. + 2007-10-26 Tim-Philipp Müller * ext/x264/gstx264enc.c: diff --git a/gst/mpeg4videoparse/mpeg4videoparse.c b/gst/mpeg4videoparse/mpeg4videoparse.c index 21be173c57..e4dffa310c 100644 --- a/gst/mpeg4videoparse/mpeg4videoparse.c +++ b/gst/mpeg4videoparse/mpeg4videoparse.c @@ -51,51 +51,81 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_BOILERPLATE (GstMpeg4VParse, gst_mpeg4vparse, GstElement, GST_TYPE_ELEMENT); +static void +gst_mpeg4vparse_align (GstMpeg4VParse * parse) +{ + guint flushed = 0; + + /* Searching for a start code */ + while (gst_adapter_available (parse->adapter) >= 4) { + /* If we have enough data, ensure we're aligned to a start code */ + const guint8 *data = gst_adapter_peek (parse->adapter, 4); + + if (data[0] == 0 && data[1] == 0 && data[2] == 1) { + GST_LOG_OBJECT (parse, "found start code with type %02X", data[3]); + parse->state = PARSE_START_FOUND; + break; + } else { + gst_adapter_flush (parse->adapter, 1); + flushed++; + parse->state = PARSE_NEED_START; + } + } + + if (G_UNLIKELY (flushed)) { + GST_LOG_OBJECT (parse, "flushed %u bytes while aligning", flushed); + } +} + static GstFlowReturn gst_mpeg4vparse_drain (GstMpeg4VParse * parse) { GstFlowReturn ret = GST_FLOW_OK; const guint8 *data = NULL; - guint i = 0, available = 0; + guint available = 0; - while (gst_adapter_available (parse->adapter) >= 4) { - /* If we have enough data, ensure we're aligned to a start code */ - data = gst_adapter_peek (parse->adapter, 4); - if (data[0] == 0 && data[1] == 0 && data[2] == 1) { - GST_LOG_OBJECT (parse, "found start code with type %02X", data[3]); - parse->found_start = TRUE; - break; - } else { - GST_LOG_OBJECT (parse, "flushing 1 byte"); - gst_adapter_flush (parse->adapter, 1); - } - } - - if (G_UNLIKELY (!parse->found_start)) { - GST_DEBUG_OBJECT (parse, "start code not found, need more data"); - goto beach; - } - - if (G_UNLIKELY (gst_adapter_available (parse->adapter) < 8)) { - GST_DEBUG_OBJECT (parse, "start code found, need more data to find next"); - goto beach; - } - - /* Found a start code, search for the next one */ available = gst_adapter_available (parse->adapter); data = gst_adapter_peek (parse->adapter, available); - for (i = 4; i < available - 4; i++) { - /* We generate packets based on VOP start code */ - if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 && - data[i + 3] == 0xB6) { - GstBuffer *out_buf = gst_adapter_take_buffer (parse->adapter, i); - GST_LOG_OBJECT (parse, "found next start code at %u", i); - if (out_buf) { - gst_buffer_set_caps (out_buf, GST_PAD_CAPS (parse->srcpad)); - gst_pad_push (parse->srcpad, out_buf); + while (parse->offset < available - 4) { + /* We generate packets based on VOP end code (next start code) */ + if (data[parse->offset] == 0 && data[parse->offset + 1] == 0 && + data[parse->offset + 2] == 1) { + switch (parse->state) { + case PARSE_START_FOUND: + if (data[parse->offset + 3] == 0xB6) { + GST_LOG_OBJECT (parse, "found VOP start marker at %u", + parse->offset); + parse->state = PARSE_VOP_FOUND; + } + /* Jump over it */ + parse->offset += 4; + break; + case PARSE_VOP_FOUND: + { /* We were in a VOP already, any start code marks the end of it */ + GstBuffer *out_buf = gst_adapter_take_buffer (parse->adapter, + parse->offset); + + GST_LOG_OBJECT (parse, "found VOP end marker at %u", parse->offset); + if (out_buf) { + gst_buffer_set_caps (out_buf, GST_PAD_CAPS (parse->srcpad)); + gst_pad_push (parse->srcpad, out_buf); + } + /* Restart now that we flushed data */ + parse->offset = 0; + parse->state = PARSE_START_FOUND; + available = gst_adapter_available (parse->adapter); + data = gst_adapter_peek (parse->adapter, available); + break; + } + default: + GST_WARNING_OBJECT (parse, "unexpected parse state (%d)", + parse->state); + ret = GST_FLOW_UNEXPECTED; + goto beach; } - parse->found_start = FALSE; + } else { /* Continue searching */ + parse->offset++; } } @@ -116,8 +146,27 @@ gst_mpeg4vparse_chain (GstPad * pad, GstBuffer * buffer) gst_adapter_push (parse->adapter, buffer); + /* We need to get aligned on a start code */ + if (G_UNLIKELY (parse->state == PARSE_NEED_START)) { + gst_mpeg4vparse_align (parse); + /* No start code found in that buffer */ + if (G_UNLIKELY (parse->state == PARSE_NEED_START)) { + GST_DEBUG_OBJECT (parse, "start code not found, need more data"); + goto beach; + } + } + + /* We need at least 8 bytes to find the next start code which marks the end + of the one we just found */ + if (G_UNLIKELY (gst_adapter_available (parse->adapter) < 8)) { + GST_DEBUG_OBJECT (parse, "start code found, need more data to find next"); + goto beach; + } + + /* Drain the accumulated blocks frame per frame */ ret = gst_mpeg4vparse_drain (parse); +beach: gst_object_unref (parse); return ret; @@ -176,7 +225,8 @@ gst_mpeg4vparse_cleanup (GstMpeg4VParse * parse) gst_adapter_clear (parse->adapter); } - parse->found_start = FALSE; + parse->state = PARSE_NEED_START; + parse->offset = 0; } static GstStateChangeReturn diff --git a/gst/mpeg4videoparse/mpeg4videoparse.h b/gst/mpeg4videoparse/mpeg4videoparse.h index 0fb434c34f..94438ad2be 100644 --- a/gst/mpeg4videoparse/mpeg4videoparse.h +++ b/gst/mpeg4videoparse/mpeg4videoparse.h @@ -40,6 +40,12 @@ G_BEGIN_DECLS typedef struct _GstMpeg4VParse GstMpeg4VParse; typedef struct _GstMpeg4VParseClass GstMpeg4VParseClass; +typedef enum { + PARSE_NEED_START, + PARSE_START_FOUND, + PARSE_VOP_FOUND +} GstMpeg4VParseState; + struct _GstMpeg4VParse { GstElement element; @@ -47,8 +53,9 @@ struct _GstMpeg4VParse { GstPad * srcpad; GstAdapter * adapter; + guint offset; - gboolean found_start; + GstMpeg4VParseState state; }; struct _GstMpeg4VParseClass {