gst-libs/gst/audio/gstbaseaudiosink.*: Make the clock sync code more accurate wrt resampling and playback at differen...

Original commit message from CVS:
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_event), (gst_base_audio_sink_render):
* gst-libs/gst/audio/gstbaseaudiosink.h:
Make the clock sync code more accurate wrt resampling and playback
at different rates.
* gst-libs/gst/audio/gstringbuffer.c:
(gst_ring_buffer_commit_full), (gst_ring_buffer_commit):
* gst-libs/gst/audio/gstringbuffer.h:
Use better algorithm to interpolate sample rates.
This commit is contained in:
Wim Taymans 2006-11-13 17:30:17 +00:00
parent 410bb3fef1
commit 0990cbf274
5 changed files with 162 additions and 151 deletions

View File

@ -1,3 +1,16 @@
2006-11-13 Wim Taymans <wim@fluendo.com>
* gst-libs/gst/audio/gstbaseaudiosink.c:
(gst_base_audio_sink_event), (gst_base_audio_sink_render):
* gst-libs/gst/audio/gstbaseaudiosink.h:
Make the clock sync code more accurate wrt resampling and playback
at different rates.
* gst-libs/gst/audio/gstringbuffer.c:
(gst_ring_buffer_commit_full), (gst_ring_buffer_commit):
* gst-libs/gst/audio/gstringbuffer.h:
Use better algorithm to interpolate sample rates.
2006-11-13 Michael Smith <msmith@fluendo.com> 2006-11-13 Michael Smith <msmith@fluendo.com>
* ext/ogg/gstoggdemux.c: (gst_ogg_pad_submit_page): * ext/ogg/gstoggdemux.c: (gst_ogg_pad_submit_page):

View File

@ -444,27 +444,12 @@ gst_base_audio_sink_event (GstBaseSink * bsink, GstEvent * event)
case GST_EVENT_NEWSEGMENT: case GST_EVENT_NEWSEGMENT:
{ {
gdouble rate; gdouble rate;
GValue src = { 0 };
GValue dst = { 0 };
/* we only need the rate */ /* we only need the rate */
gst_event_parse_new_segment_full (event, NULL, &rate, NULL, NULL, gst_event_parse_new_segment_full (event, NULL, &rate, NULL, NULL,
NULL, NULL, NULL); NULL, NULL, NULL);
g_value_init (&src, G_TYPE_DOUBLE); GST_DEBUG_OBJECT (sink, "new rate of %f", rate);
g_value_set_double (&src, rate);
g_value_init (&dst, GST_TYPE_FRACTION);
g_value_transform (&src, &dst);
g_value_unset (&src);
sink->abidata.ABI.rate_num = gst_value_get_fraction_numerator (&dst);
sink->abidata.ABI.rate_denom = gst_value_get_fraction_denominator (&dst);
sink->abidata.ABI.rate_accum = 0;
GST_DEBUG_OBJECT (sink, "set rate to %f = %d / %d", rate,
sink->abidata.ABI.rate_num, sink->abidata.ABI.rate_denom);
g_value_unset (&dst);
break; break;
} }
default: default:
@ -530,19 +515,19 @@ gst_base_audio_sink_get_offset (GstBaseAudioSink * sink)
static GstFlowReturn 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 in_offset, clock_offset;
GstClockTime time, stop, render_time, duration; GstClockTime time, stop, render_start, render_stop, sample_offset;
GstBaseAudioSink *sink; GstBaseAudioSink *sink;
GstRingBuffer *ringbuf; GstRingBuffer *ringbuf;
gint64 diff, ctime, cstop; gint64 diff, align, ctime, cstop;
guint8 *data; guint8 *data;
guint size; guint size;
guint samples, written; guint samples, written;
gint bps; gint bps;
gint accum;
GstClockTime crate_num; GstClockTime crate_num;
GstClockTime crate_denom; GstClockTime crate_denom;
gint rate_num; gint out_samples;
gint rate_denom;
GstClockTime cinternal, cexternal; GstClockTime cinternal, cexternal;
GstClock *clock; GstClock *clock;
gboolean sync; gboolean sync;
@ -562,27 +547,28 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
goto wrong_size; goto wrong_size;
samples = size / bps; samples = size / bps;
out_samples = samples;
in_offset = GST_BUFFER_OFFSET (buf); in_offset = GST_BUFFER_OFFSET (buf);
time = GST_BUFFER_TIMESTAMP (buf); time = GST_BUFFER_TIMESTAMP (buf);
duration = GST_BUFFER_DURATION (buf); stop = time + gst_util_uint64_scale_int (samples, GST_SECOND,
data = GST_BUFFER_DATA (buf); ringbuf->spec.rate);
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT, "time %" GST_TIME_FORMAT ", offset %llu, start %" GST_TIME_FORMAT
GST_TIME_ARGS (time), in_offset, GST_TIME_ARGS (bsink->segment.start)); ", samples %u", GST_TIME_ARGS (time), in_offset,
GST_TIME_ARGS (bsink->segment.start), samples);
rate_num = sink->abidata.ABI.rate_num; data = GST_BUFFER_DATA (buf);
rate_denom = sink->abidata.ABI.rate_denom;
/* if not valid timestamp or we can't clip or sync, try to play /* if not valid timestamp or we can't clip or sync, try to play
* sample ASAP */ * sample ASAP */
if (!GST_CLOCK_TIME_IS_VALID (time)) { if (!GST_CLOCK_TIME_IS_VALID (time)) {
render_offset = gst_base_audio_sink_get_offset (sink); render_start = gst_base_audio_sink_get_offset (sink);
stop = -1; render_stop = render_start + samples;
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"Buffer of size %u has no time. Using render_offset=%" G_GUINT64_FORMAT, "Buffer of size %u has no time. Using render_start=%" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (buf), render_offset); GST_BUFFER_SIZE (buf), render_start);
goto no_sync; goto no_sync;
} }
@ -592,9 +578,6 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
* boundaries */ * boundaries */
/* let's calc stop based on the number of samples in the buffer instead /* let's calc stop based on the number of samples in the buffer instead
* of trusting the DURATION */ * of trusting the DURATION */
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, if (!gst_segment_clip (&bsink->segment, GST_FORMAT_TIME, time, stop, &ctime,
&cstop)) &cstop))
goto out_of_segment; goto out_of_segment;
@ -628,45 +611,45 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
if (!sync) { if (!sync) {
/* no sync needed, play sample ASAP */ /* no sync needed, play sample ASAP */
render_offset = gst_base_audio_sink_get_offset (sink); render_start = gst_base_audio_sink_get_offset (sink);
stop = -1; render_stop = render_start + samples;
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"no sync needed. Using render_offset=%" G_GUINT64_FORMAT, "no sync needed. Using render_start=%" G_GUINT64_FORMAT, render_start);
render_offset);
goto no_sync; goto no_sync;
} }
/* bring buffer timestamp to running time */ /* bring buffer start and stop times to running time */
render_time = render_start =
gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time); gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, time);
GST_DEBUG_OBJECT (sink, "running time %" GST_TIME_FORMAT, render_stop =
GST_TIME_ARGS (render_time)); gst_segment_to_running_time (&bsink->segment, GST_FORMAT_TIME, stop);
GST_DEBUG_OBJECT (sink,
"running: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
/* get calibration parameters to compensate for speed and offset differences /* get calibration parameters to compensate for speed and offset differences
* when we are slaved */ * when we are slaved */
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);
/* add base time to get absolute clock time */ clock_offset =
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 */
render_offset =
gst_util_uint64_scale_int (render_time, ringbuf->spec.rate, GST_SECOND);
GST_DEBUG_OBJECT (sink, "render time %" GST_TIME_FORMAT GST_DEBUG_OBJECT (sink, "clock offset %" GST_TIME_FORMAT " %" G_GUINT64_FORMAT
", render offset %" G_GUINT64_FORMAT ", samples %u", "/%" G_GUINT64_FORMAT, GST_TIME_ARGS (clock_offset), crate_num,
GST_TIME_ARGS (render_time), render_offset, samples); crate_denom);
/* never try to align samples when we are slaved to another clock, just /* and bring the time to the rate corrected offset in the buffer */
* trust the rate control algorithm to align the two clocks. We don't take render_start = gst_util_uint64_scale_int (render_start + clock_offset,
* the LOCK to read the clock because it does not really matter here and the ringbuf->spec.rate, GST_SECOND);
* clock is not changed while playing normally. */ render_stop = gst_util_uint64_scale_int (render_stop + clock_offset,
if (clock != sink->provided_clock) { ringbuf->spec.rate, GST_SECOND);
GST_DEBUG_OBJECT (sink, "no align needed: we are slaved");
goto no_align; GST_DEBUG_OBJECT (sink,
} "render: start %" GST_TIME_FORMAT " - stop %" GST_TIME_FORMAT,
GST_TIME_ARGS (render_start), GST_TIME_ARGS (render_stop));
/* always resync after a discont */ /* always resync after a discont */
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) {
@ -680,11 +663,16 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
goto no_align; goto no_align;
} }
/* now try to align the sample to the previous one */ if (bsink->segment.rate >= 1.0)
if (render_offset >= sink->next_sample) sample_offset = render_start;
diff = render_offset - sink->next_sample;
else else
diff = sink->next_sample - render_offset; sample_offset = render_stop;
/* now try to align the sample to the previous one */
if (sample_offset >= sink->next_sample)
diff = sample_offset - sink->next_sample;
else
diff = sink->next_sample - sample_offset;
/* we tollerate half a second diff before we start resyncing. This /* we tollerate half 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
@ -694,8 +682,8 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
GST_DEBUG_OBJECT (sink, GST_DEBUG_OBJECT (sink,
"align with prev sample, %" G_GINT64_FORMAT " < %d", diff, "align with prev sample, %" G_GINT64_FORMAT " < %d", diff,
ringbuf->spec.rate / DIFF_TOLERANCE); ringbuf->spec.rate / DIFF_TOLERANCE);
/* just align with previous sample then */ /* calc align with previous sample */
render_offset = sink->next_sample; align = sink->next_sample - sample_offset;
} else { } else {
/* bring sample diff to seconds for error message */ /* bring sample diff to seconds for error message */
diff = gst_util_uint64_scale_int (diff, GST_SECOND, ringbuf->spec.rate); diff = gst_util_uint64_scale_int (diff, GST_SECOND, ringbuf->spec.rate);
@ -706,50 +694,41 @@ gst_base_audio_sink_render (GstBaseSink * bsink, GstBuffer * buf)
("Unexpected discontinuity in audio timestamps of more " ("Unexpected discontinuity in audio timestamps of more "
"than half a second (%" GST_TIME_FORMAT "), resyncing", "than half a second (%" GST_TIME_FORMAT "), resyncing",
GST_TIME_ARGS (diff))); GST_TIME_ARGS (diff)));
align = 0;
} }
/* apply alignment */
render_start += align;
/* only align stop if we are not slaved */
if (clock != sink->provided_clock) {
GST_DEBUG_OBJECT (sink, "no stop time align needed: we are slaved");
goto no_align;
}
render_stop += align;
no_align: no_align:
/* check if we have a clock rate adjustment to do */ /* number of target samples is difference between start and stop */
if (crate_num != 1 || crate_num != 1) { out_samples = render_stop - render_start;
gint64 num, denom;
/* make clock rate fit in int */
while ((crate_num | crate_denom) > G_MAXINT) {
crate_num /= 2;
crate_denom /= 2;
}
/* full 64bit multiplication */
num = ((gint64) crate_denom) * rate_num;
denom = ((gint64) crate_num) * rate_denom;
/* make result fit in int again */
while ((num | denom) > G_MAXINT) {
num /= 2;
denom /= 2;
}
rate_num = num;
rate_denom = denom;
GST_DEBUG_OBJECT (sink,
"clock rate: internal %" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
" %d/%d", cinternal, cexternal, rate_num, rate_denom);
}
no_sync: no_sync:
GST_DEBUG_OBJECT (sink, "rendering at %" G_GUINT64_FORMAT " %d/%d",
sink->next_sample, samples, out_samples);
/* the next sample should be current sample and its length, this will be /* we render the first or last sample first, depending on the rate */
* updated as we write samples to the ringbuffer. */ if (bsink->segment.rate >= 1.0)
sink->next_sample = render_offset; sample_offset = render_start;
else
GST_DEBUG_OBJECT (sink, "rendering at %" G_GUINT64_FORMAT, sink->next_sample); sample_offset = render_stop;
/* we need to accumulate over different runs for when we get interrupted */
accum = 0;
do { do {
written = written =
gst_ring_buffer_commit_full (ringbuf, &sink->next_sample, data, samples, gst_ring_buffer_commit_full (ringbuf, &sample_offset, data, samples,
rate_num, rate_denom, &sink->abidata.ABI.rate_accum); out_samples, &accum);
GST_DEBUG_OBJECT (sink, "wrote %u of %u, resampled %" G_GUINT64_FORMAT, GST_DEBUG_OBJECT (sink, "wrote %u of %u", written, samples);
written, samples, sink->next_sample - render_offset);
/* if we wrote all, we're done */ /* if we wrote all, we're done */
if (written == samples) if (written == samples)
break; break;
@ -762,6 +741,8 @@ no_sync:
data += written * bps; data += written * bps;
} while (TRUE); } while (TRUE);
sink->next_sample = sample_offset;
GST_DEBUG_OBJECT (sink, "next sample expected at %" G_GUINT64_FORMAT, GST_DEBUG_OBJECT (sink, "next sample expected at %" G_GUINT64_FORMAT,
sink->next_sample); sink->next_sample);

View File

@ -106,11 +106,6 @@ struct _GstBaseAudioSink {
/*< private >*/ /*< private >*/
union { union {
struct {
gint rate_num;
gint rate_denom;
gint rate_accum;
} ABI;
/* adding + 0 to mark ABI change to be undone later */ /* adding + 0 to mark ABI change to be undone later */
gpointer _gst_reserved[GST_PADDING + 0]; gpointer _gst_reserved[GST_PADDING + 0];
} abidata; } abidata;

View File

@ -1181,77 +1181,84 @@ G_STMT_START { \
/* simple copy */ \ /* simple copy */ \
if (!skip) \ if (!skip) \
memcpy (d, s, towrite); \ memcpy (d, s, towrite); \
*sample += towrite / bps; \ in_samples -= towrite / bps; \
out_samples -= towrite / bps; \
s += towrite; \ s += towrite; \
GST_DEBUG ("copy %u bytes", towrite); \ GST_DEBUG ("copy %u bytes", towrite); \
} G_STMT_END } G_STMT_END
/* in_samples >= out_samples, rate > 1.0 */
#define FWD_UP_SAMPLES(s,se,d,de) \ #define FWD_UP_SAMPLES(s,se,d,de) \
G_STMT_START { \ G_STMT_START { \
guint8 *ds = d; \ guint8 *sb = s, *db = d; \
while (s <= se && d < de) { \ while (s <= se && d < de) { \
if (!skip) \ if (!skip) \
memcpy (d, s, bps); \ memcpy (d, s, bps); \
s += bps; \ s += bps; \
*accum += denom; \ *accum += outr; \
while (*accum > 0) { \ if ((*accum << 1) >= inr) { \
*accum -= num; \ *accum -= inr; \
d += bps; \ d += bps; \
} \ } \
} \ } \
*sample += (d - ds) / bps; \ in_samples -= (s - sb)/bps; \
GST_DEBUG ("fwd_up %u bytes", d - ds); \ out_samples -= (d - db)/bps; \
GST_DEBUG ("fwd_up end %d/%d",*accum,*toprocess); \
} G_STMT_END } G_STMT_END
/* out_samples > in_samples, for rates smaller than 1.0 */
#define FWD_DOWN_SAMPLES(s,se,d,de) \ #define FWD_DOWN_SAMPLES(s,se,d,de) \
G_STMT_START { \ G_STMT_START { \
guint8 *ds = d; \ guint8 *sb = s, *db = d; \
while (s <= se && d < de) { \ while (s <= se && d < de) { \
if (!skip) \ if (!skip) \
memcpy (d, s, bps); \ memcpy (d, s, bps); \
d += bps; \ d += bps; \
*accum -= num; \ *accum += inr; \
while (*accum < 0) { \ if ((*accum << 1) >= outr) { \
*accum += denom; \ *accum -= outr; \
s += bps; \ s += bps; \
} \ } \
} \ } \
*sample += (d - ds) / bps; \ in_samples -= (s - sb)/bps; \
GST_DEBUG ("fwd_down %u bytes", d - ds); \ out_samples -= (d - db)/bps; \
GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \
} G_STMT_END } G_STMT_END
#define REV_UP_SAMPLES(s,se,d,de) \ #define REV_UP_SAMPLES(s,se,d,de) \
G_STMT_START { \ G_STMT_START { \
guint8 *ds = d; \ guint8 *sb = se, *db = d; \
while (s <= se && d < de) { \ while (s <= se && d < de) { \
if (!skip) \ if (!skip) \
memcpy (d, se, bps); \ memcpy (d, se, bps); \
se -= bps; \ se -= bps; \
*accum += denom; \ *accum += outr; \
while (*accum > 0) { \ while ((*accum << 1) >= inr) { \
*accum += num; \ *accum -= inr; \
d += bps; \ d += bps; \
} \ } \
} \ } \
*sample += (d - ds) / bps; \ in_samples -= (sb - se)/bps; \
GST_DEBUG ("rev_up %u bytes", d - ds); \ out_samples -= (d - db)/bps; \
GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \
} G_STMT_END } G_STMT_END
#define REV_DOWN_SAMPLES(s,se,d,de) \ #define REV_DOWN_SAMPLES(s,se,d,de) \
G_STMT_START { \ G_STMT_START { \
guint8 *ds = d; \ guint8 *sb = se, *db = d; \
while (s <= se && d < de) { \ while (s <= se && d < de) { \
if (!skip) \ if (!skip) \
memcpy (d, se, bps); \ memcpy (d, se, bps); \
d += bps; \ d += bps; \
*accum += num; \ *accum += inr; \
while (*accum < 0) { \ while ((*accum << 1) >= outr) { \
*accum += denom; \ *accum -= outr; \
se -= bps; \ se -= bps; \
} \ } \
} \ } \
*sample += (d - ds) / bps; \ in_samples -= (sb - se)/bps; \
GST_DEBUG ("rev_down %u bytes", d - ds); \ out_samples -= (d - db)/bps; \
GST_DEBUG ("rev_down end %d/%d",*accum,*toprocess); \
} G_STMT_END } G_STMT_END
/** /**
@ -1259,9 +1266,8 @@ G_STMT_START { \
* @buf: the #GstRingBuffer to commit * @buf: the #GstRingBuffer to commit
* @sample: the sample position of the data * @sample: the sample position of the data
* @data: the data to commit * @data: the data to commit
* @len: the number of samples in the data to commit * @in_samples: the number of samples in the data to commit
* @num: the numerator of the speed * @out_sampled: the number of samples to write to the ringbuffer
* @denom: the denominator of the speed
* @accum: accumulator for rate conversion. * @accum: accumulator for rate conversion.
* *
* Commit @len samples pointed to by @data to the ringbuffer @buf. * Commit @len samples pointed to by @data to the ringbuffer @buf.
@ -1276,9 +1282,6 @@ G_STMT_START { \
* @len does not need to be a multiple of the segment size of the ringbuffer (if * @len does not need to be a multiple of the segment size of the ringbuffer (if
* @num and @denom are both 1) although it is recommended for optimal performance. * @num and @denom are both 1) although it is recommended for optimal performance.
* *
* @sample will be updated with the next position in the ringbuffer. This
* position will take into account any resampling done when writing the samples.
*
* Returns: The number of samples written to the ringbuffer or -1 on error. The * Returns: The number of samples written to the ringbuffer or -1 on error. The
* number of samples written can be less than @len when @buf was interrupted * number of samples written can be less than @len when @buf was interrupted
* with a flush or stop. * with a flush or stop.
@ -1289,28 +1292,43 @@ G_STMT_START { \
*/ */
guint guint
gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample, gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample,
guchar * data, guint len, gint num, gint denom, gint * accum) guchar * data, gint in_samples, gint out_samples, gint * accum)
{ {
gint segdone; gint segdone;
gint segsize, segtotal, bps, sps; gint segsize, segtotal, bps, sps;
guint8 *dest, *data_end; guint8 *dest, *data_end;
gint writeseg, sampleoff; gint writeseg, sampleoff;
gint *toprocess;
gint inr, outr;
gboolean reverse;
if (G_UNLIKELY (len == 0)) if (G_UNLIKELY (in_samples == 0 || out_samples == 0))
return 0; return 0;
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);
g_return_val_if_fail (data != NULL, -1); g_return_val_if_fail (data != NULL, -1);
g_return_val_if_fail (denom != 0, -1);
dest = GST_BUFFER_DATA (buf->data); dest = GST_BUFFER_DATA (buf->data);
segsize = buf->spec.segsize; segsize = buf->spec.segsize;
segtotal = buf->spec.segtotal; segtotal = buf->spec.segtotal;
bps = buf->spec.bytes_per_sample; bps = buf->spec.bytes_per_sample;
sps = buf->samples_per_seg; sps = buf->samples_per_seg;
/* data_end points to the last sample we have to write, not past it. */
data_end = data + (bps * (len - 1)); reverse = out_samples < 0;
out_samples = ABS (out_samples);
if (in_samples >= out_samples)
toprocess = &in_samples;
else
toprocess = &out_samples;
inr = in_samples - 1;
outr = out_samples - 1;
/* data_end points to the last sample we have to write, not past it. This is
* needed to properly handle reverse playback: it points to the last sample. */
data_end = data + (bps * inr);
/* figure out the segment and the offset inside the segment where /* figure out the segment and the offset inside the segment where
* the first sample should be written. */ * the first sample should be written. */
@ -1318,7 +1336,7 @@ gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample,
sampleoff = (*sample % sps) * bps; sampleoff = (*sample % sps) * bps;
/* write out all samples */ /* write out all samples */
while (data <= data_end) { while (*toprocess > 0) {
gint avail; gint avail;
guint8 *d, *d_end; guint8 *d, *d_end;
gint ws; gint ws;
@ -1359,26 +1377,27 @@ gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample,
/* we can write now */ /* we can write now */
ws = writeseg % segtotal; ws = writeseg % segtotal;
avail = segsize - sampleoff; avail = MIN (segsize - sampleoff, bps * out_samples);
d = dest + (ws * segsize) + sampleoff; d = dest + (ws * segsize) + sampleoff;
d_end = d + avail; d_end = d + avail;
*sample += avail / bps;
GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d", GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d",
dest + ws * segsize, ws, sps, sampleoff, avail); dest + ws * segsize, ws, sps, sampleoff, avail);
if (G_LIKELY (num == 1 && denom == 1)) { if (G_LIKELY (inr == outr && !reverse)) {
/* no rate conversion, simply copy samples */ /* no rate conversion, simply copy samples */
FWD_SAMPLES (data, data_end, d, d_end); FWD_SAMPLES (data, data_end, d, d_end);
} else if (num >= 0) { } else if (!reverse) {
if (num >= denom) if (inr >= outr)
/* forward speed up */ /* forward speed up */
FWD_UP_SAMPLES (data, data_end, d, d_end); FWD_UP_SAMPLES (data, data_end, d, d_end);
else else
/* forward slow down */ /* forward slow down */
FWD_DOWN_SAMPLES (data, data_end, d, d_end); FWD_DOWN_SAMPLES (data, data_end, d, d_end);
} else { } else {
if (-num >= denom) if (inr >= outr)
/* reverse speed up */ /* reverse speed up */
REV_UP_SAMPLES (data, data_end, d, d_end); REV_UP_SAMPLES (data, data_end, d, d_end);
else else
@ -1390,9 +1409,11 @@ gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 * sample,
writeseg++; writeseg++;
sampleoff = 0; sampleoff = 0;
} }
/* we consumed all samples here */
data = data_end + bps;
done: done:
return (len - 1) - ((data_end - data) / bps); return inr - ((data_end - data) / bps);
/* ERRORS */ /* ERRORS */
not_started: not_started:
@ -1422,9 +1443,9 @@ gst_ring_buffer_commit (GstRingBuffer * buf, guint64 sample, guchar * data,
guint len) guint len)
{ {
guint res; guint res;
guint64 spos = sample; guint64 samplep = sample;
res = gst_ring_buffer_commit_full (buf, &spos, data, len, 1, 1, NULL); res = gst_ring_buffer_commit_full (buf, &samplep, data, len, len, NULL);
return res; return res;
} }

View File

@ -337,9 +337,10 @@ void gst_ring_buffer_clear_all (GstRingBuffer *buf);
/* commit samples */ /* commit samples */
guint gst_ring_buffer_commit (GstRingBuffer *buf, guint64 sample, guint gst_ring_buffer_commit (GstRingBuffer *buf, guint64 sample,
guchar *data, guint len); guchar *data, guint len);
guint gst_ring_buffer_commit_full (GstRingBuffer *buf, guint64 *sample, guint gst_ring_buffer_commit_full (GstRingBuffer * buf, guint64 *sample,
guchar *data, guint len, guchar * data, gint in_samples,
gint num, gint denom, gint *accum); gint out_samples, gint * accum);
/* read samples */ /* read samples */
guint gst_ring_buffer_read (GstRingBuffer *buf, guint64 sample, guint gst_ring_buffer_read (GstRingBuffer *buf, guint64 sample,
guchar *data, guint len); guchar *data, guint len);