static gboolean gst_play_sink_convert_bin_sink_setcaps (GstPlaySinkConvertBin * self, GstCaps * caps) { GstStructure *s; const gchar *name; gboolean reconfigure = FALSE; gboolean raw; GST_DEBUG_OBJECT (self, "setcaps"); GST_PLAY_SINK_CONVERT_BIN_LOCK (self); s = gst_caps_get_structure (caps, 0); name = gst_structure_get_name (s); if (self->audio) { raw = g_str_equal (name, "audio/x-raw"); } else { raw = g_str_equal (name, "video/x-raw"); } GST_DEBUG_OBJECT (self, "raw %d, self->raw %d, blocked %d", raw, self->raw, gst_pad_is_blocked (self->sink_proxypad)); if (raw) { if (!gst_pad_is_blocked (self->sink_proxypad)) { GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (self->sinkpad)); if (!self->raw || (target && !gst_pad_query_accept_caps (target, caps))) { if (!self->raw) GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); else GST_DEBUG_OBJECT (self, "Changing caps in an incompatible way"); reconfigure = TRUE; block_proxypad (self); } if (target) gst_object_unref (target); } } else { if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw"); reconfigure = TRUE; block_proxypad (self); } } /* Otherwise the setcaps below fails */ if (reconfigure) { gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT, caps); return TRUE; }
static GstStateChangeReturn gst_play_sink_convert_bin_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret; GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (element); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); unblock_proxypad (self); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; case GST_STATE_CHANGE_READY_TO_PAUSED: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); gst_play_sink_convert_bin_set_targets (self, TRUE); self->raw = FALSE; GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); if (ret == GST_STATE_CHANGE_FAILURE) return ret; switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); gst_play_sink_convert_bin_set_targets (self, TRUE); self->raw = FALSE; GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; case GST_STATE_CHANGE_READY_TO_PAUSED: GST_PLAY_SINK_CONVERT_BIN_LOCK (self); unblock_proxypad (self); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); break; default: break; } return ret; }
static gboolean gst_play_sink_convert_bin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN (parent); gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); ret = gst_play_sink_convert_bin_sink_setcaps (self, caps); break; } default: break; } ret = gst_pad_event_default (pad, parent, gst_event_ref (event)); if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { GstSegment seg; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); gst_event_copy_segment (event, &seg); GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT, &self->segment); self->segment = seg; GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT, &self->segment); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { GST_PLAY_SINK_CONVERT_BIN_LOCK (self); GST_DEBUG_OBJECT (self, "Resetting segment"); gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); } gst_event_unref (event); return ret; }
static GstPadProbeReturn pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { GstPlaySinkConvertBin *self = user_data; GstPad *peer; GstCaps *caps; gboolean raw; if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) { GST_DEBUG_OBJECT (self, "Letting non-serialized event %s pass", GST_EVENT_TYPE_NAME (info->data)); return GST_PAD_PROBE_PASS; } GST_PLAY_SINK_CONVERT_BIN_LOCK (self); GST_DEBUG_OBJECT (self, "Pad blocked"); /* There must be a peer at this point */ peer = gst_pad_get_peer (self->sinkpad); caps = gst_pad_get_current_caps (peer); if (!caps) caps = gst_pad_query_caps (peer, NULL); gst_object_unref (peer); raw = is_raw_caps (caps, self->audio); GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw); gst_caps_unref (caps); if (raw == self->raw) goto unblock; self->raw = raw; gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL); gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL); if (raw) { GST_DEBUG_OBJECT (self, "Switching to raw conversion pipeline"); if (self->conversion_elements) g_list_foreach (self->conversion_elements, (GFunc) gst_play_sink_convert_bin_on_element_added, self); } else { GST_DEBUG_OBJECT (self, "Switch to passthrough pipeline"); gst_play_sink_convert_bin_on_element_added (self->identity, self); } gst_play_sink_convert_bin_set_targets (self, !raw); unblock: self->sink_proxypad_block_id = 0; GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); return GST_PAD_PROBE_REMOVE; }
static void gst_play_sink_audio_convert_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); GST_PLAY_SINK_CONVERT_BIN_LOCK (self); switch (prop_id) { case PROP_USE_CONVERTERS: g_value_set_boolean (value, self->use_converters); break; case PROP_USE_VOLUME: g_value_set_boolean (value, self->use_volume); break; default: break; } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); }
static GstCaps * gst_play_sink_convert_bin_getcaps (GstPad * pad, GstCaps * filter) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); GstCaps *ret; GstPad *otherpad, *peer; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); if (pad == self->srcpad) { otherpad = self->sinkpad; } else if (pad == self->sinkpad) { otherpad = self->srcpad; } else { GST_ERROR_OBJECT (pad, "Not one of our pads"); otherpad = NULL; } if (otherpad) { peer = gst_pad_get_peer (otherpad); if (peer) { GstCaps *peer_caps = gst_pad_query_caps (peer, filter); gst_object_unref (peer); if (self->converter_caps && is_raw_caps (peer_caps, self->audio)) { ret = gst_caps_merge (peer_caps, gst_caps_ref (self->converter_caps)); } else { ret = peer_caps; } } else { ret = gst_caps_ref (self->converter_caps); } } else { ret = gst_caps_new_any (); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); gst_object_unref (self); GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret); return ret; }
static void gst_play_sink_audio_convert_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object); gboolean v, changed = FALSE; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); switch (prop_id) { case PROP_USE_CONVERTERS: v = g_value_get_boolean (value); if (v != self->use_converters) { self->use_converters = v; changed = TRUE; } break; case PROP_USE_VOLUME: v = g_value_get_boolean (value); if (v != self->use_volume) { self->use_volume = v; changed = TRUE; } break; default: break; } if (changed) { GstPlaySinkConvertBin *cbin = GST_PLAY_SINK_CONVERT_BIN (self); GST_DEBUG_OBJECT (self, "Rebuilding converter bin"); gst_play_sink_convert_bin_remove_elements (cbin); gst_play_sink_audio_convert_add_conversion_elements (self); gst_play_sink_convert_bin_add_identity (cbin); gst_play_sink_convert_bin_cache_converter_caps (cbin); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); }
static GstCaps * gst_play_sink_convert_bin_getcaps (GstPad * pad, GstCaps * filter) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); GstCaps *ret; GstPad *otherpad, *peer; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); if (pad == self->srcpad) { otherpad = self->sinkpad; } else if (pad == self->sinkpad) { otherpad = self->srcpad; } else { GST_ERROR_OBJECT (pad, "Not one of our pads"); otherpad = NULL; } if (otherpad) { peer = gst_pad_get_peer (otherpad); if (peer) { GstCaps *peer_caps; GstCaps *downstream_filter = NULL; /* Add all the caps that we can convert to to the filter caps, * otherwise downstream might just return EMPTY caps because * it doesn't handle the filter caps but we could still convert * to these caps */ if (filter) { guint i, n; downstream_filter = gst_caps_new_empty (); /* Intersect raw video caps in the filter caps with the converter * caps. This makes sure that we don't accept raw video that we * can't handle, e.g. because of caps features */ n = gst_caps_get_size (filter); for (i = 0; i < n; i++) { GstStructure *s; GstCaps *tmp, *tmp2; s = gst_structure_copy (gst_caps_get_structure (filter, i)); if (gst_structure_has_name (s, self->audio ? "audio/x-raw" : "video/x-raw")) { tmp = gst_caps_new_full (s, NULL); tmp2 = gst_caps_intersect (tmp, self->converter_caps); gst_caps_append (downstream_filter, tmp2); gst_caps_unref (tmp); } else { gst_caps_append_structure (downstream_filter, s); } } downstream_filter = gst_caps_merge (downstream_filter, gst_caps_ref (self->converter_caps)); } peer_caps = gst_pad_query_caps (peer, downstream_filter); if (downstream_filter) gst_caps_unref (downstream_filter); gst_object_unref (peer); if (self->converter_caps && is_raw_caps (peer_caps, self->audio)) { GstCaps *converter_caps = gst_caps_ref (self->converter_caps); GstCapsFeatures *cf; GstStructure *s; guint i, n; ret = gst_caps_make_writable (peer_caps); /* Filter out ANY capsfeatures from the converter caps. We can't * convert to ANY capsfeatures, they are only there so that we * can passthrough whatever downstream can support... but we * definitely don't want to return them here */ n = gst_caps_get_size (converter_caps); for (i = 0; i < n; i++) { s = gst_caps_get_structure (converter_caps, i); cf = gst_caps_get_features (converter_caps, i); if (cf && gst_caps_features_is_any (cf)) continue; ret = gst_caps_merge_structure_full (ret, gst_structure_copy (s), (cf ? gst_caps_features_copy (cf) : NULL)); } gst_caps_unref (converter_caps); } else { ret = peer_caps; } } else { ret = gst_caps_ref (self->converter_caps); } GST_PLAY_SINK_CONVERT_BIN_FILTER_CAPS (filter, ret); } else { ret = filter ? gst_caps_ref (filter) : gst_caps_new_any (); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); gst_object_unref (self); GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret); return ret; }
static GstCaps * gst_play_sink_convert_bin_getcaps (GstPad * pad, GstCaps * filter) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN (gst_pad_get_parent (pad)); GstCaps *ret; GstPad *otherpad, *peer; GST_PLAY_SINK_CONVERT_BIN_LOCK (self); if (pad == self->srcpad) { otherpad = self->sinkpad; } else if (pad == self->sinkpad) { otherpad = self->srcpad; } else { GST_ERROR_OBJECT (pad, "Not one of our pads"); otherpad = NULL; } if (otherpad) { peer = gst_pad_get_peer (otherpad); if (peer) { GstCaps *peer_caps; GstCaps *downstream_filter = NULL; /* Add all the caps that we can convert to to the filter caps, * otherwise downstream might just return EMPTY caps because * it doesn't handle the filter caps but we could still convert * to these caps */ if (filter) { downstream_filter = gst_caps_copy (filter); downstream_filter = gst_caps_merge (downstream_filter, gst_caps_ref (self->converter_caps)); } peer_caps = gst_pad_query_caps (peer, downstream_filter); if (downstream_filter) gst_caps_unref (downstream_filter); gst_object_unref (peer); if (self->converter_caps && is_raw_caps (peer_caps, self->audio)) { ret = gst_caps_merge (peer_caps, gst_caps_ref (self->converter_caps)); } else { ret = peer_caps; } } else { ret = gst_caps_ref (self->converter_caps); } } else { ret = gst_caps_new_any (); } GST_PLAY_SINK_CONVERT_BIN_UNLOCK (self); gst_object_unref (self); if (filter) { GstCaps *intersection = gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (ret); ret = intersection; } GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret); return ret; }