mpeg4videoparse: handle more formats

We only need a Video Object Start code before we can start pushing out data.
Search for this code also instead of only looking for VOS and VOP.
Fixes #572551.
This commit is contained in:
Wim Taymans 2009-02-26 15:40:26 +01:00
parent 93da31ef92
commit 41b65b421b
2 changed files with 164 additions and 106 deletions

View file

@ -70,7 +70,6 @@ gst_mpeg4vparse_set_new_caps (GstMpeg4VParse * parse,
gint aspect_ratio_width, gint aspect_ratio_height, gint width, gint height) gint aspect_ratio_width, gint aspect_ratio_height, gint width, gint height)
{ {
gboolean res; gboolean res;
GstCaps *out_caps = gst_caps_new_simple ("video/mpeg", GstCaps *out_caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, 4, "mpegversion", G_TYPE_INT, 4,
"systemstream", G_TYPE_BOOLEAN, FALSE, "systemstream", G_TYPE_BOOLEAN, FALSE,
@ -146,7 +145,6 @@ typedef struct
static gboolean static gboolean
get_bits (bitstream_t * b, int num, guint32 * bits) get_bits (bitstream_t * b, int num, guint32 * bits)
{ {
*bits = 0; *bits = 0;
if (b->offset + ((b->b_offset + num) / 8) > b->size) if (b->offset + ((b->b_offset + num) / 8) > b->size)
@ -216,90 +214,16 @@ static gint aspect_ratio_table[6][2] = { {-1, -1}, {1, 1}, {12, 11},
{10, 11}, {16, 11}, {40, 33} {10, 11}, {16, 11}, {40, 33}
}; };
/* Returns whether we successfully set the caps downstream if needed */
static gboolean static gboolean
gst_mpeg4vparse_handle_vos (GstMpeg4VParse * parse, const guint8 * data, gst_mpeg4vparse_handle_vo (GstMpeg4VParse * parse, const guint8 * data,
gsize size) gsize size)
{ {
/* Skip the startcode */
guint32 bits; guint32 bits;
guint16 time_increment_resolution = 0;
guint16 fixed_time_increment = 0;
gint aspect_ratio_width = -1, aspect_ratio_height = -1;
gint height = -1, width = -1;
guint8 profile;
gboolean equal;
bitstream_t bs = { data, 0, 0, size }; bitstream_t bs = { data, 0, 0, size };
guint16 time_increment_resolution = 0;
/* Parse the config from the VOS frame */ guint16 fixed_time_increment = 0;
bs.offset = 5; gint aspect_ratio_width = -1, aspect_ratio_height = -1;
gint height = -1, width = -1;
profile = data[4];
/* invalid profile, yikes */
if (profile == 0)
return FALSE;
equal = FALSE;
if (G_LIKELY (parse->config &&
memcmp (GST_BUFFER_DATA (parse->config), data, size) == 0))
equal = TRUE;
if (G_LIKELY (parse->profile == profile && equal)) {
/* We know this profile and config data, so we can just keep the same caps
*/
return TRUE;
}
/* Even if we fail to parse, then some other element might succeed, so always
* put the VOS in the config */
parse->profile = profile;
if (parse->config != NULL)
gst_buffer_unref (parse->config);
parse->config = gst_buffer_new_and_alloc (size);
memcpy (GST_BUFFER_DATA (parse->config), data, size);
/* Expect Visual Object startcode */
GET_BITS (&bs, 32, &bits);
if (bits != VISUAL_OBJECT_STARTCODE_MARKER)
goto failed;
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* Skip visual_object_verid and priority */
GET_BITS (&bs, 7, &bits);
}
GET_BITS (&bs, 4, &bits);
/* Only support video ID */
if (bits != 0x1)
goto failed;
/* video signal type */
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* video signal type, ignore format and range */
GET_BITS (&bs, 4, &bits);
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* ignore color description */
GET_BITS (&bs, 24, &bits);
}
}
if (!next_start_code (&bs))
goto failed;
/* expecting a video object startcode */ /* expecting a video object startcode */
GET_BITS (&bs, 32, &bits); GET_BITS (&bs, 32, &bits);
@ -386,20 +310,120 @@ gst_mpeg4vparse_handle_vos (GstMpeg4VParse * parse, const guint8 * data,
height = bits; height = bits;
MARKER_BIT (&bs); MARKER_BIT (&bs);
/* ok we know there is enough data in the stream to decode it and we can start
* pushing the data */
parse->have_config = TRUE;
out: out:
return gst_mpeg4vparse_set_new_caps (parse, time_increment_resolution, return gst_mpeg4vparse_set_new_caps (parse, time_increment_resolution,
fixed_time_increment, aspect_ratio_width, aspect_ratio_height, fixed_time_increment, aspect_ratio_width, aspect_ratio_height,
width, height); width, height);
/* ERRORS */
failed: failed:
GST_WARNING_OBJECT (parse, "Failed to parse config data"); {
goto out; GST_WARNING_OBJECT (parse, "Failed to parse config data");
goto out;
}
}
/* Returns whether we successfully set the caps downstream if needed */
static gboolean
gst_mpeg4vparse_handle_vos (GstMpeg4VParse * parse, const guint8 * data,
gsize size)
{
/* Skip the startcode */
guint32 bits;
guint8 profile;
gboolean equal;
bitstream_t bs = { data, 0, 0, size };
/* Parse the config from the VOS frame */
bs.offset = 5;
profile = data[4];
/* invalid profile, yikes */
if (profile == 0)
return FALSE;
equal = FALSE;
if (G_LIKELY (parse->config &&
memcmp (GST_BUFFER_DATA (parse->config), data, size) == 0))
equal = TRUE;
if (G_LIKELY (parse->profile == profile && equal)) {
/* We know this profile and config data, so we can just keep the same caps
*/
return TRUE;
}
/* Even if we fail to parse, then some other element might succeed, so always
* put the VOS in the config */
parse->profile = profile;
if (parse->config != NULL)
gst_buffer_unref (parse->config);
parse->config = gst_buffer_new_and_alloc (size);
memcpy (GST_BUFFER_DATA (parse->config), data, size);
parse->have_config = TRUE;
/* Expect Visual Object startcode */
GET_BITS (&bs, 32, &bits);
if (bits != VISUAL_OBJECT_STARTCODE_MARKER)
goto failed;
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* Skip visual_object_verid and priority */
GET_BITS (&bs, 7, &bits);
}
GET_BITS (&bs, 4, &bits);
/* Only support video ID */
if (bits != 0x1)
goto failed;
/* video signal type */
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* video signal type, ignore format and range */
GET_BITS (&bs, 4, &bits);
GET_BITS (&bs, 1, &bits);
if (bits == 0x1) {
/* ignore color description */
GET_BITS (&bs, 24, &bits);
}
}
if (!next_start_code (&bs))
goto failed;
data = &bs.data[bs.offset];
size -= bs.offset;
return gst_mpeg4vparse_handle_vo (parse, data, size);
out:
return gst_mpeg4vparse_set_new_caps (parse, 0, 0, -1, -1, -1, -1);
/* ERRORS */
failed:
{
GST_WARNING_OBJECT (parse, "Failed to parse config data");
goto out;
}
} }
static void static void
gst_mpeg4vparse_push (GstMpeg4VParse * parse, gsize size) gst_mpeg4vparse_push (GstMpeg4VParse * parse, gsize size)
{ {
if (G_UNLIKELY (parse->config == NULL && parse->drop)) { if (G_UNLIKELY (!parse->have_config && parse->drop)) {
GST_LOG_OBJECT (parse, "Dropping %d bytes", parse->offset); GST_LOG_OBJECT (parse, "Dropping %d bytes", parse->offset);
gst_adapter_flush (parse->adapter, size); gst_adapter_flush (parse->adapter, size);
} else { } else {
@ -428,9 +452,7 @@ static GstFlowReturn
gst_mpeg4vparse_drain (GstMpeg4VParse * parse, GstBuffer * last_buffer) gst_mpeg4vparse_drain (GstMpeg4VParse * parse, GstBuffer * last_buffer)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
const guint8 *data = NULL; const guint8 *data = NULL;
guint available = 0; guint available = 0;
available = gst_adapter_available (parse->adapter); available = gst_adapter_available (parse->adapter);
@ -449,26 +471,44 @@ gst_mpeg4vparse_drain (GstMpeg4VParse * parse, GstBuffer * last_buffer)
switch (parse->state) { switch (parse->state) {
case PARSE_NEED_START: case PARSE_NEED_START:
switch (data[parse->offset + 3]) { {
gboolean found = FALSE;
guint8 code;
code = data[parse->offset + 3];
switch (code) {
case VOP_STARTCODE: case VOP_STARTCODE:
case VOS_STARTCODE: case VOS_STARTCODE:
case GOP_STARTCODE: case GOP_STARTCODE:
/* valid starts of a frame */ found = TRUE;
parse->state = PARSE_START_FOUND;
if (parse->offset > 0) {
GST_LOG_OBJECT (parse, "Flushing %u bytes", parse->offset);
gst_adapter_flush (parse->adapter, parse->offset);
parse->offset = 0;
available = gst_adapter_available (parse->adapter);
data = gst_adapter_peek (parse->adapter, available);
}
break; break;
default: default:
parse->offset += 4; if (code <= 0x11f)
found = TRUE;
break;
} }
if (found) {
/* valid starts of a frame */
parse->state = PARSE_START_FOUND;
if (parse->offset > 0) {
GST_LOG_OBJECT (parse, "Flushing %u bytes", parse->offset);
gst_adapter_flush (parse->adapter, parse->offset);
parse->offset = 0;
available = gst_adapter_available (parse->adapter);
data = gst_adapter_peek (parse->adapter, available);
}
} else
parse->offset += 4;
break; break;
}
case PARSE_START_FOUND: case PARSE_START_FOUND:
switch (data[parse->offset + 3]) { {
guint8 code;
code = data[parse->offset + 3];
switch (code) {
case VOP_STARTCODE: case VOP_STARTCODE:
GST_LOG_OBJECT (parse, "found VOP start marker at %u", GST_LOG_OBJECT (parse, "found VOP start marker at %u",
parse->offset); parse->offset);
@ -484,10 +524,33 @@ gst_mpeg4vparse_drain (GstMpeg4VParse * parse, GstBuffer * last_buffer)
parse->vos_offset = parse->offset; parse->vos_offset = parse->offset;
parse->state = PARSE_VOS_FOUND; parse->state = PARSE_VOS_FOUND;
break; break;
default:
if (code <= 0x11f) {
GST_LOG_OBJECT (parse, "found VO start marker at %u",
parse->offset);
parse->vos_offset = parse->offset;
parse->state = PARSE_VO_FOUND;
}
break;
} }
/* Jump over it */ /* Jump over it */
parse->offset += 4; parse->offset += 4;
break; break;
}
case PARSE_VO_FOUND:
switch (data[parse->offset + 3]) {
case GOP_STARTCODE:
case VOP_STARTCODE:
/* end of VOS found, interpret the config data and restart the
* search for the VOP */
gst_mpeg4vparse_handle_vo (parse, data + parse->vos_offset,
parse->offset - parse->vos_offset);
parse->state = PARSE_START_FOUND;
break;
default:
parse->offset += 4;
}
break;
case PARSE_VOS_FOUND: case PARSE_VOS_FOUND:
switch (data[parse->offset + 3]) { switch (data[parse->offset + 3]) {
case GOP_STARTCODE: case GOP_STARTCODE:
@ -531,7 +594,6 @@ static GstFlowReturn
gst_mpeg4vparse_chain (GstPad * pad, GstBuffer * buffer) gst_mpeg4vparse_chain (GstPad * pad, GstBuffer * buffer)
{ {
GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad)); GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad));
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GST_DEBUG_OBJECT (parse, "received buffer of %u bytes with ts %" GST_DEBUG_OBJECT (parse, "received buffer of %u bytes with ts %"
@ -553,11 +615,8 @@ static gboolean
gst_mpeg4vparse_sink_setcaps (GstPad * pad, GstCaps * caps) gst_mpeg4vparse_sink_setcaps (GstPad * pad, GstCaps * caps)
{ {
gboolean res = TRUE; gboolean res = TRUE;
GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad)); GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad));
GstStructure *s; GstStructure *s;
const GValue *value; const GValue *value;
GST_DEBUG_OBJECT (parse, "setcaps called with %" GST_PTR_FORMAT, caps); GST_DEBUG_OBJECT (parse, "setcaps called with %" GST_PTR_FORMAT, caps);
@ -584,7 +643,6 @@ static gboolean
gst_mpeg4vparse_sink_event (GstPad * pad, GstEvent * event) gst_mpeg4vparse_sink_event (GstPad * pad, GstEvent * event)
{ {
gboolean res = TRUE; gboolean res = TRUE;
GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad)); GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad));
GST_DEBUG_OBJECT (parse, "handling event type %s", GST_DEBUG_OBJECT (parse, "handling event type %s",
@ -614,7 +672,6 @@ static gboolean
gst_mpeg4vparse_src_query (GstPad * pad, GstQuery * query) gst_mpeg4vparse_src_query (GstPad * pad, GstQuery * query)
{ {
GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad)); GstMpeg4VParse *parse = GST_MPEG4VIDEOPARSE (gst_pad_get_parent (pad));
gboolean res; gboolean res;
switch (GST_QUERY_TYPE (query)) { switch (GST_QUERY_TYPE (query)) {
@ -671,6 +728,7 @@ gst_mpeg4vparse_cleanup (GstMpeg4VParse * parse)
} }
parse->state = PARSE_NEED_START; parse->state = PARSE_NEED_START;
parse->have_config = FALSE;
parse->offset = 0; parse->offset = 0;
} }
@ -753,9 +811,7 @@ static void
gst_mpeg4vparse_class_init (GstMpeg4VParseClass * klass) gst_mpeg4vparse_class_init (GstMpeg4VParseClass * klass)
{ {
GObjectClass *gobject_class; GObjectClass *gobject_class;
GstElementClass *gstelement_class; GstElementClass *gstelement_class;
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gobject_class = G_OBJECT_CLASS (klass); gobject_class = G_OBJECT_CLASS (klass);

View file

@ -43,6 +43,7 @@ typedef struct _GstMpeg4VParseClass GstMpeg4VParseClass;
typedef enum { typedef enum {
PARSE_NEED_START, PARSE_NEED_START,
PARSE_START_FOUND, PARSE_START_FOUND,
PARSE_VO_FOUND,
PARSE_VOS_FOUND, PARSE_VOS_FOUND,
PARSE_VOP_FOUND PARSE_VOP_FOUND
} GstMpeg4VParseState; } GstMpeg4VParseState;
@ -62,6 +63,7 @@ struct _GstMpeg4VParse {
GstClockTime timestamp; GstClockTime timestamp;
GstBuffer *config; GstBuffer *config;
gboolean have_config;
guint8 profile; guint8 profile;
GstClockTime frame_duration; GstClockTime frame_duration;