summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--ext/soundtouch/gstpitch.cc105
-rw-r--r--ext/soundtouch/gstpitch.hh3
3 files changed, 106 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 88f2535f..97930237 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
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>
+
Based on a patch by:
Hans de Goede <j dot w dot r dot degoede at hhs dot nl>
diff --git a/ext/soundtouch/gstpitch.cc b/ext/soundtouch/gstpitch.cc
index 7ee726f0..9e68fda4 100644
--- a/ext/soundtouch/gstpitch.cc
+++ b/ext/soundtouch/gstpitch.cc
@@ -194,6 +194,7 @@ gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class)
pitch->priv->st->setPitch (pitch->pitch);
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 */
pitch->sample_size = (sizeof (gfloat) * channels);
- pitch->sample_duration = gst_util_uint64_scale_int (GST_SECOND, 1, rate);
GST_OBJECT_UNLOCK (pitch);
@@ -371,7 +371,8 @@ gst_pitch_prepare_buffer (GstPitch * pitch)
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 */
GST_BUFFER_OFFSET (buffer) = (gint64) samples;
@@ -466,18 +467,17 @@ gst_pitch_convert (GstPitch * pitch,
GstFormat * dst_format, gint64 * dst_value)
{
gboolean res = TRUE;
- GstClockTime sample_duration;
guint sample_size;
+ gint samplerate;
g_return_val_if_fail (dst_format && dst_value, FALSE);
GST_OBJECT_LOCK (pitch);
- sample_duration = pitch->sample_duration;
sample_size = pitch->sample_size;
+ samplerate = pitch->samplerate;
GST_OBJECT_UNLOCK (pitch);
- if (sample_size == 0 || sample_duration == 0 ||
- sample_duration == GST_CLOCK_TIME_NONE) {
+ if (sample_size == 0 || samplerate == 0) {
return FALSE;
}
@@ -490,11 +490,12 @@ gst_pitch_convert (GstPitch * pitch,
case GST_FORMAT_BYTES:
switch (*dst_format) {
case GST_FORMAT_TIME:
- *dst_value = src_value / sample_size;
- *dst_value *= sample_duration;
+ *dst_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND,
+ sample_size * samplerate);
break;
case GST_FORMAT_DEFAULT:
- *dst_value = src_value / sample_size;
+ *dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
break;
default:
res = FALSE;
@@ -504,11 +505,13 @@ gst_pitch_convert (GstPitch * pitch,
case GST_FORMAT_TIME:
switch (*dst_format) {
case GST_FORMAT_BYTES:
- *dst_value = src_value / sample_duration;
- *dst_value *= sample_size;
+ *dst_value =
+ gst_util_uint64_scale_int (src_value, samplerate * sample_size,
+ GST_SECOND);
break;
case GST_FORMAT_DEFAULT:
- *dst_value = src_value / sample_duration;
+ *dst_value =
+ gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
break;
default:
res = FALSE;
@@ -518,10 +521,11 @@ gst_pitch_convert (GstPitch * pitch,
case GST_FORMAT_DEFAULT:
switch (*dst_format) {
case GST_FORMAT_BYTES:
- *dst_value = src_value * sample_size;
+ *dst_value = gst_util_uint64_scale_int (src_value, sample_size, 1);
break;
case GST_FORMAT_TIME:
- *dst_value = src_value * sample_duration;
+ *dst_value =
+ gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
break;
default:
res = FALSE;
@@ -543,6 +547,7 @@ gst_pitch_get_query_types (GstPad * pad)
GST_QUERY_POSITION,
GST_QUERY_DURATION,
GST_QUERY_CONVERT,
+ GST_QUERY_LATENCY,
GST_QUERY_NONE
};
@@ -630,6 +635,46 @@ gst_pitch_src_query (GstPad * pad, GstQuery * query)
}
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:
res = gst_pad_query_default (pad, query);
break;
@@ -728,10 +773,12 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
case GST_EVENT_FLUSH_STOP:
gst_pitch_flush_buffer (pitch, FALSE);
pitch->priv->st->clear ();
+ pitch->min_latency = pitch->max_latency = 0;
break;
case GST_EVENT_EOS:
gst_pitch_flush_buffer (pitch, TRUE);
pitch->priv->st->clear ();
+ pitch->min_latency = pitch->max_latency = 0;
break;
case GST_EVENT_NEWSEGMENT:
if (!gst_pitch_process_segment (pitch, &event)) {
@@ -742,6 +789,7 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
event = NULL;
}
pitch->priv->st->clear ();
+ pitch->min_latency = pitch->max_latency = 0;
break;
default:
break;
@@ -755,17 +803,41 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event)
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
gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
{
GstPitch *pitch;
GstPitchPrivate *priv;
+ GstClockTime timestamp;
pitch = GST_PITCH (GST_PAD_PARENT (pad));
priv = GST_PITCH_GET_PRIVATE (pitch);
gst_object_sync_values (G_OBJECT (pitch), pitch->next_buffer_time);
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+
/* push the received samples on the soundtouch buffer */
GST_LOG_OBJECT (pitch, "incoming buffer (%d samples)",
(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_unref (buffer);
+ /* Calculate latency */
+
+ gst_pitch_update_latency (pitch, timestamp);
+
/* and try to extract some samples from the soundtouch buffer */
if (!priv->st->isEmpty ()) {
GstBuffer *out_buffer;
@@ -818,6 +894,7 @@ gst_pitch_change_state (GstElement * element, GstStateChange transition)
pitch->next_buffer_time = 0;
pitch->next_buffer_offset = 0;
pitch->priv->st->clear ();
+ pitch->min_latency = pitch->max_latency = 0;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
diff --git a/ext/soundtouch/gstpitch.hh b/ext/soundtouch/gstpitch.hh
index 24d42ba2..32cb04dd 100644
--- a/ext/soundtouch/gstpitch.hh
+++ b/ext/soundtouch/gstpitch.hh
@@ -67,12 +67,13 @@ struct _GstPitch
gint samplerate; /* samplerate */
gint channels; /* number of audio channels */
gsize sample_size; /* number of bytes for a single sample */
- GstClockTime sample_duration; /* time for 1 sample */
/* stream tracking */
GstClockTime next_buffer_time;
gint64 next_buffer_offset;
+ GstClockTimeDiff min_latency, max_latency;
+
GstPitchPrivate *priv;
};