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.
This commit is contained in:
David Schleef 2009-11-24 21:22:03 -08:00
parent 1273909419
commit 78aad52cbf
4 changed files with 114 additions and 31 deletions

View file

@ -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 =

View file

@ -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

View file

@ -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,

View file

@ -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