static void gst_interleave_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstInterleave *self = GST_INTERLEAVE (object); switch (prop_id) { case PROP_CHANNEL_POSITIONS: if (self->channel_positions && self->channel_positions != self->input_channel_positions) g_value_array_free (self->channel_positions); self->channel_positions = g_value_dup_boxed (value); self->channel_positions_from_input = FALSE; self->channels = self->channel_positions->n_values; break; case PROP_CHANNEL_POSITIONS_FROM_INPUT: self->channel_positions_from_input = g_value_get_boolean (value); if (self->channel_positions_from_input) { if (self->channel_positions && self->channel_positions != self->input_channel_positions) g_value_array_free (self->channel_positions); self->channel_positions = self->input_channel_positions; } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_interleave_sink_query (GstCollectPads * pads, GstCollectData * data, GstQuery * query, gpointer user_data) { GstInterleave *self = GST_INTERLEAVE (user_data); gboolean ret = TRUE; GST_DEBUG ("Got %s query on pad %s:%s", GST_QUERY_TYPE_NAME (query), GST_DEBUG_PAD_NAME (data->pad)); switch (GST_QUERY_TYPE (query)) { case GST_QUERY_CAPS: { GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); caps = gst_interleave_sink_getcaps (data->pad, self, filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); ret = TRUE; break; } default: ret = gst_collect_pads_query_default (pads, data, query, FALSE); break; } return ret; }
static void gst_interleave_finalize (GObject * object) { GstInterleave *self = GST_INTERLEAVE (object); if (self->collect) { gst_object_unref (self->collect); self->collect = NULL; } if (self->channel_positions && self->channel_positions != self->input_channel_positions) { g_value_array_free (self->channel_positions); self->channel_positions = NULL; } if (self->input_channel_positions) { g_value_array_free (self->input_channel_positions); self->input_channel_positions = NULL; } gst_caps_replace (&self->sinkcaps, NULL); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_interleave_sink_event (GstPad * pad, GstEvent * event) { GstInterleave *self = GST_INTERLEAVE (gst_pad_get_parent (pad)); gboolean ret; GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: /* mark a pending new segment. This event is synchronized * with the streaming thread so we can safely update the * variable without races. It's somewhat weird because we * assume the collectpads forwarded the FLUSH_STOP past us * and downstream (using our source pad, the bastard!). */ self->segment_pending = TRUE; break; default: break; } /* now GstCollectPads can take care of the rest, e.g. EOS */ ret = self->collect_event (pad, event); gst_object_unref (self); return ret; }
static gboolean gst_interleave_src_event (GstPad * pad, GstEvent * event) { GstInterleave *self = GST_INTERLEAVE (gst_pad_get_parent (pad)); gboolean result; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_QOS: /* QoS might be tricky */ result = FALSE; break; case GST_EVENT_SEEK: { GstSeekFlags flags; GstSeekType curtype; gint64 cur; /* parse the seek parameters */ gst_event_parse_seek (event, &self->segment_rate, NULL, &flags, &curtype, &cur, NULL, NULL); /* check if we are flushing */ if (flags & GST_SEEK_FLAG_FLUSH) { /* make sure we accept nothing anymore and return WRONG_STATE */ gst_collect_pads_set_flushing (self->collect, TRUE); /* flushing seek, start flush downstream, the flush will be done * when all pads received a FLUSH_STOP. */ gst_pad_push_event (self->src, gst_event_new_flush_start ()); } /* now wait for the collected to be finished and mark a new * segment */ GST_OBJECT_LOCK (self->collect); if (curtype == GST_SEEK_TYPE_SET) self->segment_position = cur; else self->segment_position = 0; self->segment_pending = TRUE; GST_OBJECT_UNLOCK (self->collect); result = forward_event (self, event); break; } case GST_EVENT_NAVIGATION: /* navigation is rather pointless. */ result = FALSE; break; default: /* just forward the rest for now */ result = forward_event (self, event); break; } gst_object_unref (self); return result; }
static gboolean gst_interleave_src_query (GstPad * pad, GstObject * parent, GstQuery * query) { GstInterleave *self = GST_INTERLEAVE (parent); gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { GstFormat format; gst_query_parse_position (query, &format, NULL); switch (format) { case GST_FORMAT_TIME: /* FIXME, bring to stream time, might be tricky */ gst_query_set_position (query, format, self->timestamp); res = TRUE; break; case GST_FORMAT_BYTES: gst_query_set_position (query, format, self->offset * self->channels * self->width); res = TRUE; break; case GST_FORMAT_DEFAULT: gst_query_set_position (query, format, self->offset); res = TRUE; break; default: break; } break; } case GST_QUERY_DURATION: res = gst_interleave_src_query_duration (self, query); break; case GST_QUERY_LATENCY: res = gst_interleave_src_query_latency (self, query); break; case GST_QUERY_CAPS: { GstCaps *filter, *caps; gst_query_parse_caps (query, &filter); caps = gst_interleave_sink_getcaps (pad, parent, filter); gst_query_set_caps_result (query, caps); gst_caps_unref (caps); res = TRUE; } default: /* FIXME, needs a custom query handler because we have multiple * sinkpads */ res = gst_pad_query_default (pad, parent, query); break; } return res; }
static void gst_interleave_release_pad (GstElement * element, GstPad * pad) { GstInterleave *self = GST_INTERLEAVE (element); GList *l; GstAudioChannelPosition position; g_return_if_fail (GST_IS_INTERLEAVE_PAD (pad)); /* Take lock to make sure we're not changing this when processing buffers */ GST_OBJECT_LOCK (self->collect); 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_INTERLEAVE_PAD_CAST (pad)->channel; g_value_array_remove (self->input_channel_positions, position); /* Update channel numbers */ GST_OBJECT_LOCK (self); for (l = GST_ELEMENT_CAST (self)->sinkpads; l != NULL; l = l->next) { GstInterleavePad *ipad = GST_INTERLEAVE_PAD (l->data); if (GST_INTERLEAVE_PAD_CAST (pad)->channel < ipad->channel) ipad->channel--; } GST_OBJECT_UNLOCK (self); /* Update the src caps if we already have them */ 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_interleave_set_channel_positions (self, s); gst_pad_set_active (self->src, TRUE); gst_pad_set_caps (self->src, srccaps); gst_caps_unref (srccaps); } else { gst_caps_replace (&self->sinkcaps, NULL); } } GST_OBJECT_UNLOCK (self->collect); gst_collect_pads_remove_pad (self->collect, pad); gst_element_remove_pad (element, pad); }
static GstStateChangeReturn gst_interleave_change_state (GstElement * element, GstStateChange transition) { GstInterleave *self; GstStateChangeReturn ret; self = GST_INTERLEAVE (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: self->timestamp = 0; self->offset = 0; self->segment_pending = TRUE; self->segment_position = 0; self->segment_rate = 1.0; gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); gst_collect_pads_start (self->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; default: break; } /* Stop before calling the parent's state change function as * GstCollectPads might take locks and we would deadlock in that * case */ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) gst_collect_pads_stop (self->collect); ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: gst_pad_set_caps (self->src, NULL); gst_caps_replace (&self->sinkcaps, NULL); break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
static void gst_interleave_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstInterleave *self = GST_INTERLEAVE (object); switch (prop_id) { case PROP_CHANNEL_POSITIONS: g_value_set_boxed (value, self->channel_positions); break; case PROP_CHANNEL_POSITIONS_FROM_INPUT: g_value_set_boolean (value, self->channel_positions_from_input); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
/* we can only accept caps that we and downstream can handle. */ static GstCaps * gst_interleave_sink_getcaps (GstPad * pad) { GstInterleave *self = GST_INTERLEAVE (gst_pad_get_parent (pad)); GstCaps *result, *peercaps, *sinkcaps; GST_OBJECT_LOCK (self); /* If we already have caps on one of the sink pads return them */ if (self->sinkcaps) { result = gst_caps_copy (self->sinkcaps); } else { /* get the downstream possible caps */ peercaps = gst_pad_peer_get_caps (self->src); /* get the allowed caps on this sinkpad */ sinkcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); __remove_channels (sinkcaps); if (peercaps) { __remove_channels (peercaps); /* if the peer has caps, intersect */ GST_DEBUG_OBJECT (pad, "intersecting peer and template caps"); result = gst_caps_intersect (peercaps, sinkcaps); gst_caps_unref (peercaps); gst_caps_unref (sinkcaps); } else { /* the peer has no caps (or there is no peer), just use the allowed caps * of this sinkpad. */ GST_DEBUG_OBJECT (pad, "no peer caps, using sinkcaps"); result = sinkcaps; } __set_channels (result, 1); } GST_OBJECT_UNLOCK (self); gst_object_unref (self); GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, result); return result; }
static gboolean gst_interleave_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstInterleave *self = GST_INTERLEAVE (parent); gboolean result; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_QOS: /* QoS might be tricky */ result = FALSE; break; case GST_EVENT_SEEK: { GstSeekFlags flags; gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL); /* check if we are flushing */ if (flags & GST_SEEK_FLAG_FLUSH) { /* make sure we accept nothing anymore and return WRONG_STATE */ gst_collect_pads_set_flushing (self->collect, TRUE); /* flushing seek, start flush downstream, the flush will be done * when all pads received a FLUSH_STOP. */ gst_pad_push_event (self->src, gst_event_new_flush_start ()); } result = forward_event (self, event); break; } case GST_EVENT_NAVIGATION: /* navigation is rather pointless. */ result = FALSE; break; default: /* just forward the rest for now */ result = forward_event (self, event); break; } return result; }
static gboolean gst_interleave_sink_event (GstCollectPads * pads, GstCollectData * data, GstEvent * event, gpointer user_data) { GstInterleave *self = GST_INTERLEAVE (user_data); gboolean ret = TRUE; GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (data->pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: GST_OBJECT_LOCK (self); gst_event_replace (&self->pending_segment, NULL); GST_OBJECT_UNLOCK (self); break; case GST_EVENT_SEGMENT: { GST_OBJECT_LOCK (self); gst_event_replace (&self->pending_segment, event); GST_OBJECT_UNLOCK (self); break; } case GST_EVENT_CAPS: { GstCaps *caps; GstAudioInfo info; GValue *val; guint channel; gst_event_parse_caps (event, &caps); if (!gst_audio_info_from_caps (&info, caps)) { GST_WARNING_OBJECT (self, "invalid sink caps"); gst_event_unref (event); event = NULL; ret = FALSE; break; } if (self->channel_positions_from_input && GST_AUDIO_INFO_CHANNELS (&info) == 1) { channel = GST_INTERLEAVE_PAD_CAST (data->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 (!gst_pad_has_current_caps (data->pad)) g_atomic_int_add (&self->configured_sinkpads_counter, 1); /* Last caps that are set on a sink pad are used as output caps */ if (g_atomic_int_get (&self->configured_sinkpads_counter) == self->channels) { ret = gst_interleave_sink_setcaps (self, data->pad, caps, &info); gst_event_unref (event); event = NULL; } break; } case GST_EVENT_TAG: GST_FIXME_OBJECT (self, "FIXME: merge tags and send after stream-start"); break; default: break; } /* now GstCollectPads can take care of the rest, e.g. EOS */ if (event != NULL) return gst_collect_pads_event_default (pads, data, event, FALSE); return ret; }
static GstPad * gst_interleave_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) { GstInterleave *self = GST_INTERLEAVE (element); GstPad *new_pad; gchar *pad_name; gint channels, padnumber; GValue val = { 0, }; if (templ->direction != GST_PAD_SINK) goto not_sink_pad; padnumber = g_atomic_int_add (&self->padcounter, 1); if (self->channel_positions_from_input) channels = g_atomic_int_add (&self->channels, 1); else channels = padnumber; pad_name = g_strdup_printf ("sink_%u", padnumber); new_pad = GST_PAD_CAST (g_object_new (GST_TYPE_INTERLEAVE_PAD, "name", pad_name, "direction", templ->direction, "template", templ, NULL)); GST_INTERLEAVE_PAD_CAST (new_pad)->channel = channels; GST_DEBUG_OBJECT (self, "requested new pad %s", pad_name); g_free (pad_name); gst_pad_use_fixed_caps (new_pad); gst_collect_pads_add_pad (self->collect, new_pad, sizeof (GstCollectData), NULL, TRUE); gst_collect_pads_set_event_function (self->collect, (GstCollectPadsEventFunction) GST_DEBUG_FUNCPTR (gst_interleave_sink_event), self); if (!gst_element_add_pad (element, new_pad)) goto could_not_add; g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_NONE); self->input_channel_positions = g_value_array_append (self->input_channel_positions, &val); g_value_unset (&val); /* Update the src caps if we already have them */ if (self->sinkcaps) { GstCaps *srccaps; GstStructure *s; /* Take lock to make sure processing finishes first */ GST_OBJECT_LOCK (self->collect); 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_interleave_set_channel_positions (self, s); /* FIXME: send caps event after stream-start event */ gst_pad_set_active (self->src, TRUE); gst_pad_set_caps (self->src, srccaps); gst_caps_unref (srccaps); GST_OBJECT_UNLOCK (self->collect); } return new_pad; /* errors */ not_sink_pad: { g_warning ("interleave: requested new pad that is not a SINK pad\n"); return NULL; } could_not_add: { GST_DEBUG_OBJECT (self, "could not add pad %s", GST_PAD_NAME (new_pad)); gst_collect_pads_remove_pad (self->collect, new_pad); gst_object_unref (new_pad); return NULL; } }
static gboolean gst_interleave_sink_setcaps (GstPad * pad, GstCaps * caps) { GstInterleave *self; g_return_val_if_fail (GST_IS_INTERLEAVE_PAD (pad), FALSE); self = GST_INTERLEAVE (gst_pad_get_parent (pad)); /* First caps that are set on a sink pad are used as output caps */ /* TODO: handle caps changes */ if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps)) { goto cannot_change_caps; } else { GstCaps *srccaps; GstStructure *s; gboolean res; s = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (s, "width", &self->width)) goto no_width; if (!gst_structure_get_int (s, "rate", &self->rate)) goto no_rate; gst_interleave_set_process_function (self); if (gst_structure_has_field (s, "channel-positions")) { const GValue *pos_array; pos_array = gst_structure_get_value (s, "channel-positions"); if (GST_VALUE_HOLDS_ARRAY (pos_array) && gst_value_array_get_size (pos_array) == 1) { const GValue *pos = gst_value_array_get_value (pos_array, 0); GValue *apos = g_value_array_get_nth (self->input_channel_positions, GST_INTERLEAVE_PAD_CAST (pad)->channel); g_value_set_enum (apos, g_value_get_enum (pos)); } } srccaps = gst_caps_copy (caps); s = gst_caps_get_structure (srccaps, 0); gst_structure_set (s, "channels", G_TYPE_INT, self->channels, NULL); gst_interleave_set_channel_positions (self, s); res = gst_pad_set_caps (self->src, srccaps); gst_caps_unref (srccaps); if (!res) goto src_did_not_accept; } if (!self->sinkcaps) { GstCaps *sinkcaps = gst_caps_copy (caps); GstStructure *s = gst_caps_get_structure (sinkcaps, 0); gst_structure_remove_field (s, "channel-positions"); gst_caps_replace (&self->sinkcaps, sinkcaps); gst_caps_unref (sinkcaps); } gst_object_unref (self); return TRUE; cannot_change_caps: { GST_DEBUG_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't " "change", self->sinkcaps); gst_object_unref (self); return FALSE; } src_did_not_accept: { GST_DEBUG_OBJECT (self, "src did not accept setcaps()"); gst_object_unref (self); return FALSE; } no_width: { GST_WARNING_OBJECT (self, "caps did not have width: %" GST_PTR_FORMAT, caps); gst_object_unref (self); return FALSE; } no_rate: { GST_WARNING_OBJECT (self, "caps did not have rate: %" GST_PTR_FORMAT, caps); gst_object_unref (self); return FALSE; } }