gstreamer学习笔记---typefind功能流程简单分析

版权声明:本文为博主原创文章,未经允许,不得转载。 https://blog.csdn.net/weixin_41944449/article/details/82865256

  使用gstreamer播放音视频都知道,当我们直接通过playbin播放视频的时候,playbin会根据当前播放的音视频数据自动查找相应的element添加到pipeline进行对数据进行下一步处理,那么,当playbin在解析数据的时候,发现上一个element发现需要某一个caps的时候,是谁来查找,究竟有那个element支持这个caps的处理,然后又选择它添加到pipeline中?这个,就是我们今天的主角----typefind完成的。下面,我们通过使用playbin播放一个编码格式为H264、封装格式为MP4的本地文件,了解playbin是如何根据播放的内容逐个查找相应的element,它又是如何完成这些操作的呢。

一、typefind的创建

  首先的,你希望可以查找别人,在这个前提,你得是合法的,所以也会将自己注册到gstreamer系统。首先的,typefind的plugin注册函数在gstreamer-xxx/plugins/elements目录下的gstelements.c,在该文件中,就有一个plugin_init()函数将它注册到gstreamer core。注册完成之后,gstreamer就可以使用该类型的element。
  playbin,在gstreamer中是一个element,只不过是特殊的element,一个bin,这个bin中还会包含这其他的bin,比如说uridecodebin将会解析源的uri以及解码,而decodebin又会比它小一层,将会负责解封装解码等操作。而typefind又是什么时候登场的呢,它是包含在decodebin中,在decodebin的init()函数有以下操作。

static void
gst_decode_bin_init (GstDecodeBin * decode_bin)
{
  ...

  /* we create the typefind element only once */
  decode_bin->typefind = gst_element_factory_make ("typefind", "typefind");

  ...
}

  在uridecodebin创建decodebin,将会添加其到uridecodebin,而decodebin也会将typefind添加到自身,接下来就会是bin中的各个element状态切换。
  在decodebin状态切换到PAUSED的时候,也会带动typefind的状态切换为PAUSED。在激活pad的时候,有查询scheduling,确定使用push模式还是pull模式。而typefind将会将会选择PULL模式,将会创建task运行gst_type_find_element_loop()函数。

二、typefind查找

  typefind的功能,很像一个查找器,是从source读取到数据,然后从这部分数据解析信息,查找相应的caps,知道相应的caps之后,接着将会把caps信息通过信号发送出去,等待别人的处理。下面来看看在task中是如何查找的。

static void
gst_type_find_element_loop (GstPad * pad)
{
  if (typefind->mode == MODE_TYPEFIND) {
      /* 这个peer一般是proxy pad */
      peer = gst_pad_get_peer (pad);
      if (peer) {
        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;
        }
        
        /* 通过上游的uri,查询source的文件后缀,比如返回mp4 */
        ext = gst_type_find_get_extension (typefind, pad);

        /* 查找caps */
        found_caps =
            gst_type_find_helper_get_range (GST_OBJECT_CAST (peer),
            GST_OBJECT_PARENT (peer),
            (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
            (guint64) size, ext, &probability);
         ...
      }
      ...
  }
  ...
}

  gst_type_find_helper_get_range()函数又是如何查找caps的呢,将会是先根据后缀名,对gstreamer支持的caps进行一个排序,方便后续对比,排序之后,将会预读取数据,分析具体的数据,是否可以使用相应的caps。下面我们将一起来看代码。

GstCaps *
gst_type_find_helper_get_range (GstObject * obj, GstObject * parent,
    GstTypeFindHelperGetRangeFunction func, guint64 size,
    const gchar * extension, GstTypeFindProbability * prob)
{
  ...
  /* 1. 通过该函数,将会返回注册到gstreamer core的
   * GST_TYPE_TYPE_FIND_FACTORY型GstPluginFeature */
  type_list = gst_type_find_factory_get_list ();
  
  /* 2. 将会在这里,根据之前从uri得到的文件后缀,对Feature链表
   * 进行排序,文件后缀名与feature的extension信息对比,相同的
    * 将会将feature移动到表头 */
  if (extension) {
    for (l = type_list; l; l = next) {
      ...
    }
    
  ...
  /* 3. 将会在这里调用feature的功能探测函数预读取数据,确认文件数据格式 */
  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)
      break;
  }
  }
  ...
  
}

  可能看到上面的代码注释觉得很困惑,第1和第3步,调用到的究竟是什么,看完下面就知道了。
  第一步的操作,上面已经说了,该函数将会返回GST_TYPE_TYPE_FIND_FACTORY类型的feature,这些feature都是查找类型的feature。它们在注册feature的时候,将会注册相应的探测函数,在探测函数中通过读取数据,然后判断类型,是否需要该feature可以解析处理的,就是这样的一个功能,gstreamer才会知道,当前数据该用那个caps。
  这些feature的注册是在gst-plugins-base-1.xx.xx/gst/typefind目录下的gsttypefindfunctions.c文件注册的,在该文件的plugin_init()函数中分别通过宏TYPE_FIND_REGISTER_START_WITHTYPE_FIND_REGISTER_RIFFTYPE_FIND_REGISTER向gstreamer core注册了相应的GST_TYPE_TYPE_FIND_FACTORY类型的feature,在注册的时候,赋值了相应的探测function、extension等信息。接着,在我们通过gst_type_find_helper_get_range()函数探测caps的时候,就会有第一步的获取feature,第二步的根据extension排序,第三步的通过功能探测函数确认最终的feature。
  介绍到这里,小伙伴们应该知道gst_type_find_helper_get_range()返回的是什么了吧,它返回的就是支持解析、处理输入数据的caps,比如输入的是封装格式为MOV的mp4文件,那么返回的是video/quicktime;输入的是编码格式为H264的RAW数据,返回的是video/x-h264,这样说,应该大家都清楚了吧。
  回到gst_type_find_element_loop()函数,当查找到caps的时候,又将会发送相应的信号,代码如下:

static void
gst_type_find_element_loop (GstPad * pad)
{
  if (typefind->mode == MODE_TYPEFIND) {
        ...
 
        /* 查找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_type_find_element_loop()函数中通过gst_type_find_element_emit_have_type()函数将caps信息生成caps EVENT然后保存在pad,接着发送have-type信号。谁来接收这个信号呢,往下看。

三、have-type信号处理

  上面说到的,typefind发送信号,哪里接收呢。之前有提到过,这个typefind是包含在decodebin,在decodebin的init()函数创建typefind实例对象。而在decodebin的状态切换函数,从READY切换到PAUSED的时候将会监听该信号,接收到该函数将会调用decodebin文件的type_found()函数。

static GstStateChangeReturn
gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
{
  switch (transition) {
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      ...
      /* connect a signal to find out when the typefind element found
       * a type */
      dbin->have_type_id =
          g_signal_connect (dbin->typefind, "have-type",
          G_CALLBACK (type_found), dbin);
     ...
     break;
     ...
  }
  ...
}

  在type_found()函数中,最终将会调用analyze_new_pad()函数。在该函数进行了许多操作,会检查当前decodebin的elements状态,检查现在的数据状态是流通到什么位置,typefind传递过来的是什么类型的feature,是Parser还是Converter还是其他,当前的caps是什么类型的caps,最后确认之后才会根据caps查找feature。我们重点介绍一下根据caps查找feature,在该函数将会发送autoplug-factories信号。最终的,逐个传递将会在playbin真正处理这个信号,处理该信号的函数是autoplug_factories_cb()。

static gboolean
analyze_new_pad (GstDecodeBin * dbin, GstElement * src, GstPad * pad,
    GstCaps * caps, GstDecodeChain * chain, GstDecodeChain ** new_chain)
{
  ...
  /* 1.d else get the factories and if there's no compatible factory goto
   * unknown_type */
  g_signal_emit (G_OBJECT (dbin),
      gst_decode_bin_signals[SIGNAL_AUTOPLUG_FACTORIES], 0, dpad, caps,
      &factories);
  ...
}

  autoplug_factories_cb()函数将会从gstreamer core中获取所有的feature,然后对比它们的sink pad caps,如果一致,将会把该feature保存到链表,进行相应的检查之后将返回feature链表。

static GValueArray *
autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
    GstCaps * caps, GstSourceGroup * group)
{
  /* 将根据caps查找sink pad支持该caps的element返回factory_list */
  g_mutex_lock (&playbin->elements_lock);
  gst_play_bin_update_elements_list (playbin);
  factory_list =
      gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
      gst_caps_is_fixed (caps));
  g_mutex_unlock (&playbin->elements_lock);
  ...
}

  得到支持caps的element之后,回到analyze_new_pad()函数,在该函数中将会通过connect_pad()函数创建并连接该element,创建的函数是element = gst_element_factory_create (factory, NULL),得到element之后,将其添加到bin。最后就是element的状态切换与查询了。在这个element查找的功能,bin进行了很多工作,会有根据caps查找到的element进行不同的分类查找处理等操作,感兴趣的具体可以看看代码。
  同时的,decodebin还监控pad-added信号,在相应的element添加pad时,发送该信号,调用到decodebin的pad_added_cb()函数,在该函数中最终也还是会调用到analyze_new_pad()函数进行查找element以及link等操作,具体的操作,可以看代码,这一块没有往下跟踪了。

四、总结

  typefind的功能,并不是它单纯就完成相应的element查找功能,与bin交互较多,它更多的是根据注册的caps探测函数,查找相应的caps,然后将caps返回给bin,bin再根据caps从gstreamer core查找element并创建连接。而typefind则像完成一部分功能一样,往前推进,继续查找解析数据的caps。




  以上是个人理解,有理解错误的地方,欢迎指出,感谢

猜你喜欢

转载自blog.csdn.net/weixin_41944449/article/details/82865256
今日推荐