ext/faad/gstfaad.*: Add basic reverse playback support.

Original commit message from CVS:
* ext/faad/gstfaad.c: (gst_faad_dispose), (clear_queued),
(flush_queued), (gst_faad_drain), (gst_faad_do_raw_seek),
(gst_faad_src_event), (gst_faad_sink_event), (gst_faad_chain),
(gst_faad_change_state):
* ext/faad/gstfaad.h:
Add basic reverse playback support.
Clear decoder state after disconts.
Remove some unused code.
Mark output buffers with a discont after a decoding error.
This commit is contained in:
Wim Taymans 2008-06-02 10:18:25 +00:00
parent d0d99f937b
commit eea50a9a85
3 changed files with 108 additions and 55 deletions

View file

@ -1,3 +1,15 @@
2008-06-02 Wim Taymans <wim.taymans@collabora.co.uk>
* ext/faad/gstfaad.c: (gst_faad_dispose), (clear_queued),
(flush_queued), (gst_faad_drain), (gst_faad_do_raw_seek),
(gst_faad_src_event), (gst_faad_sink_event), (gst_faad_chain),
(gst_faad_change_state):
* ext/faad/gstfaad.h:
Add basic reverse playback support.
Clear decoder state after disconts.
Remove some unused code.
Mark output buffers with a discont after a decoding error.
2008-06-02 Sebastian Dröge <slomo@circular-chaos.org>
Patch by: Sjoerd Simons <sjoerd at luon dot net>

View file

@ -246,7 +246,6 @@ gst_faad_dispose (GObject * object)
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
@ -757,6 +756,48 @@ gst_faad_srcconnect (GstPad * pad, const GstCaps * caps)
return GST_PAD_LINK_REFUSED;
}*/
static void
clear_queued (GstFaad * faad)
{
g_list_foreach (faad->queued, (GFunc) gst_mini_object_unref, NULL);
g_list_free (faad->queued);
faad->queued = NULL;
}
static GstFlowReturn
flush_queued (GstFaad * faad)
{
GstFlowReturn ret = GST_FLOW_OK;
while (faad->queued) {
GstBuffer *buf = GST_BUFFER_CAST (faad->queued->data);
GST_LOG_OBJECT (faad, "pushing buffer %p, timestamp %"
GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
/* iterate ouput queue an push downstream */
ret = gst_pad_push (faad->srcpad, buf);
faad->queued = g_list_delete_link (faad->queued, faad->queued);
}
return ret;
}
static GstFlowReturn
gst_faad_drain (GstFaad * faad)
{
GstFlowReturn ret = GST_FLOW_OK;
if (faad->segment->rate < 0.0) {
/* if we have some queued frames for reverse playback, flush
* them now */
ret = flush_queued (faad);
}
return ret;
}
static gboolean
gst_faad_do_raw_seek (GstFaad * faad, GstEvent * event)
{
@ -786,7 +827,7 @@ gst_faad_do_raw_seek (GstFaad * faad, GstEvent * event)
GST_DEBUG_OBJECT (faad, "seeking to %" GST_TIME_FORMAT " at byte offset %"
G_GINT64_FORMAT, GST_TIME_ARGS (start_time), start);
return gst_pad_send_event (GST_PAD_PEER (faad->sinkpad), event);
return gst_pad_push_event (faad->sinkpad, event);
}
static gboolean
@ -803,14 +844,14 @@ gst_faad_src_event (GstPad * pad, GstEvent * event)
case GST_EVENT_SEEK:{
/* try upstream first, there might be a demuxer */
gst_event_ref (event);
if (!(res = gst_pad_event_default (pad, event))) {
if (!(res = gst_pad_push_event (faad->sinkpad, event))) {
res = gst_faad_do_raw_seek (faad, event);
}
gst_event_unref (event);
break;
}
default:
res = gst_pad_event_default (pad, event);
res = gst_pad_push_event (faad->sinkpad, event);
break;
}
@ -828,9 +869,17 @@ gst_faad_sink_event (GstPad * pad, GstEvent * event)
GST_LOG_OBJECT (faad, "Handling %s event", GST_EVENT_TYPE_NAME (event));
/* FIXME: we should probably handle FLUSH */
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP:
if (faad->tempbuf != NULL) {
gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL;
}
clear_queued (faad);
res = gst_pad_push_event (faad->srcpad, event);
break;
case GST_EVENT_EOS:
gst_faad_drain (faad);
if (faad->tempbuf != NULL) {
gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL;
@ -846,6 +895,7 @@ gst_faad_sink_event (GstPad * pad, GstEvent * event)
gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start,
&end, &base);
if (fmt == GST_FORMAT_TIME) {
GST_DEBUG ("Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%"
GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start),
@ -871,6 +921,9 @@ gst_faad_sink_event (GstPad * pad, GstEvent * event)
}
gst_event_unref (event);
/* drain queued buffers before we activate the new segment */
gst_faad_drain (faad);
event = gst_event_new_new_segment (is_update, rate,
GST_FORMAT_TIME, new_start, new_end, new_start);
@ -1173,6 +1226,16 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
faad = GST_FAAD (gst_pad_get_parent (pad));
if (GST_BUFFER_IS_DISCONT (buffer)) {
gst_faad_drain (faad);
faacDecPostSeekReset (faad->handle, 0);
if (faad->tempbuf != NULL) {
gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL;
}
faad->discont = TRUE;
}
GST_OBJECT_LOCK (faad);
faad->bytes_in += GST_BUFFER_SIZE (buffer);
GST_OBJECT_UNLOCK (faad);
@ -1264,47 +1327,11 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
input_size - skip_bytes);
if (info.error > 0) {
guint32 rate;
guint8 ch;
GST_DEBUG_OBJECT (faad, "decoding error: %s",
GST_WARNING_OBJECT (faad, "decoding error: %s",
faacDecGetErrorMessage (info.error));
/* mark discont for the next buffer */
faad->discont = TRUE;
goto out;
if (!faad->packetised)
goto decode_error;
/* decode error? try again using faacDecInit2
* fabricated private codec data from sink caps */
gst_faad_close_decoder (faad);
if (!gst_faad_open_decoder (faad))
goto init2_failed;
GST_DEBUG_OBJECT (faad, "decoding error, reopening with faacDecInit2()");
if ((gint8) faacDecInit2 (faad->handle, faad->fake_codec_data, 2,
&rate, &ch) < 0) {
goto init2_failed;
}
GST_DEBUG_OBJECT (faad, "faacDecInit2(): rate=%d,channels=%d", rate, ch);
/* let's try again */
info.error = 0;
out = faacDecDecode (faad->handle, &info, input_data + skip_bytes,
input_size - skip_bytes);
if (info.error) {
faad->error_count++;
if (faad->error_count >= MAX_DECODE_ERRORS)
goto decode_error;
GST_DEBUG_OBJECT (faad,
"Failed to decode buffer: %s, count = %d, trying to resync",
faacDecGetErrorMessage (info.error), faad->error_count);
continue;
}
faad->error_count = 0; /* all fine, reset error counter */
}
if (info.bytesconsumed > input_size)
@ -1378,7 +1405,20 @@ gst_faad_chain (GstPad * pad, GstBuffer * buffer)
"pushing buffer, off=%" G_GUINT64_FORMAT ", ts=%" GST_TIME_FORMAT,
GST_BUFFER_OFFSET (outbuf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
ret = gst_pad_push (faad->srcpad, outbuf);
if (faad->discont) {
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
faad->discont = FALSE;
}
if (faad->segment->rate > 0.0) {
ret = gst_pad_push (faad->srcpad, outbuf);
} else {
/* reverse playback, queue frame till later when we get a discont. */
GST_DEBUG_OBJECT (faad, "queued frame");
faad->queued = g_list_prepend (faad->queued, outbuf);
ret = GST_FLOW_OK;
}
if (ret != GST_FLOW_OK)
goto out;
}
@ -1421,13 +1461,6 @@ init2_failed:
ret = GST_FLOW_ERROR;
goto out;
}
decode_error:
{
GST_ELEMENT_ERROR (faad, STREAM, DECODE, (NULL),
("Failed to decode buffer: %s", faacDecGetErrorMessage (info.error)));
ret = GST_FLOW_ERROR;
goto out;
}
}
static gboolean
@ -1498,13 +1531,14 @@ gst_faad_change_state (GstElement * element, GstStateChange transition)
faad->bytes_in = 0;
faad->sum_dur_out = 0;
faad->error_count = 0;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_faad_close_decoder (faad);
if (faad->tempbuf) {
gst_buffer_unref (faad->tempbuf);
faad->tempbuf = NULL;
}
clear_queued (faad);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_faad_close_decoder (faad);
break;
default:
break;

View file

@ -68,9 +68,16 @@ typedef struct _GstFaad {
guint64 bytes_in; /* bytes received */
guint64 sum_dur_out; /* sum of durations of decoded buffers we sent out */
gint error_count;
gboolean discont;
/* segment handling */
GstSegment * segment;
/* list of raw output buffers for reverse playback */
GList *queued;
/* gather/decode queues for reverse playback */
GList *gather;
GList *decode;
} GstFaad;
typedef struct _GstFaadClass {