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);
}
Example #3
0
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;
}
Example #9
0
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;
}
Example #10
0
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;
}