gstreamer之typefind源码剖析

插件库:libgsttypefindfunctions.so

源码位置:gsttypefindfunctionsplugin.c/gsttypefindfunctions.c/gsttypefindfunctionsriff.c/gsttypefindfunctionsstartwith.c

(开源typefind主要在libgsttypefindfunctions.so以及libgstlibav.so)

typefind插件注册:

static gboolean
plugin_init (GstPlugin * plugin)
{
  /* can't initialize this via a struct as caps can't be statically initialized */
  GST_DEBUG_CATEGORY_INIT (type_find_functions_debug, "typefindfunctions",
      GST_DEBUG_FG_GREEN | GST_DEBUG_BG_RED, "generic type find functions");
  /* note: asx/wax/wmx are XML files, asf doesn't handle them */
  /* must use strings, macros don't accept initializers */

  /*Riff Type find register */
  GST_TYPE_FIND_REGISTER (avi, plugin);
  GST_TYPE_FIND_REGISTER (qcp, plugin);
  GST_TYPE_FIND_REGISTER (cdxa, plugin);
  GST_TYPE_FIND_REGISTER (riff_mid, plugin);
  {省略}
  GST_TYPE_FIND_REGISTER (aa, plugin);
  GST_TYPE_FIND_REGISTER (tap, plugin);

  return TRUE;
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    typefindfunctions,
    "default typefind functions",
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

typefind函数实现:gsttypefindfunctions


/* Try and detect at least 4 packets in at most 10 packets worth of
 * data. Need to try several possible packet sizes */
static void
mpeg_ts_type_find (GstTypeFind * tf, gpointer unused)
{
  /* TS packet sizes to test: normal, DVHS packet size and
   * FEC with 16 or 20 byte codes packet size. */
  const gint pack_sizes[] = { 188, 192, 204, 208 };
  const guint8 *data = NULL;
  guint size = 0;
  guint64 skipped = 0;

  while (skipped < GST_MPEGTS_TYPEFIND_SCAN_LENGTH) {
    if (size < MPEGTS_HDR_SIZE) {
      data = gst_type_find_peek (tf, skipped, GST_MPEGTS_TYPEFIND_SYNC_SIZE);
      if (!data)
        break;
      size = GST_MPEGTS_TYPEFIND_SYNC_SIZE;
    }

    /* Have at least MPEGTS_HDR_SIZE bytes at this point */
    if (IS_MPEGTS_HEADER (data)) {
      gsize p;

      GST_LOG ("possible mpeg-ts sync at offset %" G_GUINT64_FORMAT, skipped);

      for (p = 0; p < G_N_ELEMENTS (pack_sizes); p++) {
        gint found;

        /* Probe ahead at size pack_sizes[p] */
        found = mpeg_ts_probe_headers (tf, skipped, pack_sizes[p]);
        if (found >= GST_MPEGTS_TYPEFIND_MIN_HEADERS) {
          gint probability;

          /* found at least 4 headers. 10 headers = MAXIMUM probability.
           * Arbitrarily, I assigned 10% probability for each header we
           * found, 40% -> 100% */
          probability = MIN (10 * found, GST_TYPE_FIND_MAXIMUM);

          gst_type_find_suggest_simple (tf, probability, "video/mpegts",
              "systemstream", G_TYPE_BOOLEAN, TRUE,
              "packetsize", G_TYPE_INT, pack_sizes[p], NULL);
          return;
        }
      }
    }
    data++;
    skipped++;
    size--;
  }
}

GST_TYPE_FIND_REGISTER_DEFINE (mpeg_sys, "video/mpeg-sys", GST_RANK_PRIMARY,
    mpeg_sys_type_find, "mpe,mpeg,mpg", MPEG_SYS_CAPS, NULL, NULL);
GST_TYPE_FIND_REGISTER_DEFINE (mpeg_ts, "video/mpegts", GST_RANK_PRIMARY,
    mpeg_ts_type_find, "ts,mts", MPEGTS_CAPS, NULL, NULL);

typefind函数实现:gsttypefindfunctionsriff

static void
start_with_type_find (GstTypeFind * tf, gpointer private)
{
  GstTypeFindData *start_with = (GstTypeFindData *) private;
  const guint8 *data;

  GST_LOG ("trying to find mime type %s with the first %u bytes of data",
      gst_structure_get_name (gst_caps_get_structure (start_with->caps, 0)),
      start_with->size);
  data = gst_type_find_peek (tf, 0, start_with->size);
  if (data && memcmp (data, start_with->data, start_with->size) == 0) {
    gst_type_find_suggest (tf, start_with->probability, start_with->caps);
  }
}

#define TYPE_FIND_REGISTER_START_WITH_DEFINE(typefind_name, name, rank, ext, _data, _size, _probability)\
G_BEGIN_DECLS \
static gboolean \
G_PASTE(_private_type_find_start_with_, typefind_name) (GstPlugin * plugin) \
{ \
  GstTypeFindData *sw_data = g_slice_new (GstTypeFindData);             \
  sw_data->data = (const guint8 *)_data;                                \
  sw_data->size = _size;                                                \
  sw_data->probability = _probability;                                  \
  sw_data->caps = gst_caps_new_empty_simple (name);                     \
  if (!gst_type_find_register (plugin, name, rank, start_with_type_find,\
                     ext, sw_data->caps, sw_data,                       \
                     (GDestroyNotify) (sw_data_destroy))) {             \
    sw_data_destroy (sw_data);                                          \
    return FALSE; \
  } \
  return TRUE; \
}\
GST_TYPE_FIND_REGISTER_DEFINE_CUSTOM (typefind_name, G_PASTE(_private_type_find_start_with_, typefind_name)); \
G_END_DECLS

/*'Start with' type find definition */
TYPE_FIND_REGISTER_START_WITH_DEFINE (asf, "video/x-ms-asf",
    GST_RANK_SECONDARY, "asf,wm,wma,wmv",
    "\060\046\262\165\216\146\317\021\246\331\000\252\000\142\316\154", 16,
    GST_TYPE_FIND_MAXIMUM);

typefind函数实现:gsttypefindfunctionsstartwith

static void
start_with_type_find (GstTypeFind * tf, gpointer private)
{
  GstTypeFindData *start_with = (GstTypeFindData *) private;
  const guint8 *data;

  GST_LOG ("trying to find mime type %s with the first %u bytes of data",
      gst_structure_get_name (gst_caps_get_structure (start_with->caps, 0)),
      start_with->size);
  data = gst_type_find_peek (tf, 0, start_with->size);
  if (data && memcmp (data, start_with->data, start_with->size) == 0) {
    gst_type_find_suggest (tf, start_with->probability, start_with->caps);
  }
}

#define TYPE_FIND_REGISTER_START_WITH_DEFINE(typefind_name, name, rank, ext, _data, _size, _probability)\
G_BEGIN_DECLS \
static gboolean \
G_PASTE(_private_type_find_start_with_, typefind_name) (GstPlugin * plugin) \
{ \
  GstTypeFindData *sw_data = g_slice_new (GstTypeFindData);             \
  sw_data->data = (const guint8 *)_data;                                \
  sw_data->size = _size;                                                \
  sw_data->probability = _probability;                                  \
  sw_data->caps = gst_caps_new_empty_simple (name);                     \
  if (!gst_type_find_register (plugin, name, rank, start_with_type_find,\
                     ext, sw_data->caps, sw_data,                       \
                     (GDestroyNotify) (sw_data_destroy))) {             \
    sw_data_destroy (sw_data);                                          \
    return FALSE; \
  } \
  return TRUE; \
}\
GST_TYPE_FIND_REGISTER_DEFINE_CUSTOM (typefind_name, G_PASTE(_private_type_find_start_with_, typefind_name)); \
G_END_DECLS

/*'Start with' type find definition */
TYPE_FIND_REGISTER_START_WITH_DEFINE (asf, "video/x-ms-asf",
    GST_RANK_SECONDARY, "asf,wm,wma,wmv",
    "\060\046\262\165\216\146\317\021\246\331\000\252\000\142\316\154", 16,
    GST_TYPE_FIND_MAXIMUM);
TYPE_FIND_REGISTER_START_WITH_DEFINE (vcd, "video/x-vcd", GST_RANK_PRIMARY,
    "dat", "\000\377\377\377\377\377\377\377\377\377\377\000", 12,
    GST_TYPE_FIND_MAXIMUM);

三者没有本质区别,最终都是通过gst_type_find_register注册。

/**
 * gst_type_find_register:
 * @plugin: (nullable): A #GstPlugin, or %NULL for a static typefind function
 * @name: The name for registering
 * @rank: The rank (or importance) of this typefind function
 * @func: The #GstTypeFindFunction to use
 * @extensions: (nullable): Optional comma-separated list of extensions
 *     that could belong to this type
 * @possible_caps: (nullable): Optionally the caps that could be returned when typefinding
 *                 succeeds
 * @data: Optional user data. This user data must be available until the plugin
 *        is unloaded.
 * @data_notify: a #GDestroyNotify that will be called on @data when the plugin
 *        is unloaded.
 *
 * Registers a new typefind function to be used for typefinding. After
 * registering this function will be available for typefinding.
 * This function is typically called during an element's plugin initialization.
 *
 * Returns: %TRUE on success, %FALSE otherwise
 */
gboolean
gst_type_find_register (GstPlugin * plugin, const gchar * name, guint rank,
    GstTypeFindFunction func, const gchar * extensions,
    GstCaps * possible_caps, gpointer data, GDestroyNotify data_notify)
{
  GstTypeFindFactory *factory;

  g_return_val_if_fail (name != NULL, FALSE);

  GST_INFO ("registering typefind function for %s", name);

  factory = g_object_new (GST_TYPE_TYPE_FIND_FACTORY, NULL);
  GST_DEBUG_OBJECT (factory, "using new typefind factory for %s", name);

  gst_plugin_feature_set_name (GST_PLUGIN_FEATURE_CAST (factory), name);
  gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE_CAST (factory), rank);

  if (extensions)
    factory->extensions = g_strsplit (extensions, ",", -1);

  gst_caps_replace (&factory->caps, possible_caps);
  factory->function = func;
  factory->user_data = data;
  factory->user_data_notify = data_notify;
  if (plugin && plugin->desc.name) {
    GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = plugin->desc.name; /* interned string */
    GST_PLUGIN_FEATURE_CAST (factory)->plugin = plugin;
    g_object_add_weak_pointer ((GObject *) plugin,
        (gpointer *) & GST_PLUGIN_FEATURE_CAST (factory)->plugin);
  } else {
    GST_PLUGIN_FEATURE_CAST (factory)->plugin_name = "NULL";
    GST_PLUGIN_FEATURE_CAST (factory)->plugin = NULL;
  }
  GST_PLUGIN_FEATURE_CAST (factory)->loaded = TRUE;

  gst_registry_add_feature (gst_registry_get (),
      GST_PLUGIN_FEATURE_CAST (factory));

  return TRUE;
}

将对应插件保存到gst_registry_get ()注册表中,包括factory->function = func,也就是各种格式相应的GstTypeFindFunction。

那我们看下typefind是如何识别出媒体资源格式的。

playbin篇章我们讲过decodebin的gst_decode_bin_init中创建typefind,并监听"have-type"信号

decode_bin->typefind = gst_element_factory_make ("typefind", "typefind");

dbin->have_type_id = g_signal_connect (dbin->typefind, "have-type",G_CALLBACK (type_found), dbin);

gsttypefindelement.c

#define _do_init \
    GST_DEBUG_CATEGORY_INIT (gst_type_find_element_debug, "typefind",           \
        GST_DEBUG_BG_YELLOW | GST_DEBUG_FG_GREEN, "type finding element");
#define gst_type_find_element_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstTypeFindElement, gst_type_find_element,
    GST_TYPE_ELEMENT, _do_init);
GST_ELEMENT_REGISTER_DEFINE (typefind, "typefind", GST_RANK_NONE,
    GST_TYPE_TYPE_FIND_ELEMENT);

看下sinkpad的模式,如果支持GST_PAD_MODE_PULL则开启task :gst_type_find_element_loop,否则PUSH模式。gst_pad_activate_mode会调用

gst_type_find_element_activate_sink_mode。

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);
  }
}

看下gst_type_find_element_init,设置了一些函数:

  gst_pad_set_activatemode_function (typefind->src,
      GST_DEBUG_FUNCPTR (gst_type_find_element_activate_src_mode));
  gst_pad_set_getrange_function (typefind->src,
      GST_DEBUG_FUNCPTR (gst_type_find_element_getrange));
  gst_pad_set_chain_function (typefind->sink,
      GST_DEBUG_FUNCPTR (gst_type_find_element_chain));
  gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
  typefind->mode = MODE_TYPEFIND;

 PULL模式,如filesrc

static void
gst_type_find_element_loop (GstPad * pad)
{
  GstTypeFindElement *typefind;
  GstFlowReturn ret = GST_FLOW_OK;

  typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));

  if (typefind->need_stream_start) {
    gchar *stream_id;
    GstEvent *event;

    stream_id = gst_pad_create_stream_id (typefind->src,
        GST_ELEMENT_CAST (typefind), NULL);

    GST_DEBUG_OBJECT (typefind, "Pushing STREAM_START");
    event = gst_event_new_stream_start (stream_id);
    gst_event_set_group_id (event, gst_util_group_id_next ());
    gst_pad_push_event (typefind->src, event);

    typefind->need_stream_start = FALSE;
    g_free (stream_id);
  }

  if (typefind->mode == MODE_TYPEFIND) {
    GstPad *peer = NULL;
    GstCaps *found_caps = NULL;
    GstTypeFindProbability probability = GST_TYPE_FIND_NONE;

    GST_DEBUG_OBJECT (typefind, "find type in pull mode");

    GST_OBJECT_LOCK (typefind);
    if (typefind->force_caps) {
      found_caps = gst_caps_ref (typefind->force_caps);
      probability = GST_TYPE_FIND_MAXIMUM;
    }
    GST_OBJECT_UNLOCK (typefind);

    if (!found_caps) {
      peer = gst_pad_get_peer (pad);
      if (peer) {
        gint64 size;
        gchar *ext;

        if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &size)) {
          GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
          gst_object_unref (peer);

          ret = GST_FLOW_ERROR;
          goto pause;
        }

        /* the size if 0, we cannot continue */
        if (size == 0) {
          /* keep message in sync with message in sink event handler */
          GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
              (_("Stream contains no data.")), ("Can't typefind empty stream"));
          gst_object_unref (peer);
          ret = GST_FLOW_ERROR;
          goto pause;
        }
        ext = gst_type_find_get_extension (typefind, pad);

        ret =
            gst_type_find_helper_get_range_full (GST_OBJECT_CAST (peer),
            GST_OBJECT_PARENT (peer),
            (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
            (guint64) size, ext, &found_caps, &probability);
        g_free (ext);

        GST_DEBUG ("Found caps %" GST_PTR_FORMAT, found_caps);

        gst_object_unref (peer);

        if (ret != GST_FLOW_OK)
          goto pause;
      }
    }

    if (!found_caps || probability < typefind->min_probability) {
      GST_DEBUG ("Trying to guess using extension");
      gst_caps_replace (&found_caps, NULL);
      found_caps =
          gst_type_find_guess_by_extension (typefind, pad, &probability);
    }

    if (!found_caps || probability < typefind->min_probability) {
      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
      gst_caps_replace (&found_caps, NULL);
      ret = GST_FLOW_ERROR;
      goto pause;
    }

    GST_DEBUG ("Emitting found caps %" GST_PTR_FORMAT, found_caps);
    /* Set to MODE_NORMAL before emitting have-type, in case it triggers a seek */
    typefind->mode = MODE_NORMAL;
    gst_type_find_element_emit_have_type (typefind, probability, found_caps);
    gst_caps_unref (found_caps);
  } else if (typefind->mode == MODE_NORMAL) {
    GstBuffer *outbuf = NULL;

    if (typefind->need_segment) {
      GstEvent *event;
      typefind->need_segment = FALSE;
      event = gst_event_new_segment (&typefind->segment);
      if (typefind->seqnum != 0)
        gst_event_set_seqnum (event, typefind->seqnum);
      gst_pad_push_event (typefind->src, event);
    }

    /* Pull 4k blocks and send downstream */
    ret = gst_pad_pull_range (typefind->sink, typefind->offset, 4096, &outbuf);
    if (ret != GST_FLOW_OK)
      goto pause;

    typefind->offset += gst_buffer_get_size (outbuf);

    ret = gst_pad_push (typefind->src, outbuf);
    if (ret != GST_FLOW_OK)
      goto pause;
  } else {
    /* Error out */
    ret = GST_FLOW_ERROR;
    goto pause;
  }

  return;
......
}

关键流程:

(1)gst_type_find_element_loop,其中need_stream_start=true,push STREAM_START 到下游插件.

(2)第一次typefind->mode == MODE_TYPEFIND,第一阶段这里是true,typefind有两个阶段,第一个阶段是识别数据格式,第二个阶段是透传数据到下游插件。

a)关键流程gst_type_find_get_extension,获取uri后缀结尾,如.mp3,然后调用gst_type_find_helper_get_range_full去识别数据格式found_caps。

b)如果流程a没成功识别,则继续gst_type_find_guess_by_extension识别

c)识别成功,则切换到第二阶段:typefind->mode = MODE_NORMAL,并且触发gst_type_find_element_emit_have_type信号

(3)typefind->mode = MODE_NORMAL阶段,就是通过gst_pad_pull_range不断读取数据然后push到下游了。

我们重点看下gst_type_find_helper_get_range_full:

/**
 * gst_type_find_helper_get_range_full:
 * @obj: A #GstObject that will be passed as first argument to @func
 * @parent: (allow-none): the parent of @obj or %NULL
 * @func: (scope call): A generic #GstTypeFindHelperGetRangeFunction that will
 *        be used to access data at random offsets when doing the typefinding
 * @size: The length in bytes
 * @extension: (allow-none): extension of the media, or %NULL
 * @caps: (out) (transfer full): returned caps
 * @prob: (out) (allow-none): location to store the probability of the found
 *     caps, or %NULL
 *
 * Utility function to do pull-based typefinding. Unlike gst_type_find_helper()
 * however, this function will use the specified function @func to obtain the
 * data needed by the typefind functions, rather than operating on a given
 * source pad. This is useful mostly for elements like tag demuxers which
 * strip off data at the beginning and/or end of a file and want to typefind
 * the stripped data stream before adding their own source pad (the specified
 * callback can then call the upstream peer pad with offsets adjusted for the
 * tag size, for example).
 *
 * When @extension is not %NULL, this function will first try the typefind
 * functions for the given extension, which might speed up the typefinding
 * in many cases.
 *
 * Returns: the last %GstFlowReturn from pulling a buffer or %GST_FLOW_OK if
 *          typefinding was successful.
 *
 * Since: 1.14.3
 */
GstFlowReturn
gst_type_find_helper_get_range_full (GstObject * obj, GstObject * parent,
    GstTypeFindHelperGetRangeFunction func, guint64 size,
    const gchar * extension, GstCaps ** caps, GstTypeFindProbability * prob)
{
  GstTypeFindHelper helper;
  GstTypeFind find;
  GSList *walk;
  GList *l, *type_list;
  GstCaps *result = NULL;

  g_return_val_if_fail (GST_IS_OBJECT (obj), GST_FLOW_ERROR);
  g_return_val_if_fail (func != NULL, GST_FLOW_ERROR);
  g_return_val_if_fail (caps != NULL, GST_FLOW_ERROR);

  *caps = NULL;

  helper.buffers = NULL;
  helper.size = size;
  helper.last_offset = 0;
  helper.func = func;
  helper.best_probability = GST_TYPE_FIND_NONE;
  helper.caps = NULL;
  helper.obj = obj;
  helper.parent = parent;
  helper.flow_ret = GST_FLOW_OK;

  find.data = &helper;
  find.peek = helper_find_peek;
  find.suggest = helper_find_suggest;

  if (size == 0 || size == (guint64) - 1) {
    find.get_length = NULL;
  } else {
    find.get_length = helper_find_get_length;
  }

  type_list = gst_type_find_factory_get_list ();
  type_list = prioritize_extension (obj, type_list, extension);

  for (l = type_list; l; l = l->next) {
    helper.factory = GST_TYPE_FIND_FACTORY (l->data);
    gst_type_find_factory_call_function (helper.factory, &find);
    if (helper.best_probability >= GST_TYPE_FIND_MAXIMUM) {
      /* Any other flow return can be ignored here, we found
       * something before any error with highest probability */
      helper.flow_ret = GST_FLOW_OK;
      break;
    } else if (helper.flow_ret != GST_FLOW_OK
        && helper.flow_ret != GST_FLOW_EOS) {
      /* We had less than maximum probability and an error, don't return
       * any caps as they might be with a lower probability than what
       * we would've gotten when continuing if there was no error */
      gst_caps_replace (&helper.caps, NULL);
      break;
    }
  }
  gst_plugin_feature_list_free (type_list);

  for (walk = helper.buffers; walk; walk = walk->next) {
    GstMappedBuffer *bmap = (GstMappedBuffer *) walk->data;

    gst_buffer_unmap (bmap->buffer, &bmap->map);
    gst_buffer_unref (bmap->buffer);
    g_slice_free (GstMappedBuffer, bmap);
  }
  g_slist_free (helper.buffers);

  if (helper.best_probability > 0)
    result = helper.caps;

  if (prob)
    *prob = helper.best_probability;

  *caps = result;
  if (helper.flow_ret == GST_FLOW_EOS) {
    /* Some typefinder might've tried to read too much, if we
     * didn't get any meaningful caps because of that this is
     * just a normal error */
    helper.flow_ret = GST_FLOW_ERROR;
  }

  GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT " (probability = %u)",
      result, (guint) helper.best_probability);

  return helper.flow_ret;
}

关键流程

(1)helper.func = func,也就是GST_PAD_GETRANGEFUNC (peer),filesrc中的getrange函数

(2)gst_type_find_factory_get_list,从注册表中获取factory到type_list

(3)prioritize_extension,对type_list进行sort,将uri后缀匹配的,调整到最前面。

(4)gst_type_find_factory_call_function,遍历type_list会调用factory的typefindfunction,也就是之前gst_typefind_register讲解的识别函数。

(5)返回识别的caps以及probability。

到这里我们就已经搞清楚了typefind的PULL模式的运转流程了。

 PUSH模式,如souphttpsrc

当上游推送数据会调用gst_type_find_element_chain

static GstFlowReturn
gst_type_find_element_chain (GstPad * pad, GstObject * parent,
    GstBuffer * buffer)
{
  GstTypeFindElement *typefind;
  GstFlowReturn res = GST_FLOW_OK;

  typefind = GST_TYPE_FIND_ELEMENT (parent);

  GST_LOG_OBJECT (typefind, "handling buffer in mode %d", typefind->mode);

  switch (typefind->mode) {
    case MODE_ERROR:
      /* we should already have called GST_ELEMENT_ERROR */
      return GST_FLOW_ERROR;
    case MODE_NORMAL:
      /* don't take object lock as typefind->caps should not change anymore */
      return gst_pad_push (typefind->src, buffer);
    case MODE_TYPEFIND:
    {
      GST_OBJECT_LOCK (typefind);
      if (typefind->initial_offset == GST_BUFFER_OFFSET_NONE)
        typefind->initial_offset = GST_BUFFER_OFFSET (buffer);
      gst_adapter_push (typefind->adapter, buffer);
      GST_OBJECT_UNLOCK (typefind);

      res = gst_type_find_element_chain_do_typefinding (typefind, TRUE, FALSE);

      if (typefind->mode == MODE_ERROR)
        res = GST_FLOW_ERROR;

      break;
    }
    default:
      g_assert_not_reached ();
      return GST_FLOW_ERROR;
  }

  return res;
}

 一样是两个阶段:MODE_NORMAL和MODE_TYPEFIND

重点函数gst_type_find_element_chain_do_typefinding

static GstFlowReturn
gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind,
    gboolean check_avail, gboolean at_eos)
{
  GstTypeFindProbability probability;
  GstCaps *caps = NULL;
  gsize avail;
  const guint8 *data;
  gboolean have_min, have_max;
  gchar *ext;

  GST_OBJECT_LOCK (typefind);
  if (typefind->force_caps) {
    caps = gst_caps_ref (typefind->force_caps);
    probability = GST_TYPE_FIND_MAXIMUM;
  }

  if (!caps) {
    avail = gst_adapter_available (typefind->adapter);

    if (check_avail) {
      have_min = avail >= TYPE_FIND_MIN_SIZE;
      have_max = avail >= TYPE_FIND_MAX_SIZE;
    } else {
      have_min = avail > 0;
      have_max = TRUE;
    }

    if (!have_min)
      goto not_enough_data;

    ext = gst_type_find_get_extension (typefind, typefind->sink);
    /* map all available data */
    data = gst_adapter_map (typefind->adapter, avail);
    caps = gst_type_find_helper_for_data_with_extension (GST_OBJECT (typefind),
        data, avail, ext, &probability);
    gst_adapter_unmap (typefind->adapter);
    g_free (ext);

    if (caps == NULL && have_max)
      goto no_type_found;
    else if (caps == NULL)
      goto wait_for_data;

    /* found a type */
    if (probability < typefind->min_probability)
      goto low_probability;
  }

  GST_OBJECT_UNLOCK (typefind);

  /* probability is good enough too, so let's make it known ... emitting this
   * signal calls our object handler which sets the caps. */
  /* Set to MODE_NORMAL before emitting have-type, in case it triggers a seek */
  typefind->mode = MODE_NORMAL;
  gst_type_find_element_emit_have_type (typefind, probability, caps);

  /* .. and send out the accumulated data */
  stop_typefinding (typefind);
  gst_caps_unref (caps);

  return GST_FLOW_OK;
......
}

一样流程gst_type_find_get_extension->gst_type_find_helper_for_data_with_extension识别到caps,然后切换到MODE_NORMAL阶段。

到这里我们就已经搞清楚了typefind的PULL和PUSH模式的运转流程了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值