playbin播放test.wav加载插件过程分析

  • 一、前言
  • 二、playbin 播放 .wav 音频插件加载一览
  • 三、测试代码
    • 3.1 gst_init
    • 3.2 gst_element_set_state
  • 四、gst_element_set_state 触发 playbin 加载流程分析
    • 4.1 gst_element_factory_make 创建 playbin时流程
    • 4.2 gst_element_set_state 是如何触发 playbin 的呢?
    • 4.3 playbin 在何时创建 uridecodebin & uridecodebin 又加载了哪些插件
      • 4.3.1 playbin 在何时创建 uridecodebin
      • 4.3.2 uridecodebin 又加载了哪些插件
    • 4.3.3 playbin 加载 inputSelector
    • 4.3.3 playsink 加载了哪些插件呢?
      • 4.3.3.1 playsink 加载 tee
      • 4.3.3.1 playbin 的 gst_play_sink_reconfigure
  • 五、总结

一、前言

希望通过这篇文章记录一下 playbin 加载 plugin 的机制,熟悉一些 playbin (或者是 GStreamer)的源码

二、playbin 播放 .wav 音频插件加载一览


从上图可以知道 playbin可分为两部分,中间用 input-selector 连接起来

uridecodebin -> input-selector -> playsink

当然,uridecodebin 和 playsink 中也包含很多组件,我们一点一点来分析。

三、测试代码

#include <gst/gst.h>
// In which states all these operations can be performed./* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {GstElement *playbin;  /* Our one and only element */gboolean playing;      /* Are we in the PLAYING state? */gboolean terminate;    /* Should we terminate execution? */gboolean seek_enabled; /* Is seeking enabled for this media? */gboolean seek_done;    /* Have we performed the seek already? */gint64 duration;       /* How long does this media last, in nanoseconds */
} CustomData;/* Forward definition of the message processing function */
static void handle_message (CustomData *data, GstMessage *msg);int main(int argc, char *argv[]) {CustomData data;GstBus *bus;GstMessage *msg;GstStateChangeReturn ret;data.playing = FALSE;data.terminate = FALSE;data.seek_enabled = FALSE;data.seek_done = FALSE;data.duration = GST_CLOCK_TIME_NONE;/* Initialize GStreamer */gst_init (&argc, &argv);// return 0;/* Create the elements */data.playbin = gst_element_factory_make ("playbin", "playbin");if (!data.playbin) {g_printerr ("Not all elements could be created.\n");return -1;}/* Set the URI to play */g_object_set (data.playbin, "uri", "file:///home/joshua/Project/gst_base_tutorial/media/test.wav", NULL);/* Start playing */ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);if (ret == GST_STATE_CHANGE_FAILURE) {g_printerr ("Unable to set the pipeline to the playing state.\n");gst_object_unref (data.playbin);return -1;}/* Listen to the bus */bus = gst_element_get_bus (data.playbin);do {msg = gst_bus_timed_pop_filtered (bus, 100 * GST_MSECOND,GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_DURATION);/* Parse message */if (msg != NULL) {handle_message (&data, msg);} else {/* We got no message, this means the timeout expired */if (data.playing) {gint64 current = -1;/* Query the current position of the stream */if (!gst_element_query_position (data.playbin, GST_FORMAT_TIME, &current)) {g_printerr ("Could not query current position.\n");}/* If we didn't know it yet, query the stream duration */if (!GST_CLOCK_TIME_IS_VALID (data.duration)) {if (!gst_element_query_duration (data.playbin, GST_FORMAT_TIME, &data.duration)) {g_printerr ("Could not query current duration.\n");}}/* Print current position and total duration */g_print ("Position %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r",GST_TIME_ARGS (current), GST_TIME_ARGS (data.duration));/* If seeking is enabled, we have not done it yet, and the time is right, seek */if (data.seek_enabled && !data.seek_done && current > 10 * GST_SECOND) {g_print ("\nReached 10s, performing seek...\n");gst_element_seek_simple (data.playbin, GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 30 * GST_SECOND);data.seek_done = TRUE;}}}} while (!data.terminate);/* Free resources */gst_object_unref (bus);gst_element_set_state (data.playbin, GST_STATE_NULL);gst_object_unref (data.playbin);return 0;
}static void handle_message (CustomData *data, GstMessage *msg) {GError *err;gchar *debug_info;switch (GST_MESSAGE_TYPE (msg)) {case GST_MESSAGE_ERROR:gst_message_parse_error (msg, &err, &debug_info);g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");g_clear_error (&err);g_free (debug_info);data->terminate = TRUE;break;case GST_MESSAGE_EOS:g_print ("\nEnd-Of-Stream reached.\n");data->terminate = TRUE;break;case GST_MESSAGE_DURATION:/* The duration has changed, mark the current one as invalid */data->duration = GST_CLOCK_TIME_NONE;break;case GST_MESSAGE_STATE_CHANGED: {//   GstState old_state, new_state, pending_state;//   gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);//   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {//     g_print ("Pipeline state changed from %s to %s:\n",//         gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));//     /* Remember whether we are in the PLAYING state or not *///     data->playing = (new_state == GST_STATE_PLAYING);//     if (data->playing) {//       /* We just moved to PLAYING. Check if seeking is possible *///       GstQuery *query;//       gint64 start, end;//       query = gst_query_new_seeking (GST_FORMAT_TIME);//       if (gst_element_query (data->playbin, query)) {//         gst_query_parse_seeking (query, NULL, &data->seek_enabled, &start, &end);//         if (data->seek_enabled) {//           g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",//               GST_TIME_ARGS (start), GST_TIME_ARGS (end));//         } else {//           g_print ("Seeking is DISABLED for this stream.\n");//         }//       }//       else {//         g_printerr ("Seeking query failed.");//       }//       gst_query_unref (query);//     }//   }} break;default:/* We should not reach here */g_printerr ("Unexpected message received.\n");break;}gst_message_unref (msg);
}

3.1 gst_init

gst_init (&argc, &argv);
data.playbin = gst_element_factory_make ("playbin", "playbin");
g_object_set (data.playbin, "uri", "file:///home/joshua/Project/gst_base_tutorial/media/test.wav", NULL);
  • gst_init & gst_element_factory_make,不是这里的重点,就不深究了;
  • g_object_set 这里可以简单看下 playbin2.c 源码里是怎么实现的。这里需要了解 gobject install property 的一个小知识点,利用 g_object_class_install_property 可以向 plugin 中添加属性

3.2 gst_element_set_state

这里简简单单的一步对于 element state 的设置,却牵一发动全身,不仅让 playbin 把整个 pipeline 搭建了起来,还把 wav的整个流播放了起来。

四、gst_element_set_state 触发 playbin 加载流程分析

4.1 gst_element_factory_make 创建 playbin时流程

  1. playbin 的继承关系
GObject+----GInitiallyUnowned+----GstObject+----GstElement+----GstBin+----GstPipeline+----GstPlayBin

从上面的继承关系中我们可以得知,创建playbin时也同时调用 element, bin, pipeline 的init,这就是基于GObject的继承关系;
(plugin_init里面就不分析了,无非就是 plugin的登记)

  • gst_play_bin_init 中创建了一个 playsink 的对象,并把playsink 加入到bin中,作为playbin的一个子element。从dot图中可以知道这是playbin中sink的最外层,用于存放后面加载的plugin。
// ---/* add sink */playbin->playsink =g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",1, NULL);gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
// ---
  • gst_play_sink_init 中 创建 streamsynchronizer, 并把streamsynchronizer加入到bin中,作为playsink的一个子element。
  playsink->stream_synchronizer =g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);gst_bin_add (GST_BIN_CAST (playsink),GST_ELEMENT_CAST (playsink->stream_synchronizer));

4.2 gst_element_set_state 是如何触发 playbin 的呢?

  • gst_element_set_state
  if (oclass->set_state)result = (oclass->set_state) (element, state);

从这里可以知道, gst_element_set_state 是调用了 oclass->set_state

  • 在 gst_element_class_init 中,我们又看到这样一段赋值,实际上是调用 gst_element_set_state_func


  • 在 gst_element_set_state_func 中,我们又可以定位到比较关键的一行
  • 在 gst_element_change_state 中,我们终于找到了底层的调用func,也就是 oclass->change_state; 注意一下,谁是oclass呢?
oclass = GST_ELEMENT_GET_CLASS (element);

这里的 element 是 playbin,也就是说,这里调用的是 playbin 中的 change_state。(因为 playbin 继承自 GstElement,在gst_play_bin_class_init中,重写了 GstElement 的 change_state 函数)

  • playbin2.c 中 gst_play_bin_change_state 是一个非常重要的函数,其中 transition 的更迭更是playbin众多业务的触发点,我们下面可以从gst_play_bin_change_state 来分析 playbin的加载流程,也可以看到 GStreamer 设计的巧妙,如何通过 设置流的状态 来推动 整个pipeline的运行。



4.3 playbin 在何时创建 uridecodebin & uridecodebin 又加载了哪些插件

4.3.1 playbin 在何时创建 uridecodebin

  1. 从 gst_play_bin_change_state 中可以看到 GstStateChange: GST_STATE_CHANGE_READY_TO_PAUSED 时,调用setup_next_source,在 setup_next_source -> activate_group -> 加载 uridecodebin plugin
// gstplaybin2.ccase GST_STATE_CHANGE_READY_TO_PAUSED:if ((ret =setup_next_source (playbin,GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)goto failure;if (ret == GST_STATE_CHANGE_SUCCESS)ret = GST_STATE_CHANGE_ASYNC;
  1. activate_group 中加载 uridecodebin ,并把 uridecodebin add 到 playbin 里;
    GST_DEBUG_OBJECT (playbin, "making new uridecodebin");uridecodebin = gst_element_factory_make ("uridecodebin", NULL);if (!uridecodebin)goto no_decodebin;gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);

4.3.2 uridecodebin 又加载了哪些插件

  1. gst_uri_decode_bin_change_state 中可以看到 GstStateChange: GST_STATE_CHANGE_READY_TO_PAUSED 时,调用 setup_source -> gen_source_element -> gst_element_make_from_uri 加载 filesrc
 //gsturidecodebin.ccase GST_STATE_CHANGE_READY_TO_PAUSED:GST_DEBUG ("ready to paused");if (!setup_source (decoder))goto source_failed;ret = GST_STATE_CHANGE_ASYNC;
  1. gst_element_make_from_uri 加载 filesrc。gst_element_make_from_uri内部就不深究了,它可以根据uri来获取协议便于产生不同的src,目前结构: (uridecodebin)【filesrc】
 //gsturidecodebin.csource =gst_element_make_from_uri (GST_URI_SRC, decoder->uri, "source", &err);
  1. setup_source -> make_decoder,加载 decodebin,目前 pipeline = (uridecodebin)【filesrc - decodebin】
    /* now create the decoder element */decodebin = gst_element_factory_make ("decodebin", NULL);GST_LOG_OBJECT(decoder, "making new decodebin end");if (!decodebin)goto no_decodebin;
  1. 然而decodebin也可以细化,在decodebin init 的过程中,它加载 神奇的插件 typefind,目前 pipeline = (uridecodebin)【filesrc - (decodebin)【typefind】】(这个typefind的功能很诡异,后面可以着重看一下)
  //decodebin.c/* we create the typefind element only once */decode_bin->typefind = gst_element_factory_make ("typefind", "typefind");if (!decode_bin->typefind) {g_warning ("can't find typefind element, decodebin will not work");} else {GstPad *pad;GstPad *gpad;GstPadTemplate *pad_tmpl;/* add the typefind element */if (!gst_bin_add (GST_BIN (decode_bin), decode_bin->typefind)) {g_warning ("Could not add typefind element, decodebin will not work");gst_object_unref (decode_bin->typefind);decode_bin->typefind = NULL;}
  1. 再看 gst_decode_bin_change_state 中可以看到 GstStateChange: GST_STATE_CHANGE_READY_TO_PAUSED,这里绑定 “have-type” 的信号,找到合适类型的时候会触发 type_found 回调,type_found -> analyze_new_pad ->connect_pad
      /* 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);
  1. connect_pad 中这里,加载 wavparse 插件,并放入decodebin中,目前 pipeline = (uridecodebin)【filesrc +(decodebin)【typefind + wavparse 】】,至此 uridecodebin 里的 plugin 全部加载完毕。
    /* 2.1. Try to create an element */if ((element = gst_element_factory_create (factory, NULL)) == NULL) {GST_WARNING_OBJECT (dbin, "Could not create an element from %s",gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));g_string_append_printf (error_details,"Could not create an element from %s\n",gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));continue;}
/// --------------------------------/* ... add it ... */if (!(gst_bin_add (GST_BIN_CAST (dbin), element))) {GST_WARNING_OBJECT (dbin, "Couldn't add %s to the bin",GST_ELEMENT_NAME (element));remove_error_filter (dbin, element, NULL);g_string_append_printf (error_details, "Couldn't add %s to the bin\n",GST_ELEMENT_NAME (element));gst_object_unref (element);continue;}

4.3.3 playbin 加载 inputSelector

1.playbin 中 绑定处理 uridecodebin 的 “padd-added” 信号,这里 uridecodebin 最右边的已经没有控件了,需要 request a new pad,playbin 这里处理了这个信号,并 加载了 input-selector

  /* connect pads and other things */group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",G_CALLBACK (pad_added_cb), group);
    /* no combiner, create one */GST_DEBUG_OBJECT (playbin, "creating new input selector");if (custom_combiner)combine->combiner = custom_combiner;elsecombine->combiner = gst_element_factory_make ("input-selector", NULL);

4.3.3 playsink 加载了哪些插件呢?

从外部的大框架上来看,playbin = uridecodebin + inputseletor + playsink。因为 uridecodebin 内部的控件加载特别多,所以 playsink的内部也不简单。

4.3.3.1 playsink 加载 tee

1.playbin 中 绑定处理 uridecodebin 的 “no-more-pads” 信号,这个代表 不能产生更多的动态插件,会触发 回调 no_more_pads_cb -> gst_play_sink_request_pad -> make tee plugin,目前 playsink = tee + streamsynchronizer

        GST_LOG_OBJECT (playsink, "creating tee");/* create tee when needed. This element will feed the audio sink chain* and the vis chain. */playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");if (playsink->audio_tee == NULL) {post_missing_element_message (playsink, "tee");GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,(_("Missing element '%s' - check your GStreamer installation."),"tee"), (NULL));res = NULL;break;}playsink->audio_tee_sink =gst_element_get_static_pad (playsink->audio_tee, "sink");gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);

4.3.3.1 playbin 的 gst_play_sink_reconfigure

下面的 plugin非常多,且创建 和 加载前后顺序不一致,这里就从 gst_play_sink_reconfigure 进行往下分析吧。

  1. no_more_pads_cb -> gst_play_sink_reconfigure
gboolean
gst_play_sink_reconfigure (GstPlaySink * playsink)
{GST_LOG_OBJECT (playsink, "Triggering reconfiguration");GST_PLAY_SINK_LOCK (playsink);video_set_blocked (playsink, TRUE);audio_set_blocked (playsink, TRUE);text_set_blocked (playsink, TRUE);GST_PLAY_SINK_UNLOCK (playsink);return TRUE;
}
  1. audio_set_blocked -> sinkpad_blocked_cb -> gst_play_sink_do_reconfigure -> gen_audio_chain
    if (!playsink->audiochain) {GST_DEBUG_OBJECT (playsink, "creating new audio chain");playsink->audiochain = gen_audio_chain (playsink, raw);}
  • make “autoaudiosink”, autoaudiosink 内部会 加载 fakesink, plusesink,不细谈了,这里用的是 plusesink
    /* only try fallback if no specific sink was chosen */if (chain->sink == NULL) {GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");elem = gst_element_factory_make ("autoaudiosink", "audiosink");chain->sink = try_element (playsink, elem, TRUE);}
  • make abin, 作为容器,管理插件用的
chain->chain.bin = gst_bin_new ("abin");bin = GST_BIN_CAST (chain->chain.bin);gst_object_ref_sink (bin);gst_bin_add (bin, chain->sink);
  • make “queue”
  /* we have to add a queue when we need to decouple for the video sink in* visualisations and for streamsynchronizer */GST_DEBUG_OBJECT (playsink, "adding audio queue");chain->queue = gst_element_factory_make ("queue", "aqueue");if (chain->queue == NULL) {post_missing_element_message (playsink, "queue");GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,(_("Missing element '%s' - check your GStreamer installation."),"queue"), ("audio playback and visualizations might not work"));
  • make “audioconvert”
      GST_DEBUG_OBJECT (playsink, "adding audio filter");chain->filter_conv =gst_element_factory_make ("audioconvert", "filter-convert");if (!chain->filter_conv) {post_missing_element_message (playsink, "audioconvert");GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,(_("Missing element '%s' - check your GStreamer installation."),"audioconvert"),("audio playback and visualizations might not work"));} else {gst_bin_add (bin, chain->filter_conv);head = prev = chain->filter_conv;}

五、总结

简单记录一下一段 playbin 源码之旅;
即使现在,我也不能说清楚里面的万分之一,只能把自己走过的路一点点记下来,留个印象。草草收了尾,很多地方不理解,很多地方需要去挖掘,只是暂时没有更多的时间了。
我要去一个新的地方了,希望以后开心一点,可以得到别人的认可。

【GStreamer源码分析】playbin播放test.wav加载插件过程分析相关推荐

  1. Mybatis 源码分析(一)配置文件加载流程

    Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...

  2. 【SA8295P 源码分析】08 - XBL Loader 加载 SMSS、XBL Config、SHRM、CDT 、DDR、APDP、RamDump、OEM_MISC、AOP、QSEE过程分析

    [SA8295P 源码分析]08 - XBL Loader 加载 SMSS.XBL Config.SHRM.CDT .DDR.APDP.RamDump.OEM_MISC.AOP.QSEE Dev Co ...

  3. Tomcat源码分析——server.xml文件的加载

    前言 作为Java程序员,对于tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载的进行分析. 源码分析 Bootstrap的 ...

  4. thinkphp源码分析(三)—自动加载篇(Loader的分析)

    源码分析 自动加载 系统会调用 Loader::register()方法注册自动加载,在这一步完成后,所有符合规范的类库(包括Composer依赖加载的第三方类库)都将自动加载. 系统的自动加载由下面 ...

  5. Tomcat7.0源码分析——server.xml文件的加载与解析

    前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析进行分析. 加载过程分析 Bootst ...

  6. flume源码分析2--配置文件的加载

    上面提到Application启动的时候,PollingPropertiesFileConfigurationProvider作为唯一的LifecycleAware类型的组件被交给监护者Lifecyc ...

  7. 描述一下JAVA的加载过程_JVM源码分析之Java类的加载过程

    简书 占小狼 转载请注明原创出处,谢谢! 趁着年轻,多学习 背景 最近对Java细节的底层实现比较感兴趣,比如Java类文件是如何加载到虚拟机的,类对象和方法是以什么数据结构存在于虚拟机中?虚方法.实 ...

  8. 【Spring源码分析系列】bean的加载

    前言 以 BeanFactory bf  = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过 ...

  9. live555 源码分析:播放启动

    本文分析 live555 中,流媒体播放启动,数据开始通过 RTP/RTCP 传输的过程. 如我们在 live555 源码分析:子会话 SETUP 中看到的,一个流媒体子会话的播放启动,由 Strea ...

最新文章

  1. python 命令行参数-python获取命令行参数的方法(汇总)
  2. 在做mvc时遇到的一些问题
  3. 计算机大学生学生证,电子学生证运行一月争议多:学生感觉被监控
  4. qt执行linux sudo命令语句,linux命令_sudo
  5. MySQL获取连接_MySQL 连接查询超全详解
  6. Windows 下安装Python包(Numpy)的错误:Unable to find vcvarsall.bat
  7. 实用!Mybatis中trim标签的使用教程
  8. 011 使用AOP操作注解
  9. oracle重置口令是什么意思,Oracle重置数据库命令
  10. 找maven本地仓库
  11. Unity-Xlua
  12. 后缀–ize_动词后加ize的后缀有什么作用
  13. html页面整体隐藏,三种隐藏 HTML 元素的方式
  14. 菜鸟的Vue基础快速入门
  15. MS PROJECT 下载
  16. 【画方】画方网络准入管理系统
  17. Ag-Grid学习-angular8
  18. 如何将视频中的某一段截取制作gif动图
  19. LoadLibrary无法加载.dll解决思路
  20. Java精品项目源码第127期新闻发布网站系统

热门文章

  1. matlab 保存图像分辨率改变问题(saveas、imwrite、print)
  2. transform a 3d plane to xy-plane
  3. 将域名绑定到Office 365上
  4. 游戏开发当中的声音系统
  5. system,sys密码找回
  6. 展望南阳广告业的发展
  7. 数据分析中的细分维度
  8. usb hub 要驱动 linux,Linux驱动中配置支持特定USB HUB
  9. 超囧超脑残的GALGAME问题(论坛防恶意注册问题)
  10. 改进的point in polygon problem算法介绍