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 void kms_composite_mixer_recalculate_sizes (gpointer data) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (data); GstCaps *filtercaps; gint width, height, top, left, counter, n_columns, n_rows; GList *l; GList *values = g_hash_table_get_values (self->priv->ports); if (self->priv->n_elems <= 0) { return; } counter = 0; values = g_list_sort (values, compare_port_data); n_columns = (gint) ceil (sqrt (self->priv->n_elems)); n_rows = (gint) ceil ((float) self->priv->n_elems / (float) n_columns); GST_DEBUG_OBJECT (self, "columns %d rows %d", n_columns, n_rows); width = self->priv->output_width / n_columns; height = self->priv->output_height / n_rows; for (l = values; l != NULL; l = l->next) { KmsCompositeMixerData *port_data = l->data; if (port_data->input == FALSE) { continue; } filtercaps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); g_object_set (port_data->capsfilter, "caps", filtercaps, NULL); gst_caps_unref (filtercaps); top = ((counter / n_columns) * height); left = ((counter % n_columns) * width); g_object_set (port_data->video_mixer_pad, "xpos", left, "ypos", top, "alpha", 1.0, NULL); counter++; GST_DEBUG_OBJECT (self, "counter %d id_port %d ", counter, port_data->id); GST_DEBUG_OBJECT (self, "top %d left %d width %d height %d", top, left, width, height); } g_list_free (values); }
static void kms_composite_mixer_recalculate_sizes (gpointer data) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (data); GstCaps *filtercaps; gint width, height, top, left, counter; GList *l; GList *values = g_hash_table_get_values (self->priv->ports); counter = 0; values = g_list_sort (values, compare_port_data); for (l = values; l != NULL; l = l->next) { KmsCompositeMixerData *port_data = l->data; if (port_data->input == FALSE) { continue; } if (self->priv->n_elems == 1) { width = self->priv->output_width; } else { width = self->priv->output_width / N_ELEMENTS_WIDTH; } if (self->priv->n_elems < N_ELEMENTS_WIDTH) { height = self->priv->output_height; } else { height = self->priv->output_height / ((self->priv->n_elems / N_ELEMENTS_WIDTH) + (self->priv->n_elems % N_ELEMENTS_WIDTH)); } filtercaps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "AYUV", "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "framerate", GST_TYPE_FRACTION, 15, 1, NULL); g_object_set (port_data->capsfilter, "caps", filtercaps, NULL); gst_caps_unref (filtercaps); top = ((counter / N_ELEMENTS_WIDTH) * height); left = ((counter % N_ELEMENTS_WIDTH) * width); g_object_set (port_data->video_mixer_pad, "xpos", left, "ypos", top, "alpha", 1.0, NULL); counter++; GST_DEBUG ("counter %d id_port %d ", counter, port_data->id); GST_DEBUG ("top %d left %d width %d height %d", top, left, width, height); } g_list_free (values); }
static void kms_composite_mixer_finalize (GObject * object) { KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (object); g_rec_mutex_clear (&self->priv->mutex); if (self->priv->ports != NULL) { g_hash_table_unref (self->priv->ports); self->priv->ports = NULL; } G_OBJECT_CLASS (kms_composite_mixer_parent_class)->finalize (object); }
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 void pad_added_cb (GstElement * element, GstPad * pad, gpointer data) { gint id; KmsCompositeMixer *self = KMS_COMPOSITE_MIXER (data); if (gst_pad_get_direction (pad) != GST_PAD_SRC) return; id = get_stream_id_from_padname (GST_OBJECT_NAME (pad)); if (id < 0) { GST_ERROR_OBJECT (self, "Invalid HubPort for %" GST_PTR_FORMAT, pad); return; } kms_base_hub_link_audio_src (KMS_BASE_HUB (self), id, self->priv->audiomixer, GST_OBJECT_NAME (pad), TRUE); }
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 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; }