gst-libs/gst/audio/gstbaseaudiosink.c: Improve debugging.

Original commit message from CVS:
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_setcaps), (gst_base_audio_sink_event),
(gst_base_audio_sink_preroll), (gst_base_audio_sink_render):
Improve debugging.
Post error when caps cannot be parsed.
Resync on discontinuity in the stream.
Clip samples to segment boundaries.
return WRONG_STATE sooner when we are flushing.

* gst-libs/gst/audio/gstbaseaudiosrc.c: (gst_base_audio_src_init),
(gst_base_audio_src_get_time), (gst_base_audio_src_create):
Make audiosrc operate in TIME.
Set TIMESTAMP and DURATION on buffers.
This commit is contained in:
Wim Taymans 2006-01-25 09:27:01 +00:00
parent 8eb4e006a2
commit 2bc5ca1786
3 changed files with 124 additions and 55 deletions

View File

@ -1,3 +1,19 @@
2006-01-25 Wim Taymans <wim@fluendo.com>
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_setcaps), (gst_base_audio_sink_event),
(gst_base_audio_sink_preroll), (gst_base_audio_sink_render):
Improve debugging.
Post error when caps cannot be parsed.
Resync on discontinuity in the stream.
Clip samples to segment boundaries.
return WRONG_STATE sooner when we are flushing.
* gst-libs/gst/audio/gstbaseaudiosrc.c: (gst_base_audio_src_init),
(gst_base_audio_src_get_time), (gst_base_audio_src_create):
Make audiosrc operate in TIME.
Set TIMESTAMP and DURATION on buffers.
2006-01-24 Tim-Philipp Müller <tim at centricular dot net> 2006-01-24 Tim-Philipp Müller <tim at centricular dot net>
* tests/examples/seek/seek.c: (main): * tests/examples/seek/seek.c: (main):

View File

@ -262,12 +262,12 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
spec = &sink->ringbuffer->spec; spec = &sink->ringbuffer->spec;
GST_DEBUG ("release old ringbuffer"); GST_DEBUG_OBJECT (sink, "release old ringbuffer");
/* release old ringbuffer */ /* release old ringbuffer */
gst_ring_buffer_release (sink->ringbuffer); gst_ring_buffer_release (sink->ringbuffer);
GST_DEBUG ("parse caps"); GST_DEBUG_OBJECT (sink, "parse caps");
spec->buffer_time = sink->buffer_time; spec->buffer_time = sink->buffer_time;
spec->latency_time = sink->latency_time; spec->latency_time = sink->latency_time;
@ -278,7 +278,7 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
gst_ring_buffer_debug_spec_buff (spec); gst_ring_buffer_debug_spec_buff (spec);
GST_DEBUG ("acquire new ringbuffer"); GST_DEBUG_OBJECT (sink, "acquire new ringbuffer");
if (!gst_ring_buffer_acquire (sink->ringbuffer, spec)) if (!gst_ring_buffer_acquire (sink->ringbuffer, spec))
goto acquire_error; goto acquire_error;
@ -297,12 +297,14 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
/* ERRORS */ /* ERRORS */
parse_error: parse_error:
{ {
GST_DEBUG ("could not parse caps"); GST_DEBUG_OBJECT (sink, "could not parse caps");
GST_ELEMENT_ERROR (sink, STREAM, FORMAT,
("cannot parse audio format."), ("cannot parse audio format."));
return FALSE; return FALSE;
} }
acquire_error: acquire_error:
{ {
GST_DEBUG ("could not acquire ringbuffer"); GST_DEBUG_OBJECT (sink, "could not acquire ringbuffer");
return FALSE; return FALSE;
} }
} }
@ -332,7 +334,9 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event)
gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE);
break; break;
case GST_EVENT_EOS: case GST_EVENT_EOS:
/* need to start playback when we reach EOS */
gst_ring_buffer_start (sink->ringbuffer); gst_ring_buffer_start (sink->ringbuffer);
/* now wait till we played everything */
break; break;
default: default:
break; break;
@ -355,7 +359,7 @@ gst_base_audio_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
wrong_state: wrong_state:
{ {
GST_DEBUG ("ringbuffer in wrong state"); GST_DEBUG_OBJECT (sink, "ringbuffer in wrong state");
GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
("sink not negotiated."), ("sink not negotiated.")); ("sink not negotiated."), ("sink not negotiated."));
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
@ -396,11 +400,10 @@ static GstFlowReturn
gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
{ {
guint64 render_offset, in_offset; guint64 render_offset, in_offset;
GstClockTime time, render_time, duration; GstClockTime time, stop, render_time, duration;
GstClockTimeDiff render_diff;
GstBaseAudioSink *sink; GstBaseAudioSink *sink;
GstRingBuffer *ringbuf; GstRingBuffer *ringbuf;
gint64 diff; gint64 diff, ctime, cstop;
guint8 *data; guint8 *data;
guint size; guint size;
guint samples; guint samples;
@ -412,6 +415,11 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
sink = GST_BASE_AUDIO_SINK (bsink); sink = GST_BASE_AUDIO_SINK (bsink);
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
/* always resync after a discont */
sink->next_sample = -1;
}
ringbuf = sink->ringbuffer; ringbuf = sink->ringbuffer;
/* can't do anything when we don't have the device */ /* can't do anything when we don't have the device */
@ -431,80 +439,109 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
duration = GST_BUFFER_DURATION (buf); duration = GST_BUFFER_DURATION (buf);
data = GST_BUFFER_DATA (buf); data = GST_BUFFER_DATA (buf);
GST_DEBUG ("time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (sink,
"time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT,
GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->segment.start)); GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->segment.start));
/* if not valid timestamp or we don't need to sync, try to play /* if not valid timestamp or we don't need to sync, try to play
* sample ASAP */ * sample ASAP */
if (!GST_CLOCK_TIME_IS_VALID (time) || !bsink->sync) { if (!GST_CLOCK_TIME_IS_VALID (time) || !bsink->sync) {
render_offset = gst_base_audio_sink_get_offset (sink); render_offset = gst_base_audio_sink_get_offset (sink);
GST_DEBUG ("Buffer of size %u has no time. Using render_offset=%" stop = -1;
G_GUINT64_FORMAT, GST_BUFFER_SIZE (buf), render_offset); GST_DEBUG_OBJECT (sink,
"Buffer of size %u has no time. Using render_offset=%" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (buf), render_offset);
goto no_sync; goto no_sync;
} }
render_diff = time - bsink->segment.start;
/* samples should be rendered based on their timestamp. All samples /* samples should be rendered based on their timestamp. All samples
* arriving before the segment.start are to be thrown away */ * arriving before the segment.start or after segment.stop are to be
/* FIXME, for now we drop the sample completely, we should * thrown away. All samples should also be clipped to the segment
* in fact clip the sample. Same for the segment.stop, actually. */ * boundaries */
if (render_diff < 0) stop =
time + gst_util_uint64_scale_int (samples, GST_SECOND,
ringbuf->spec.rate);
if (!gst_segment_clip (&bsink->segment, GST_FORMAT_TIME, time, stop, &ctime,
&cstop))
goto out_of_segment; goto out_of_segment;
/* see if some clipping happened */
diff = ctime - time;
if (diff > 0) {
diff = gst_util_uint64_scale_int (diff, ringbuf->spec.rate, GST_SECOND);
GST_DEBUG_OBJECT (sink, "clipping start to %" GST_TIME_FORMAT " %"
G_GUINT64_FORMAT " samples", GST_TIME_ARGS (ctime), diff);
samples -= diff;
data += samples * bps;
time = ctime;
}
diff = stop - cstop;
if (diff > 0) {
diff = gst_util_uint64_scale_int (diff, ringbuf->spec.rate, GST_SECOND);
GST_DEBUG_OBJECT (sink, "clipping stop to %" GST_TIME_FORMAT " %"
G_GUINT64_FORMAT " samples", GST_TIME_ARGS (cstop), diff);
samples -= diff;
stop = cstop;
}
gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal, gst_clock_get_calibration (sink->provided_clock, &cinternal, &cexternal,
&crate_num, &crate_denom); &crate_num, &crate_denom);
/* bring buffer timestamp to stream time */ /* bring buffer timestamp to running time */
render_time = render_diff; render_time =
/* adjust for rate */ gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time);
render_time /= ABS (bsink->segment.rate);
/* adjust for accumulated segments */
render_time += bsink->segment.accum;
/* add base time to get absolute clock time */ /* add base time to get absolute clock time */
render_time += render_time +=
(gst_element_get_base_time (GST_ELEMENT_CAST (bsink)) - cexternal) + (gst_element_get_base_time (GST_ELEMENT_CAST (bsink)) - cexternal) +
cinternal; cinternal;
/* and bring the time to the offset in the buffer */ /* and bring the time to the offset in the buffer */
render_offset = render_time * ringbuf->spec.rate / GST_SECOND; render_offset =
gst_util_uint64_scale_int (render_time, ringbuf->spec.rate, GST_SECOND);
GST_DEBUG_OBJECT (sink, "render time %" GST_TIME_FORMAT
", render offset %llu, samples %lu",
GST_TIME_ARGS (render_time), render_offset, samples);
/* roundoff errors in timestamp conversion */ /* roundoff errors in timestamp conversion */
if (sink->next_sample != -1) if (sink->next_sample != -1) {
diff = ABS ((gint64) render_offset - (gint64) sink->next_sample); diff = ABS ((gint64) render_offset - (gint64) sink->next_sample);
else
diff = ringbuf->spec.rate;
GST_DEBUG ("render time %" GST_TIME_FORMAT
", render offset %llu, diff %lld, samples %lu",
GST_TIME_ARGS (render_time), render_offset, diff, samples);
/* we tollerate a 10th of a second diff before we start resyncing. This /* we tollerate a 10th of a second diff before we start resyncing. This
* should be enough to compensate for various rounding errors in the timestamp * should be enough to compensate for various rounding errors in the timestamp
* and sample offset position. */ * and sample offset position. */
if (diff < ringbuf->spec.rate / DIFF_TOLERANCE) { if (diff < ringbuf->spec.rate / DIFF_TOLERANCE) {
GST_DEBUG ("align with prev sample, %" G_GINT64_FORMAT " < %lu", diff, GST_DEBUG_OBJECT (sink,
"align with prev sample, %" G_GINT64_FORMAT " < %lu", diff,
ringbuf->spec.rate / DIFF_TOLERANCE); ringbuf->spec.rate / DIFF_TOLERANCE);
/* just align with previous sample then */ /* just align with previous sample then */
render_offset = sink->next_sample; render_offset = sink->next_sample;
} else { } else {
GST_DEBUG ("resync"); GST_DEBUG_OBJECT (sink,
"resync after discont with previous sample of diff: %lu", diff);
}
} else {
GST_DEBUG_OBJECT (sink, "resync after discont");
} }
crate = ((gdouble) crate_num) / crate_denom; crate = ((gdouble) crate_num) / crate_denom;
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"internal %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", rate %g", "internal %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", rate %g",
cinternal, cexternal, crate); cinternal, cexternal, crate);
no_sync: no_sync:
/* clip length based on rate */ /* clip length based on rate */
samples = MIN (samples, samples / (crate * ABS (bsink->segment.rate))); samples = MIN (samples, samples / (crate * bsink->segment.abs_rate));
/* the next sample should be current sample and its length */ /* the next sample should be current sample and its length */
sink->next_sample = render_offset + samples; sink->next_sample = render_offset + samples;
gst_ring_buffer_commit (ringbuf, render_offset, data, samples); samples = gst_ring_buffer_commit (ringbuf, render_offset, data, samples);
if (samples == -1)
goto stopping;
if (GST_CLOCK_TIME_IS_VALID (time) && time + duration >= bsink->segment.stop) { if (GST_CLOCK_TIME_IS_VALID (stop) && stop >= bsink->segment.stop) {
GST_DEBUG ("start playback because we are at the end of segment"); GST_DEBUG_OBJECT (sink,
"start playback because we are at the end of segment");
gst_ring_buffer_start (ringbuf); gst_ring_buffer_start (ringbuf);
} }
@ -512,26 +549,32 @@ no_sync:
out_of_segment: out_of_segment:
{ {
GST_DEBUG ("dropping sample out of segment time %" GST_TIME_FORMAT GST_DEBUG_OBJECT (sink,
", start %" GST_TIME_FORMAT, "dropping sample out of segment time %" GST_TIME_FORMAT ", start %"
GST_TIME_ARGS (time), GST_TIME_ARGS (bsink->segment.start)); GST_TIME_FORMAT, GST_TIME_ARGS (time),
GST_TIME_ARGS (bsink->segment.start));
return GST_FLOW_OK; return GST_FLOW_OK;
} }
wrong_state: wrong_state:
{ {
GST_DEBUG ("ringbuffer not negotiated"); GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated");
GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
("sink not negotiated."), ("sink not negotiated.")); ("sink not negotiated."), ("sink not negotiated."));
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
} }
wrong_size: wrong_size:
{ {
GST_DEBUG ("wrong size"); GST_DEBUG_OBJECT (sink, "wrong size");
GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
("sink received buffer of wrong size."), ("sink received buffer of wrong size."),
("sink received buffer of wrong size.")); ("sink received buffer of wrong size."));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
stopping:
{
GST_DEBUG_OBJECT (sink, "ringbuffer is stopping");
return GST_FLOW_WRONG_STATE;
}
} }
GstRingBuffer * GstRingBuffer *

View File

@ -138,6 +138,8 @@ gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc,
gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc), gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc),
gst_base_audio_src_fixate); gst_base_audio_src_fixate);
gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME);
} }
static GstClock * static GstClock *
@ -161,7 +163,8 @@ gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src)
samples = gst_ring_buffer_samples_done (src->ringbuffer); samples = gst_ring_buffer_samples_done (src->ringbuffer);
result = samples * GST_SECOND / src->ringbuffer->spec.rate; result = gst_util_uint64_scale_int (samples, GST_SECOND,
src->ringbuffer->spec.rate);
return result; return result;
} }
@ -319,11 +322,14 @@ gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
guint len, samples; guint len, samples;
guint res; guint res;
guint64 sample; guint64 sample;
GstRingBuffer *ringbuffer;
if (!gst_ring_buffer_is_acquired (src->ringbuffer)) ringbuffer = src->ringbuffer;
if (!gst_ring_buffer_is_acquired (ringbuffer))
goto wrong_state; goto wrong_state;
buf = gst_buffer_new_and_alloc (src->ringbuffer->spec.segsize); buf = gst_buffer_new_and_alloc (ringbuffer->spec.segsize);
data = GST_BUFFER_DATA (buf); data = GST_BUFFER_DATA (buf);
len = GST_BUFFER_SIZE (buf); len = GST_BUFFER_SIZE (buf);
@ -334,13 +340,17 @@ gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
sample = 0; sample = 0;
} }
samples = len / src->ringbuffer->spec.bytes_per_sample; samples = len / ringbuffer->spec.bytes_per_sample;
res = gst_ring_buffer_read (src->ringbuffer, sample, data, samples); res = gst_ring_buffer_read (ringbuffer, sample, data, samples);
if (res == -1) if (res == -1)
goto stopped; goto stopped;
GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (sample,
GST_SECOND, ringbuffer->spec.rate);
src->next_sample = sample + samples; src->next_sample = sample + samples;
GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (src->next_sample,
GST_SECOND, ringbuffer->spec.rate) - GST_BUFFER_TIMESTAMP (buf);
gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc))); gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (psrc)));