diff options
-rw-r--r-- | ChangeLog | 22 | ||||
-rw-r--r-- | gst/matroska/matroska-demux.c | 296 | ||||
-rw-r--r-- | gst/matroska/matroska-demux.h | 5 |
3 files changed, 264 insertions, 59 deletions
@@ -1,3 +1,25 @@ +2008-06-13 Sebastian Dröge <slomo@circular-chaos.org> + + * gst/matroska/matroska-demux.c: (gst_matroska_demux_reset), + (gst_matroska_demux_parse_tracks), + (gst_matroska_demux_parse_index), (gst_matroska_demux_parse_info), + (gst_matroska_demux_parse_attachments), + (gst_matroska_demux_parse_chapters), + (gst_matroska_demux_parse_contents_seekentry), + (gst_matroska_demux_loop_stream_parse_id): + * gst/matroska/matroska-demux.h: + Only parse Tracks, SeekHead and SegmentInfo elements once but allow + Tags multiple times. The first ones can appear more than once but must + contain the same content as the first for backup purposes so we ignore + all but the first one. Tags can appear multiple times with different + content. + + Jump to all elements except Clusters that are available from a + SeekHead to make it more likely to have all required informations + before getting to the first Clusters. + + Add dummy functions for parsing Attachments and Chapters. + 2008-06-13 Wim Taymans <wim.taymans@collabora.co.uk> * gst/udp/gstudpsrc.c: (gst_udpsrc_class_init), (gst_udpsrc_init), diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 1f1456fa..66a4938c 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -99,6 +99,9 @@ static GstStaticPadTemplate subtitle_src_templ = "application/x-subtitle-unknown") ); +static GstFlowReturn gst_matroska_demux_parse_contents (GstMatroskaDemux * + demux, gboolean * p_run_loop); + /* element functions */ static void gst_matroska_demux_loop (GstPad * pad); @@ -306,8 +309,9 @@ gst_matroska_demux_reset (GstElement * element) demux->time_scale = 1000000; demux->created = G_MININT64; - demux->metadata_parsed = FALSE; demux->index_parsed = FALSE; + demux->tracks_parsed = FALSE; + demux->segmentinfo_parsed = FALSE; gst_segment_init (&demux->segment, GST_FORMAT_TIME); } @@ -1807,6 +1811,8 @@ gst_matroska_demux_parse_tracks (GstMatroskaDemux * demux) } } + demux->tracks_parsed = TRUE; + return ret; } @@ -2036,6 +2042,8 @@ gst_matroska_demux_parse_index (GstMatroskaDemux * demux, gboolean prevent_eos) } } + demux->index_parsed = TRUE; + return ret; } @@ -2148,6 +2156,8 @@ gst_matroska_demux_parse_info (GstMatroskaDemux * demux) } } + demux->segmentinfo_parsed = TRUE; + return ret; } @@ -2388,6 +2398,107 @@ gst_matroska_demux_parse_metadata (GstMatroskaDemux * demux, return ret; } +static GstFlowReturn +gst_matroska_demux_parse_attachments (GstMatroskaDemux * demux, + gboolean prevent_eos) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + + guint64 length = 0; + + guint32 id; + + GstFlowReturn ret = GST_FLOW_OK; + + GST_WARNING_OBJECT (demux, "Parsing of attachments not implemented yet"); + + /* TODO: implement parsing of attachments */ + + if (prevent_eos) { + length = gst_ebml_read_get_length (ebml); + } + + while (ret == GST_FLOW_OK) { + /* We're an element that can be seeked to. If we are, then + * we want to prevent EOS, since that'll kill us. So we cache + * file size and seek until there, and don't call EOS upon os. */ + if (prevent_eos && length == ebml->offset) + break; + + if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK) + return ret; + + if (demux->level_up) { + demux->level_up--; + break; + } + + switch (id) { + default: + case GST_EBML_ID_VOID: + ret = gst_ebml_read_skip (ebml); + break; + } + + if (demux->level_up) { + demux->level_up--; + break; + } + } + + return ret; +} + +static GstFlowReturn +gst_matroska_demux_parse_chapters (GstMatroskaDemux * demux, + gboolean prevent_eos) +{ + GstEbmlRead *ebml = GST_EBML_READ (demux); + + guint64 length = 0; + + guint32 id; + + GstFlowReturn ret = GST_FLOW_OK; + + GST_WARNING_OBJECT (demux, "Parsing of chapters not implemented yet"); + + /* TODO: implement parsing of chapters */ + if (prevent_eos) { + length = gst_ebml_read_get_length (ebml); + } + + while (ret == GST_FLOW_OK) { + /* We're an element that can be seeked to. If we are, then + * we want to prevent EOS, since that'll kill us. So we cache + * file size and seek until there, and don't call EOS upon os. */ + if (prevent_eos && length == ebml->offset) + break; + + if ((ret = gst_ebml_peek_id (ebml, &demux->level_up, &id)) != GST_FLOW_OK) + return ret; + + if (demux->level_up) { + demux->level_up--; + break; + } + + switch (id) { + default: + case GST_EBML_ID_VOID: + ret = gst_ebml_read_skip (ebml); + break; + } + + if (demux->level_up) { + demux->level_up--; + break; + } + } + + return ret; +} + /* * Read signed/unsigned "EBML" numbers. * Return: number of bytes processed. @@ -3434,11 +3545,14 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, return GST_FLOW_OK; } - /* FIXME: Do this for the other elements except CLUSTERS too. - * We can't know that they will come before the CLUSTERS */ switch (seek_id) { case GST_MATROSKA_ID_CUES: case GST_MATROSKA_ID_TAGS: + case GST_MATROSKA_ID_TRACKS: + case GST_MATROSKA_ID_SEEKHEAD: + case GST_MATROSKA_ID_SEGMENTINFO: + case GST_MATROSKA_ID_ATTACHMENTS: + case GST_MATROSKA_ID_CHAPTERS: { guint level_up = demux->level_up; @@ -3485,29 +3599,93 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, /* read master + parse */ switch (id) { case GST_MATROSKA_ID_CUES: + if (!demux->index_parsed) { + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_index (demux, + TRUE)) != GST_FLOW_OK) + return ret; + } + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + break; + case GST_MATROSKA_ID_TAGS: if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) return ret; if ((ret = - gst_matroska_demux_parse_index (demux, TRUE)) != GST_FLOW_OK) + gst_matroska_demux_parse_metadata (demux, + TRUE)) != GST_FLOW_OK) return ret; - /* FIXME: why is this here? */ if (gst_ebml_read_get_length (ebml) == ebml->offset) *p_run_loop = FALSE; - else - demux->index_parsed = TRUE; break; - case GST_MATROSKA_ID_TAGS: + case GST_MATROSKA_ID_TRACKS: + if (!demux->tracks_parsed) { + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = gst_matroska_demux_parse_tracks (demux)) != GST_FLOW_OK) + return ret; + } + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + break; + + case GST_MATROSKA_ID_SEGMENTINFO: + if (!demux->segmentinfo_parsed) { + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = gst_matroska_demux_parse_info (demux)) != GST_FLOW_OK) + return ret; + } + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + break; + case GST_MATROSKA_ID_SEEKHEAD: + { + GList *l; + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) return ret; + + /* Prevent infinite recursion if there's a cycle from + * one seekhead to the same again. Simply break if + * we already had this seekhead, finish will clean up + * everything. */ + for (l = ebml->level; l; l = l->next) { + GstEbmlLevel *level = (GstEbmlLevel *) l->data; + + if (level->start == ebml->offset && l->next) + goto finish; + } + if ((ret = - gst_matroska_demux_parse_metadata (demux, + gst_matroska_demux_parse_contents (demux, + p_run_loop)) != GST_FLOW_OK) + return ret; + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + break; + } + case GST_MATROSKA_ID_ATTACHMENTS: + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_attachments (demux, + TRUE)) != GST_FLOW_OK) + return ret; + if (gst_ebml_read_get_length (ebml) == ebml->offset) + *p_run_loop = FALSE; + break; + case GST_MATROSKA_ID_CHAPTERS: + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_chapters (demux, TRUE)) != GST_FLOW_OK) return ret; - /* FIXME: why is this here? */ if (gst_ebml_read_get_length (ebml) == ebml->offset) *p_run_loop = FALSE; - else - demux->metadata_parsed = TRUE; break; } @@ -3596,32 +3774,41 @@ gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux, GstFlowReturn ret; switch (id) { - /* FIXME: not mandatory... will things break? - * Can only happen exactly once, ignore second - * occurences! */ - /* stream info */ + /* stream info + * Can exist more than once but following occurences + * must have the same content so ignore them */ case GST_MATROSKA_ID_SEGMENTINFO: - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) - return ret; - if ((ret = gst_matroska_demux_parse_info (demux)) != GST_FLOW_OK) - return ret; + if (!demux->segmentinfo_parsed) { + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = gst_matroska_demux_parse_info (demux)) != GST_FLOW_OK) + return ret; + } else { + if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK) + return ret; + } break; - /* FIXME: might happen more than once, second - * occurences must be exactly the same so drop them */ - /* track info headers */ + /* track info headers + * Can exist more than once but following occurences + * must have the same content so ignore them */ case GST_MATROSKA_ID_TRACKS: { - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) - return ret; - if ((ret = gst_matroska_demux_parse_tracks (demux)) != GST_FLOW_OK) - return ret; + if (!demux->tracks_parsed) { + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = gst_matroska_demux_parse_tracks (demux)) != GST_FLOW_OK) + return ret; + } else { + if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK) + return ret; + } break; } - /* FIXME: can only happen once or never but - * for the sake of sanity ignore second occurences */ - /* stream index */ + /* cues - seek table + * Either exists exactly one time or never but ignore + * following occurences for the sake of sanity */ case GST_MATROSKA_ID_CUES: { if (!demux->index_parsed) { @@ -3637,28 +3824,19 @@ gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux, break; } - /* FIXME: can be there more than once, why do we have - * ->metadata_parsed? */ - /* metadata */ + /* metadata + * can exist more than one time with different content */ case GST_MATROSKA_ID_TAGS: { - if (!demux->metadata_parsed) { - if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) - return ret; - if ((ret = - gst_matroska_demux_parse_metadata (demux, - FALSE)) != GST_FLOW_OK) - return ret; - } else { - if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK) - return ret; - } + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_metadata (demux, FALSE)) != GST_FLOW_OK) + return ret; break; } - /* FIXME: must not be there but can happen more than once with - * different content */ - /* file index (if seekable, seek to Cues/Tags to parse it) */ + /* file index (if seekable, seek to Cues/Tags/etc to parse it) */ case GST_MATROSKA_ID_SEEKHEAD: { if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) @@ -3670,7 +3848,7 @@ gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux, break; } - /* FIXME: must not be there */ + /* cluster - contains the payload */ case GST_MATROSKA_ID_CLUSTER: { if (demux->state != GST_MATROSKA_DEMUX_STATE_DATA) { @@ -3704,21 +3882,25 @@ gst_matroska_demux_loop_stream_parse_id (GstMatroskaDemux * demux, break; } - /* TODO: Implement parsing of attachments and push them - * through an attachment pad, one for each attachment */ - /* FIXME: must not be there but can only be once */ + /* attachments - contains files attached to the mkv container + * like album art, etc */ case GST_MATROSKA_ID_ATTACHMENTS:{ - GST_INFO ("Attachments elements are not supported yet"); - if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK) + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_attachments (demux, + FALSE)) != GST_FLOW_OK) return ret; break; } - /* TODO: Implement parsing of chapters */ - /* FIXME: Must not be there but can only be once */ + /* chapters - contains meta information about how to group + * the file into chapters, similar to DVD */ case GST_MATROSKA_ID_CHAPTERS:{ - GST_INFO ("Chapters elements are not supported yet"); - if ((ret = gst_ebml_read_skip (ebml)) != GST_FLOW_OK) + if ((ret = gst_ebml_read_master (ebml, &id)) != GST_FLOW_OK) + return ret; + if ((ret = + gst_matroska_demux_parse_chapters (demux, FALSE)) != GST_FLOW_OK) return ret; break; } diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h index 9da56603..413cdc8a 100644 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -77,9 +77,10 @@ typedef struct _GstMatroskaDemux { GstMatroskaDemuxState state; guint level_up; - /* did we parse metadata/cues already? */ - gboolean metadata_parsed; + /* did we parse cues/tracks/segmentinfo already? */ gboolean index_parsed; + gboolean tracks_parsed; + gboolean segmentinfo_parsed; /* start-of-segment */ guint64 ebml_segment_start; |