pitch: fix multithread accesses

- fully protect accesses to the libsoundtouch API that is not
  thread-safe.
- fully protect accesses to GstPitch members that could be read by a
  downstream query thread while written by an upstream streaming thread
  or a user thread.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6247>
This commit is contained in:
Loïc Le Page 2024-02-28 18:47:58 +01:00 committed by GStreamer Marge Bot
parent 9f5bb30d3a
commit 35165da586

View file

@ -98,6 +98,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS (SUPPORTED_CAPS)); GST_STATIC_CAPS (SUPPORTED_CAPS));
static void gst_pitch_dispose (GObject * object); static void gst_pitch_dispose (GObject * object);
static void gst_pitch_finalize (GObject * object);
static void gst_pitch_set_property (GObject * object, static void gst_pitch_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec); guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_pitch_get_property (GObject * object, static void gst_pitch_get_property (GObject * object,
@ -136,6 +137,7 @@ gst_pitch_class_init (GstPitchClass * klass)
gobject_class->set_property = gst_pitch_set_property; gobject_class->set_property = gst_pitch_set_property;
gobject_class->get_property = gst_pitch_get_property; gobject_class->get_property = gst_pitch_get_property;
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pitch_dispose); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pitch_dispose);
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_pitch_finalize);
g_object_class_install_property (gobject_class, ARG_PITCH, g_object_class_install_property (gobject_class, ARG_PITCH,
g_param_spec_float ("pitch", "Pitch", g_param_spec_float ("pitch", "Pitch",
@ -223,12 +225,23 @@ gst_pitch_dispose (GObject * object)
GstPitch *pitch = GST_PITCH (object); GstPitch *pitch = GST_PITCH (object);
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
gst_clear_event (&priv->pending_segment);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_pitch_finalize (GObject * object)
{
GstPitch *pitch = GST_PITCH (object);
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
if (priv->st) { if (priv->st) {
delete priv->st; delete priv->st;
priv->st = NULL; priv->st = NULL;
} }
G_OBJECT_CLASS (parent_class)->dispose (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
static void static void
@ -247,9 +260,9 @@ gst_pitch_set_property (GObject * object, guint prop_id,
GstPitch *pitch = GST_PITCH (object); GstPitch *pitch = GST_PITCH (object);
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
GST_OBJECT_LOCK (pitch);
switch (prop_id) { switch (prop_id) {
case ARG_TEMPO: case ARG_TEMPO:
GST_OBJECT_LOCK (pitch);
pitch->tempo = g_value_get_float (value); pitch->tempo = g_value_get_float (value);
priv->stream_time_ratio = priv->stream_time_ratio =
pitch->tempo * pitch->rate * pitch->segment_applied_rate; pitch->tempo * pitch->rate * pitch->segment_applied_rate;
@ -258,6 +271,7 @@ gst_pitch_set_property (GObject * object, guint prop_id,
gst_pitch_update_duration (pitch); gst_pitch_update_duration (pitch);
break; break;
case ARG_RATE: case ARG_RATE:
GST_OBJECT_LOCK (pitch);
pitch->rate = g_value_get_float (value); pitch->rate = g_value_get_float (value);
priv->stream_time_ratio = priv->stream_time_ratio =
pitch->tempo * pitch->rate * pitch->segment_applied_rate; pitch->tempo * pitch->rate * pitch->segment_applied_rate;
@ -267,17 +281,18 @@ gst_pitch_set_property (GObject * object, guint prop_id,
break; break;
case ARG_OUTPUT_RATE: case ARG_OUTPUT_RATE:
/* Has no effect until the next input segment */ /* Has no effect until the next input segment */
GST_OBJECT_LOCK (pitch);
pitch->output_rate = g_value_get_float (value); pitch->output_rate = g_value_get_float (value);
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
break; break;
case ARG_PITCH: case ARG_PITCH:
GST_OBJECT_LOCK (pitch);
pitch->pitch = g_value_get_float (value); pitch->pitch = g_value_get_float (value);
priv->st->setPitch (pitch->pitch); priv->st->setPitch (pitch->pitch);
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
GST_OBJECT_UNLOCK (pitch);
break; break;
} }
} }
@ -288,43 +303,47 @@ gst_pitch_get_property (GObject * object, guint prop_id,
{ {
GstPitch *pitch = GST_PITCH (object); GstPitch *pitch = GST_PITCH (object);
GST_OBJECT_LOCK (pitch);
switch (prop_id) { switch (prop_id) {
case ARG_TEMPO: case ARG_TEMPO:
GST_OBJECT_LOCK (pitch);
g_value_set_float (value, pitch->tempo); g_value_set_float (value, pitch->tempo);
GST_OBJECT_UNLOCK (pitch);
break; break;
case ARG_RATE: case ARG_RATE:
GST_OBJECT_LOCK (pitch);
g_value_set_float (value, pitch->rate); g_value_set_float (value, pitch->rate);
GST_OBJECT_UNLOCK (pitch);
break; break;
case ARG_OUTPUT_RATE: case ARG_OUTPUT_RATE:
GST_OBJECT_LOCK (pitch);
g_value_set_float (value, pitch->output_rate); g_value_set_float (value, pitch->output_rate);
GST_OBJECT_UNLOCK (pitch);
break; break;
case ARG_PITCH: case ARG_PITCH:
GST_OBJECT_LOCK (pitch);
g_value_set_float (value, pitch->pitch); g_value_set_float (value, pitch->pitch);
GST_OBJECT_UNLOCK (pitch);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
GST_OBJECT_UNLOCK (pitch);
} }
static gboolean static gboolean
gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps) gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps)
{ {
GstPitchPrivate *priv; GstAudioInfo info;
if (!gst_audio_info_from_caps (&info, caps))
priv = GST_PITCH_GET_PRIVATE (pitch);
if (!gst_audio_info_from_caps (&pitch->info, caps))
return FALSE; return FALSE;
GST_OBJECT_LOCK (pitch); GST_OBJECT_LOCK (pitch);
pitch->info = info;
/* notify the soundtouch instance of this change */ /* notify the soundtouch instance of this change */
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
priv->st->setSampleRate (pitch->info.rate); priv->st->setSampleRate (pitch->info.rate);
priv->st->setChannels (pitch->info.channels); priv->st->setChannels (pitch->info.channels);
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
return TRUE; return TRUE;
@ -336,6 +355,8 @@ gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
{ {
gint samples; gint samples;
GST_OBJECT_LOCK (pitch);
GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time; GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time;
pitch->next_buffer_time += GST_BUFFER_DURATION (buffer); pitch->next_buffer_time += GST_BUFFER_DURATION (buffer);
@ -348,6 +369,8 @@ gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
"] (%d samples)", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), "] (%d samples)", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
GST_TIME_ARGS (pitch->next_buffer_time), samples); GST_TIME_ARGS (pitch->next_buffer_time), samples);
GST_OBJECT_UNLOCK (pitch);
return gst_pad_push (pitch->srcpad, buffer); return gst_pad_push (pitch->srcpad, buffer);
} }
@ -355,35 +378,36 @@ gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
static GstBuffer * static GstBuffer *
gst_pitch_prepare_buffer (GstPitch * pitch) gst_pitch_prepare_buffer (GstPitch * pitch)
{ {
GstPitchPrivate *priv; GstBuffer *buffer = NULL;
guint samples;
GstBuffer *buffer;
GstMapInfo info;
priv = GST_PITCH_GET_PRIVATE (pitch);
GST_LOG_OBJECT (pitch, "preparing buffer"); GST_LOG_OBJECT (pitch, "preparing buffer");
samples = priv->st->numSamples (); GST_OBJECT_LOCK (pitch);
if (samples == 0)
return NULL;
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
guint samples = priv->st->numSamples ();
if (samples > 0) {
buffer = gst_buffer_new_and_alloc (samples * pitch->info.bpf); buffer = gst_buffer_new_and_alloc (samples * pitch->info.bpf);
GstMapInfo info;
gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE); gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE);
samples = samples =
priv->st->receiveSamples ((soundtouch::SAMPLETYPE *) info.data, samples); priv->st->receiveSamples ((soundtouch::SAMPLETYPE *) info.data,
samples);
gst_buffer_unmap (buffer, &info); gst_buffer_unmap (buffer, &info);
if (samples <= 0) { if (samples > 0) {
gst_buffer_unref (buffer);
return NULL;
}
GST_BUFFER_DURATION (buffer) = GST_BUFFER_DURATION (buffer) =
gst_util_uint64_scale (samples, GST_SECOND, pitch->info.rate); gst_util_uint64_scale (samples, GST_SECOND, pitch->info.rate);
/* 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;
} else {
gst_buffer_unref (buffer);
buffer = NULL;
}
}
GST_OBJECT_UNLOCK (pitch);
return buffer; return buffer;
} }
@ -397,10 +421,12 @@ gst_pitch_flush_buffer (GstPitch * pitch, gboolean send)
GstBuffer *buffer; GstBuffer *buffer;
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
GST_OBJECT_LOCK (pitch);
if (priv->st->numUnprocessedSamples () != 0) { if (priv->st->numUnprocessedSamples () != 0) {
GST_DEBUG_OBJECT (pitch, "flushing buffer"); GST_DEBUG_OBJECT (pitch, "flushing SoundTouch buffer");
priv->st->flush (); priv->st->flush ();
} }
GST_OBJECT_UNLOCK (pitch);
if (!send) if (!send)
return GST_FLOW_OK; return GST_FLOW_OK;
@ -560,6 +586,7 @@ gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
gfloat stream_time_ratio; gfloat stream_time_ratio;
gint64 next_buffer_offset; gint64 next_buffer_offset;
GstClockTime next_buffer_time; GstClockTime next_buffer_time;
GstClockTimeDiff min_latency, max_latency;
pitch = GST_PITCH (parent); pitch = GST_PITCH (parent);
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
@ -570,6 +597,8 @@ gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
stream_time_ratio = priv->stream_time_ratio; stream_time_ratio = priv->stream_time_ratio;
next_buffer_time = pitch->next_buffer_time; next_buffer_time = pitch->next_buffer_time;
next_buffer_offset = pitch->next_buffer_offset; next_buffer_offset = pitch->next_buffer_offset;
min_latency = pitch->min_latency;
max_latency = pitch->max_latency;
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
switch (GST_QUERY_TYPE (query)) { switch (GST_QUERY_TYPE (query)) {
@ -654,12 +683,11 @@ gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT
", max %" GST_TIME_FORMAT, ", max %" GST_TIME_FORMAT,
GST_TIME_ARGS (pitch->min_latency), GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
GST_TIME_ARGS (pitch->max_latency));
min += pitch->min_latency; min += min_latency;
if (max != GST_CLOCK_TIME_NONE) if (max != GST_CLOCK_TIME_NONE)
max += pitch->max_latency; max += max_latency;
GST_DEBUG ("Calculated total latency : min %" GST_DEBUG ("Calculated total latency : min %"
GST_TIME_FORMAT " max %" GST_TIME_FORMAT, GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
@ -693,6 +721,7 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event)
GstSegment seg; GstSegment seg;
g_return_val_if_fail (event, FALSE); g_return_val_if_fail (event, FALSE);
g_return_val_if_fail (*event, FALSE);
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
@ -765,26 +794,37 @@ gst_pitch_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_STOP: case GST_EVENT_FLUSH_STOP:
gst_pitch_flush_buffer (pitch, FALSE); gst_pitch_flush_buffer (pitch, FALSE);
GST_OBJECT_LOCK (pitch);
priv->st->clear (); priv->st->clear ();
pitch->next_buffer_offset = 0; pitch->next_buffer_offset = 0;
pitch->next_buffer_time = GST_CLOCK_TIME_NONE; pitch->next_buffer_time = GST_CLOCK_TIME_NONE;
pitch->min_latency = pitch->max_latency = 0; pitch->min_latency = pitch->max_latency = 0;
GST_OBJECT_UNLOCK (pitch);
break; break;
case GST_EVENT_EOS: case GST_EVENT_EOS:
gst_pitch_flush_buffer (pitch, TRUE); gst_pitch_flush_buffer (pitch, TRUE);
GST_OBJECT_LOCK (pitch);
priv->st->clear (); priv->st->clear ();
pitch->min_latency = pitch->max_latency = 0; pitch->min_latency = pitch->max_latency = 0;
GST_OBJECT_UNLOCK (pitch);
break; break;
case GST_EVENT_SEGMENT: case GST_EVENT_SEGMENT:
if (!gst_pitch_process_segment (pitch, &event)) { if (!gst_pitch_process_segment (pitch, &event)) {
GST_LOG_OBJECT (pad, "not enough data known, stalling segment"); GST_LOG_OBJECT (pad, "not enough data known, stalling segment");
if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment)
gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment); GST_OBJECT_LOCK (pitch);
GST_PITCH_GET_PRIVATE (pitch)->pending_segment = event; if (priv->pending_segment)
gst_event_unref (priv->pending_segment);
priv->pending_segment = event;
GST_OBJECT_UNLOCK (pitch);
event = NULL; event = NULL;
} }
GST_OBJECT_LOCK (pitch);
priv->st->clear (); priv->st->clear ();
pitch->min_latency = pitch->max_latency = 0; pitch->min_latency = pitch->max_latency = 0;
GST_OBJECT_UNLOCK (pitch);
break; break;
case GST_EVENT_CAPS: case GST_EVENT_CAPS:
{ {
@ -814,7 +854,9 @@ gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
{ {
GstClockTimeDiff current_latency, min_latency, max_latency; GstClockTimeDiff current_latency, min_latency, max_latency;
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
gboolean post_new_latency_message = FALSE;
GST_OBJECT_LOCK (pitch);
current_latency = current_latency =
(GstClockTimeDiff) (timestamp / priv->stream_time_ratio) - (GstClockTimeDiff) (timestamp / priv->stream_time_ratio) -
pitch->next_buffer_time; pitch->next_buffer_time;
@ -825,12 +867,15 @@ gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) { if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
pitch->min_latency = min_latency; pitch->min_latency = min_latency;
pitch->max_latency = max_latency; pitch->max_latency = max_latency;
post_new_latency_message = TRUE;
}
GST_OBJECT_UNLOCK (pitch);
if (post_new_latency_message) {
/* FIXME: what about the LATENCY event? It only has /* FIXME: what about the LATENCY event? It only has
* one latency value, should it be current, min or max? * one latency value, should it be current, min or max?
* Should it include upstream latencies? * Should it include upstream latencies?
*/ */
gst_element_post_message (GST_ELEMENT (pitch), gst_element_post_message (GST_ELEMENT (pitch),
gst_message_new_latency (GST_OBJECT (pitch))); gst_message_new_latency (GST_OBJECT (pitch)));
} }
@ -839,74 +884,72 @@ gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
static GstFlowReturn static GstFlowReturn
gst_pitch_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) gst_pitch_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{ {
GstPitch *pitch; GstPitch *pitch = GST_PITCH (parent);
GstPitchPrivate *priv; GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
GstClockTime timestamp;
GstMapInfo info;
pitch = GST_PITCH (parent); GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
priv = GST_PITCH_GET_PRIVATE (pitch);
timestamp = GST_BUFFER_TIMESTAMP (buffer);
// Remember the first time and corresponding offset
if (!GST_CLOCK_TIME_IS_VALID (pitch->next_buffer_time)) {
gfloat stream_time_ratio;
GstFormat out_format = GST_FORMAT_DEFAULT;
GST_OBJECT_LOCK (pitch); GST_OBJECT_LOCK (pitch);
stream_time_ratio = priv->stream_time_ratio; GstClockTime next_buffer_time = pitch->next_buffer_time;
gfloat stream_time_ratio = priv->stream_time_ratio;
gint bytes_per_frame = pitch->info.bpf;
GstEvent *pending_segment = priv->pending_segment;
priv->pending_segment = NULL;
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
pitch->next_buffer_time = timestamp / stream_time_ratio; // Remember the first time and corresponding offset
if (!GST_CLOCK_TIME_IS_VALID (next_buffer_time)) {
GstFormat out_format = GST_FORMAT_DEFAULT;
gint64 next_buffer_offset;
next_buffer_time = timestamp / stream_time_ratio;
gst_pitch_convert (pitch, GST_FORMAT_TIME, timestamp, &out_format, gst_pitch_convert (pitch, GST_FORMAT_TIME, timestamp, &out_format,
&pitch->next_buffer_offset); &next_buffer_offset);
GST_OBJECT_LOCK (pitch);
pitch->next_buffer_time = next_buffer_time;
pitch->next_buffer_offset = next_buffer_offset;
GST_OBJECT_UNLOCK (pitch);
} }
gst_object_sync_values (GST_OBJECT (pitch), pitch->next_buffer_time); gst_object_sync_values (GST_OBJECT (pitch), next_buffer_time);
/* 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_TIME_FORMAT, GST_LOG_OBJECT (pitch, "incoming buffer (%d samples) %" GST_TIME_FORMAT,
(gint) (gst_buffer_get_size (buffer) / pitch->info.bpf), (gint) (gst_buffer_get_size (buffer) / bytes_per_frame),
GST_TIME_ARGS (timestamp)); GST_TIME_ARGS (timestamp));
if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) { if (pending_segment) {
GstEvent *event =
gst_event_copy (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
GST_LOG_OBJECT (pitch, "processing stalled segment"); GST_LOG_OBJECT (pitch, "processing stalled segment");
if (!gst_pitch_process_segment (pitch, &event)) {
if (!gst_pitch_process_segment (pitch, &pending_segment)) {
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
gst_event_unref (event); gst_event_unref (pending_segment);
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
if (!gst_pad_event_default (pitch->sinkpad, parent, event)) { if (!gst_pad_event_default (pitch->sinkpad, parent, pending_segment)) {
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
gst_event_unref (event);
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
} }
GstMapInfo info;
gst_buffer_map (buffer, &info, GST_MAP_READ); gst_buffer_map (buffer, &info, GST_MAP_READ);
GST_OBJECT_LOCK (pitch); GST_OBJECT_LOCK (pitch);
priv->st->putSamples ((soundtouch::SAMPLETYPE *) info.data, priv->st->putSamples ((soundtouch::SAMPLETYPE *) info.data,
info.size / pitch->info.bpf); info.size / bytes_per_frame);
gboolean has_output_samples_available = !priv->st->isEmpty ();
GST_OBJECT_UNLOCK (pitch); GST_OBJECT_UNLOCK (pitch);
gst_buffer_unmap (buffer, &info); gst_buffer_unmap (buffer, &info);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
/* Calculate latency */ /* Calculate latency */
gst_pitch_update_latency (pitch, timestamp); gst_pitch_update_latency (pitch, timestamp);
/* and try to extract some samples from the soundtouch buffer */
if (!priv->st->isEmpty ()) {
GstBuffer *out_buffer;
out_buffer = gst_pitch_prepare_buffer (pitch); /* and try to extract some samples from the soundtouch buffer */
if (has_output_samples_available) {
GstBuffer *out_buffer = gst_pitch_prepare_buffer (pitch);
if (out_buffer) if (out_buffer)
return gst_pitch_forward_buffer (pitch, out_buffer); return gst_pitch_forward_buffer (pitch, out_buffer);
} }
@ -922,15 +965,13 @@ gst_pitch_change_state (GstElement * element, GstStateChange transition)
GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch); GstPitchPrivate *priv = GST_PITCH_GET_PRIVATE (pitch);
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
GST_OBJECT_LOCK (pitch);
pitch->next_buffer_time = GST_CLOCK_TIME_NONE; pitch->next_buffer_time = GST_CLOCK_TIME_NONE;
pitch->next_buffer_offset = 0; pitch->next_buffer_offset = 0;
priv->st->clear (); priv->st->clear ();
pitch->min_latency = pitch->max_latency = 0; pitch->min_latency = pitch->max_latency = 0;
break; GST_OBJECT_UNLOCK (pitch);
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break; break;
default: default:
break; break;
@ -941,15 +982,14 @@ gst_pitch_change_state (GstElement * element, GstStateChange transition)
return ret; return ret;
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:
if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) { GST_OBJECT_LOCK (pitch);
gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment); if (priv->pending_segment) {
GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL; gst_event_unref (priv->pending_segment);
priv->pending_segment = NULL;
} }
GST_OBJECT_UNLOCK (pitch);
break; break;
case GST_STATE_CHANGE_READY_TO_NULL:
default: default:
break; break;
} }