static gint kms_composite_mixer_handle_port (KmsBaseHub * mixer, GstElement * mixer_end_point) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (mixer); KmsCompositeMixerData *port_data; gint port_id; port_id = KMS_BASE_HUB_CLASS (G_OBJECT_CLASS (kms_composite_mixer_parent_class))->handle_port (mixer, mixer_end_point); if (port_id < 0) { return port_id; } KMS_COMPOSITE_MIXER_LOCK (self); if (self->priv->videomixer == NULL) { GstElement *videorate_mixer; videorate_mixer = gst_element_factory_make ("videorate", NULL); self->priv->videomixer = gst_element_factory_make ("compositor", NULL); g_object_set (G_OBJECT (self->priv->videomixer), "background", 1, NULL); self->priv->mixer_video_agnostic = gst_element_factory_make ("agnosticbin", NULL); gst_bin_add_many (GST_BIN (mixer), self->priv->videomixer, videorate_mixer, self->priv->mixer_video_agnostic, NULL); gst_element_sync_state_with_parent (self->priv->videomixer); gst_element_sync_state_with_parent (videorate_mixer); gst_element_sync_state_with_parent (self->priv->mixer_video_agnostic); gst_element_link_many (self->priv->videomixer, videorate_mixer, self->priv->mixer_video_agnostic, NULL); } if (self->priv->audiomixer == NULL) { self->priv->audiomixer = gst_element_factory_make ("kmsaudiomixer", NULL); gst_bin_add (GST_BIN (mixer), self->priv->audiomixer); gst_element_sync_state_with_parent (self->priv->audiomixer); g_signal_connect (self->priv->audiomixer, "pad-added", G_CALLBACK (pad_added_cb), self); g_signal_connect (self->priv->audiomixer, "pad-removed", G_CALLBACK (pad_removed_cb), self); } kms_base_hub_link_video_src (KMS_BASE_HUB (self), port_id, self->priv->mixer_video_agnostic, "src_%u", TRUE); port_data = kms_composite_mixer_port_data_create (self, port_id); g_hash_table_insert (self->priv->ports, create_gint (port_id), port_data); KMS_COMPOSITE_MIXER_UNLOCK (self); return port_id; }
static void kms_composite_mixer_dispose (GObject * object) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (object); KMS_COMPOSITE_MIXER_LOCK (self); g_hash_table_remove_all (self->priv->ports); KMS_COMPOSITE_MIXER_UNLOCK (self); g_clear_object (&self->priv->loop); G_OBJECT_CLASS (kms_composite_mixer_parent_class)->dispose (object); }
static gboolean remove_elements_from_pipeline (KmsCompositeMixerData * port_data) { KmsCompositeMixer *self = port_data->mixer; KMS_COMPOSITE_MIXER_LOCK (self); gst_element_unlink (port_data->capsfilter, self->priv->videomixer); if (port_data->video_mixer_pad != NULL) { gst_element_release_request_pad (self->priv->videomixer, port_data->video_mixer_pad); g_object_unref (port_data->video_mixer_pad); port_data->video_mixer_pad = NULL; } g_object_unref (port_data->videoconvert_sink_pad); gst_bin_remove_many (GST_BIN (self), g_object_ref (port_data->input_capsfilter), g_object_ref (port_data->videoconvert), g_object_ref (port_data->videoscale), g_object_ref (port_data->capsfilter), g_object_ref (port_data->videorate), g_object_ref (port_data->queue), NULL); kms_base_hub_unlink_video_src (KMS_BASE_HUB (self), port_data->id); KMS_COMPOSITE_MIXER_UNLOCK (self); gst_element_set_state (port_data->input_capsfilter, GST_STATE_NULL); gst_element_set_state (port_data->videoconvert, GST_STATE_NULL); gst_element_set_state (port_data->videoscale, GST_STATE_NULL); gst_element_set_state (port_data->videorate, GST_STATE_NULL); gst_element_set_state (port_data->capsfilter, GST_STATE_NULL); gst_element_set_state (port_data->queue, GST_STATE_NULL); g_object_unref (port_data->input_capsfilter); g_object_unref (port_data->videoconvert); g_object_unref (port_data->videoscale); g_object_unref (port_data->videorate); g_object_unref (port_data->capsfilter); g_object_unref (port_data->queue); port_data->videoconvert_sink_pad = NULL; port_data->input_capsfilter = NULL; port_data->videoconvert = NULL; port_data->videoscale = NULL; port_data->capsfilter = NULL; port_data->videorate = NULL; port_data->queue = NULL; return G_SOURCE_REMOVE; }
static GstPadProbeReturn cb_EOS_received (GstPad * pad, GstPadProbeInfo * info, gpointer data) { KmsCompositeMixerData *port_data = (KmsCompositeMixerData *) data; KmsCompositeMixer *self = port_data->mixer; GstEvent *event; if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_EOS) { return GST_PAD_PROBE_OK; } KMS_COMPOSITE_MIXER_LOCK (self); if (!port_data->removing) { port_data->eos_managed = TRUE; KMS_COMPOSITE_MIXER_UNLOCK (self); return GST_PAD_PROBE_OK; } if (port_data->probe_id > 0) { gst_pad_remove_probe (pad, port_data->probe_id); port_data->probe_id = 0; } KMS_COMPOSITE_MIXER_UNLOCK (self); event = gst_event_new_eos (); gst_pad_send_event (pad, event); kms_loop_idle_add_full (self->priv->loop, G_PRIORITY_DEFAULT, (GSourceFunc) remove_elements_from_pipeline, KMS_COMPOSITE_MIXER_REF (port_data), (GDestroyNotify) kms_ref_struct_unref); return GST_PAD_PROBE_OK; }
static void kms_composite_mixer_unhandle_port (KmsBaseHub * mixer, gint id) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (mixer); GST_DEBUG ("unhandle id %d", id); KMS_COMPOSITE_MIXER_LOCK (self); g_hash_table_remove (self->priv->ports, &id); KMS_COMPOSITE_MIXER_UNLOCK (self); KMS_BASE_HUB_CLASS (G_OBJECT_CLASS (kms_composite_mixer_parent_class))->unhandle_port (mixer, id); }
static gboolean remove_elements_from_pipeline (KmsCompositeMixerData * port_data) { KmsCompositeMixer *self = port_data->mixer; KMS_COMPOSITE_MIXER_LOCK (self); gst_element_unlink (port_data->capsfilter, self->priv->videomixer); if (port_data->latency_probe_id > 0) { gst_pad_remove_probe (port_data->video_mixer_pad, port_data->latency_probe_id); port_data->latency_probe_id = 0; } if (port_data->video_mixer_pad != NULL) { gst_element_release_request_pad (self->priv->videomixer, port_data->video_mixer_pad); g_object_unref (port_data->video_mixer_pad); port_data->video_mixer_pad = NULL; } gst_bin_remove_many (GST_BIN (self), g_object_ref (port_data->capsfilter), g_object_ref (port_data->tee), g_object_ref (port_data->fakesink), NULL); kms_base_hub_unlink_video_src (KMS_BASE_HUB (self), port_data->id); KMS_COMPOSITE_MIXER_UNLOCK (self); gst_element_set_state (port_data->capsfilter, GST_STATE_NULL); gst_element_set_state (port_data->tee, GST_STATE_NULL); gst_element_set_state (port_data->fakesink, GST_STATE_NULL); g_object_unref (port_data->capsfilter); g_object_unref (port_data->tee); g_object_unref (port_data->fakesink); g_object_unref (port_data->tee_sink_pad); port_data->tee_sink_pad = NULL; port_data->capsfilter = NULL; port_data->tee = NULL; port_data->fakesink = NULL; return G_SOURCE_REMOVE; }
static GstPadProbeReturn link_to_videomixer (GstPad * pad, GstPadProbeInfo * info, KmsCompositeMixerData * data) { GstPadTemplate *sink_pad_template; KmsCompositeMixer *mixer; if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_STREAM_START) { return GST_PAD_PROBE_PASS; } mixer = KMS_COMPOSITE_MIXER (data->mixer); GST_DEBUG ("stream start detected %d", data->id); KMS_COMPOSITE_MIXER_LOCK (mixer); data->link_probe_id = 0; sink_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mixer->priv-> videomixer), "sink_%u"); if (G_UNLIKELY (sink_pad_template == NULL)) { GST_ERROR_OBJECT (mixer, "Error taking a new pad from videomixer"); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_DROP; } if (mixer->priv->videotestsrc == NULL) { GstElement *capsfilter; GstCaps *filtercaps; GstPad *pad; mixer->priv->videotestsrc = gst_element_factory_make ("videotestsrc", NULL); capsfilter = gst_element_factory_make ("capsfilter", NULL); g_object_set (mixer->priv->videotestsrc, "is-live", TRUE, "pattern", /*black */ 2, NULL); filtercaps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "AYUV", "width", G_TYPE_INT, mixer->priv->output_width, "height", G_TYPE_INT, mixer->priv->output_height, "framerate", GST_TYPE_FRACTION, 15, 1, NULL); g_object_set (G_OBJECT (capsfilter), "caps", filtercaps, NULL); gst_caps_unref (filtercaps); gst_bin_add_many (GST_BIN (mixer), mixer->priv->videotestsrc, capsfilter, NULL); gst_element_link (mixer->priv->videotestsrc, capsfilter); /*link capsfilter -> videomixer */ pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (capsfilter, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (pad)); g_object_set (pad, "xpos", 0, "ypos", 0, "alpha", 0.0, NULL); g_object_unref (pad); gst_element_sync_state_with_parent (capsfilter); gst_element_sync_state_with_parent (mixer->priv->videotestsrc); } data->videoscale = gst_element_factory_make ("videoscale", NULL); data->capsfilter = gst_element_factory_make ("capsfilter", NULL); data->videorate = gst_element_factory_make ("videorate", NULL); data->queue = gst_element_factory_make ("queue", NULL); data->input = TRUE; gst_bin_add_many (GST_BIN (mixer), data->queue, data->videorate, data->videoscale, data->capsfilter, NULL); g_object_set (data->videorate, "average-period", 200 * GST_MSECOND, NULL); g_object_set (data->queue, "flush-on-eos", TRUE, "max-size-buffers", 60, NULL); gst_element_link_many (data->videorate, data->queue, data->videoscale, data->capsfilter, NULL); /*link capsfilter -> videomixer */ data->video_mixer_pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); gst_element_link_pads (data->capsfilter, NULL, mixer->priv->videomixer, GST_OBJECT_NAME (data->video_mixer_pad)); gst_element_link (data->videoconvert, data->videorate); data->probe_id = gst_pad_add_probe (data->video_mixer_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) cb_EOS_received, KMS_COMPOSITE_MIXER_REF (data), (GDestroyNotify) kms_ref_struct_unref); gst_element_sync_state_with_parent (data->videoscale); gst_element_sync_state_with_parent (data->capsfilter); gst_element_sync_state_with_parent (data->videorate); gst_element_sync_state_with_parent (data->queue); /*recalculate the output sizes */ mixer->priv->n_elems++; kms_composite_mixer_recalculate_sizes (mixer); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_REMOVE; }
static void kms_composite_mixer_port_data_destroy (gpointer data) { KmsCompositeMixerData *port_data = (KmsCompositeMixerData *) data; KmsCompositeMixer *self = port_data->mixer; GstPad *audiosink; gchar *padname; KMS_COMPOSITE_MIXER_LOCK (self); port_data->removing = TRUE; kms_base_hub_unlink_video_sink (KMS_BASE_HUB (self), port_data->id); kms_base_hub_unlink_audio_sink (KMS_BASE_HUB (self), port_data->id); if (port_data->input) { GstEvent *event; gboolean result; GstPad *pad; if (port_data->videorate == NULL) { KMS_COMPOSITE_MIXER_UNLOCK (self); return; } pad = gst_element_get_static_pad (port_data->videorate, "sink"); if (pad == NULL) { KMS_COMPOSITE_MIXER_UNLOCK (self); return; } if (!GST_OBJECT_FLAG_IS_SET (pad, GST_PAD_FLAG_EOS)) { event = gst_event_new_eos (); result = gst_pad_send_event (pad, event); if (port_data->input && self->priv->n_elems > 0) { port_data->input = FALSE; self->priv->n_elems--; kms_composite_mixer_recalculate_sizes (self); } KMS_COMPOSITE_MIXER_UNLOCK (self); if (!result) { GST_WARNING ("EOS event did not send"); } } else { gboolean remove = FALSE; /* EOS callback was triggered before we could remove the port data */ /* so we have to remove elements to avoid memory leaks. */ remove = port_data->eos_managed; KMS_COMPOSITE_MIXER_UNLOCK (self); if (remove) { /* Remove pipeline without helding the mutex */ kms_loop_idle_add_full (self->priv->loop, G_PRIORITY_DEFAULT, (GSourceFunc) remove_elements_from_pipeline, KMS_COMPOSITE_MIXER_REF (port_data), (GDestroyNotify) kms_ref_struct_unref); } } gst_element_unlink (port_data->input_capsfilter, port_data->videoconvert); gst_element_unlink (port_data->videoconvert, port_data->videorate); g_object_unref (pad); } else { if (port_data->probe_id > 0) { gst_pad_remove_probe (port_data->video_mixer_pad, port_data->probe_id); } if (port_data->link_probe_id > 0) { gst_pad_remove_probe (port_data->videoconvert_sink_pad, port_data->link_probe_id); } KMS_COMPOSITE_MIXER_UNLOCK (self); gst_element_unlink (port_data->input_capsfilter, port_data->videoconvert); gst_bin_remove (GST_BIN (self), g_object_ref (port_data->input_capsfilter)); gst_element_set_state (port_data->input_capsfilter, GST_STATE_NULL); g_object_unref (port_data->input_capsfilter); port_data->input_capsfilter = NULL; gst_bin_remove (GST_BIN (self), g_object_ref (port_data->videoconvert)); gst_element_set_state (port_data->videoconvert, GST_STATE_NULL); g_object_unref (port_data->videoconvert); port_data->videoconvert = NULL; } padname = g_strdup_printf (AUDIO_SINK_PAD, port_data->id); audiosink = gst_element_get_static_pad (self->priv->audiomixer, padname); gst_element_release_request_pad (self->priv->audiomixer, audiosink); gst_object_unref (audiosink); g_free (padname); }
static gint kms_composite_mixer_handle_port (KmsBaseHub * mixer, GstElement * mixer_end_point) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (mixer); KmsCompositeMixerData *port_data; gint port_id; GST_DEBUG ("handle new port"); port_id = KMS_BASE_HUB_CLASS (G_OBJECT_CLASS (kms_composite_mixer_parent_class))->handle_port (mixer, mixer_end_point); if (port_id < 0) { return port_id; } KMS_COMPOSITE_MIXER_LOCK (self); if (self->priv->videomixer == NULL) { self->priv->videomixer = gst_element_factory_make ("compositor", NULL); g_object_set (G_OBJECT (self->priv->videomixer), "background", 1 /*black */ , "start-time-selection", 1 /*first */ , "latency", LATENCY * GST_MSECOND, NULL); self->priv->mixer_video_agnostic = gst_element_factory_make ("agnosticbin", NULL); gst_bin_add_many (GST_BIN (mixer), self->priv->videomixer, self->priv->mixer_video_agnostic, NULL); if (self->priv->videotestsrc == NULL) { GstElement *capsfilter; GstCaps *filtercaps; GstPad *pad; GstPadTemplate *sink_pad_template; sink_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->priv->videomixer), "sink_%u"); if (G_UNLIKELY (sink_pad_template == NULL)) { GST_ERROR_OBJECT (self, "Error taking a new pad from videomixer"); } self->priv->videotestsrc = gst_element_factory_make ("videotestsrc", NULL); capsfilter = gst_element_factory_make ("capsfilter", NULL); g_object_set (G_OBJECT (capsfilter), "caps-change-mode", 1, NULL); g_object_set (self->priv->videotestsrc, "is-live", TRUE, "pattern", /*black */ 2, NULL); filtercaps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, self->priv->output_width, "height", G_TYPE_INT, self->priv->output_height, "framerate", GST_TYPE_FRACTION, 15, 1, NULL); g_object_set (G_OBJECT (capsfilter), "caps", filtercaps, NULL); gst_caps_unref (filtercaps); gst_bin_add_many (GST_BIN (self), self->priv->videotestsrc, capsfilter, NULL); gst_element_link (self->priv->videotestsrc, capsfilter); /*link capsfilter -> videomixer */ pad = gst_element_request_pad (self->priv->videomixer, sink_pad_template, NULL, NULL); gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, (GstPadProbeCallback) cb_latency, NULL, NULL); gst_element_link_pads (capsfilter, NULL, self->priv->videomixer, GST_OBJECT_NAME (pad)); g_object_set (pad, "xpos", 0, "ypos", 0, "alpha", 0.0, NULL); g_object_unref (pad); gst_element_sync_state_with_parent (capsfilter); gst_element_sync_state_with_parent (self->priv->videotestsrc); } gst_element_sync_state_with_parent (self->priv->videomixer); gst_element_sync_state_with_parent (self->priv->mixer_video_agnostic); gst_element_link (self->priv->videomixer, self->priv->mixer_video_agnostic); } if (self->priv->audiomixer == NULL) { self->priv->audiomixer = gst_element_factory_make ("kmsaudiomixer", NULL); gst_bin_add (GST_BIN (mixer), self->priv->audiomixer); gst_element_sync_state_with_parent (self->priv->audiomixer); g_signal_connect (self->priv->audiomixer, "pad-added", G_CALLBACK (pad_added_cb), self); g_signal_connect (self->priv->audiomixer, "pad-removed", G_CALLBACK (pad_removed_cb), self); } kms_base_hub_link_video_src (KMS_BASE_HUB (self), port_id, self->priv->mixer_video_agnostic, "src_%u", TRUE); port_data = kms_composite_mixer_port_data_create (self, port_id); g_hash_table_insert (self->priv->ports, create_gint (port_id), port_data); KMS_COMPOSITE_MIXER_UNLOCK (self); return port_id; }
static GstPadProbeReturn link_to_videomixer (GstPad * pad, GstPadProbeInfo * info, KmsCompositeMixerData * data) { GstPadTemplate *sink_pad_template; KmsCompositeMixer *mixer; GstPad *tee_src; if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info)) != GST_EVENT_STREAM_START) { return GST_PAD_PROBE_PASS; } mixer = KMS_COMPOSITE_MIXER (data->mixer); GST_DEBUG ("stream start detected %d", data->id); KMS_COMPOSITE_MIXER_LOCK (mixer); data->link_probe_id = 0; data->latency_probe_id = 0; sink_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (mixer-> priv->videomixer), "sink_%u"); if (G_UNLIKELY (sink_pad_template == NULL)) { GST_ERROR_OBJECT (mixer, "Error taking a new pad from videomixer"); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_DROP; } data->input = TRUE; /*link tee -> videomixer */ data->video_mixer_pad = gst_element_request_pad (mixer->priv->videomixer, sink_pad_template, NULL, NULL); tee_src = gst_element_get_request_pad (data->tee, "src_%u"); gst_element_link_pads (data->tee, GST_OBJECT_NAME (tee_src), mixer->priv->videomixer, GST_OBJECT_NAME (data->video_mixer_pad)); g_object_unref (tee_src); data->probe_id = gst_pad_add_probe (data->video_mixer_pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) cb_EOS_received, KMS_COMPOSITE_MIXER_REF (data), (GDestroyNotify) kms_ref_struct_unref); data->latency_probe_id = gst_pad_add_probe (data->video_mixer_pad, GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, (GstPadProbeCallback) cb_latency, NULL, NULL); /*recalculate the output sizes */ mixer->priv->n_elems++; kms_composite_mixer_recalculate_sizes (mixer); //Recalculate latency to avoid video freezes when an element stops to send media. gst_bin_recalculate_latency (GST_BIN (mixer)); KMS_COMPOSITE_MIXER_UNLOCK (mixer); return GST_PAD_PROBE_REMOVE; }