mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
ext/vorbis/vorbisdec.*: First stab at vorbis reverse playback.
Original commit message from CVS: * ext/vorbis/vorbisdec.c: (vorbis_dec_sink_event), (vorbis_dec_push_forward), (vorbis_dec_push_reverse), (vorbis_handle_data_packet), (vorbis_dec_decode_buffer), (vorbis_dec_flush_decode), (vorbis_dec_chain_reverse), (vorbis_dec_chain_forward), (vorbis_dec_chain): * ext/vorbis/vorbisdec.h: First stab at vorbis reverse playback.
This commit is contained in:
parent
0990cbf274
commit
cadc881165
3 changed files with 290 additions and 55 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2006-11-13 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* ext/vorbis/vorbisdec.c: (vorbis_dec_sink_event),
|
||||
(vorbis_dec_push_forward), (vorbis_dec_push_reverse),
|
||||
(vorbis_handle_data_packet), (vorbis_dec_decode_buffer),
|
||||
(vorbis_dec_flush_decode), (vorbis_dec_chain_reverse),
|
||||
(vorbis_dec_chain_forward), (vorbis_dec_chain):
|
||||
* ext/vorbis/vorbisdec.h:
|
||||
First stab at vorbis reverse playback.
|
||||
|
||||
2006-11-13 Wim Taymans <wim@fluendo.com>
|
||||
|
||||
* gst-libs/gst/audio/gstbaseaudiosink.c:
|
||||
|
|
|
@ -87,6 +87,8 @@ GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstElement, GST_TYPE_ELEMENT);
|
|||
static void vorbis_dec_finalize (GObject * object);
|
||||
static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event);
|
||||
static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer);
|
||||
static GstFlowReturn vorbis_dec_chain_forward (GstVorbisDec * vd,
|
||||
gboolean discont, GstBuffer * buffer);
|
||||
static GstStateChangeReturn vorbis_dec_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
|
@ -523,13 +525,10 @@ vorbis_dec_sink_event (GstPad * pad, GstEvent * event)
|
|||
gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
|
||||
&start, &stop, &time);
|
||||
|
||||
/* we need time and a positive rate for now */
|
||||
/* we need time for now */
|
||||
if (format != GST_FORMAT_TIME)
|
||||
goto newseg_wrong_format;
|
||||
|
||||
if (rate <= 0.0)
|
||||
goto newseg_wrong_rate;
|
||||
|
||||
GST_DEBUG_OBJECT (dec,
|
||||
"newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
|
||||
", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
|
||||
|
@ -565,11 +564,6 @@ newseg_wrong_format:
|
|||
GST_DEBUG_OBJECT (dec, "received non TIME newsegment");
|
||||
goto done;
|
||||
}
|
||||
newseg_wrong_rate:
|
||||
{
|
||||
GST_DEBUG_OBJECT (dec, "negative rates not supported yet");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
@ -862,7 +856,7 @@ clipped:
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_push (GstVorbisDec * dec, GstBuffer * buf)
|
||||
vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn result;
|
||||
gint64 outoffset = GST_BUFFER_OFFSET (buf);
|
||||
|
@ -926,6 +920,16 @@ vorbis_dec_push (GstVorbisDec * dec, GstBuffer * buf)
|
|||
return result;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn result = GST_FLOW_OK;
|
||||
|
||||
dec->queued = g_list_prepend (dec->queued, buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
|
||||
{
|
||||
|
@ -983,18 +987,12 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
|
|||
vd->vi.channels);
|
||||
|
||||
GST_BUFFER_SIZE (out) = size;
|
||||
GST_BUFFER_OFFSET (out) = vd->granulepos;
|
||||
if (vd->granulepos != -1) {
|
||||
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
|
||||
GST_BUFFER_TIMESTAMP (out) =
|
||||
gst_util_uint64_scale_int (vd->granulepos, GST_SECOND, vd->vi.rate);
|
||||
} else {
|
||||
GST_BUFFER_TIMESTAMP (out) = -1;
|
||||
}
|
||||
|
||||
/* this should not overflow */
|
||||
GST_BUFFER_DURATION (out) = sample_count * GST_SECOND / vd->vi.rate;
|
||||
|
||||
if (vd->cur_timestamp != GST_CLOCK_TIME_NONE) {
|
||||
/* we have incomming timestamps */
|
||||
GST_BUFFER_TIMESTAMP (out) = vd->cur_timestamp;
|
||||
GST_DEBUG_OBJECT (vd,
|
||||
"cur_timestamp: %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT " = %"
|
||||
|
@ -1005,12 +1003,25 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet)
|
|||
GST_BUFFER_OFFSET (out) = GST_CLOCK_TIME_TO_FRAMES (vd->cur_timestamp,
|
||||
vd->vi.rate);
|
||||
GST_BUFFER_OFFSET_END (out) = GST_BUFFER_OFFSET (out) + sample_count;
|
||||
} else {
|
||||
/* we have incomming granulepos */
|
||||
GST_BUFFER_OFFSET (out) = vd->granulepos;
|
||||
if (vd->granulepos != -1) {
|
||||
GST_BUFFER_OFFSET_END (out) = vd->granulepos + sample_count;
|
||||
GST_BUFFER_TIMESTAMP (out) =
|
||||
gst_util_uint64_scale_int (vd->granulepos, GST_SECOND, vd->vi.rate);
|
||||
} else {
|
||||
GST_BUFFER_TIMESTAMP (out) = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (vd->granulepos != -1)
|
||||
vd->granulepos += sample_count;
|
||||
|
||||
result = vorbis_dec_push (vd, out);
|
||||
if (vd->segment.rate >= 0.0)
|
||||
result = vorbis_dec_push_forward (vd, out);
|
||||
else
|
||||
result = vorbis_dec_push_reverse (vd, out);
|
||||
|
||||
done:
|
||||
vorbis_synthesis_read (&vd->vd, sample_count);
|
||||
|
@ -1050,28 +1061,13 @@ wrong_samples:
|
|||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
||||
vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer, gboolean eos)
|
||||
{
|
||||
GstVorbisDec *vd;
|
||||
ogg_packet packet;
|
||||
GstFlowReturn result = GST_FLOW_OK;
|
||||
GstClockTime timestamp;
|
||||
guint64 offset_end;
|
||||
|
||||
vd = GST_VORBIS_DEC (gst_pad_get_parent (pad));
|
||||
|
||||
/* resync on DISCONT */
|
||||
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_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;
|
||||
#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
|
||||
vorbis_synthesis_restart (&vd->vd);
|
||||
#endif
|
||||
vd->discont = TRUE;
|
||||
}
|
||||
|
||||
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||
offset_end = GST_BUFFER_OFFSET_END (buffer);
|
||||
|
||||
|
@ -1099,7 +1095,7 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
|||
* Yes there is, keep one packet at all times and only push out when
|
||||
* you receive a new one. Implement this.
|
||||
*/
|
||||
packet.e_o_s = 0;
|
||||
packet.e_o_s = (eos ? 1 : 0);
|
||||
|
||||
if (G_UNLIKELY (packet.bytes < 1))
|
||||
goto wrong_size;
|
||||
|
@ -1121,9 +1117,6 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
|||
GST_DEBUG_OBJECT (vd, "offset end: %" G_GUINT64_FORMAT, offset_end);
|
||||
|
||||
done:
|
||||
gst_buffer_unref (buffer);
|
||||
gst_object_unref (vd);
|
||||
|
||||
return result;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -1136,6 +1129,232 @@ wrong_size:
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Input:
|
||||
* Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS
|
||||
* Discont flag: D D D D
|
||||
*
|
||||
* - Each Discont marks a discont in the decoding order.
|
||||
*
|
||||
* for vorbis, each buffer is a keyframe when we have the previous
|
||||
* 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
|
||||
* prepend each incomming buffer so that they are in reversed order.
|
||||
*
|
||||
* gather queue: 9 8 7
|
||||
* decode queue:
|
||||
* output queue:
|
||||
*
|
||||
* When a DISCONT is received (buffer 4), we move the gather queue to the
|
||||
* decode queue. This simply done be taking the head of the gather queue
|
||||
* and prepending it to the decode queue. This yields:
|
||||
*
|
||||
* gather queue:
|
||||
* decode queue: 7 8 9
|
||||
* 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 and output
|
||||
* because it needs the previous buffer (6) which did not arrive yet. This
|
||||
* yields:
|
||||
*
|
||||
* gather queue:
|
||||
* decode queue: 7 8 9
|
||||
* output queue: 8 9
|
||||
*
|
||||
* 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
|
||||
* 6. This yields:
|
||||
*
|
||||
* gather queue:
|
||||
* decode queue: 7
|
||||
* output queue: 9 8
|
||||
*
|
||||
* Then we accumulate more buffers:
|
||||
*
|
||||
* gather queue: 6 5 4
|
||||
* decode queue: 7
|
||||
* output queue:
|
||||
*
|
||||
* prepending to the decode queue on DISCONT yields:
|
||||
*
|
||||
* gather queue:
|
||||
* decode queue: 4 5 6 7
|
||||
* output queue:
|
||||
*
|
||||
* after decoding and keeping buffer 4:
|
||||
*
|
||||
* gather queue:
|
||||
* decode queue: 4
|
||||
* output queue: 7 6 5
|
||||
*
|
||||
* Etc..
|
||||
*/
|
||||
static GstFlowReturn
|
||||
vorbis_dec_flush_decode (GstVorbisDec * dec)
|
||||
{
|
||||
GstFlowReturn res = GST_FLOW_OK;
|
||||
GList *walk;
|
||||
|
||||
walk = dec->decode;
|
||||
|
||||
GST_DEBUG_OBJECT (dec, "flushing buffers to decoder");
|
||||
|
||||
while (walk) {
|
||||
GList *next;
|
||||
GstBuffer *buf = GST_BUFFER_CAST (walk->data);
|
||||
|
||||
GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT,
|
||||
buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||
|
||||
next = g_list_next (walk);
|
||||
|
||||
/* decode buffer, prepend to output queue */
|
||||
res = vorbis_dec_decode_buffer (dec, buf, next == NULL);
|
||||
|
||||
/* if we generated output, we can discard the buffer, else we
|
||||
* keep it in the queue */
|
||||
if (dec->queued) {
|
||||
GST_DEBUG_OBJECT (dec, "decoded buffer to %p", dec->queued->data);
|
||||
dec->decode = g_list_delete_link (dec->decode, walk);
|
||||
gst_buffer_unref (buf);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (dec, "buffer did not decode, keeping");
|
||||
}
|
||||
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 (vorbis_do_clip (dec, buf)) {
|
||||
GST_DEBUG_OBJECT (dec, "clipped buffer %p", buf);
|
||||
gst_buffer_unref (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;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf)
|
||||
{
|
||||
GstFlowReturn result = GST_FLOW_OK;
|
||||
|
||||
/* if we have a discont, move buffers to the decode list */
|
||||
if (G_UNLIKELY (discont)) {
|
||||
GST_DEBUG_OBJECT (vd, "received discont");
|
||||
while (vd->gather) {
|
||||
GstBuffer *gbuf;
|
||||
|
||||
gbuf = GST_BUFFER_CAST (vd->gather->data);
|
||||
/* remove from the gather list */
|
||||
vd->gather = g_list_delete_link (vd->gather, vd->gather);
|
||||
/* copy to decode queue */
|
||||
vd->decode = g_list_prepend (vd->decode, gbuf);
|
||||
}
|
||||
/* flush and decode the decode queue */
|
||||
result = vorbis_dec_flush_decode (vd);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (vd, "gathering buffer %p, size %u", buf,
|
||||
GST_BUFFER_SIZE (buf));
|
||||
/* add buffer to gather queue */
|
||||
vd->gather = g_list_prepend (vd->gather, buf);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont,
|
||||
GstBuffer * buffer)
|
||||
{
|
||||
GstFlowReturn result;
|
||||
|
||||
result = vorbis_dec_decode_buffer (vd, buffer, FALSE);
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vorbis_dec_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstVorbisDec *vd;
|
||||
GstFlowReturn result = GST_FLOW_OK;
|
||||
gboolean discont;
|
||||
|
||||
vd = GST_VORBIS_DEC (gst_pad_get_parent (pad));
|
||||
|
||||
discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT);
|
||||
|
||||
/* 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;
|
||||
#ifdef HAVE_VORBIS_SYNTHESIS_RESTART
|
||||
vorbis_synthesis_restart (&vd->vd);
|
||||
#endif
|
||||
vd->discont = TRUE;
|
||||
}
|
||||
|
||||
if (vd->segment.rate >= 0.0)
|
||||
result = vorbis_dec_chain_forward (vd, discont, buffer);
|
||||
else
|
||||
result = vorbis_dec_chain_reverse (vd, discont, buffer);
|
||||
|
||||
gst_object_unref (vd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
vorbis_dec_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
|
|
|
@ -48,29 +48,35 @@ typedef struct _GstVorbisDecClass GstVorbisDecClass;
|
|||
* Opaque data structure.
|
||||
*/
|
||||
struct _GstVorbisDec {
|
||||
GstElement element;
|
||||
GstElement element;
|
||||
|
||||
GstPad * sinkpad;
|
||||
GstPad * srcpad;
|
||||
GstPad *sinkpad;
|
||||
GstPad *srcpad;
|
||||
|
||||
vorbis_dsp_state vd;
|
||||
vorbis_info vi;
|
||||
vorbis_comment vc;
|
||||
vorbis_block vb;
|
||||
guint64 granulepos;
|
||||
vorbis_dsp_state vd;
|
||||
vorbis_info vi;
|
||||
vorbis_comment vc;
|
||||
vorbis_block vb;
|
||||
guint64 granulepos;
|
||||
|
||||
gboolean initialized;
|
||||
gboolean initialized;
|
||||
|
||||
GList *queued;
|
||||
/* list of buffers that need timestamps */
|
||||
GList *queued;
|
||||
/* list of raw output buffers */
|
||||
GList *output;
|
||||
/* gather/decode queues for reverse playback */
|
||||
GList *gather;
|
||||
GList *decode;
|
||||
|
||||
GstSegment segment;
|
||||
gboolean discont;
|
||||
GstSegment segment;
|
||||
gboolean discont;
|
||||
|
||||
GstClockTime cur_timestamp; /* only used with non-ogg container formats */
|
||||
GstClockTime prev_timestamp; /* only used with non-ogg container formats */
|
||||
GstClockTime cur_timestamp; /* only used with non-ogg container formats */
|
||||
GstClockTime prev_timestamp; /* only used with non-ogg container formats */
|
||||
|
||||
GList *pendingevents;
|
||||
GstTagList *taglist;
|
||||
GList *pendingevents;
|
||||
GstTagList *taglist;
|
||||
};
|
||||
|
||||
struct _GstVorbisDecClass {
|
||||
|
|
Loading…
Reference in a new issue