vorbisdec: adapt to new oggdemux

Remove all granulepos hacks and simply use the timestamps from the new oggdemux
like any other decoder.
This commit is contained in:
Wim Taymans 2009-12-09 19:03:16 +01:00
parent 79b25e634c
commit 43bdb13ec2
2 changed files with 103 additions and 274 deletions

View file

@ -57,7 +57,6 @@ struct _GstVorbisDec {
vorbis_info vi;
vorbis_comment vc;
vorbis_block vb;
guint64 granulepos;
gboolean initialized;
@ -73,8 +72,7 @@ struct _GstVorbisDec {
gboolean discont;
guint32 seqnum;
GstClockTime cur_timestamp; /* only used with non-ogg container formats */
GstClockTime prev_timestamp; /* only used with non-ogg container formats */
GstClockTime last_timestamp;
GList *pendingevents;
GstTagList *taglist;

View file

@ -184,9 +184,7 @@ vorbis_dec_finalize (GObject * object)
static void
gst_vorbis_dec_reset (GstVorbisDec * dec)
{
dec->cur_timestamp = GST_CLOCK_TIME_NONE;
dec->prev_timestamp = GST_CLOCK_TIME_NONE;
dec->granulepos = -1;
dec->last_timestamp = GST_CLOCK_TIME_NONE;
dec->discont = TRUE;
dec->seqnum = gst_util_seqnum_next ();
gst_segment_init (&dec->segment, GST_FORMAT_TIME);
@ -307,23 +305,14 @@ vorbis_dec_src_query (GstPad * pad, GstQuery * query)
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
gint64 granulepos, value;
GstFormat my_format, format;
gint64 value;
GstFormat format;
gint64 time;
/* we start from the last seen granulepos */
granulepos = dec->granulepos;
gst_query_parse_position (query, &format, NULL);
/* and convert to the final format in two steps with time as the
* intermediate step */
my_format = GST_FORMAT_TIME;
if (!(res =
vorbis_dec_convert (pad, GST_FORMAT_DEFAULT, granulepos,
&my_format, &time)))
goto error;
/* we start from the last seen time */
time = dec->last_timestamp;
/* correct for the segment values */
time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time);
@ -331,7 +320,8 @@ vorbis_dec_src_query (GstPad * pad, GstQuery * query)
"query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time));
/* and convert to the final format */
if (!(res = vorbis_dec_convert (pad, my_format, time, &format, &value)))
if (!(res =
vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value)))
goto error;
gst_query_set_position (query, format, value);
@ -443,12 +433,7 @@ vorbis_dec_src_event (GstPad * pad, GstEvent * event)
seqnum = gst_event_get_seqnum (event);
gst_event_unref (event);
/* we have to ask our peer to seek to time here as we know
* nothing about how to generate a granulepos from the src
* formats or anything.
*
* First bring the requested format to time
*/
/* First bring the requested format to time */
tformat = GST_FORMAT_TIME;
if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur)))
goto convert_error;
@ -873,93 +858,26 @@ static GstFlowReturn
vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf)
{
GstFlowReturn result;
gint64 outoffset, origoffset;
origoffset = GST_BUFFER_OFFSET (buf);
again:
outoffset = origoffset;
if (outoffset == -1) {
dec->queued = g_list_append (dec->queued, buf);
GST_DEBUG_OBJECT (dec, "queued buffer");
result = GST_FLOW_OK;
} else {
if (G_UNLIKELY (dec->queued)) {
guint size;
GstClockTime ts;
GList *walk;
GST_DEBUG_OBJECT (dec, "first buffer with offset %" G_GINT64_FORMAT,
outoffset);
ts = gst_util_uint64_scale_int (outoffset, GST_SECOND, dec->vi.rate);
size = g_list_length (dec->queued);
/* we walk the queued up list in reverse, and set the buffer fields
* calculating backwards */
for (walk = g_list_last (dec->queued); walk;
walk = g_list_previous (walk)) {
GstBuffer *buffer = GST_BUFFER (walk->data);
guint offset;
offset = GST_BUFFER_SIZE (buffer) / (sizeof (float) * dec->vi.channels);
if (outoffset >= offset)
outoffset -= offset;
else {
/* we can't go below 0, this means this first offset was at the eos
* page and we need to clip to it instead */
GST_DEBUG_OBJECT (dec, "clipping %" G_GINT64_FORMAT,
offset - outoffset);
origoffset += (offset - outoffset);
goto again;
}
GST_BUFFER_OFFSET (buffer) = outoffset;
GST_BUFFER_TIMESTAMP (buffer) =
gst_util_uint64_scale_int (outoffset, GST_SECOND, dec->vi.rate);
GST_BUFFER_DURATION (buffer) = GST_CLOCK_DIFF (GST_BUFFER_TIMESTAMP
(buffer), ts);
ts = GST_BUFFER_TIMESTAMP (buffer);
GST_DEBUG_OBJECT (dec, "patch buffer %u, offset %" G_GUINT64_FORMAT
", timestamp %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
size, outoffset,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
size--;
}
for (walk = dec->queued; walk; walk = g_list_next (walk)) {
GstBuffer *buffer = GST_BUFFER (walk->data);
/* clips to the configured segment, or returns NULL with buffer
* unreffed when the input buffer is completely outside the segment */
if (!(buffer = gst_audio_buffer_clip (buffer, &dec->segment,
dec->vi.rate, dec->vi.channels * sizeof (float))))
continue;
if (dec->discont) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
dec->discont = FALSE;
}
/* ignore the result */
gst_pad_push (dec->srcpad, buffer);
}
g_list_free (dec->queued);
dec->queued = NULL;
}
/* clip */
if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate,
dec->vi.channels * sizeof (float))))
dec->vi.channels * sizeof (float)))) {
GST_LOG_OBJECT (dec, "clipped buffer");
return GST_FLOW_OK;
}
if (dec->discont) {
GST_LOG_OBJECT (dec, "setting DISCONT");
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
dec->discont = FALSE;
}
GST_DEBUG_OBJECT (dec,
"pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
result = gst_pad_push (dec->srcpad, buf);
}
return result;
}
@ -974,27 +892,41 @@ vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf)
return result;
}
static void
vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse,
GstClockTime timestamp, GstClockTime duration)
{
/* interpolate reverse */
if (vd->last_timestamp != -1 && reverse)
vd->last_timestamp -= duration;
/* take buffer timestamp, use interpolated timestamp otherwise */
if (timestamp != -1)
vd->last_timestamp = timestamp;
else
timestamp = vd->last_timestamp;
/* interpolate forwards */
if (vd->last_timestamp != -1 && !reverse)
vd->last_timestamp += duration;
GST_BUFFER_TIMESTAMP (buf) = timestamp;
GST_BUFFER_DURATION (buf) = duration;
}
static GstFlowReturn
vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet,
GstClockTime timestamp, GstClockTime duration)
{
float **pcm;
guint sample_count;
GstBuffer *out;
GstFlowReturn result;
GstClockTime timestamp = GST_CLOCK_TIME_NONE, nextts;
gint size;
if (!vd->initialized)
if (G_UNLIKELY (!vd->initialized))
goto not_initialized;
/* FIXME, we should queue undecoded packets here until we get
* a timestamp, then we reverse timestamp the queued packets and
* clip them, then we decode only the ones we want and don't
* keep decoded data in memory.
* Ideally, of course, the demuxer gives us a valid timestamp on
* the first packet.
*/
/* normal data packet */
/* FIXME, we can skip decoding if the packet is outside of the
* segment, this is however not very trivial as we need a previous
@ -1014,8 +946,9 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0)
goto done;
GST_LOG_OBJECT (vd, "%d samples ready for reading", sample_count);
size = sample_count * vd->vi.channels * sizeof (float);
GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count,
size);
/* alloc buffer for it */
result =
@ -1032,50 +965,14 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
copy_samples ((float *) GST_BUFFER_DATA (out), pcm, sample_count,
vd->vi.channels);
GST_LOG_OBJECT (vd, "setting output size to %d", size);
GST_BUFFER_SIZE (out) = size;
/* this should not overflow */
GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate;
if (duration == -1)
duration = sample_count * GST_SECOND / vd->vi.rate;
if (packet->granulepos != -1)
vd->granulepos = packet->granulepos - sample_count;
if (vd->cur_timestamp != GST_CLOCK_TIME_NONE) {
/* we have incoming timestamps */
timestamp = vd->cur_timestamp;
GST_DEBUG_OBJECT (vd,
"cur_timestamp: %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT " = %"
GST_TIME_FORMAT, GST_TIME_ARGS (vd->cur_timestamp),
GST_TIME_ARGS (GST_BUFFER_DURATION (out)),
GST_TIME_ARGS (vd->cur_timestamp + GST_BUFFER_DURATION (out)));
vd->cur_timestamp += GST_BUFFER_DURATION (out);
GST_BUFFER_OFFSET (out) = GST_CLOCK_TIME_TO_FRAMES (timestamp, vd->vi.rate);
GST_BUFFER_OFFSET_END (out) = GST_BUFFER_OFFSET (out) + sample_count;
} else {
/* we have incoming granulepos */
GST_BUFFER_OFFSET (out) = vd->granulepos;
if (vd->granulepos != -1) {
GST_DEBUG_OBJECT (vd, "granulepos: %" G_GINT64_FORMAT, vd->granulepos);
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
timestamp =
gst_util_uint64_scale_int (vd->granulepos, GST_SECOND, vd->vi.rate);
nextts =
gst_util_uint64_scale_int (vd->granulepos + sample_count,
GST_SECOND, vd->vi.rate);
GST_DEBUG_OBJECT (vd, "corresponding timestamp %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
/* calculate a nano-second accurate duration */
GST_BUFFER_DURATION (out) = GST_CLOCK_DIFF (timestamp, nextts);
GST_DEBUG_OBJECT (vd, "set duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_DURATION (out)));
} else {
timestamp = -1;
}
}
GST_BUFFER_TIMESTAMP (out) = timestamp;
if (vd->granulepos != -1)
vd->granulepos += sample_count;
vorbis_do_timestamps (vd, out, FALSE, timestamp, duration);
if (vd->segment.rate >= 0.0)
result = vorbis_dec_push_forward (vd, out);
@ -1085,14 +982,6 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
done:
vorbis_synthesis_read (&vd->vd, sample_count);
GST_DEBUG_OBJECT (vd,
"decoded %ld bytes into %d samples, ts %" GST_TIME_FORMAT, packet->bytes,
sample_count, GST_TIME_ARGS (timestamp));
/* granulepos is the last sample in the packet */
if (packet->granulepos != -1)
vd->granulepos = packet->granulepos;
return result;
/* ERRORS */
@ -1128,38 +1017,18 @@ vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer)
{
ogg_packet packet;
GstFlowReturn result = GST_FLOW_OK;
GstClockTime timestamp;
guint64 offset_end;
timestamp = GST_BUFFER_TIMESTAMP (buffer);
offset_end = GST_BUFFER_OFFSET_END (buffer);
/* only ogg has granulepos, demuxers of other container formats
* might provide us with timestamps instead (e.g. matroskademux) */
if (offset_end == GST_BUFFER_OFFSET_NONE && timestamp != GST_CLOCK_TIME_NONE) {
/* we might get multiple consecutive buffers with the same timestamp */
if (timestamp != vd->prev_timestamp) {
vd->cur_timestamp = timestamp;
vd->prev_timestamp = timestamp;
}
} else {
vd->cur_timestamp = GST_CLOCK_TIME_NONE;
vd->prev_timestamp = GST_CLOCK_TIME_NONE;
}
/* make ogg_packet out of the buffer */
packet.packet = GST_BUFFER_DATA (buffer);
packet.bytes = GST_BUFFER_SIZE (buffer);
packet.granulepos = offset_end;
packet.granulepos = -1;
packet.packetno = 0; /* we don't care */
/*
* FIXME. Is there anyway to know that this is the last packet and
* set e_o_s??
* Yes there is, keep one packet at all times and only push out when
* you receive a new one. Implement this.
*/
/* EOS does not matter, it is used in vorbis to implement clipping the last
* block of samples based on the granulepos. We clip based on segments. */
packet.e_o_s = 0;
GST_LOG_OBJECT (vd, "decode buffer of size %ld", packet.bytes);
/* error out on empty header packets, but just skip empty data packets */
if (G_UNLIKELY (packet.bytes == 0)) {
if (vd->initialized)
@ -1168,9 +1037,6 @@ vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer)
goto empty_header;
}
GST_DEBUG_OBJECT (vd, "vorbis granule: %" G_GINT64_FORMAT,
(gint64) packet.granulepos);
/* switch depending on packet type */
if (packet.packet[0] & 1) {
if (vd->initialized) {
@ -1179,7 +1045,12 @@ vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer)
}
result = vorbis_handle_header_packet (vd, &packet);
} else {
result = vorbis_handle_data_packet (vd, &packet);
GstClockTime timestamp, duration;
timestamp = GST_BUFFER_TIMESTAMP (buffer);
duration = GST_BUFFER_DURATION (buffer);
result = vorbis_handle_data_packet (vd, &packet, timestamp, duration);
}
done:
@ -1190,8 +1061,6 @@ empty_buffer:
/* don't error out here, just ignore the buffer, it's invalid for vorbis
* but not fatal. */
GST_WARNING_OBJECT (vd, "empty buffer received, ignoring");
if (packet.granulepos != -1)
vd->granulepos = packet.granulepos;
result = GST_FLOW_OK;
goto done;
}
@ -1302,56 +1171,18 @@ vorbis_dec_flush_decode (GstVorbisDec * dec)
}
walk = next;
}
if (dec->granulepos != -1) {
GstClockTime endts;
endts =
gst_util_uint64_scale_int (dec->granulepos, GST_SECOND, dec->vi.rate);
GST_DEBUG_OBJECT (dec, "we have granulepos %" G_GUINT64_FORMAT ", ts %"
GST_TIME_FORMAT, dec->granulepos, GST_TIME_ARGS (endts));
while (dec->queued) {
GstBuffer *buf;
guint sample_count;
GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
GstClockTime timestamp, duration;
buf = GST_BUFFER_CAST (dec->queued->data);
timestamp = GST_BUFFER_TIMESTAMP (buf);
duration = GST_BUFFER_DURATION (buf);
sample_count =
GST_BUFFER_SIZE (buf) / (dec->vi.channels * sizeof (float));
vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration);
res = vorbis_dec_push_forward (dec, buf);
GST_BUFFER_OFFSET_END (buf) = dec->granulepos;
endts =
gst_util_uint64_scale_int (dec->granulepos, GST_SECOND, dec->vi.rate);
dec->granulepos -= sample_count;
GST_BUFFER_OFFSET (buf) = dec->granulepos;
GST_BUFFER_TIMESTAMP (buf) =
gst_util_uint64_scale_int (dec->granulepos, GST_SECOND, dec->vi.rate);
GST_BUFFER_DURATION (buf) = endts - GST_BUFFER_TIMESTAMP (buf);
/* clip, this will unref the buffer in case of clipping */
if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate,
dec->vi.channels * sizeof (float)))) {
GST_DEBUG_OBJECT (dec, "clipped buffer %p", buf);
goto next;
}
if (dec->discont) {
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
dec->discont = FALSE;
}
GST_DEBUG_OBJECT (dec, "pushing buffer %p, samples %u, "
"ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
buf, sample_count, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
res = gst_pad_push (dec->srcpad, buf);
next:
dec->queued = g_list_delete_link (dec->queued, dec->queued);
}
} else {
GST_DEBUG_OBJECT (dec, "we don't have a granulepos yet, delayed push");
}
return res;
}
@ -1376,8 +1207,11 @@ vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf)
result = vorbis_dec_flush_decode (vd);
}
GST_DEBUG_OBJECT (vd, "gathering buffer %p, size %u", buf,
GST_BUFFER_SIZE (buf));
GST_DEBUG_OBJECT (vd, "gathering buffer %p of size %u, time %" GST_TIME_FORMAT
", dur %" GST_TIME_FORMAT, buf, GST_BUFFER_SIZE (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
/* add buffer to gather queue */
vd->gather = g_list_prepend (vd->gather, buf);
@ -1395,7 +1229,6 @@ vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont,
gst_buffer_unref (buffer);
return result;
}
static GstFlowReturn
@ -1412,9 +1245,7 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
/* resync on DISCONT */
if (G_UNLIKELY (discont)) {
GST_DEBUG_OBJECT (vd, "received DISCONT buffer");
vd->granulepos = -1;
vd->cur_timestamp = GST_CLOCK_TIME_NONE;
vd->prev_timestamp = GST_CLOCK_TIME_NONE;
vd->last_timestamp = GST_CLOCK_TIME_NONE;
#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
vorbis_synthesis_restart (&vd->vd);
#endif