+/* Caps negotiation */
+
+/* Unref after usage */
+static GstAudioAggregatorPad *
+gst_audio_aggregator_get_first_configured_pad (GstAggregator * agg)
+{
+ GstAudioAggregatorPad *res = NULL;
+ GList *l;
+
+ GST_OBJECT_LOCK (agg);
+ for (l = GST_ELEMENT (agg)->sinkpads; l; l = l->next) {
+ GstAudioAggregatorPad *aaggpad = l->data;
+
+ if (GST_AUDIO_INFO_FORMAT (&aaggpad->info) != GST_AUDIO_FORMAT_UNKNOWN) {
+ res = gst_object_ref (aaggpad);
+ break;
+ }
+ }
+ GST_OBJECT_UNLOCK (agg);
+
+ return res;
+}
+
+static GstCaps *
+gst_audio_aggregator_sink_getcaps (GstPad * pad, GstAggregator * agg,
+ GstCaps * filter)
+{
+ GstAudioAggregatorPad *first_configured_pad =
+ gst_audio_aggregator_get_first_configured_pad (agg);
+ GstCaps *sink_template_caps = gst_pad_get_pad_template_caps (pad);
+ GstCaps *downstream_caps = gst_pad_get_allowed_caps (agg->srcpad);
+ GstCaps *sink_caps;
+ GstStructure *s, *s2;
+ gint downstream_rate;
+
+ sink_template_caps = gst_caps_make_writable (sink_template_caps);
+ s = gst_caps_get_structure (sink_template_caps, 0);
+
+ if (downstream_caps && !gst_caps_is_empty (downstream_caps))
+ s2 = gst_caps_get_structure (downstream_caps, 0);
+ else
+ s2 = NULL;
+
+ if (s2 && gst_structure_get_int (s2, "rate", &downstream_rate)) {
+ gst_structure_fixate_field_nearest_int (s, "rate", downstream_rate);
+ } else if (first_configured_pad) {
+ gst_structure_fixate_field_nearest_int (s, "rate",
+ first_configured_pad->info.rate);
+ }
+
+ if (first_configured_pad)
+ gst_object_unref (first_configured_pad);
+
+ sink_caps = filter ? gst_caps_intersect (sink_template_caps,
+ filter) : gst_caps_ref (sink_template_caps);
+
+ GST_INFO_OBJECT (pad, "Getting caps with filter %" GST_PTR_FORMAT, filter);
+ GST_DEBUG_OBJECT (pad, "sink template caps : %" GST_PTR_FORMAT,
+ sink_template_caps);
+ GST_DEBUG_OBJECT (pad, "downstream caps %" GST_PTR_FORMAT, downstream_caps);
+ GST_INFO_OBJECT (pad, "returned sink caps : %" GST_PTR_FORMAT, sink_caps);
+
+ gst_caps_unref (sink_template_caps);
+
+ if (downstream_caps)
+ gst_caps_unref (downstream_caps);
+
+ return sink_caps;
+}
+
+static gboolean
+gst_audio_aggregator_sink_setcaps (GstAudioAggregatorPad * aaggpad,
+ GstAggregator * agg, GstCaps * caps)
+{
+ GstAudioAggregatorPad *first_configured_pad =
+ gst_audio_aggregator_get_first_configured_pad (agg);
+ GstCaps *downstream_caps = gst_pad_get_allowed_caps (agg->srcpad);
+ GstAudioInfo info;
+ gboolean ret = TRUE;
+ gint downstream_rate;
+ GstStructure *s;
+
+ if (!downstream_caps || gst_caps_is_empty (downstream_caps)) {
+ ret = FALSE;
+ goto done;
+ }
+
+ gst_audio_info_from_caps (&info, caps);
+ s = gst_caps_get_structure (downstream_caps, 0);
+
+ /* TODO: handle different rates on sinkpads, a bit complex
+ * because offsets will have to be updated, and audio resampling
+ * has a latency to take into account
+ */
+ if ((gst_structure_get_int (s, "rate", &downstream_rate)
+ && info.rate != downstream_rate) || (first_configured_pad
+ && info.rate != first_configured_pad->info.rate)) {
+ gst_pad_push_event (GST_PAD (aaggpad), gst_event_new_reconfigure ());
+ gst_object_unref (first_configured_pad);
+ ret = FALSE;
+ } else {
+ GST_OBJECT_LOCK (aaggpad);
+ gst_audio_info_from_caps (&aaggpad->info, caps);
+ if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad))
+ GST_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)->
+ priv->converter_config_changed = TRUE;
+ GST_OBJECT_UNLOCK (aaggpad);
+ }
+
+done:
+ if (downstream_caps)
+ gst_caps_unref (downstream_caps);
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_audio_aggregator_update_src_caps (GstAggregator * agg,
+ GstCaps * caps, GstCaps ** ret)
+{
+ GstCaps *src_template_caps = gst_pad_get_pad_template_caps (agg->srcpad);
+ GstCaps *downstream_caps =
+ gst_pad_peer_query_caps (agg->srcpad, src_template_caps);
+
+ gst_caps_unref (src_template_caps);
+
+ *ret = gst_caps_intersect (caps, downstream_caps);
+
+ GST_INFO ("Updated src caps to %" GST_PTR_FORMAT, *ret);
+
+ if (downstream_caps)
+ gst_caps_unref (downstream_caps);
+
+ return GST_FLOW_OK;
+}
+
+/* At that point if the caps are not fixed, this means downstream
+ * didn't have fully specified requirements, we'll just go ahead
+ * and fixate raw audio fields using our first configured pad, we don't for
+ * now need a more complicated heuristic
+ */
+static GstCaps *
+gst_audio_aggregator_fixate_src_caps (GstAggregator * agg, GstCaps * caps)
+{
+ GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (agg);
+ GstAudioAggregatorPad *first_configured_pad;
+
+ if (!aaggclass->convert_buffer)
+ return
+ GST_AGGREGATOR_CLASS
+ (gst_audio_aggregator_parent_class)->fixate_src_caps (agg, caps);
+
+ first_configured_pad = gst_audio_aggregator_get_first_configured_pad (agg);
+
+ if (first_configured_pad) {
+ GstStructure *s, *s2;
+ GstCaps *first_configured_caps =
+ gst_audio_info_to_caps (&first_configured_pad->info);
+ gint first_configured_rate, first_configured_channels;
+
+ caps = gst_caps_make_writable (caps);
+ s = gst_caps_get_structure (caps, 0);
+ s2 = gst_caps_get_structure (first_configured_caps, 0);
+
+ gst_structure_get_int (s2, "rate", &first_configured_rate);
+ gst_structure_get_int (s2, "channels", &first_configured_channels);
+
+ gst_structure_fixate_field_string (s, "format",
+ gst_structure_get_string (s2, "format"));
+ gst_structure_fixate_field_string (s, "layout",
+ gst_structure_get_string (s2, "layout"));
+ gst_structure_fixate_field_nearest_int (s, "rate", first_configured_rate);
+ gst_structure_fixate_field_nearest_int (s, "channels",
+ first_configured_channels);
+
+ gst_caps_unref (first_configured_caps);
+ gst_object_unref (first_configured_pad);
+ }
+
+ if (!gst_caps_is_fixed (caps))
+ caps = gst_caps_fixate (caps);
+
+ GST_INFO_OBJECT (agg, "Fixated src caps to %" GST_PTR_FORMAT, caps);
+
+ return caps;
+}
+
+/* Must be called with OBJECT_LOCK taken */
+static void
+gst_audio_aggregator_update_converters (GstAudioAggregator * aagg,
+ GstAudioInfo * new_info)
+{
+ GList *l;
+
+ for (l = GST_ELEMENT (aagg)->sinkpads; l; l = l->next) {
+ GstAudioAggregatorPad *aaggpad = l->data;
+
+ if (GST_IS_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad))
+ GST_AUDIO_AGGREGATOR_CONVERT_PAD (aaggpad)->
+ priv->converter_config_changed = TRUE;
+
+ /* If we currently were mixing a buffer, we need to convert it to the new
+ * format */
+ if (aaggpad->priv->buffer) {
+ GstBuffer *new_converted_buffer =
+ gst_audio_aggregator_convert_buffer (aagg, GST_PAD (aaggpad),
+ &aaggpad->info, new_info, aaggpad->priv->input_buffer);
+ gst_buffer_replace (&aaggpad->priv->buffer, new_converted_buffer);
+ }
+ }
+}
+
+/* We now have our final output caps, we can create the required converters */
+static gboolean
+gst_audio_aggregator_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
+{
+ GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (agg);
+ GstAudioAggregatorClass *aaggclass = GST_AUDIO_AGGREGATOR_GET_CLASS (agg);
+ GstAudioInfo info;
+
+ GST_INFO_OBJECT (agg, "src caps negotiated %" GST_PTR_FORMAT, caps);
+
+ if (!gst_audio_info_from_caps (&info, caps)) {
+ GST_WARNING_OBJECT (aagg, "Rejecting invalid caps: %" GST_PTR_FORMAT, caps);
+ return FALSE;
+ }
+
+ GST_AUDIO_AGGREGATOR_LOCK (aagg);
+ GST_OBJECT_LOCK (aagg);
+
+ if (aaggclass->convert_buffer) {
+ gst_audio_aggregator_update_converters (aagg, &info);
+
+ if (aagg->priv->current_buffer
+ && !gst_audio_info_is_equal (&aagg->info, &info)) {
+ GstBuffer *converted =
+ gst_audio_aggregator_convert_buffer (aagg, agg->srcpad, &aagg->info,
+ &info, aagg->priv->current_buffer);
+ gst_buffer_unref (aagg->priv->current_buffer);
+ aagg->priv->current_buffer = converted;
+ }
+ }
+
+ if (!gst_audio_info_is_equal (&info, &aagg->info)) {
+ GST_INFO_OBJECT (aagg, "setting caps to %" GST_PTR_FORMAT, caps);
+ gst_caps_replace (&aagg->current_caps, caps);
+
+ memcpy (&aagg->info, &info, sizeof (info));
+ }
+
+ GST_OBJECT_UNLOCK (aagg);
+ GST_AUDIO_AGGREGATOR_UNLOCK (aagg);
+
+ return
+ GST_AGGREGATOR_CLASS
+ (gst_audio_aggregator_parent_class)->negotiated_src_caps (agg, caps);
+}