mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-21 07:46:38 +00:00
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:
parent
9f5bb30d3a
commit
35165da586
1 changed files with 125 additions and 85 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue