Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
  }
}
Exemplo n.º 4
0
static GstFlowReturn
gst_interleave_collected (GstCollectPads * pads, GstInterleave * self)
{
  guint size;
  GstBuffer *outbuf = NULL;
  GstFlowReturn ret = GST_FLOW_OK;
  GSList *collected;
  guint nsamples;
  guint ncollected = 0;
  gboolean empty = TRUE;
  gint width = self->width / 8;
  GstMapInfo write_info;
  GstClockTime timestamp = -1;

  /* FIXME: send caps and tags after stream-start */
#if 0
  if (self->send_stream_start) {
    gchar s_id[32];

    /* stream-start (FIXME: create id based on input ids) */
    g_snprintf (s_id, sizeof (s_id), "interleave-%08x", g_random_int ());
    gst_pad_push_event (self->src, gst_event_new_stream_start (s_id));
    self->send_stream_start = FALSE;
  }
#endif

  size = gst_collect_pads_available (pads);
  if (size == 0)
    goto eos;

  g_return_val_if_fail (self->func != NULL, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->width > 0, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->channels > 0, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->rate > 0, GST_FLOW_NOT_NEGOTIATED);

  g_return_val_if_fail (size % width == 0, GST_FLOW_ERROR);

  GST_DEBUG_OBJECT (self, "Starting to collect %u bytes from %d channels", size,
      self->channels);

  nsamples = size / width;

  outbuf = gst_buffer_new_allocate (NULL, size * self->channels, NULL);

  if (outbuf == NULL || gst_buffer_get_size (outbuf) < size * self->channels) {
    gst_buffer_unref (outbuf);
    return GST_FLOW_NOT_NEGOTIATED;
  }

  gst_buffer_map (outbuf, &write_info, GST_MAP_WRITE);
  memset (write_info.data, 0, size * self->channels);

  for (collected = pads->data; collected != NULL; collected = collected->next) {
    GstCollectData *cdata;
    GstBuffer *inbuf;
    guint8 *outdata;
    GstMapInfo input_info;

    cdata = (GstCollectData *) collected->data;

    inbuf = gst_collect_pads_take_buffer (pads, cdata, size);
    if (inbuf == NULL) {
      GST_DEBUG_OBJECT (cdata->pad, "No buffer available");
      goto next;
    }
    ncollected++;
    gst_buffer_map (inbuf, &input_info, GST_MAP_READ);

    if (timestamp == -1)
      timestamp = GST_BUFFER_TIMESTAMP (inbuf);

    if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))
      goto next;

    empty = FALSE;
    outdata =
        write_info.data + width * GST_INTERLEAVE_PAD_CAST (cdata->pad)->channel;

    self->func (outdata, input_info.data, self->channels, nsamples);
    gst_buffer_unmap (inbuf, &input_info);

  next:
    if (inbuf)
      gst_buffer_unref (inbuf);
  }

  if (ncollected == 0) {
    gst_buffer_unmap (outbuf, &write_info);
    goto eos;
  }

  GST_OBJECT_LOCK (self);
  if (self->pending_segment) {
    GstEvent *event;
    GstSegment segment;

    event = self->pending_segment;
    self->pending_segment = NULL;
    GST_OBJECT_UNLOCK (self);

    /* convert the input segment to time now */
    gst_event_copy_segment (event, &segment);

    if (segment.format != GST_FORMAT_TIME) {
      gst_event_unref (event);

      /* not time, convert */
      switch (segment.format) {
        case GST_FORMAT_BYTES:
          segment.start *= width;
          if (segment.stop != -1)
            segment.stop *= width;
          if (segment.position != -1)
            segment.position *= width;
          /* fallthrough for the samples case */
        case GST_FORMAT_DEFAULT:
          segment.start =
              gst_util_uint64_scale_int (segment.start, GST_SECOND, self->rate);
          if (segment.stop != -1)
            segment.stop =
                gst_util_uint64_scale_int (segment.stop, GST_SECOND,
                self->rate);
          if (segment.position != -1)
            segment.position =
                gst_util_uint64_scale_int (segment.position, GST_SECOND,
                self->rate);
          break;
        default:
          GST_WARNING ("can't convert segment values");
          segment.start = 0;
          segment.stop = -1;
          segment.position = 0;
          break;
      }
      event = gst_event_new_segment (&segment);
    }
    gst_pad_push_event (self->src, event);

    GST_OBJECT_LOCK (self);
  }
  GST_OBJECT_UNLOCK (self);

  if (timestamp != -1) {
    self->offset = gst_util_uint64_scale_int (timestamp, self->rate,
        GST_SECOND);
    self->timestamp = timestamp;
  }

  GST_BUFFER_TIMESTAMP (outbuf) = self->timestamp;
  GST_BUFFER_OFFSET (outbuf) = self->offset;

  self->offset += nsamples;
  self->timestamp = gst_util_uint64_scale_int (self->offset,
      GST_SECOND, self->rate);

  GST_BUFFER_DURATION (outbuf) =
      self->timestamp - GST_BUFFER_TIMESTAMP (outbuf);

  if (empty)
    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);

  gst_buffer_unmap (outbuf, &write_info);

  GST_LOG_OBJECT (self, "pushing outbuf, timestamp %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
  ret = gst_pad_push (self->src, outbuf);

  return ret;

eos:
  {
    GST_DEBUG_OBJECT (self, "no data available, must be EOS");
    if (outbuf)
      gst_buffer_unref (outbuf);
    gst_pad_push_event (self->src, gst_event_new_eos ());
    return GST_FLOW_EOS;
  }
}
Exemplo n.º 5
0
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;
  }
}
Exemplo n.º 6
0
static GstFlowReturn
gst_interleave_collected (GstCollectPads * pads, GstInterleave * self)
{
  guint size;

  GstBuffer *outbuf;

  GstFlowReturn ret = GST_FLOW_OK;

  GSList *collected;

  guint nsamples;

  guint ncollected = 0;

  gboolean empty = TRUE;

  gint width = self->width / 8;

  g_return_val_if_fail (self->func != NULL, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->width > 0, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->channels > 0, GST_FLOW_NOT_NEGOTIATED);
  g_return_val_if_fail (self->rate > 0, GST_FLOW_NOT_NEGOTIATED);

  size = gst_collect_pads_available (pads);

  g_return_val_if_fail (size % width == 0, GST_FLOW_ERROR);

  GST_DEBUG_OBJECT (self, "Starting to collect %u bytes from %d channels", size,
      self->channels);

  nsamples = size / width;

  ret =
      gst_pad_alloc_buffer (self->src, GST_BUFFER_OFFSET_NONE,
      size * self->channels, GST_PAD_CAPS (self->src), &outbuf);

  if (ret != GST_FLOW_OK) {
    return ret;
  } else if (outbuf == NULL || GST_BUFFER_SIZE (outbuf) < size * self->channels) {
    gst_buffer_unref (outbuf);
    return GST_FLOW_NOT_NEGOTIATED;
  } else if (!gst_caps_is_equal (GST_BUFFER_CAPS (outbuf),
          GST_PAD_CAPS (self->src))) {
    gst_buffer_unref (outbuf);
    return GST_FLOW_NOT_NEGOTIATED;
  }

  memset (GST_BUFFER_DATA (outbuf), 0, size * self->channels);

  for (collected = pads->data; collected != NULL; collected = collected->next) {
    GstCollectData *cdata;

    GstBuffer *inbuf;

    guint8 *outdata;

    cdata = (GstCollectData *) collected->data;

    inbuf = gst_collect_pads_take_buffer (pads, cdata, size);
    if (inbuf == NULL) {
      GST_DEBUG_OBJECT (cdata->pad, "No buffer available");
      goto next;
    }
    ncollected++;

    if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))
      goto next;

    empty = FALSE;
    outdata =
        GST_BUFFER_DATA (outbuf) +
        width * GST_INTERLEAVE_PAD_CAST (cdata->pad)->channel;

    self->func (outdata, GST_BUFFER_DATA (inbuf), self->channels, nsamples);

  next:
    if (inbuf)
      gst_buffer_unref (inbuf);
  }

  if (ncollected == 0)
    goto eos;

  if (self->segment_pending) {
    GstEvent *event;

    event = gst_event_new_new_segment_full (FALSE, self->segment_rate,
        1.0, GST_FORMAT_TIME, self->timestamp, -1, self->segment_position);

    gst_pad_push_event (self->src, event);
    self->segment_pending = FALSE;
    self->segment_position = 0;
  }

  GST_BUFFER_TIMESTAMP (outbuf) = self->timestamp;
  GST_BUFFER_OFFSET (outbuf) = self->offset;

  self->offset += nsamples;
  self->timestamp = gst_util_uint64_scale_int (self->offset,
      GST_SECOND, self->rate);

  GST_BUFFER_DURATION (outbuf) = self->timestamp -
      GST_BUFFER_TIMESTAMP (outbuf);

  if (empty)
    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);

  GST_LOG_OBJECT (self, "pushing outbuf, timestamp %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
  ret = gst_pad_push (self->src, outbuf);

  return ret;

eos:
  {
    GST_DEBUG_OBJECT (self, "no data available, must be EOS");
    gst_buffer_unref (outbuf);
    gst_pad_push_event (self->src, gst_event_new_eos ());
    return GST_FLOW_UNEXPECTED;
  }
}