audiointerleave: Set src caps in aggregate

This prevents races between the setcaps of the sink pads

https://bugzilla.gnome.org/show_bug.cgi?id=740236
This commit is contained in:
Olivier Crête 2015-03-06 13:49:48 -05:00
parent fb8339de40
commit edde3c326e
2 changed files with 79 additions and 105 deletions

View file

@ -322,14 +322,14 @@ gst_audio_interleave_channel_positions_to_mask (GValueArray * positions,
return ret; return ret;
} }
static void
gst_audio_interleave_set_channel_positions (GstAudioInterleave * self, /* Must be called with the object lock held */
GstStructure * s)
static guint64
gst_audio_interleave_get_channel_mask (GstAudioInterleave * self)
{ {
guint64 channel_mask = 0; guint64 channel_mask = 0;
GST_OBJECT_LOCK (self);
if (self->channel_positions != NULL && if (self->channel_positions != NULL &&
self->channels == self->channel_positions->n_values) { self->channels == self->channel_positions->n_values) {
if (!gst_audio_interleave_channel_positions_to_mask if (!gst_audio_interleave_channel_positions_to_mask
@ -341,9 +341,8 @@ gst_audio_interleave_set_channel_positions (GstAudioInterleave * self,
} else { } else {
GST_WARNING_OBJECT (self, "Using NONE channel positions"); GST_WARNING_OBJECT (self, "Using NONE channel positions");
} }
gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
GST_OBJECT_UNLOCK (self); return channel_mask;
} }
@ -377,11 +376,10 @@ interleave_24 (guint8 * out, guint8 * in, guint stride, guint nframes)
} }
static void static void
gst_audio_interleave_set_process_function (GstAudioInterleave * self) gst_audio_interleave_set_process_function (GstAudioInterleave * self,
GstAudioInfo * info)
{ {
GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); switch (GST_AUDIO_INFO_WIDTH (info)) {
switch (GST_AUDIO_INFO_WIDTH (&aagg->info)) {
case 8: case 8:
self->func = (GstInterleaveFunc) interleave_8; self->func = (GstInterleaveFunc) interleave_8;
break; break;
@ -412,56 +410,17 @@ gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad,
GstCaps * caps) GstCaps * caps)
{ {
GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self); GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self);
GstAudioAggregatorPad *aaggpad = GST_AUDIO_AGGREGATOR_PAD (pad);
GstAudioInfo info; GstAudioInfo info;
GValue *val; GValue *val;
guint channel; guint channel;
GstCaps *srccaps; gboolean new = FALSE;
GstStructure *s;
gboolean ret;
if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps))
goto cannot_change_caps;
if (!gst_audio_info_from_caps (&info, caps)) if (!gst_audio_info_from_caps (&info, caps))
goto invalid_format; goto invalid_format;
if (aaggpad->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN) GST_OBJECT_LOCK (self);
g_atomic_int_add (&self->configured_sinkpads_counter, 1); if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps))
goto cannot_change_caps;
gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad),
caps);
if (self->channel_positions_from_input
&& GST_AUDIO_INFO_CHANNELS (&info) == 1) {
channel = GST_AUDIO_INTERLEAVE_PAD (pad)->channel;
val = g_value_array_get_nth (self->input_channel_positions, channel);
g_value_set_enum (val, GST_AUDIO_INFO_POSITION (&info, 0));
}
if (g_atomic_int_get (&self->configured_sinkpads_counter) < self->channels)
return TRUE;
srccaps = gst_caps_copy (caps);
s = gst_caps_get_structure (srccaps, 0);
gst_structure_remove_field (s, "channel-mask");
gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout",
G_TYPE_STRING, "interleaved", NULL);
gst_audio_interleave_set_channel_positions (self, s);
ret = gst_audio_aggregator_set_src_caps (aagg, srccaps);
gst_caps_unref (srccaps);
if (!ret)
goto src_did_not_accept;
gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad),
caps);
gst_audio_interleave_set_process_function (self);
if (!self->sinkcaps) { if (!self->sinkcaps) {
GstCaps *sinkcaps = gst_caps_copy (caps); GstCaps *sinkcaps = gst_caps_copy (caps);
@ -474,8 +433,23 @@ gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad,
gst_caps_replace (&self->sinkcaps, sinkcaps); gst_caps_replace (&self->sinkcaps, sinkcaps);
gst_caps_unref (sinkcaps); gst_caps_unref (sinkcaps);
new = TRUE;
self->new_caps = TRUE;
} }
if (self->channel_positions_from_input
&& GST_AUDIO_INFO_CHANNELS (&info) == 1) {
channel = GST_AUDIO_INTERLEAVE_PAD (pad)->channel;
val = g_value_array_get_nth (self->input_channel_positions, channel);
g_value_set_enum (val, GST_AUDIO_INFO_POSITION (&info, 0));
}
GST_OBJECT_UNLOCK (self);
gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad),
caps);
if (!new)
return TRUE;
GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps);
@ -490,15 +464,11 @@ invalid_format:
} }
cannot_change_caps: cannot_change_caps:
{ {
GST_OBJECT_UNLOCK (self);
GST_WARNING_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't " GST_WARNING_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't "
"change", self->sinkcaps); "change", self->sinkcaps);
return FALSE; return FALSE;
} }
src_did_not_accept:
{
GST_WARNING_OBJECT (self, "src did not accept setcaps()");
return FALSE;
}
} }
static gboolean static gboolean
@ -532,6 +502,48 @@ gst_audio_interleave_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad,
return res; return res;
} }
static GstFlowReturn
gst_audio_interleave_aggregate (GstAggregator * aggregator, gboolean timeout)
{
GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (aggregator);
GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (aggregator);
GST_OBJECT_LOCK (aggregator);
if (self->new_caps) {
GstCaps *srccaps;
GstStructure *s;
gboolean ret;
srccaps = gst_caps_copy (self->sinkcaps);
s = gst_caps_get_structure (srccaps, 0);
gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout",
G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK,
gst_audio_interleave_get_channel_mask (self), NULL);
GST_OBJECT_UNLOCK (aggregator);
ret = gst_audio_aggregator_set_src_caps (aagg, srccaps);
gst_caps_unref (srccaps);
if (!ret)
goto src_did_not_accept;
GST_OBJECT_LOCK (aggregator);
gst_audio_interleave_set_process_function (self, &aagg->info);
self->new_caps = FALSE;
}
GST_OBJECT_UNLOCK (aggregator);
return GST_AGGREGATOR_CLASS (parent_class)->aggregate (aggregator, timeout);
src_did_not_accept:
GST_WARNING_OBJECT (self, "src did not accept setcaps()");
return GST_FLOW_NOT_NEGOTIATED;;
}
static void static void
gst_audio_interleave_class_init (GstAudioInterleaveClass * klass) gst_audio_interleave_class_init (GstAudioInterleaveClass * klass)
{ {
@ -567,6 +579,7 @@ gst_audio_interleave_class_init (GstAudioInterleaveClass * klass)
agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_query); agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_query);
agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_event); agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_event);
agg_class->stop = gst_audio_interleave_stop; agg_class->stop = gst_audio_interleave_stop;
agg_class->aggregate = gst_audio_interleave_aggregate;
aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer; aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer;
@ -701,6 +714,7 @@ gst_audio_interleave_stop (GstAggregator * agg)
if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg)) if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
return FALSE; return FALSE;
self->new_caps = FALSE;
gst_caps_replace (&self->sinkcaps, NULL); gst_caps_replace (&self->sinkcaps, NULL);
return TRUE; return TRUE;
@ -746,22 +760,9 @@ gst_audio_interleave_request_new_pad (GstElement * element,
g_value_unset (&val); g_value_unset (&val);
/* Update the src caps if we already have them */ /* Update the src caps if we already have them */
if (self->sinkcaps) { GST_OBJECT_LOCK (self);
GstCaps *srccaps; self->new_caps = TRUE;
GstStructure *s; GST_OBJECT_UNLOCK (self);
/* Take lock to make sure processing finishes first */
srccaps = gst_caps_copy (self->sinkcaps);
s = gst_caps_get_structure (srccaps, 0);
gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL);
gst_audio_interleave_set_channel_positions (self, s);
gst_audio_aggregator_set_src_caps (GST_AUDIO_AGGREGATOR (self), srccaps);
gst_caps_unref (srccaps);
}
return GST_PAD_CAST (newpad); return GST_PAD_CAST (newpad);
@ -786,9 +787,6 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad)
g_atomic_int_add (&self->channels, -1); g_atomic_int_add (&self->channels, -1);
if (gst_pad_has_current_caps (pad))
g_atomic_int_add (&self->configured_sinkpads_counter, -1);
position = GST_AUDIO_INTERLEAVE_PAD (pad)->channel; position = GST_AUDIO_INTERLEAVE_PAD (pad)->channel;
g_value_array_remove (self->input_channel_positions, position); g_value_array_remove (self->input_channel_positions, position);
@ -801,32 +799,8 @@ gst_audio_interleave_release_pad (GstElement * element, GstPad * pad)
ipad->channel--; ipad->channel--;
} }
self->new_caps = TRUE;
/* Update the src caps if we already have them */ GST_OBJECT_UNLOCK (self);
if (self->sinkcaps) {
if (self->channels > 0) {
GstCaps *srccaps;
GstStructure *s;
srccaps = gst_caps_copy (self->sinkcaps);
s = gst_caps_get_structure (srccaps, 0);
gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL);
gst_audio_interleave_set_channel_positions (self, s);
GST_OBJECT_UNLOCK (self);
gst_audio_aggregator_set_src_caps (GST_AUDIO_AGGREGATOR (self), srccaps);
gst_caps_unref (srccaps);
} else {
gst_caps_replace (&self->sinkcaps, NULL);
GST_OBJECT_UNLOCK (self);
}
} else {
GST_OBJECT_UNLOCK (self);
}
GST_DEBUG_OBJECT (self, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); GST_DEBUG_OBJECT (self, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));

View file

@ -58,8 +58,8 @@ struct _GstAudioInterleave {
gint padcounter; gint padcounter;
guint channels; guint channels;
gboolean new_caps;
GstCaps *sinkcaps; GstCaps *sinkcaps;
gint configured_sinkpads_counter;
GValueArray *channel_positions; GValueArray *channel_positions;
GValueArray *input_channel_positions; GValueArray *input_channel_positions;