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:
parent
410bb3fef1
commit
0990cbf274
13
ChangeLog
13
ChangeLog
@ -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):
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user