Source插件之GstBaseSrc/GstPushSrc源码剖析

1.GstBaseSrc剖析

gst插件流程分析先看init构造函数gst_base_src_init(本文流程主要基于filesrc插件讲解)

static void
gst_base_src_init (GstBaseSrc * basesrc, gpointer g_class)
{
  GstPad *pad;
  GstPadTemplate *pad_template;

  basesrc->priv = gst_base_src_get_instance_private (basesrc);

  basesrc->is_live = FALSE;
  g_mutex_init (&basesrc->live_lock);
  g_cond_init (&basesrc->live_cond);
  basesrc->num_buffers = DEFAULT_NUM_BUFFERS;
  basesrc->num_buffers_left = -1;
  g_atomic_int_set (&basesrc->priv->automatic_eos, TRUE);

  basesrc->can_activate_push = TRUE;

  pad_template =
      gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
  g_return_if_fail (pad_template != NULL);

  GST_DEBUG_OBJECT (basesrc, "creating src pad");
  pad = gst_pad_new_from_template (pad_template, "src");

  GST_DEBUG_OBJECT (basesrc, "setting functions on src pad");
  gst_pad_set_activatemode_function (pad, gst_base_src_activate_mode);
  gst_pad_set_event_function (pad, gst_base_src_event);
  gst_pad_set_query_function (pad, gst_base_src_query);
  gst_pad_set_getrange_function (pad, gst_base_src_getrange);

  /* hold pointer to pad */
  basesrc->srcpad = pad;
  GST_DEBUG_OBJECT (basesrc, "adding src pad");
  gst_element_add_pad (GST_ELEMENT (basesrc), pad);

  basesrc->blocksize = DEFAULT_BLOCKSIZE;
  basesrc->clock_id = NULL;
  /* we operate in BYTES by default */
  gst_base_src_set_format (basesrc, GST_FORMAT_BYTES);
  basesrc->priv->do_timestamp = DEFAULT_DO_TIMESTAMP;
  g_atomic_int_set (&basesrc->priv->have_events, FALSE);

  g_cond_init (&basesrc->priv->async_cond);
  basesrc->priv->start_result = GST_FLOW_FLUSHING;
  GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTED);
  GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING);
  GST_OBJECT_FLAG_SET (basesrc, GST_ELEMENT_FLAG_SOURCE);

  GST_DEBUG_OBJECT (basesrc, "init done");
}

比较关键的点:创建basesrc->srcpad ,注册activatemode、event、query、getrange函数。

在看下gst_base_src_change_state,然而对于非live可以看到并没有触发什么流程,那么src的流程是怎么走的呢?

static GstStateChangeReturn
gst_base_src_change_state (GstElement * element, GstStateChange transition)
{
  GstBaseSrc *basesrc;
  GstStateChangeReturn result;
  gboolean no_preroll = FALSE;

  basesrc = GST_BASE_SRC (element);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      no_preroll = gst_base_src_is_live (basesrc);
      break;
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      GST_DEBUG_OBJECT (basesrc, "PAUSED->PLAYING");
      if (gst_base_src_is_live (basesrc)) {
        /* now we can start playback */
        gst_base_src_set_playing (basesrc, TRUE);
      }
      break;
    default:
      break;
  }

  if ((result =
          GST_ELEMENT_CLASS (parent_class)->change_state (element,
              transition)) == GST_STATE_CHANGE_FAILURE)
    goto failure;

  switch (transition) {
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
      GST_DEBUG_OBJECT (basesrc, "PLAYING->PAUSED");
      if (gst_base_src_is_live (basesrc)) {
        /* make sure we block in the live cond in PAUSED */
        gst_base_src_set_playing (basesrc, FALSE);
        no_preroll = TRUE;
      }
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
    {
      /* we don't need to unblock anything here, the pad deactivation code
       * already did this */
      if (g_atomic_int_get (&basesrc->priv->has_pending_eos)) {
        GST_OBJECT_LOCK (basesrc);
        CLEAR_PENDING_EOS (basesrc);
        GST_OBJECT_UNLOCK (basesrc);
      }
      gst_event_replace (&basesrc->pending_seek, NULL);
      break;
    }
    case GST_STATE_CHANGE_READY_TO_NULL:
      break;
    default:
      break;
  }

  if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
    result = GST_STATE_CHANGE_NO_PREROLL;

  return result;

  /* ERRORS */
failure:
  {
    GST_DEBUG_OBJECT (basesrc, "parent failed state change");
    return result;
  }
}

先贴一下日志

 其实是必须要调用gst_base_src_start,但是首先可以看到查询函数gst_base_src_default_query

    case GST_QUERY_SCHEDULING:
    {
      gboolean random_access;

      random_access = gst_base_src_is_random_access (src);

      /* we can operate in getrange mode if the native format is bytes
       * and we are seekable, this condition is set in the random_access
       * flag and is set in the _start() method. */
      gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
      if (random_access)
        gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
      gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);

      res = TRUE;
      break;
    }
static gboolean
gst_base_src_is_random_access (GstBaseSrc * src)
{
  /* we need to start the basesrc to check random access */
  if (!GST_BASE_SRC_IS_STARTED (src)) {
    GST_LOG_OBJECT (src, "doing start/stop to check get_range support");
    if (G_LIKELY (gst_base_src_start (src))) {
      if (gst_base_src_start_wait (src) != GST_FLOW_OK)
        goto start_failed;
      gst_base_src_stop (src);
    }
  }

  return src->random_access;

  /* ERRORS */
start_failed:
  {
    GST_DEBUG_OBJECT (src, "failed to start");
    return FALSE;
  }
}

如果还没有GST_BASE_SRC_IS_STARTED则回去调用gst_base_src_start

static gboolean
gst_base_src_start (GstBaseSrc * basesrc)
{
  GstBaseSrcClass *bclass;
  gboolean result;

  GST_LIVE_LOCK (basesrc);

  GST_OBJECT_LOCK (basesrc);
  if (GST_BASE_SRC_IS_STARTING (basesrc))
    goto was_starting;
  if (GST_BASE_SRC_IS_STARTED (basesrc))
    goto was_started;

  basesrc->priv->start_result = GST_FLOW_FLUSHING;
  GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_FLAG_STARTING);
  gst_segment_init (&basesrc->segment, basesrc->segment.format);
  GST_OBJECT_UNLOCK (basesrc);

  basesrc->num_buffers_left = basesrc->num_buffers;
  basesrc->running = FALSE;
  basesrc->priv->segment_pending = FALSE;
  basesrc->priv->segment_seqnum = gst_util_seqnum_next ();
  basesrc->priv->forced_eos = FALSE;
  GST_LIVE_UNLOCK (basesrc);

  bclass = GST_BASE_SRC_GET_CLASS (basesrc);
  if (bclass->start)
    result = bclass->start (basesrc);
  else
    result = TRUE;

  if (!result)
    goto could_not_start;

  if (!gst_base_src_is_async (basesrc)) {
    gst_base_src_start_complete (basesrc, GST_FLOW_OK);
    /* not really waiting here, we call this to get the result
     * from the start_complete call */
    result = gst_base_src_start_wait (basesrc) == GST_FLOW_OK;
  }

  return result;

  /* ERROR */
was_starting:
  {
    GST_DEBUG_OBJECT (basesrc, "was starting");
    GST_OBJECT_UNLOCK (basesrc);
    GST_LIVE_UNLOCK (basesrc);
    return TRUE;
  }
was_started:
  {
    GST_DEBUG_OBJECT (basesrc, "was started");
    GST_OBJECT_UNLOCK (basesrc);
    GST_LIVE_UNLOCK (basesrc);
    return TRUE;
  }
could_not_start:
  {
    GST_DEBUG_OBJECT (basesrc, "could not start");
    /* subclass is supposed to post a message but we post one as a fallback
     * just in case. We don't have to call _stop. */
    GST_ELEMENT_ERROR (basesrc, CORE, STATE_CHANGE, (NULL),
        ("Failed to start"));
    gst_base_src_start_complete (basesrc, GST_FLOW_ERROR);
    return FALSE;
  }
}

比较关键的流程是会调用bclass->start (basesrc),这里就是调用到filesrc中的start函数了。

继续走到gst_base_src_start_complete,但是这时候srcpad还没有active,所以会goto not_activated_yet。后续gst_base_src_start_wait会返回error。所以gst_base_src_start最后是error状态,没有到达正常started状态。日志可以看到后面的一次query依旧是如此。

oid
gst_base_src_start_complete (GstBaseSrc * basesrc, GstFlowReturn ret)
{
  gboolean have_size;
  guint64 size;
  gboolean seekable;
  GstFormat format;
  GstPadMode mode;
  GstEvent *event;

  if (ret != GST_FLOW_OK)
    goto error;

  GST_DEBUG_OBJECT (basesrc, "starting source");
  format = basesrc->segment.format;

  /* figure out the size */
  have_size = FALSE;
  size = -1;
  if (format == GST_FORMAT_BYTES) {
    GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (basesrc);

    if (bclass->get_size) {
      if (!(have_size = bclass->get_size (basesrc, &size)))
        size = -1;
    }
    GST_DEBUG_OBJECT (basesrc, "setting size %" G_GUINT64_FORMAT, size);
    /* only update the size when operating in bytes, subclass is supposed
     * to set duration in the start method for other formats */
    GST_OBJECT_LOCK (basesrc);
    basesrc->segment.duration = size;
    GST_OBJECT_UNLOCK (basesrc);
  }

  GST_DEBUG_OBJECT (basesrc,
      "format: %s, have size: %d, size: %" G_GUINT64_FORMAT ", duration: %"
      G_GINT64_FORMAT, gst_format_get_name (format), have_size, size,
      basesrc->segment.duration);

  seekable = gst_base_src_seekable (basesrc);
  GST_DEBUG_OBJECT (basesrc, "is seekable: %d", seekable);

  /* update for random access flag */
  basesrc->random_access = seekable && format == GST_FORMAT_BYTES;

  GST_DEBUG_OBJECT (basesrc, "is random_access: %d", basesrc->random_access);

  gst_pad_mark_reconfigure (GST_BASE_SRC_PAD (basesrc));

  GST_OBJECT_LOCK (basesrc->srcpad);
  mode = GST_PAD_MODE (basesrc->srcpad);
  GST_OBJECT_UNLOCK (basesrc->srcpad);

  /* take the stream lock here, we only want to let the task run when we have
   * set the STARTED flag */
  GST_PAD_STREAM_LOCK (basesrc->srcpad);
  switch (mode) {
    case GST_PAD_MODE_PUSH:
      /* do initial seek, which will start the task */
      GST_OBJECT_LOCK (basesrc);
      event = basesrc->pending_seek;
      basesrc->pending_seek = NULL;
      GST_OBJECT_UNLOCK (basesrc);

      /* The perform seek code will start the task when finished. We don't have to
       * unlock the streaming thread because it is not running yet */
      if (G_UNLIKELY (!gst_base_src_perform_seek (basesrc, event, FALSE)))
        goto seek_failed;

      if (event)
        gst_event_unref (event);
      break;
    case GST_PAD_MODE_PULL:
      /* if not random_access, we cannot operate in pull mode for now */
      if (G_UNLIKELY (!basesrc->random_access))
        goto no_get_range;
      break;
    default:
      goto not_activated_yet;
      break;
  }

  GST_OBJECT_LOCK (basesrc);
  GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_FLAG_STARTED);
  GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING);
  basesrc->priv->start_result = ret;
  GST_ASYNC_SIGNAL (basesrc);
  GST_OBJECT_UNLOCK (basesrc);

  GST_PAD_STREAM_UNLOCK (basesrc->srcpad);

  return;

seek_failed:
  {
    GST_PAD_STREAM_UNLOCK (basesrc->srcpad);
    GST_ERROR_OBJECT (basesrc, "Failed to perform initial seek");
    gst_base_src_stop (basesrc);
    if (event)
      gst_event_unref (event);
    ret = GST_FLOW_ERROR;
    goto error;
  }
no_get_range:
  {
    GST_PAD_STREAM_UNLOCK (basesrc->srcpad);
    gst_base_src_stop (basesrc);
    GST_ERROR_OBJECT (basesrc, "Cannot operate in pull mode, stopping");
    ret = GST_FLOW_ERROR;
    goto error;
  }
not_activated_yet:
  {
    GST_PAD_STREAM_UNLOCK (basesrc->srcpad);
    gst_base_src_stop (basesrc);
    GST_WARNING_OBJECT (basesrc, "pad not activated yet");
    ret = GST_FLOW_ERROR;
    goto error;
  }
error:
  {
    GST_OBJECT_LOCK (basesrc);
    basesrc->priv->start_result = ret;
    GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING);
    GST_ASYNC_SIGNAL (basesrc);
    GST_OBJECT_UNLOCK (basesrc);
    return;
  }
}

 那start到底如何被调用到呢?init中我们看到注册了gst_pad_set_activatemode_function (pad, gst_base_src_activate_mode)函数。

static gboolean
gst_base_src_activate_mode (GstPad * pad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean res;
  GstBaseSrc *src = GST_BASE_SRC (parent);

  src->priv->stream_start_pending = FALSE;

  GST_DEBUG_OBJECT (pad, "activating in mode %d", mode);

  switch (mode) {
    case GST_PAD_MODE_PULL:
      res = gst_base_src_activate_pull (pad, parent, active);
      break;
    case GST_PAD_MODE_PUSH:
      src->priv->stream_start_pending = active;
      res = gst_base_src_activate_push (pad, parent, active);
      break;
    default:
      GST_LOG_OBJECT (pad, "unknown activation mode %d", mode);
      res = FALSE;
      break;
  }
  return res;
}
static gboolean
gst_base_src_activate_pull (GstPad * pad, GstObject * parent, gboolean active)
{
  GstBaseSrc *basesrc;

  basesrc = GST_BASE_SRC (parent);

  /* prepare subclass first */
  if (active) {
    GST_DEBUG_OBJECT (basesrc, "Activating in pull mode");
    if (G_UNLIKELY (!gst_base_src_start (basesrc)))
      goto error_start;
  } else {
    GST_DEBUG_OBJECT (basesrc, "Deactivating in pull mode");
    if (G_UNLIKELY (!gst_base_src_stop (basesrc)))
      goto error_stop;
  }
  return TRUE;

  /* ERRORS */
error_start:
  {
    GST_ERROR_OBJECT (basesrc, "Failed to start in pull mode");
    return FALSE;
  }
error_stop:
  {
    GST_ERROR_OBJECT (basesrc, "Failed to stop in pull mode");
    return FALSE;
  }
}

以GST_PAD_MODE_PULL为例,如果需要active会调用gst_base_src_start。对应日志看是走到这里。Pad 的激活是为了确保它能够正确处理数据流、事件和查询。而Pad 的激活模式决定了它如何参与数据流动。激活模式有三种:GST_PAD_MODE_NONE,GST_PAD_MODE_PUSH,GST_PAD_MODE_PULL

那么我们看下basesrc的激活函数是如何被触发的?直接说结论吧是在下游插件typefind中

static gboolean
gst_type_find_element_activate_sink (GstPad * pad, GstObject * parent)
{
  GstQuery *query;
  gboolean pull_mode;

  query = gst_query_new_scheduling ();

  if (!gst_pad_peer_query (pad, query)) {
    gst_query_unref (query);
    goto typefind_push;
  }

  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);

  gst_query_unref (query);

  if (!pull_mode)
    goto typefind_push;

  if (!gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE))
    goto typefind_push;

  /* only start our task if we ourselves decide to start in pull mode */
  return gst_pad_start_task (pad, (GstTaskFunction) gst_type_find_element_loop,
      pad, NULL);

typefind_push:
  {
    return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
  }
}

 从typefind的sinkpad发起scheduling模式的查询,如果支持pull_mode则gst_pad_activate_mode设置并且激活pad,(前面我们看到查询函数中GST_QUERY_SCHEDULING中是支持PULL和PUSH模式的)简单看下activate_mode_internal

static gboolean
activate_mode_internal (GstPad * pad, GstObject * parent, GstPadMode mode,
    gboolean active)
{
  gboolean res = FALSE;
  GstPadMode old, new;
  GstPadDirection dir;
  GstPad *peer;

  GST_OBJECT_LOCK (pad);
  old = GST_PAD_MODE (pad);
  dir = GST_PAD_DIRECTION (pad);
  GST_OBJECT_UNLOCK (pad);

  new = active ? mode : GST_PAD_MODE_NONE;

  if (old == new)
    goto was_ok;

  if (active && old != mode && old != GST_PAD_MODE_NONE) {
    /* pad was activate in the wrong direction, deactivate it
     * and reactivate it in the requested mode */
    GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode",
        gst_pad_mode_get_name (old));

    if (G_UNLIKELY (!activate_mode_internal (pad, parent, old, FALSE)))
      goto deactivate_failed;
    old = GST_PAD_MODE_NONE;
  }

  switch (mode) {
    case GST_PAD_MODE_PULL:
    {
      if (dir == GST_PAD_SINK) {
        if ((peer = gst_pad_get_peer (pad))) {
          GST_DEBUG_OBJECT (pad, "calling peer");
          if (G_UNLIKELY (!gst_pad_activate_mode (peer, mode, active)))
            goto peer_failed;
          gst_object_unref (peer);
        } else {
          /* there is no peer, this is only fatal when we activate. When we
           * deactivate, we must assume the application has unlinked the peer and
           * will deactivate it eventually. */
          if (active)
            goto not_linked;
          else
            GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
        }
      } else {
        if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
          goto failure;         /* Can't activate pull on a src without a
                                   getrange function */
      }
      break;
    }
    default:
      break;
  }

  /* Mark pad as needing reconfiguration */
  if (active)
    GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);

  /* pre_activate returns TRUE if we weren't already in the process of
   * switching to the 'new' mode */
  if (pre_activate (pad, new)) {

    if (GST_PAD_ACTIVATEMODEFUNC (pad)) {
      if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode,
                  active)))
        goto failure;
    } else {
      /* can happen for sinks of passthrough elements */
    }

    post_activate (pad, new);
  }

  GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in %s mode",
      active ? "activated" : "deactivated", gst_pad_mode_get_name (mode));

exit_success:
  res = TRUE;

  /* Clear sticky flags on deactivation */
  if (!active) {
    GST_OBJECT_LOCK (pad);
    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_EOS);
    GST_OBJECT_UNLOCK (pad);
  }

exit:
  return res;

was_ok:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in %s mode",
        active ? "activated" : "deactivated", gst_pad_mode_get_name (mode));
    goto exit_success;
  }
deactivate_failed:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
        "failed to %s in switch to %s mode from %s mode",
        (active ? "activate" : "deactivate"), gst_pad_mode_get_name (mode),
        gst_pad_mode_get_name (old));
    goto exit;
  }
peer_failed:
  {
    GST_OBJECT_LOCK (peer);
    GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
        "activate_mode on peer (%s:%s) failed", GST_DEBUG_PAD_NAME (peer));
    GST_OBJECT_UNLOCK (peer);
    gst_object_unref (peer);
    goto exit;
  }
not_linked:
  {
    GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "can't activate unlinked sink "
        "pad in pull mode");
    goto exit;
  }
failure:
  {
    GST_OBJECT_LOCK (pad);
    GST_CAT_INFO_OBJECT (GST_CAT_PADS, pad, "failed to %s in %s mode",
        active ? "activate" : "deactivate", gst_pad_mode_get_name (mode));
    GST_PAD_SET_FLUSHING (pad);
    GST_PAD_MODE (pad) = old;
    pad->priv->in_activation = FALSE;
    g_cond_broadcast (&pad->priv->activation_cond);
    GST_OBJECT_UNLOCK (pad);
    goto exit;
  }
}

关键流程

peer = gst_pad_get_peer (pad)就是获取typefind->sink的peer,也就是basesrc的srcpad。

然后调用gst_pad_activate_mode(srcpad),然后调用GST_PAD_ACTIVATEMODEFUNC(GST_PAD_ACTIVATEMODEFUNC),也就调用到了basesrc中。

(2)pre_activate、post_activate

到这里basesrc已经start并且是PULL模式,PULL模式需要upstream插件请求数据。也就是gst_base_src_getrange函数

/* must be called with LIVE_LOCK */
static GstFlowReturn
gst_base_src_get_range (GstBaseSrc * src, guint64 offset, guint length,
    GstBuffer ** buf)
{
  GstFlowReturn ret;
  GstBaseSrcClass *bclass;
  GstClockReturn status;
  GstBuffer *res_buf;
  GstBuffer *in_buf;
  gboolean own_res_buf;

  bclass = GST_BASE_SRC_GET_CLASS (src);

again:
  if (src->is_live) {
    if (G_UNLIKELY (!src->live_running)) {
      ret = gst_base_src_wait_playing_unlocked (src);
      if (ret != GST_FLOW_OK)
        goto stopped;
    }
  }

  if (G_UNLIKELY (!GST_BASE_SRC_IS_STARTED (src)
          && !GST_BASE_SRC_IS_STARTING (src)))
    goto not_started;

  if (G_UNLIKELY (!bclass->create))
    goto no_function;

  if (G_UNLIKELY (!gst_base_src_update_length (src, offset, &length, FALSE)))
    goto unexpected_length;

  /* track position */
  GST_OBJECT_LOCK (src);
  if (src->segment.format == GST_FORMAT_BYTES)
    src->segment.position = offset;
  GST_OBJECT_UNLOCK (src);

  /* normally we don't count buffers */
  if (G_UNLIKELY (src->num_buffers_left >= 0)) {
    if (src->num_buffers_left == 0)
      goto reached_num_buffers;
    else
      src->num_buffers_left--;
  }

  /* don't enter the create function if a pending EOS event was set. For the
   * logic of the has_pending_eos, check the event function of this class. */
  if (G_UNLIKELY (g_atomic_int_get (&src->priv->has_pending_eos))) {
    src->priv->forced_eos = TRUE;
    goto eos;
  }

  GST_DEBUG_OBJECT (src,
      "calling create offset %" G_GUINT64_FORMAT " length %u, time %"
      G_GINT64_FORMAT, offset, length, src->segment.time);

  res_buf = in_buf = *buf;
  own_res_buf = (*buf == NULL);

  GST_LIVE_UNLOCK (src);
  ret = bclass->create (src, offset, length, &res_buf);
  GST_LIVE_LOCK (src);

  /* As we released the LIVE_LOCK, the state may have changed */
  if (src->is_live) {
    if (G_UNLIKELY (!src->live_running)) {
      GstFlowReturn wait_ret;
      wait_ret = gst_base_src_wait_playing_unlocked (src);
      if (wait_ret != GST_FLOW_OK) {
        if (ret == GST_FLOW_OK && own_res_buf)
          gst_buffer_unref (res_buf);
        ret = wait_ret;
        goto stopped;
      }
    }
  }

  /* The create function could be unlocked because we have a pending EOS. It's
   * possible that we have a valid buffer from create that we need to
   * discard when the create function returned _OK. */
  if (G_UNLIKELY (g_atomic_int_get (&src->priv->has_pending_eos))) {
    if (ret == GST_FLOW_OK) {
      if (own_res_buf)
        gst_buffer_unref (res_buf);
    }
    src->priv->forced_eos = TRUE;
    goto eos;
  }

  if (G_UNLIKELY (ret != GST_FLOW_OK))
    goto not_ok;

  /* fallback in case the create function didn't fill a provided buffer */
  if (in_buf != NULL && res_buf != in_buf) {
    GstMapInfo info;
    gsize copied_size;

    GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, src, "create function didn't "
        "fill the provided buffer, copying");

    if (!gst_buffer_map (in_buf, &info, GST_MAP_WRITE))
      goto map_failed;

    copied_size = gst_buffer_extract (res_buf, 0, info.data, info.size);
    gst_buffer_unmap (in_buf, &info);
    gst_buffer_set_size (in_buf, copied_size);

    gst_buffer_copy_into (in_buf, res_buf, GST_BUFFER_COPY_METADATA, 0, -1);

    gst_buffer_unref (res_buf);
    res_buf = in_buf;
  }

  if (res_buf == NULL) {
    GstBufferList *pending_list = src->priv->pending_bufferlist;

    if (pending_list == NULL || gst_buffer_list_length (pending_list) == 0)
      goto null_buffer;

    res_buf = gst_buffer_list_get_writable (pending_list, 0);
    own_res_buf = FALSE;
  }

  /* no timestamp set and we are at offset 0, we can timestamp with 0 */
  if (offset == 0 && src->segment.time == 0
      && GST_BUFFER_DTS (res_buf) == -1 && !src->is_live) {
    GST_DEBUG_OBJECT (src, "setting first timestamp to 0");
    res_buf = gst_buffer_make_writable (res_buf);
    GST_BUFFER_DTS (res_buf) = 0;
  }

  /* now sync before pushing the buffer */
  status = gst_base_src_do_sync (src, res_buf);

  /* waiting for the clock could have made us flushing */
  if (G_UNLIKELY (src->priv->flushing))
    goto flushing;

  switch (status) {
    case GST_CLOCK_EARLY:
      /* the buffer is too late. We currently don't drop the buffer. */
      GST_DEBUG_OBJECT (src, "buffer too late!, returning anyway");
      break;
    case GST_CLOCK_OK:
      /* buffer synchronised properly */
      GST_DEBUG_OBJECT (src, "buffer ok");
      break;
    case GST_CLOCK_UNSCHEDULED:
      /* this case is triggered when we were waiting for the clock and
       * it got unlocked because we did a state change. In any case, get rid of
       * the buffer. */
      if (own_res_buf)
        gst_buffer_unref (res_buf);

      if (!src->live_running) {
        /* We return FLUSHING when we are not running to stop the dataflow also
         * get rid of the produced buffer. */
        GST_DEBUG_OBJECT (src,
            "clock was unscheduled (%d), returning FLUSHING", status);
        ret = GST_FLOW_FLUSHING;
      } else {
        /* If we are running when this happens, we quickly switched between
         * pause and playing. We try to produce a new buffer */
        GST_DEBUG_OBJECT (src,
            "clock was unscheduled (%d), but we are running", status);
        goto again;
      }
      break;
    default:
      /* all other result values are unexpected and errors */
      GST_ELEMENT_ERROR (src, CORE, CLOCK,
          (_("Internal clock error.")),
          ("clock returned unexpected return value %d", status));
      if (own_res_buf)
        gst_buffer_unref (res_buf);
      ret = GST_FLOW_ERROR;
      break;
  }
  if (G_LIKELY (ret == GST_FLOW_OK))
    *buf = res_buf;

  return ret;

  /* ERROR */
stopped:
  {
    GST_DEBUG_OBJECT (src, "wait_playing returned %d (%s)", ret,
        gst_flow_get_name (ret));
    return ret;
  }
not_ok:
  {
    GST_DEBUG_OBJECT (src, "create returned %d (%s)", ret,
        gst_flow_get_name (ret));
    return ret;
  }
map_failed:
  {
    GST_ELEMENT_ERROR (src, RESOURCE, BUSY,
        (_("Failed to map buffer.")),
        ("failed to map result buffer in WRITE mode"));
    if (own_res_buf)
      gst_buffer_unref (res_buf);
    return GST_FLOW_ERROR;
  }
not_started:
  {
    GST_DEBUG_OBJECT (src, "getrange but not started");
    return GST_FLOW_FLUSHING;
  }
no_function:
  {
    GST_DEBUG_OBJECT (src, "no create function");
    return GST_FLOW_NOT_SUPPORTED;
  }
unexpected_length:
  {
    GST_DEBUG_OBJECT (src, "unexpected length %u (offset=%" G_GUINT64_FORMAT
        ", size=%" G_GINT64_FORMAT ")", length, offset, src->segment.duration);
    return GST_FLOW_EOS;
  }
reached_num_buffers:
  {
    GST_DEBUG_OBJECT (src, "sent all buffers");
    return GST_FLOW_EOS;
  }
flushing:
  {
    GST_DEBUG_OBJECT (src, "we are flushing");
    if (own_res_buf)
      gst_buffer_unref (res_buf);
    return GST_FLOW_FLUSHING;
  }
eos:
  {
    GST_DEBUG_OBJECT (src, "we are EOS");
    return GST_FLOW_EOS;
  }
null_buffer:
  {
    GST_ELEMENT_ERROR (src, STREAM, FAILED,
        (_("Internal data flow error.")),
        ("Subclass %s neither returned a buffer nor submitted a buffer list "
            "from its create function", G_OBJECT_TYPE_NAME (src)));
    return GST_FLOW_ERROR;
  }
}

关键流程:

(1)bclass->create (src, offset, length, &res_buf),读取数据到res_buf,子类filesrc没有重写此函数,则调用gst_base_src_default_create

(2)bclass->alloc (src, offset, size, &res_buf),为res_buf创建buffer分配内存,子类filesrc没有重写此函数,则调用gst_base_src_default_alloc

(3)bclass->fill (src, offset, size, res_buf),为读取数据并写到res_buf,子类filesrc实现。具体细节就不讲解了

static GstFlowReturn
gst_base_src_default_alloc (GstBaseSrc * src, guint64 offset,
    guint size, GstBuffer ** buffer)
{
  GstFlowReturn ret;
  GstBaseSrcPrivate *priv = src->priv;
  GstBufferPool *pool = NULL;
  GstAllocator *allocator = NULL;
  GstAllocationParams params;

  GST_OBJECT_LOCK (src);
  if (priv->pool) {
    pool = gst_object_ref (priv->pool);
  } else if (priv->allocator) {
    allocator = gst_object_ref (priv->allocator);
  }
  params = priv->params;
  GST_OBJECT_UNLOCK (src);

  if (pool) {
    ret = gst_buffer_pool_acquire_buffer (pool, buffer, NULL);
  } else if (size != -1) {
    *buffer = gst_buffer_new_allocate (allocator, size, &params);
    if (G_UNLIKELY (*buffer == NULL))
      goto alloc_failed;

    ret = GST_FLOW_OK;
  } else {
    GST_WARNING_OBJECT (src, "Not trying to alloc %u bytes. Blocksize not set?",
        size);
    goto alloc_failed;
  }

done:
  if (pool)
    gst_object_unref (pool);
  if (allocator)
    gst_object_unref (allocator);

  return ret;

  /* ERRORS */
alloc_failed:
  {
    GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size);
    ret = GST_FLOW_ERROR;
    goto done;
  }
}

主要流程基本就这里,当我们需要实现一个source插件时,继承basesrc,并重写其几个关键函数就可以了。整个流程还是basesrc把控,重写几个关键挂件函数,完成数据获取等功能。

gstreamer媒体插件都是此架构,base类实现了相关功能的逻辑控制,子类通过重写相关接口,就可以实现对应功能,大大缩减了开发难度,例如 sink,parse,decoder,filter等模块

filesrc例子:

有几个属性可以看下:

(1)PROP_NUM_BUFFERS:Number of buffers to output before sending EOS

gst_base_src_get_range中逻辑,限制buffer数量;

  /* normally we don't count buffers */
  if (G_UNLIKELY (src->num_buffers_left >= 0)) {
    if (src->num_buffers_left == 0)
      goto reached_num_buffers;
    else
      src->num_buffers_left--;
  }

(2)DEFAULT_BLOCKSIZE:Size in bytes to read per buffer

PUSH模式中每次推送的buffer大小,gst_base_src_loop

  blocksize = src->blocksize;

  /* if we operate in bytes, we can calculate an offset */
  if (src->segment.format == GST_FORMAT_BYTES) {
    position = src->segment.position;
    /* for negative rates, start with subtracting the blocksize */
    if (src->segment.rate < 0.0) {
      /* we cannot go below segment.start */
      if (position > src->segment.start + blocksize)
        position -= blocksize;
      else {
        /* last block, remainder up to segment.start */
        blocksize = position - src->segment.start;
        position = src->segment.start;
      }
    }
  } else
    position = -1;

  GST_LOG_OBJECT (src, "next_ts %" GST_TIME_FORMAT " size %u",
      GST_TIME_ARGS (position), blocksize);

  /* clean up just in case we got interrupted or so last time round */
  if (src->priv->pending_bufferlist != NULL) {
    gst_buffer_list_unref (src->priv->pending_bufferlist);
    src->priv->pending_bufferlist = NULL;
  }

  ret = gst_base_src_get_range (src, position, blocksize, &buf);

2.GstPushSrc剖析

GstPushSrc是GstBaseSrc的一个子类,顾名思义仅支持PUSH模式,如CurlHttpSrc、SoupHttpSrc插件。

static void
gst_push_src_class_init (GstPushSrcClass * klass)
{
  GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;

  gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_push_src_create);
  gstbasesrc_class->alloc = GST_DEBUG_FUNCPTR (gst_push_src_alloc);
  gstbasesrc_class->fill = GST_DEBUG_FUNCPTR (gst_push_src_fill);
  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_push_src_query);
}

实现上没有太大区别,重写相关函数,可以看到GST_QUERY_SCHEDULING仅支持PUSH模式。

static gboolean
gst_push_src_query (GstBaseSrc * src, GstQuery * query)
{
  gboolean ret;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_SCHEDULING:
    {
      /* a pushsrc can by default never operate in pull mode override
       * if you want something different. */
      gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1,
          0);
      gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);

      ret = TRUE;
      break;
    }
    default:
      ret = GST_BASE_SRC_CLASS (parent_class)->query (src, query);
      break;
  }
  return ret;
}

3.filesrc剖析

Source需要实现接口

static void gst_file_src_uri_handler_init (gpointer g_iface,
    gpointer iface_data);

#define _do_init \
  G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, gst_file_src_uri_handler_init); \
  GST_DEBUG_CATEGORY_INIT (gst_file_src_debug, "filesrc", 0, "filesrc element");
#define gst_file_src_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstFileSrc, gst_file_src, GST_TYPE_BASE_SRC, _do_init);
GST_ELEMENT_REGISTER_DEFINE (filesrc, "filesrc", GST_RANK_PRIMARY,
    GST_TYPE_FILE_SRC);

static void
gst_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;

  iface->get_type = gst_file_src_uri_get_type;
  iface->get_protocols = gst_file_src_uri_get_protocols;
  iface->get_uri = gst_file_src_uri_get_uri;
  iface->set_uri = gst_file_src_uri_set_uri;
}
/*** GSTURIHANDLER INTERFACE *************************************************/

static GstURIType
gst_file_src_uri_get_type (GType type)
{
  return GST_URI_SRC;
}

static const gchar *const *
gst_file_src_uri_get_protocols (GType type)
{
  static const gchar *protocols[] = { "file", NULL };

  return protocols;
}

static gchar *
gst_file_src_uri_get_uri (GstURIHandler * handler)
{
  GstFileSrc *src = GST_FILE_SRC (handler);

  /* FIXME: make thread-safe */
  return g_strdup (src->uri);
}

static gboolean
gst_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
    GError ** err)
{
  gchar *location, *hostname = NULL;
  gboolean ret = FALSE;
  GstFileSrc *src = GST_FILE_SRC (handler);

  if (strcmp (uri, "file://") == 0) {
    /* Special case for "file://" as this is used by some applications
     *  to test with gst_element_make_from_uri if there's an element
     *  that supports the URI protocol. */
    gst_file_src_set_location (src, NULL, NULL);
    return TRUE;
  }

  location = g_filename_from_uri (uri, &hostname, err);

  if (!location || (err != NULL && *err != NULL)) {
    GST_WARNING_OBJECT (src, "Invalid URI '%s' for filesrc: %s", uri,
        (err != NULL && *err != NULL) ? (*err)->message : "unknown error");
    goto beach;
  }

  if ((hostname) && (strcmp (hostname, "localhost"))) {
    /* Only 'localhost' is permitted */
    GST_WARNING_OBJECT (src, "Invalid hostname '%s' for filesrc", hostname);
    g_set_error (err, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
        "File URI with invalid hostname '%s'", hostname);
    goto beach;
  }
#ifdef G_OS_WIN32
  /* Unfortunately, g_filename_from_uri() doesn't handle some UNC paths
   * correctly on windows, it leaves them with an extra backslash
   * at the start if they're of the mozilla-style file:/host/path/file
   * form. Correct this.
   */
  if (location[0] == '\\' && location[1] == '\\' && location[2] == '\\')
    memmove (location, location + 1, strlen (location + 1) + 1);
#endif

  ret = gst_file_src_set_location (src, location, err);

beach:
  if (location)
    g_free (location);
  if (hostname)
    g_free (hostname);

  return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值