ext/soundtouch/gstpitch.*: Implement LATENCY query and notify about latency changes.
Original commit message from CVS: * ext/soundtouch/gstpitch.cc: * ext/soundtouch/gstpitch.hh: Implement LATENCY query and notify about latency changes. Unfortunately we don't have a fixed latency but it changes a bit with each buffer so we only send an LATENCY event with the maximum latency if it changes. Always calculate the timestamp, duration, etc from the sample rate instead of using a pre-calculated duration for one sample to prevent large rounding errors.
This commit is contained in:
parent
5b791c2ce5
commit
26b4a028d9
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
2008-01-27 Sebastian Dröge <slomo@circular-chaos.org>
|
||||||
|
|
||||||
|
* ext/soundtouch/gstpitch.cc:
|
||||||
|
* ext/soundtouch/gstpitch.hh:
|
||||||
|
Implement LATENCY query and notify about latency changes.
|
||||||
|
Unfortunately we don't have a fixed latency but it changes
|
||||||
|
a bit with each buffer so we only send an LATENCY event with
|
||||||
|
the maximum latency if it changes.
|
||||||
|
|
||||||
|
Always calculate the timestamp, duration, etc from the sample
|
||||||
|
rate instead of using a pre-calculated duration for one sample
|
||||||
|
to prevent large rounding errors.
|
||||||
|
|
||||||
2008-01-27 Sebastian Dröge <slomo@circular-chaos.org>
|
2008-01-27 Sebastian Dröge <slomo@circular-chaos.org>
|
||||||
|
|
||||||
Based on a patch by:
|
Based on a patch by:
|
||||||
|
@ -194,6 +194,7 @@ gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class)
|
|||||||
pitch->priv->st->setPitch (pitch->pitch);
|
pitch->priv->st->setPitch (pitch->pitch);
|
||||||
|
|
||||||
pitch->priv->stream_time_ratio = 1.0;
|
pitch->priv->stream_time_ratio = 1.0;
|
||||||
|
pitch->min_latency = pitch->max_latency = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -312,7 +313,6 @@ gst_pitch_sink_setcaps (GstPad * pad, GstCaps * caps)
|
|||||||
|
|
||||||
/* calculate sample size */
|
/* calculate sample size */
|
||||||
pitch->sample_size = (sizeof (gfloat) * channels);
|
pitch->sample_size = (sizeof (gfloat) * channels);
|
||||||
pitch->sample_duration = gst_util_uint64_scale_int (GST_SECOND, 1, rate);
|
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (pitch);
|
GST_OBJECT_UNLOCK (pitch);
|
||||||
|
|
||||||
@ -371,7 +371,8 @@ gst_pitch_prepare_buffer (GstPitch * pitch)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_BUFFER_DURATION (buffer) = samples * pitch->sample_duration;
|
GST_BUFFER_DURATION (buffer) =
|
||||||
|
gst_util_uint64_scale (samples, GST_SECOND, pitch->samplerate);
|
||||||
/* temporary store samples here, to avoid having to recalculate this */
|
/* temporary store samples here, to avoid having to recalculate this */
|
||||||
GST_BUFFER_OFFSET (buffer) = (gint64) samples;
|
GST_BUFFER_OFFSET (buffer) = (gint64) samples;
|
||||||
|
|
||||||
@ -466,18 +467,17 @@ gst_pitch_convert (GstPitch * pitch,
|
|||||||
GstFormat * dst_format, gint64 * dst_value)
|
GstFormat * dst_format, gint64 * dst_value)
|
||||||
{
|
{
|
||||||
gboolean res = TRUE;
|
gboolean res = TRUE;
|
||||||
GstClockTime sample_duration;
|
|
||||||
guint sample_size;
|
guint sample_size;
|
||||||
|
gint samplerate;
|
||||||
|
|
||||||
g_return_val_if_fail (dst_format && dst_value, FALSE);
|
g_return_val_if_fail (dst_format && dst_value, FALSE);
|
||||||
|
|
||||||
GST_OBJECT_LOCK (pitch);
|
GST_OBJECT_LOCK (pitch);
|
||||||
sample_duration = pitch->sample_duration;
|
|
||||||
sample_size = pitch->sample_size;
|
sample_size = pitch->sample_size;
|
||||||
|
samplerate = pitch->samplerate;
|
||||||
GST_OBJECT_UNLOCK (pitch);
|
GST_OBJECT_UNLOCK (pitch);
|
||||||
|
|
||||||
if (sample_size == 0 || sample_duration == 0 ||
|
if (sample_size == 0 || samplerate == 0) {
|
||||||
sample_duration == GST_CLOCK_TIME_NONE) {
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,11 +490,12 @@ gst_pitch_convert (GstPitch * pitch,
|
|||||||
case GST_FORMAT_BYTES:
|
case GST_FORMAT_BYTES:
|
||||||
switch (*dst_format) {
|
switch (*dst_format) {
|
||||||
case GST_FORMAT_TIME:
|
case GST_FORMAT_TIME:
|
||||||
*dst_value = src_value / sample_size;
|
*dst_value =
|
||||||
*dst_value *= sample_duration;
|
gst_util_uint64_scale_int (src_value, GST_SECOND,
|
||||||
|
sample_size * samplerate);
|
||||||
break;
|
break;
|
||||||
case GST_FORMAT_DEFAULT:
|
case GST_FORMAT_DEFAULT:
|
||||||
*dst_value = src_value / sample_size;
|
*dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
@ -504,11 +505,13 @@ gst_pitch_convert (GstPitch * pitch,
|
|||||||
case GST_FORMAT_TIME:
|
case GST_FORMAT_TIME:
|
||||||
switch (*dst_format) {
|
switch (*dst_format) {
|
||||||
case GST_FORMAT_BYTES:
|
case GST_FORMAT_BYTES:
|
||||||
*dst_value = src_value / sample_duration;
|
*dst_value =
|
||||||
*dst_value *= sample_size;
|
gst_util_uint64_scale_int (src_value, samplerate * sample_size,
|
||||||
|
GST_SECOND);
|
||||||
break;
|
break;
|
||||||
case GST_FORMAT_DEFAULT:
|
case GST_FORMAT_DEFAULT:
|
||||||
*dst_value = src_value / sample_duration;
|
*dst_value =
|
||||||
|
gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
@ -518,10 +521,11 @@ gst_pitch_convert (GstPitch * pitch,
|
|||||||
case GST_FORMAT_DEFAULT:
|
case GST_FORMAT_DEFAULT:
|
||||||
switch (*dst_format) {
|
switch (*dst_format) {
|
||||||
case GST_FORMAT_BYTES:
|
case GST_FORMAT_BYTES:
|
||||||
*dst_value = src_value * sample_size;
|
*dst_value = gst_util_uint64_scale_int (src_value, sample_size, 1);
|
||||||
break;
|
break;
|
||||||
case GST_FORMAT_TIME:
|
case GST_FORMAT_TIME:
|
||||||
*dst_value = src_value * sample_duration;
|
*dst_value =
|
||||||
|
gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
@ -543,6 +547,7 @@ gst_pitch_get_query_types (GstPad * pad)
|
|||||||
GST_QUERY_POSITION,
|
GST_QUERY_POSITION,
|
||||||
GST_QUERY_DURATION,
|
GST_QUERY_DURATION,
|
||||||
GST_QUERY_CONVERT,
|
GST_QUERY_CONVERT,
|
||||||
|
GST_QUERY_LATENCY,
|
||||||
GST_QUERY_NONE
|
GST_QUERY_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -630,6 +635,46 @@ gst_pitch_src_query (GstPad * pad, GstQuery * query)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case GST_QUERY_LATENCY:
|
||||||
|
{
|
||||||
|
GstClockTime min, max;
|
||||||
|
gboolean live;
|
||||||
|
GstPad *peer;
|
||||||
|
|
||||||
|
if ((peer = gst_pad_get_peer (pitch->sinkpad))) {
|
||||||
|
if ((res = gst_pad_query (peer, query))) {
|
||||||
|
gst_query_parse_latency (query, &live, &min, &max);
|
||||||
|
|
||||||
|
GST_DEBUG ("Peer latency: min %"
|
||||||
|
GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (min), GST_TIME_ARGS (max));
|
||||||
|
|
||||||
|
/* add our own latency */
|
||||||
|
|
||||||
|
GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT
|
||||||
|
", max %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (pitch->min_latency),
|
||||||
|
GST_TIME_ARGS (pitch->max_latency));
|
||||||
|
|
||||||
|
min += pitch->min_latency;
|
||||||
|
if (max != GST_CLOCK_TIME_NONE)
|
||||||
|
max += pitch->max_latency;
|
||||||
|
else
|
||||||
|
max = pitch->max_latency;
|
||||||
|
|
||||||
|
GST_DEBUG ("Calculated total latency : min %"
|
||||||
|
GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (min), GST_TIME_ARGS (max));
|
||||||
|
g_print ("Calculated total latency : min %"
|
||||||
|
GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (min), GST_TIME_ARGS (max));
|
||||||
|
|
||||||
|
gst_query_set_latency (query, live, min, max);
|
||||||
|
}
|
||||||
|
gst_object_unref (peer);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
res = gst_pad_query_default (pad, query);
|
res = gst_pad_query_default (pad, query);
|
||||||
break;
|
break;
|
||||||
@ -728,10 +773,12 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
|
|||||||
case GST_EVENT_FLUSH_STOP:
|
case GST_EVENT_FLUSH_STOP:
|
||||||
gst_pitch_flush_buffer (pitch, FALSE);
|
gst_pitch_flush_buffer (pitch, FALSE);
|
||||||
pitch->priv->st->clear ();
|
pitch->priv->st->clear ();
|
||||||
|
pitch->min_latency = pitch->max_latency = 0;
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
gst_pitch_flush_buffer (pitch, TRUE);
|
gst_pitch_flush_buffer (pitch, TRUE);
|
||||||
pitch->priv->st->clear ();
|
pitch->priv->st->clear ();
|
||||||
|
pitch->min_latency = pitch->max_latency = 0;
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_NEWSEGMENT:
|
case GST_EVENT_NEWSEGMENT:
|
||||||
if (!gst_pitch_process_segment (pitch, &event)) {
|
if (!gst_pitch_process_segment (pitch, &event)) {
|
||||||
@ -742,6 +789,7 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
|
|||||||
event = NULL;
|
event = NULL;
|
||||||
}
|
}
|
||||||
pitch->priv->st->clear ();
|
pitch->priv->st->clear ();
|
||||||
|
pitch->min_latency = pitch->max_latency = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -755,17 +803,41 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
|
||||||
|
{
|
||||||
|
GstClockTimeDiff current_latency, min_latency, max_latency;
|
||||||
|
|
||||||
|
current_latency =
|
||||||
|
timestamp / pitch->priv->stream_time_ratio - pitch->next_buffer_time;
|
||||||
|
|
||||||
|
min_latency = MIN (pitch->min_latency, current_latency);
|
||||||
|
max_latency = MAX (pitch->max_latency, current_latency);
|
||||||
|
|
||||||
|
if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
|
||||||
|
pitch->min_latency = min_latency;
|
||||||
|
pitch->max_latency = max_latency;
|
||||||
|
|
||||||
|
gst_pad_push_event (pitch->sinkpad, gst_event_new_latency (max_latency));
|
||||||
|
gst_element_post_message (GST_ELEMENT (pitch),
|
||||||
|
gst_message_new_latency (GST_OBJECT (pitch)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
|
gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstPitch *pitch;
|
GstPitch *pitch;
|
||||||
GstPitchPrivate *priv;
|
GstPitchPrivate *priv;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
|
||||||
pitch = GST_PITCH (GST_PAD_PARENT (pad));
|
pitch = GST_PITCH (GST_PAD_PARENT (pad));
|
||||||
priv = GST_PITCH_GET_PRIVATE (pitch);
|
priv = GST_PITCH_GET_PRIVATE (pitch);
|
||||||
|
|
||||||
gst_object_sync_values (G_OBJECT (pitch), pitch->next_buffer_time);
|
gst_object_sync_values (G_OBJECT (pitch), pitch->next_buffer_time);
|
||||||
|
|
||||||
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
|
|
||||||
/* push the received samples on the soundtouch buffer */
|
/* push the received samples on the soundtouch buffer */
|
||||||
GST_LOG_OBJECT (pitch, "incoming buffer (%d samples)",
|
GST_LOG_OBJECT (pitch, "incoming buffer (%d samples)",
|
||||||
(gint) (GST_BUFFER_SIZE (buffer) / pitch->sample_size));
|
(gint) (GST_BUFFER_SIZE (buffer) / pitch->sample_size));
|
||||||
@ -793,6 +865,10 @@ gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
|
|||||||
GST_BUFFER_SIZE (buffer) / pitch->sample_size);
|
GST_BUFFER_SIZE (buffer) / pitch->sample_size);
|
||||||
gst_buffer_unref (buffer);
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
/* Calculate latency */
|
||||||
|
|
||||||
|
gst_pitch_update_latency (pitch, timestamp);
|
||||||
|
|
||||||
/* and try to extract some samples from the soundtouch buffer */
|
/* and try to extract some samples from the soundtouch buffer */
|
||||||
if (!priv->st->isEmpty ()) {
|
if (!priv->st->isEmpty ()) {
|
||||||
GstBuffer *out_buffer;
|
GstBuffer *out_buffer;
|
||||||
@ -818,6 +894,7 @@ gst_pitch_change_state (GstElement * element, GstStateChange transition)
|
|||||||
pitch->next_buffer_time = 0;
|
pitch->next_buffer_time = 0;
|
||||||
pitch->next_buffer_offset = 0;
|
pitch->next_buffer_offset = 0;
|
||||||
pitch->priv->st->clear ();
|
pitch->priv->st->clear ();
|
||||||
|
pitch->min_latency = pitch->max_latency = 0;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
break;
|
break;
|
||||||
|
@ -67,12 +67,13 @@ struct _GstPitch
|
|||||||
gint samplerate; /* samplerate */
|
gint samplerate; /* samplerate */
|
||||||
gint channels; /* number of audio channels */
|
gint channels; /* number of audio channels */
|
||||||
gsize sample_size; /* number of bytes for a single sample */
|
gsize sample_size; /* number of bytes for a single sample */
|
||||||
GstClockTime sample_duration; /* time for 1 sample */
|
|
||||||
|
|
||||||
/* stream tracking */
|
/* stream tracking */
|
||||||
GstClockTime next_buffer_time;
|
GstClockTime next_buffer_time;
|
||||||
gint64 next_buffer_offset;
|
gint64 next_buffer_offset;
|
||||||
|
|
||||||
|
GstClockTimeDiff min_latency, max_latency;
|
||||||
|
|
||||||
GstPitchPrivate *priv;
|
GstPitchPrivate *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user