From 78aad52cbff7af9ac94f01d9bbb27f60efc7fa5e Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 24 Nov 2009 21:22:03 -0800 Subject: [PATCH] oggdemux: Fix vorbis parsing Add a granule to granulepos conversion function. Fix the duration function for vorbis. Handle timestamps on header packets differently and be more careful about calculating OFFSET and OFFSET_END. After this change, timestamps for vorbis don't exactly match up with the timestamps that vorbisparse outputs, but it's unclear if vorbisparse is actually correct and it would add a lot more code to make oggdemux match vorbisparse. Fixes #602790. --- ext/ogg/gstoggdemux.c | 47 +++++++++++++++++---- ext/ogg/gstoggstream.c | 94 ++++++++++++++++++++++++++++++++---------- ext/ogg/gstoggstream.h | 2 + ext/ogg/vorbis_parse.c | 2 - 4 files changed, 114 insertions(+), 31 deletions(-) diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 69cb986b7c..c6ebe6d459 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -479,14 +479,34 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) duration = gst_ogg_stream_get_packet_duration (&pad->map, packet); - GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule); - pad->current_granule += duration; - GST_BUFFER_DURATION (buf) = gst_ogg_stream_granule_to_time (&pad->map, - pad->current_granule) - GST_BUFFER_TIMESTAMP (buf); + if (packet->b_o_s) { + GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_DURATION (buf) = GST_CLOCK_TIME_NONE; + GST_BUFFER_OFFSET (buf) = 0; + GST_BUFFER_OFFSET_END (buf) = -1; + } else { + GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_granule_to_time (&pad->map, + pad->current_granule); + GST_BUFFER_DURATION (buf) = gst_ogg_stream_granule_to_time (&pad->map, + pad->current_granule + duration) - GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_OFFSET (buf) = -1; - GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + pad->current_granule += duration; + if (packet->granulepos != -1) { + gint64 granule; + granule = gst_ogg_stream_granulepos_to_granule (&pad->map, + packet->granulepos); + if (granule != pad->current_granule) { + GST_WARNING ("calculated granule didn't match actual (%lld != %lld)", + pad->current_granule, granule); + } + pad->current_granule = granule; + } + GST_BUFFER_OFFSET_END (buf) = + gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, + pad->current_granule); + GST_BUFFER_OFFSET (buf) = + gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); + } /* Mark discont on the buffer */ if (pad->discont) { @@ -586,7 +606,11 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) pad->current_granule = granule; } - if (!gst_ogg_stream_packet_is_header (&pad->map, packet)) { + /* Overload the value of b_o_s in ogg_packet with a flag whether or + * not this is a header packet. Maybe some day this could be cleaned + * up. */ + packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet); + if (!packet->b_o_s) { if (pad->start_time == GST_CLOCK_TIME_NONE) { gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet); if (duration != -1) { @@ -602,6 +626,10 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) pad->start_time = gst_ogg_stream_granule_to_time (&pad->map, start_granule); + GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time); + } else { + packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map, + pad->map.accumulated_granule, pad->map.accumulated_granule); } } } else { @@ -848,6 +876,7 @@ gst_ogg_chain_mark_discont (GstOggChain * chain) GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); pad->discont = TRUE; + pad->map.last_size = 0; } } @@ -880,6 +909,7 @@ gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) GST_PAD_DIRECTION (ret) = GST_PAD_SRC; ret->discont = TRUE; + ret->map.last_size = 0; ret->chain = chain; ret->ogg = chain->ogg; @@ -1420,6 +1450,7 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, /* mark discont */ pad->discont = TRUE; + pad->map.last_size = 0; pad->last_ret = GST_FLOW_OK; pad->is_sparse = diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index 0511d849d9..98dec07e49 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -39,6 +39,8 @@ typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad, gint64 granulepos); typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad, gint64 granulepos); +typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad, + gint64 granule, gint64 keyframe_granule); /* returns TRUE if the granulepos denotes a key frame */ typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad, @@ -63,6 +65,7 @@ struct _GstOggMap const gchar *media_type; GstOggMapSetupFunc setup_func; GstOggMapToGranuleFunc granulepos_to_granule_func; + GstOggMapToGranuleposFunc granule_to_granulepos_func; GstOggMapIsKeyFrameFunc is_key_frame_func; GstOggMapIsHeaderPacketFunc is_header_func; GstOggMapPacketDurationFunc packet_duration_func; @@ -126,13 +129,30 @@ gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos) } if (mappers[pad->map].granulepos_to_granule_func == NULL) { - GST_WARNING ("Failed to convert granulepos to time"); - return GST_CLOCK_TIME_NONE; + GST_WARNING ("Failed to convert granulepos to granule"); + return -1; } return mappers[pad->map].granulepos_to_granule_func (pad, granulepos); } +gint64 +gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule, + gint64 keyframe_granule) +{ + if (granule == -1 || granule == 0) { + return granule; + } + + if (mappers[pad->map].granule_to_granulepos_func == NULL) { + GST_WARNING ("Failed to convert granule to granulepos"); + return -1; + } + + return mappers[pad->map].granule_to_granulepos_func (pad, granule, + keyframe_granule); +} + gboolean gst_ogg_stream_packet_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos) @@ -196,6 +216,20 @@ granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos) } } +static gint64 +granule_to_granulepos_default (GstOggStream * pad, gint64 granule, + gint64 keyframe_granule) +{ + gint64 keyoffset; + + if (pad->granuleshift != 0) { + keyoffset = granule - keyframe_granule; + return (keyframe_granule << pad->granuleshift) | keyoffset; + } else { + return granule; + } +} + static gboolean is_header_unknown (GstOggStream * pad, ogg_packet * packet) { @@ -357,6 +391,15 @@ granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp) return dt + 4; } +static gint64 +granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule, + gint64 keyframe_granule) +{ + /* This conversion requires knowing more details about the Dirac + * stream. */ + return -1; +} + /* vorbis */ @@ -373,6 +416,7 @@ setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet) pad->granulerate_n = GST_READ_UINT32_LE (data); pad->granulerate_d = 1; pad->granuleshift = 0; + pad->last_size = 0; GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n); pad->n_header_packets = 3; @@ -408,28 +452,20 @@ packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet) int size; int duration; - mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1); - size = pad->vorbis_mode_sizes[mode]; + if (packet->packet[0] & 1) + return 0; - if (size) { - switch ((packet->packet[0] >> (1 + pad->vorbis_log2_num_modes)) & 3) { - case 0: - case 3: - duration = pad->long_size / 2; - break; - case 1: - duration = (pad->long_size + pad->short_size) / 4; - break; - case 2: - duration = (3 * pad->long_size - pad->short_size) / 4; - break; - default: - duration = -1; - break; - } + mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1); + size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size; + + if (pad->last_size == 0) { + duration = 0; } else { - duration = pad->short_size / 2; + duration = pad->last_size / 4 + size / 4; } + pad->last_size = size; + + GST_DEBUG ("duration %d", (int) duration); return duration; } @@ -898,6 +934,7 @@ static const GstOggMap mappers[] = { "video/x-theora", setup_theora_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, is_keyframe_theora, is_header_theora, packet_duration_constant @@ -907,6 +944,7 @@ static const GstOggMap mappers[] = { "audio/x-vorbis", setup_vorbis_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, is_keyframe_true, is_header_vorbis, packet_duration_vorbis @@ -916,6 +954,7 @@ static const GstOggMap mappers[] = { "audio/x-speex", setup_speex_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, is_keyframe_true, is_header_count, packet_duration_constant @@ -926,6 +965,7 @@ static const GstOggMap mappers[] = { setup_pcm_mapper, NULL, NULL, + NULL, is_header_count, NULL }, @@ -935,6 +975,7 @@ static const GstOggMap mappers[] = { setup_cmml_mapper, NULL, NULL, + NULL, is_header_count, NULL }, @@ -943,6 +984,7 @@ static const GstOggMap mappers[] = { "application/x-annodex", setup_fishead_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, NULL, is_header_count, NULL @@ -953,6 +995,7 @@ static const GstOggMap mappers[] = { setup_fishead_mapper, NULL, NULL, + NULL, is_header_true, NULL }, @@ -961,6 +1004,7 @@ static const GstOggMap mappers[] = { "audio/x-flac", setup_fLaC_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, NULL, is_header_count, NULL @@ -970,6 +1014,7 @@ static const GstOggMap mappers[] = { "audio/x-flac", setup_flac_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, NULL, is_header_count, packet_duration_flac @@ -981,12 +1026,14 @@ static const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, }, { "CELT ", 8, 0, "audio/x-celt", setup_celt_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, NULL, is_header_count, packet_duration_constant @@ -997,6 +1044,7 @@ static const GstOggMap mappers[] = { setup_kate_mapper, NULL, NULL, + NULL, is_header_count, NULL }, @@ -1005,6 +1053,7 @@ static const GstOggMap mappers[] = { "video/x-dirac", setup_dirac_mapper, granulepos_to_granule_dirac, + granule_to_granulepos_dirac, is_keyframe_dirac, is_header_count, packet_duration_constant @@ -1014,6 +1063,7 @@ static const GstOggMap mappers[] = { "application/x-ogm-audio", setup_ogmaudio_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, is_keyframe_true, is_header_unknown, NULL @@ -1023,6 +1073,7 @@ static const GstOggMap mappers[] = { "application/x-ogm-video", setup_ogmvideo_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, NULL, is_header_unknown, NULL @@ -1032,6 +1083,7 @@ static const GstOggMap mappers[] = { "application/x-ogm-text", setup_ogmtext_mapper, granulepos_to_granule_default, + granule_to_granulepos_default, is_keyframe_true, is_header_unknown, NULL diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index 6f3310c52c..08ce1173e7 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -62,6 +62,7 @@ struct _GstOggStream int long_size; int vorbis_log2_num_modes; int vorbis_mode_sizes[256]; + int last_size; }; @@ -72,6 +73,7 @@ GstClockTime gst_ogg_stream_get_start_time_for_granulepos (GstOggStream *pad, gint64 granulepos); GstClockTime gst_ogg_stream_granule_to_time (GstOggStream *pad, gint64 granule); gint64 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos); +gint64 gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule, gint64 keyframe_granule); GstClockTime gst_ogg_stream_get_packet_start_time (GstOggStream *pad, ogg_packet *packet); gboolean gst_ogg_stream_granulepos_is_key_frame (GstOggStream *pad, diff --git a/ext/ogg/vorbis_parse.c b/ext/ogg/vorbis_parse.c index c1d4fe02df..f9ba746dd8 100644 --- a/ext/ogg/vorbis_parse.c +++ b/ext/ogg/vorbis_parse.c @@ -94,8 +94,6 @@ parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * packet) pad->short_size = short_size; pad->long_size = long_size; pad->nsn_increment = short_size >> 1; - - pad->accumulated_granule = -long_size / 2; } void