diff --git a/ChangeLog b/ChangeLog index 0d7fa191ae..3ebbd0b10c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2006-01-25 Wim Taymans + + * 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 * tests/examples/seek/seek.c: (main): diff --git a/gst-libs/gst/audio/gstbaseaudiosink.c b/gst-libs/gst/audio/gstbaseaudiosink.c index ea0850006c..cc3e00b6a7 100644 --- a/gst-libs/gst/audio/gstbaseaudiosink.c +++ b/gst-libs/gst/audio/gstbaseaudiosink.c @@ -262,12 +262,12 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) spec = &sink->ringbuffer->spec; - GST_DEBUG ("release old ringbuffer"); + GST_DEBUG_OBJECT (sink, "release old ringbuffer"); /* release old ringbuffer */ gst_ring_buffer_release (sink->ringbuffer); - GST_DEBUG ("parse caps"); + GST_DEBUG_OBJECT (sink, "parse caps"); spec->buffer_time = sink->buffer_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_DEBUG ("acquire new ringbuffer"); + GST_DEBUG_OBJECT (sink, "acquire new ringbuffer"); if (!gst_ring_buffer_acquire (sink->ringbuffer, spec)) goto acquire_error; @@ -297,12 +297,14 @@ gst_base_audio_sink_setcaps (GstBaseSink * bsink, GstCaps * caps) /* ERRORS */ 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; } acquire_error: { - GST_DEBUG ("could not acquire ringbuffer"); + GST_DEBUG_OBJECT (sink, "could not acquire ringbuffer"); return FALSE; } } @@ -332,7 +334,9 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event) gst_ring_buffer_set_flushing (sink->ringbuffer, FALSE); break; case GST_EVENT_EOS: + /* need to start playback when we reach EOS */ gst_ring_buffer_start (sink->ringbuffer); + /* now wait till we played everything */ break; default: break; @@ -355,7 +359,7 @@ gst_base_audio_sink_preroll (GstBaseSink * bsink, GstBuffer * buffer) wrong_state: { - GST_DEBUG ("ringbuffer in wrong state"); + GST_DEBUG_OBJECT (sink, "ringbuffer in wrong state"); GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("sink not negotiated."), ("sink not negotiated.")); return GST_FLOW_NOT_NEGOTIATED; @@ -396,11 +400,10 @@ static GstFlowReturn gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) { guint64 render_offset, in_offset; - GstClockTime time, render_time, duration; - GstClockTimeDiff render_diff; + GstClockTime time, stop, render_time, duration; GstBaseAudioSink *sink; GstRingBuffer *ringbuf; - gint64 diff; + gint64 diff, ctime, cstop; guint8 *data; guint size; guint samples; @@ -412,6 +415,11 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf) 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; /* 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); 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)); /* if not valid timestamp or we don't need to sync, try to play * sample ASAP */ if (!GST_CLOCK_TIME_IS_VALID (time) || !bsink->sync) { render_offset = gst_base_audio_sink_get_offset (sink); - GST_DEBUG ("Buffer of size %u has no time. Using render_offset=%" - G_GUINT64_FORMAT, GST_BUFFER_SIZE (buf), render_offset); + stop = -1; + 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; } - render_diff = time - bsink->segment.start; - /* samples should be rendered based on their timestamp. All samples - * arriving before the segment.start are to be thrown away */ - /* FIXME, for now we drop the sample completely, we should - * in fact clip the sample. Same for the segment.stop, actually. */ - if (render_diff < 0) + * arriving before the segment.start or after segment.stop are to be + * thrown away. All samples should also be clipped to the segment + * boundaries */ + 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; + /* 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, &crate_num, &crate_denom); - /* bring buffer timestamp to stream time */ - render_time = render_diff; - /* adjust for rate */ - render_time /= ABS (bsink->segment.rate); - /* adjust for accumulated segments */ - render_time += bsink->segment.accum; + /* bring buffer timestamp to running time */ + render_time = + gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time); /* add base time to get absolute clock time */ render_time += (gst_element_get_base_time (GST_ELEMENT_CAST (bsink)) - cexternal) + cinternal; /* 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 */ - if (sink->next_sample != -1) + if (sink->next_sample != -1) { 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 - * should be enough to compensate for various rounding errors in the timestamp - * and sample offset position. */ - if (diff < ringbuf->spec.rate / DIFF_TOLERANCE) { - GST_DEBUG ("align with prev sample, %" G_GINT64_FORMAT " < %lu", diff, - ringbuf->spec.rate / DIFF_TOLERANCE); - /* just align with previous sample then */ - render_offset = sink->next_sample; + /* 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 + * and sample offset position. */ + if (diff < ringbuf->spec.rate / DIFF_TOLERANCE) { + GST_DEBUG_OBJECT (sink, + "align with prev sample, %" G_GINT64_FORMAT " < %lu", diff, + ringbuf->spec.rate / DIFF_TOLERANCE); + /* just align with previous sample then */ + render_offset = sink->next_sample; + } else { + GST_DEBUG_OBJECT (sink, + "resync after discont with previous sample of diff: %lu", diff); + } } else { - GST_DEBUG ("resync"); + GST_DEBUG_OBJECT (sink, "resync after discont"); } crate = ((gdouble) crate_num) / crate_denom; GST_DEBUG_OBJECT (sink, "internal %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ", rate %g", cinternal, cexternal, crate); + no_sync: /* 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 */ 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) { - GST_DEBUG ("start playback because we are at the end of segment"); + if (GST_CLOCK_TIME_IS_VALID (stop) && stop >= bsink->segment.stop) { + GST_DEBUG_OBJECT (sink, + "start playback because we are at the end of segment"); gst_ring_buffer_start (ringbuf); } @@ -512,26 +549,32 @@ no_sync: out_of_segment: { - GST_DEBUG ("dropping sample out of segment time %" GST_TIME_FORMAT - ", start %" GST_TIME_FORMAT, - GST_TIME_ARGS (time), GST_TIME_ARGS (bsink->segment.start)); + GST_DEBUG_OBJECT (sink, + "dropping sample out of segment time %" GST_TIME_FORMAT ", start %" + GST_TIME_FORMAT, GST_TIME_ARGS (time), + GST_TIME_ARGS (bsink->segment.start)); return GST_FLOW_OK; } wrong_state: { - GST_DEBUG ("ringbuffer not negotiated"); + GST_DEBUG_OBJECT (sink, "ringbuffer not negotiated"); GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("sink not negotiated."), ("sink not negotiated.")); return GST_FLOW_NOT_NEGOTIATED; } wrong_size: { - GST_DEBUG ("wrong size"); + GST_DEBUG_OBJECT (sink, "wrong size"); GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("sink received buffer of wrong size."), ("sink received buffer of wrong size.")); return GST_FLOW_ERROR; } +stopping: + { + GST_DEBUG_OBJECT (sink, "ringbuffer is stopping"); + return GST_FLOW_WRONG_STATE; + } } GstRingBuffer * diff --git a/gst-libs/gst/audio/gstbaseaudiosrc.c b/gst-libs/gst/audio/gstbaseaudiosrc.c index 31638d7ba1..1b057f7ace 100644 --- a/gst-libs/gst/audio/gstbaseaudiosrc.c +++ b/gst-libs/gst/audio/gstbaseaudiosrc.c @@ -138,6 +138,8 @@ gst_base_audio_src_init (GstBaseAudioSrc * baseaudiosrc, gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (baseaudiosrc), gst_base_audio_src_fixate); + + gst_base_src_set_format (GST_BASE_SRC (baseaudiosrc), GST_FORMAT_TIME); } static GstClock * @@ -161,7 +163,8 @@ gst_base_audio_src_get_time (GstClock * clock, GstBaseAudioSrc * src) 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; } @@ -319,11 +322,14 @@ gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) guint len, samples; guint res; 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; - buf = gst_buffer_new_and_alloc (src->ringbuffer->spec.segsize); + buf = gst_buffer_new_and_alloc (ringbuffer->spec.segsize); data = GST_BUFFER_DATA (buf); len = GST_BUFFER_SIZE (buf); @@ -334,13 +340,17 @@ gst_base_audio_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) 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) goto stopped; + GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (sample, + GST_SECOND, ringbuffer->spec.rate); 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)));