gst-libs/gst/audio/: Do the delay calculation in the source/sink base classes as this is specific for the capture/pla...

Original commit message from CVS:
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_get_time), (gst_base_audio_sink_callback):
* gst-libs/gst/audio/gstbaseaudiosrc.c:
(gst_base_audio_src_get_time), (gst_base_audio_src_fixate),
(gst_base_audio_src_get_times), (gst_base_audio_src_get_offset),
(gst_base_audio_src_create), (gst_base_audio_src_change_state):
Do the delay calculation in the source/sink base classes as this is
specific for the capture/playback mode.
Try to fixate a bit better, like round depth up to a multiple of 8
bigger than width.
Handle underruns correctly by marking DISCONT on buffers and adjusting
timestamps to handle the gap.
Set offset/offset_end correctly on buffers.
* gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_pause),
(gst_ring_buffer_samples_done), (gst_ring_buffer_commit),
(gst_ring_buffer_read):
Remove resync and underrun recovery from the ringbuffer.
Fix ringbuffer read code on under/overrun.
This commit is contained in:
Wim Taymans 2006-09-15 14:53:44 +00:00
parent 102ec386b1
commit 65b1938b38
4 changed files with 163 additions and 65 deletions

View file

@ -1,3 +1,25 @@
2006-09-15 Wim Taymans <wim@fluendo.com>
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_get_time), (gst_base_audio_sink_callback):
* gst-libs/gst/audio/gstbaseaudiosrc.c:
(gst_base_audio_src_get_time), (gst_base_audio_src_fixate),
(gst_base_audio_src_get_times), (gst_base_audio_src_get_offset),
(gst_base_audio_src_create), (gst_base_audio_src_change_state):
Do the delay calculation in the source/sink base classes as this is
specific for the capture/playback mode.
Try to fixate a bit better, like round depth up to a multiple of 8
bigger than width.
Handle underruns correctly by marking DISCONT on buffers and adjusting
timestamps to handle the gap.
Set offset/offset_end correctly on buffers.
* gst-libs/gst/audio/gstringbuffer.c: (gst_ring_buffer_pause),
(gst_ring_buffer_samples_done), (gst_ring_buffer_commit),
(gst_ring_buffer_read):
Remove resync and underrun recovery from the ringbuffer.
Fix ringbuffer read code on under/overrun.
2006-09-15 Wim Taymans <wim@fluendo.com> 2006-09-15 Wim Taymans <wim@fluendo.com>
* gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init),

View file

@ -91,7 +91,7 @@ static void gst_base_audio_sink_get_times (GstBaseSink * bsink,
static gboolean gst_base_audio_sink_setcaps (GstBaseSink * bsink, static gboolean gst_base_audio_sink_setcaps (GstBaseSink * bsink,
GstCaps * caps); GstCaps * caps);
//static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; /* static guint gst_base_audio_sink_signals[LAST_SIGNAL] = { 0 }; */
static void static void
gst_base_audio_sink_base_init (gpointer g_class) gst_base_audio_sink_base_init (gpointer g_class)
@ -217,18 +217,32 @@ clock_disabled:
static GstClockTime static GstClockTime
gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink) gst_base_audio_sink_get_time (GstClock * clock, GstBaseAudioSink * sink)
{ {
guint64 samples; guint64 raw, samples;
guint delay;
GstClockTime result; GstClockTime result;
if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0) if (sink->ringbuffer == NULL || sink->ringbuffer->spec.rate == 0)
return GST_CLOCK_TIME_NONE; return GST_CLOCK_TIME_NONE;
/* our processed samples are always increasing */ /* our processed samples are always increasing */
samples = gst_ring_buffer_samples_done (sink->ringbuffer); raw = samples = gst_ring_buffer_samples_done (sink->ringbuffer);
/* the number of samples not yet processed, this is still queued in the
* device (not played for playback). */
delay = gst_ring_buffer_delay (sink->ringbuffer);
if (G_LIKELY (samples >= delay))
samples -= delay;
else
samples = 0;
result = gst_util_uint64_scale_int (samples, GST_SECOND, result = gst_util_uint64_scale_int (samples, GST_SECOND,
sink->ringbuffer->spec.rate); sink->ringbuffer->spec.rate);
GST_DEBUG_OBJECT (sink,
"processed samples: raw %llu, delay %u, real %llu, time %"
GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
return result; return result;
} }
@ -707,7 +721,7 @@ static void
gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len, gst_base_audio_sink_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
gpointer user_data) gpointer user_data)
{ {
//GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (data); /* GstBaseAudioSink *sink = GST_BASE_AUDIO_SINK (data); */
} }
/* should be called with the LOCK */ /* should be called with the LOCK */

View file

@ -75,7 +75,7 @@ static void gst_base_audio_src_get_times (GstBaseSrc * bsrc,
GstBuffer * buffer, GstClockTime * start, GstClockTime * end); GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps); static gboolean gst_base_audio_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps);
//static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 }; /* static guint gst_base_audio_src_signals[LAST_SIGNAL] = { 0 }; */
static void static void
gst_base_audio_src_base_init (gpointer g_class) gst_base_audio_src_base_init (gpointer g_class)
@ -201,17 +201,28 @@ wrong_state:
static GstClockTime static GstClockTime
gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src) gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
{ {
guint64 samples; guint64 raw, samples;
guint delay;
GstClockTime result; GstClockTime result;
if (G_UNLIKELY (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0)) if (G_UNLIKELY (src->ringbuffer == NULL || src->ringbuffer->spec.rate == 0))
return GST_CLOCK_TIME_NONE; return GST_CLOCK_TIME_NONE;
samples = gst_ring_buffer_samples_done (src->ringbuffer); raw = samples = gst_ring_buffer_samples_done (src->ringbuffer);
/* the number of samples not yet processed, this is still queued in the
* device (not yet read for capture). */
delay = gst_ring_buffer_delay (src->ringbuffer);
samples += delay;
result = gst_util_uint64_scale_int (samples, GST_SECOND, result = gst_util_uint64_scale_int (samples, GST_SECOND,
src->ringbuffer->spec.rate); src->ringbuffer->spec.rate);
GST_DEBUG_OBJECT (src,
"processed samples: raw %llu, delay %u, real %llu, time %"
GST_TIME_FORMAT, raw, delay, samples, GST_TIME_ARGS (result));
return result; return result;
} }
@ -271,14 +282,24 @@ static void
gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps) gst_base_audio_src_fixate (GstPad * pad, GstCaps * caps)
{ {
GstStructure *s; GstStructure *s;
gint width, depth;
s = gst_caps_get_structure (caps, 0); s = gst_caps_get_structure (caps, 0);
/* fields for all formats */
gst_structure_fixate_field_nearest_int (s, "rate", 44100); gst_structure_fixate_field_nearest_int (s, "rate", 44100);
gst_structure_fixate_field_nearest_int (s, "channels", 2); gst_structure_fixate_field_nearest_int (s, "channels", 2);
gst_structure_fixate_field_nearest_int (s, "depth", 16);
gst_structure_fixate_field_nearest_int (s, "width", 16); gst_structure_fixate_field_nearest_int (s, "width", 16);
gst_structure_set (s, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
/* fields for int */
if (gst_structure_has_field (s, "depth")) {
gst_structure_get_int (s, "width", &width);
/* round width to nearest multiple of 8 for the depth */
depth = GST_ROUND_UP_8 (width);
gst_structure_fixate_field_nearest_int (s, "depth", depth);
}
if (gst_structure_has_field (s, "signed"))
gst_structure_fixate_field_boolean (s, "signed", TRUE);
if (gst_structure_has_field (s, "endianness")) if (gst_structure_has_field (s, "endianness"))
gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER); gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
} }
@ -341,9 +362,8 @@ static void
gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer, gst_base_audio_src_get_times (GstBaseSrc * bsrc, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end) GstClockTime * start, GstClockTime * end)
{ {
/* ne need to sync to a clock here, we schedule the samples based /* no need to sync to a clock here, we schedule the samples based
* on our own clock for the moment. FIXME, implement this when * on our own clock for the moment. */
* we are not using our own clock */
*start = GST_CLOCK_TIME_NONE; *start = GST_CLOCK_TIME_NONE;
*end = GST_CLOCK_TIME_NONE; *end = GST_CLOCK_TIME_NONE;
} }
@ -369,6 +389,45 @@ gst_base_audio_src_event (GstBaseSrc * bsrc, GstEvent * event)
return TRUE; return TRUE;
} }
/* get the next offset in the ringbuffer for reading samples.
* If the next sample is too far away, this function will position itself to the
* next most recent sample, creating discontinuity */
static guint64
gst_base_audio_src_get_offset (GstBaseAudioSrc * src)
{
guint64 sample;
gint readseg, segdone, segtotal, sps;
gint diff;
/* assume we can append to the previous sample */
sample = src->next_sample;
/* no previous sample, try to read from position 0 */
if (sample == -1)
sample = 0;
sps = src->ringbuffer->samples_per_seg;
segtotal = src->ringbuffer->spec.segtotal;
/* figure out the segment and the offset inside the segment where
* the sample should be read from. */
readseg = sample / sps;
/* get the currently processed segment */
segdone = g_atomic_int_get (&src->ringbuffer->segdone)
- src->ringbuffer->segbase;
/* see how far away it is from the read segment, normally segdone (where new
* data is written in the ringbuffer) is bigger than readseg (where we are
* reading). */
diff = segdone - readseg;
if (diff >= segtotal) {
/* sample would be dropped, position to next playable position */
sample = (segdone - segtotal + 1) * sps;
}
return sample;
}
static GstFlowReturn static GstFlowReturn
gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
GstBuffer ** outbuf) GstBuffer ** outbuf)
@ -396,14 +455,17 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
/* make sure we round down to an integral number of samples */ /* make sure we round down to an integral number of samples */
length -= length % bps; length -= length % bps;
/* calculate the sequentially next sample we need to read */ /* figure out the offset in the ringbuffer */
sample = (src->next_sample != -1 ? src->next_sample : 0);
if (G_UNLIKELY (offset != -1)) { if (G_UNLIKELY (offset != -1)) {
/* if a specific offset was given it must be the next sample = offset / bps;
* sequential offset we expect or we fail. */ /* if a specific offset was given it must be the next sequential
if (offset / bps != sample) * offset we expect or we fail for now. */
if (src->next_sample != -1 && sample != src->next_sample)
goto wrong_offset; goto wrong_offset;
} else {
/* calculate the sequentially next sample we need to read. This can jump and
* create a DISCONT. */
sample = gst_base_audio_src_get_offset (src);
} }
/* get the number of samples to read */ /* get the number of samples to read */
@ -413,10 +475,19 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
buf = gst_buffer_new_and_alloc (length); buf = gst_buffer_new_and_alloc (length);
data = GST_BUFFER_DATA (buf); data = GST_BUFFER_DATA (buf);
/* read the sample */
res = gst_ring_buffer_read (ringbuffer, sample, data, samples); res = gst_ring_buffer_read (ringbuffer, sample, data, samples);
if (G_UNLIKELY (res == -1)) if (G_UNLIKELY (res == -1))
goto stopped; goto stopped;
/* mark discontinuity if needed */
if (G_UNLIKELY (sample != src->next_sample) && src->next_sample != -1) {
GST_WARNING_OBJECT (src,
"create DISCONT of %" G_GUINT64_FORMAT " samples at sample %"
G_GUINT64_FORMAT, sample - src->next_sample, sample);
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
}
/* FIXME, we timestamp against our own clock, also handle the case /* FIXME, we timestamp against our own clock, also handle the case
* where we are slaved to another clock. We currently refuse to accept * where we are slaved to another clock. We currently refuse to accept
* any other clock than the one we provide, so this code is fine for * any other clock than the one we provide, so this code is fine for
@ -426,6 +497,8 @@ gst_base_audio_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
src->next_sample = sample + samples; src->next_sample = sample + samples;
GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (src->next_sample, GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (src->next_sample,
GST_SECOND, ringbuffer->spec.rate) - GST_BUFFER_TIMESTAMP (buf); GST_SECOND, ringbuffer->spec.rate) - GST_BUFFER_TIMESTAMP (buf);
GST_BUFFER_OFFSET (buf) = sample;
GST_BUFFER_OFFSET_END (buf) = sample + samples;
gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (bsrc))); gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (bsrc)));
@ -470,13 +543,6 @@ gst_base_audio_src_create_ringbuffer (GstBaseAudioSrc * src)
return buffer; return buffer;
} }
void
gst_base_audio_src_callback (GstRingBuffer * rbuf, guint8 * data, guint len,
gpointer user_data)
{
//GstBaseAudioSrc *src = GST_BASE_AUDIO_SRC (data);
}
static GstStateChangeReturn static GstStateChangeReturn
gst_base_audio_src_change_state (GstElement * element, gst_base_audio_src_change_state (GstElement * element,
GstStateChange transition) GstStateChange transition)
@ -488,12 +554,10 @@ gst_base_audio_src_change_state (GstElement * element,
case GST_STATE_CHANGE_NULL_TO_READY: case GST_STATE_CHANGE_NULL_TO_READY:
if (src->ringbuffer == NULL) { if (src->ringbuffer == NULL) {
src->ringbuffer = gst_base_audio_src_create_ringbuffer (src); src->ringbuffer = gst_base_audio_src_create_ringbuffer (src);
gst_ring_buffer_set_callback (src->ringbuffer,
gst_base_audio_src_callback, src);
} }
if (!gst_ring_buffer_open_device (src->ringbuffer)) if (!gst_ring_buffer_open_device (src->ringbuffer))
return GST_STATE_CHANGE_FAILURE; return GST_STATE_CHANGE_FAILURE;
src->next_sample = 0; src->next_sample = -1;
break; break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_ring_buffer_set_flushing (src->ringbuffer, FALSE); gst_ring_buffer_set_flushing (src->ringbuffer, FALSE);
@ -515,7 +579,7 @@ gst_base_audio_src_change_state (GstElement * element,
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_ring_buffer_set_flushing (src->ringbuffer, TRUE); gst_ring_buffer_set_flushing (src->ringbuffer, TRUE);
gst_ring_buffer_release (src->ringbuffer); gst_ring_buffer_release (src->ringbuffer);
src->next_sample = 0; src->next_sample = -1;
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
gst_ring_buffer_close_device (src->ringbuffer); gst_ring_buffer_close_device (src->ringbuffer);

View file

@ -920,6 +920,7 @@ gst_ring_buffer_pause (GstRingBuffer * buf)
return res; return res;
/* ERRORS */
flushing: flushing:
{ {
GST_OBJECT_UNLOCK (buf); GST_OBJECT_UNLOCK (buf);
@ -989,6 +990,12 @@ done:
* implementation uses another internal buffer between the audio * implementation uses another internal buffer between the audio
* device. * device.
* *
* For playback ringbuffers this is the amount of samples transfered from the
* ringbuffer to the device but still not played.
*
* For capture ringbuffers this is the amount of samples in the device that are
* not yet transfered to the ringbuffer.
*
* Returns: The number of samples queued in the audio device. * Returns: The number of samples queued in the audio device.
* *
* MT safe. * MT safe.
@ -1020,7 +1027,8 @@ done:
* @buf: the #GstRingBuffer to query * @buf: the #GstRingBuffer to query
* *
* Get the number of samples that were processed by the ringbuffer * Get the number of samples that were processed by the ringbuffer
* since it was last started. * since it was last started. This does not include the number of samples not
* yet processed (see gst_ring_buffer_delay()).
* *
* Returns: The number of samples processed by the ringbuffer. * Returns: The number of samples processed by the ringbuffer.
* *
@ -1038,18 +1046,8 @@ gst_ring_buffer_samples_done (GstRingBuffer * buf)
/* get the amount of segments we processed */ /* get the amount of segments we processed */
segdone = g_atomic_int_get (&buf->segdone); segdone = g_atomic_int_get (&buf->segdone);
/* and the number of samples not yet processed */ /* convert to samples */
delay = gst_ring_buffer_delay (buf); samples = ((guint64) segdone) * buf->samples_per_seg;
raw = samples = ((guint64) segdone) * buf->samples_per_seg;
if (G_LIKELY (samples >= delay))
samples -= delay;
else
samples = 0;
GST_DEBUG_OBJECT (buf, "processed samples: raw %llu, delay %u, real %llu",
raw, delay, samples);
return samples; return samples;
} }
@ -1237,9 +1235,10 @@ gst_ring_buffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
GST_DEBUG GST_DEBUG
("pointer at %d, sample %llu, write to %d-%d, to_write %d, diff %d, segtotal %d, segsize %d", ("pointer at %d, sample %llu, write to %d-%d, to_write %d, diff %d, segtotal %d, segsize %d",
segdone, sample, writeseg, sampleoff, to_write, diff, segtotal, sps); segdone, sample, writeseg, sampleoff, to_write, diff, segtotal,
segsize);
/* segment too far ahead, we need to drop, hopefully UNLIKELY */ /* segment too far ahead, writer too slow, we need to drop, hopefully UNLIKELY */
if (G_UNLIKELY (diff < 0)) { if (G_UNLIKELY (diff < 0)) {
/* we need to drop one segment at a time, pretend we wrote a /* we need to drop one segment at a time, pretend we wrote a
* segment. */ * segment. */
@ -1310,6 +1309,7 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
gint segdone; gint segdone;
gint segsize, segtotal, bps, sps; gint segsize, segtotal, bps, sps;
guint8 *dest; guint8 *dest;
guint to_read;
g_return_val_if_fail (GST_IS_RING_BUFFER (buf), -1); g_return_val_if_fail (GST_IS_RING_BUFFER (buf), -1);
g_return_val_if_fail (buf->data != NULL, -1); g_return_val_if_fail (buf->data != NULL, -1);
@ -1321,13 +1321,14 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
bps = buf->spec.bytes_per_sample; bps = buf->spec.bytes_per_sample;
sps = buf->samples_per_seg; sps = buf->samples_per_seg;
to_read = len;
/* read enough samples */ /* read enough samples */
while (len > 0) { while (to_read > 0) {
gint sampleslen; gint sampleslen;
gint readseg, sampleoff; gint readseg, sampleoff;
/* figure out the segment and the offset inside the segment where /* figure out the segment and the offset inside the segment where
* the sample should be written. */ * the sample should be read from. */
readseg = sample / sps; readseg = sample / sps;
sampleoff = (sample % sps); sampleoff = (sample % sps);
@ -1337,33 +1338,29 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
/* get the currently processed segment */ /* get the currently processed segment */
segdone = g_atomic_int_get (&buf->segdone) - buf->segbase; segdone = g_atomic_int_get (&buf->segdone) - buf->segbase;
/* see how far away it is from the read segment */ /* see how far away it is from the read segment, normally segdone (where
* the hardware is writing) is bigger than readseg (where software is
* reading) */
diff = segdone - readseg; diff = segdone - readseg;
GST_DEBUG GST_DEBUG
("pointer at %d, sample %llu, read from %d-%d, len %d, diff %d, segtotal %d, segsize %d", ("pointer at %d, sample %llu, read from %d-%d, to_read %d, diff %d, segtotal %d, segsize %d",
segdone, sample, readseg, sampleoff, len, diff, segtotal, segsize); segdone, sample, readseg, sampleoff, to_read, diff, segtotal,
segsize);
/* segment too far ahead, we need to drop */ /* segment too far ahead, reader too slow */
if (diff < 0) { if (G_UNLIKELY (diff >= segtotal)) {
/* we need to drop one segment at a time, pretend we read an /* pretend we read an empty segment. */
* empty segment. */ sampleslen = MIN (sps, to_read);
sampleslen = MIN (sps, len);
memcpy (data, buf->empty_seg, sampleslen * bps); memcpy (data, buf->empty_seg, sampleslen * bps);
goto next; goto next;
} }
/* read segment is within readable range, we can break the loop and /* read segment is within readable range, we can break the loop and
* start reading the data. */ * start reading the data. */
if (diff > 0 && diff < segtotal) if (diff > 0)
break; break;
/* flush if diff has grown bigger than ringbuffer */
if (diff >= segtotal) {
gst_ring_buffer_clear_all (buf);
buf->segdone = readseg;
}
/* else we need to wait for the segment to become readable. */ /* else we need to wait for the segment to become readable. */
if (!wait_segment (buf)) if (!wait_segment (buf))
goto not_started; goto not_started;
@ -1371,26 +1368,27 @@ gst_ring_buffer_read (GstRingBuffer * buf, guint64 sample, guchar * data,
/* we can read now */ /* we can read now */
readseg = readseg % segtotal; readseg = readseg % segtotal;
sampleslen = MIN (sps - sampleoff, len); sampleslen = MIN (sps - sampleoff, to_read);
GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, len %d", GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d",
dest + readseg * segsize, readseg, sampleoff, sampleslen); dest + readseg * segsize, readseg, sampleoff, sampleslen);
memcpy (data, dest + (readseg * segsize) + (sampleoff * bps), memcpy (data, dest + (readseg * segsize) + (sampleoff * bps),
(sampleslen * bps)); (sampleslen * bps));
next: next:
len -= sampleslen; to_read -= sampleslen;
sample += sampleslen; sample += sampleslen;
data += sampleslen * bps; data += sampleslen * bps;
} }
return len; return len - to_read;
/* ERRORS */ /* ERRORS */
not_started: not_started:
{ {
GST_DEBUG_OBJECT (buf, "stopped processing"); GST_DEBUG_OBJECT (buf, "stopped processing");
/* FIXME, return len - to_read after fixing caller */
return -1; return -1;
} }
} }