summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/vorbis/gstvorbisdec.h4
-rw-r--r--ext/vorbis/vorbisdec.c371
2 files changed, 102 insertions, 273 deletions
diff --git a/ext/vorbis/gstvorbisdec.h b/ext/vorbis/gstvorbisdec.h
index 87cabda99..693ead85b 100644
--- a/ext/vorbis/gstvorbisdec.h
+++ b/ext/vorbis/gstvorbisdec.h
@@ -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;
diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c
index 55742290e..ce4bb934f 100644
--- a/ext/vorbis/vorbisdec.c
+++ b/ext/vorbis/vorbisdec.c
@@ -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)))) {
+ GST_LOG_OBJECT (dec, "clipped buffer");
+ return GST_FLOW_OK;
+ }
- /* clip */
- if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate,
- dec->vi.channels * sizeof (float))))
- 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;
+ }
- if (dec->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);
- }
+ 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 (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 (duration == -1)
+ duration = sample_count * GST_SECOND / vd->vi.rate;
- 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;
}
@@ -1206,7 +1075,7 @@ empty_header:
}
}
-/*
+/*
* Input:
* Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS
* Discont flag: D D D D
@@ -1217,55 +1086,55 @@ empty_header:
* buffer. This means that to decode buffer 7, we need buffer 6, which
* arrives out of order.
*
- * we first gather buffers in the gather queue until we get a DISCONT. We
+ * we first gather buffers in the gather queue until we get a DISCONT. We
* prepend each incomming buffer so that they are in reversed order.
- *
+ *
* gather queue: 9 8 7
- * decode queue:
- * output queue:
+ * decode queue:
+ * output queue:
*
- * When a DISCONT is received (buffer 4), we move the gather queue to the
+ * When a DISCONT is received (buffer 4), we move the gather queue to the
* decode queue. This is simply done be taking the head of the gather queue
* and prepending it to the decode queue. This yields:
- *
- * gather queue:
+ *
+ * gather queue:
* decode queue: 7 8 9
- * output queue:
+ * output queue:
*
* Then we decode each buffer in the decode queue in order and put the output
* buffer in the output queue. The first buffer (7) will not produce any output
* because it needs the previous buffer (6) which did not arrive yet. This
* yields:
*
- * gather queue:
+ * gather queue:
* decode queue: 7 8 9
* output queue: 9 8
*
* Then we remove the consumed buffers from the decode queue. Buffer 7 is not
- * completely consumed, we need to keep it around for when we receive buffer
+ * completely consumed, we need to keep it around for when we receive buffer
* 6. This yields:
*
- * gather queue:
- * decode queue: 7
+ * gather queue:
+ * decode queue: 7
* output queue: 9 8
*
* Then we accumulate more buffers:
*
* gather queue: 6 5 4
* decode queue: 7
- * output queue:
+ * output queue:
*
* prepending to the decode queue on DISCONT yields:
*
- * gather queue:
+ * gather queue:
* decode queue: 4 5 6 7
- * output queue:
+ * output queue:
*
* after decoding and keeping buffer 4:
*
- * gather queue:
- * decode queue: 4
- * output queue: 7 6 5
+ * gather queue:
+ * decode queue: 4
+ * output queue: 7 6 5
*
* Etc..
*/
@@ -1302,55 +1171,17 @@ 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;
-
- buf = GST_BUFFER_CAST (dec->queued->data);
-
- sample_count =
- GST_BUFFER_SIZE (buf) / (dec->vi.channels * sizeof (float));
-
- 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;
- }
+ while (dec->queued) {
+ GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
+ GstClockTime timestamp, duration;
- 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");
+ timestamp = GST_BUFFER_TIMESTAMP (buf);
+ duration = GST_BUFFER_DURATION (buf);
+
+ vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration);
+ res = vorbis_dec_push_forward (dec, buf);
+
+ dec->queued = g_list_delete_link (dec->queued, dec->queued);
}
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