GStreamer基础教程03——动态pipeline

原文链接:
http://blog.csdn.net/sakulafly/article/details/20936067

本教程介绍pipeline的一种新的创建方式——在运行中创建,而不是在运行前一次性的创建结束。

介绍

在这篇教程里的pipeline并非在运行前就全部创建结束的。
放松一下,这样做没有任何问题。如果我们不进行更深入的处理,那么数据在到达pipeline的末尾时就直接丢弃了,当然,我们肯定会进行深入处理的。。。

在这个例子中,我们会打开一个已经包含了音视频的文件(container file)。
负责打开这样的容器文件的element叫做demuxer,我们常见的容器格式包括MKV、QT、MOV、Ogg还有ASF、WMV、WMA等等。


一个容器中可能包含多个流(比如:一路视频,两路音频),
demuxer会把他们分离开来,然后从不同的输出口送出来。
这样在pipeline里面的不同的分支可以处理不同的数据。


在GStreamer里面有个术语描述这样的接口——pad(GstPad)。
Pad分成
sink pad——数据从这里进入一个element
source pad——数据从这里流出element。

很自然的,(这也是这两种pad命名的来源)
source element仅包含source pad,
sink element仅包含sink pad,
而过滤器两种pad都包含。


一个demuxer包含一个sink pad和多个source pad,数据从sink pad输入然后每个流都有一个source pad。


为了完整起见,给出一张示意图,图中有一个demuxer和两个分支,一个处理音频一个处理视频。
请注意,这不是本教程pipeline的示意图。


这里主要复杂在demuxer在没有看到容器文件之前无法确定需要做的工作,不能生成对应的内容
也就是说,demuxer开始时是没有source pad给其他element连接用的。


解决方法
只管建立pipeline,让source和demuxer连接起来,然后开始运行。
当demuxer接收到数据之后它就有了足够的信息生成source pad。
这时我们就可以继续把其他部分和demuxer生成的pad连接起来,生成一个完整的pipeline。

简单起见,在这个例子中,我们仅仅连接音频的pad而不处理视频的pad。

动态Hello World

[objc] view plain copy
  1. #include<gst/gst.h>
  2. /*Structuretocontainallourinformation,sowecanpassittocallbacks*/
  3. typedefstruct_CustomData{
  4. GstElement*pipeline;
  5. GstElement*source;
  6. GstElement*convert;
  7. GstElement*sink;
  8. }CustomData;
  9. /*Handlerforthepad-addedsignal*/
  10. staticvoidpad_added_handler(GstElement*src,GstPad*pad,153); font-weight:bold; background-color:inherit">CustomData*data);
  11. intmain(intargc,153); font-weight:bold; background-color:inherit">charchar*argv[]){
  12. CustomDatadata;
  13. GstBus*bus;
  14. GstMessage*msg;
  15. GstStateChangeReturnret;
  16. gbooleanterminate=FALSE;
  17. /*InitializeGStreamer*/
  18. gst_init(&argc,&argv);
  19. /*Createtheelements*/
  20. data.source=gst_element_factory_make("uridecodebin","source");
  21. data.convert=gst_element_factory_make("audioconvert","convert");
  22. data.sink=gst_element_factory_make("autoaudiosink","sink");
  23. /*Createtheemptypipeline*/
  24. data.pipeline=gst_pipeline_new("test-pipeline");
  25. if(!data.pipeline||!data.source||!data.convert||!data.sink){
  26. g_printerr("NotallelementsCouldbecreated.\n");
  27. return-1;
  28. }
  29. /*Buildthepipeline.NotethatweareNOTlinkingthesourceatthis
  30. *point.Wewilldoitlater.*/
  31. gst_bin_add_many(GST_BIN(data.pipeline),data.source,data.convert,data.sink,153); font-weight:bold; background-color:inherit">NULL);
  32. if(!gst_element_link(data.convert,data.sink)){
  33. g_printerr("ElementsCouldnotbelinked.\n");
  34. gst_object_unref(data.pipeline);
  35. return-1;
  36. }
  37. /*SettheURItoplay*/
  38. g_object_set(data.source,"uri","http://docs.gstreamer.com/media/sintel_trailer-480p.webm",0); background-color:inherit">/*Connecttothepad-addedsignal*/
  39. g_signal_connect(data.source,"pad-added",G_CALLBACK(pad_added_handler),&data);
  40. /*Startplaying*/
  41. ret=gst_element_set_state(data.pipeline,GST_STATE_PLAYING);
  42. if(ret==GST_STATE_CHANGE_FAILURE){
  43. g_printerr("Unabletosetthepipelinetotheplayingstate.\n");
  44. /*Listentothebus*/
  45. bus=gst_element_get_bus(data.pipeline);
  46. do{
  47. msg=gst_bus_timed_pop_filtered(bus,GST_CLOCK_TIME_NONE,
  48. GST_MESSAGE_STATE_CHANGED|GST_MESSAGE_ERROR|GST_MESSAGE_EOS);
  49. /*Parsemessage*/
  50. if(msg!=NULL){
  51. GError*err;
  52. gchar*debug_info;
  53. switch(GST_MESSAGE_TYPE(msg)){
  54. caseGST_MESSAGE_ERROR:
  55. gst_message_parse_error(msg,&err,&debug_info);
  56. g_printerr("ErrorreceivedfromElement%s:%s\n",GST_OBJECT_NAME(msg->src),err->message);
  57. g_printerr("Debugginginformation:%s\n",debug_info?debug_info:"none");
  58. g_clear_error(&err);
  59. g_free(debug_info);
  60. terminate=TRUE;
  61. break;
  62. GST_MESSAGE_EOS:
  63. g_print("End-Of-Streamreached.\n");
  64. GST_MESSAGE_STATE_CHANGED:
  65. /*Weareonlyinterestedinstate-changedmessagesfromthepipeline*/
  66. if(GST_MESSAGE_SRC(msg)==GST_OBJECT(data.pipeline)){
  67. GstStateold_state,new_state,pending_state;
  68. gst_message_parse_state_changed(msg,&old_state,&new_state,&pending_state);
  69. g_print("Pipelinestatechangedfrom%sto%s:\n",
  70. gst_element_state_get_name(old_state),gst_element_state_get_name(new_state));
  71. break;
  72. default:
  73. /*Weshouldnotreachhere*/
  74. g_printerr("Unexpectedmessagereceived.\n");
  75. gst_message_unref(msg);
  76. }while(!terminate);
  77. /*Freeresources*/
  78. gst_object_unref(bus);
  79. gst_element_set_state(data.pipeline,GST_STATE_NULL);
  80. gst_object_unref(data.pipeline);
  81. return0;
  82. /*Thisfunctionwillbecalledbythepad-addedsignal*/
  83. GstPad*new_pad,153); font-weight:bold; background-color:inherit">CustomData*data){
  84. GstPad*sink_pad=gst_element_get_static_pad(data->convert,248)"> GstPadLinkReturnret;
  85. GstCaps*new_pad_caps=NULL;
  86. GstStructure*new_pad_struct=NULL;
  87. constgchar*new_pad_type= g_print("Receivednewpad'%s'from'%s':\n",GST_PAD_NAME(new_pad),GST_ELEMENT_NAME(src));
  88. /*Ifourconverterisalreadylinked,wehavenothingtodohere*/
  89. if(gst_pad_is_linked(sink_pad)){
  90. g_print("Wearealreadylinked.Ignoring.\n");
  91. gotoexit;
  92. /*Checkthenewpad'stype*/
  93. new_pad_caps=gst_pad_get_caps(new_pad);
  94. new_pad_struct=gst_caps_get_structure(new_pad_caps,0);
  95. new_pad_type=gst_structure_get_name(new_pad_struct);
  96. if(!g_str_has_prefix(new_pad_type,"audio/x-raw")){
  97. g_print("Ithastype'%s'whichisnotrawaudio.Ignoring.\n",new_pad_type);
  98. gotoexit;
  99. /*Attemptthelink*/
  100. ret=gst_pad_link(new_pad,sink_pad);
  101. if(GST_PAD_LINK_Failed(ret)){
  102. g_print("Typeis'%s'butlinkFailed.\n",new_pad_type);
  103. else{
  104. g_print("Linksucceeded(type'%s').\n",248)"> exit:
  105. /*Unreferencethenewpad'scaps,ifwegotthem*/
  106. if(new_pad_caps!=NULL)
  107. gst_caps_unref(new_pad_caps);
  108. /*Unreferencethesinkpad*/
  109. gst_object_unref(sink_pad);
  110. }

工作流程 在这里我们存下了所有需要的局部变量,因为本教程中会有回调函数,所以我们把所有的数据组织成一个struct,这样比较方便调用。 这是一个声明,后面会使用这个API。

我们像前面一样创建一个个element。
​uridecodebin自己会在内部初始化必要的element,然后把一个URI变成一个原始音视频流输出,它差不多做了playbin2的一半工作。
​因为它自己带着demuxer,所以它的source pad没有初始化,我们等会会用到。

audioconvert在不同的音频格式转换时很有用。这里用这个element是为了确保应用的平台无关性。

autoaudiosink和上一篇教程里面的autovideosink是非常相似的,只是操作的时音频流。
​这个element的输出就是直接送往声卡的音频流。

这里我们把转换element和sink element连接起来,
​注意,我们没有把source连接起来——因为这个时候还没有source pad。
​我们把转换element和sink element连接起来后暂时就放在那里,等待后面在处理。
和我们在上一篇教程一样,我们把URI通过设置属性的方法设置好。

信号

从CustomData我们可以获得转换element对象,然后使用gst_element_get_static_pad()方法可以获得sink pad。
​这个pad是我们希望和new_pad连接的pad。
​在前面的教程里,我们是用element和element连接的,让GStreamer自己来选择合适的pad。
​在这里,我们是手动的把两个pad直接连接起来。
copy
uridecodebin会自动创建许多的pad,对于每一个pad,这个回调函数都会被调用。
​上面这段代码可以防止连接多次。
因为我们只处理生成的audio数据,所以我们要检查new pad输出的数据类型。
​我们前面创建了一部分处理音频的pipeline(convert+sink),没有生成处理视频的部分,所以我们只能处理音频数据。
gst_pad_get_caps()方法会获得pad的capability(也就是pad支持的数据类型),是被封装起来的GstCaps结构。
​一个pad可以有多个capability,GstCaps可以包含多个GstStructure,每个都描述了一个不同的capability。


​在这个例子中,我们知道我们的pad需要的capability是声音,我们使用gst_caps_get_structure()方法来获得GstStructure。

最后,我们用gst_structure_get_name()方法来获得structure的名字——最主要的描述部分。
​如果名字不是由audio/x-raw开始的,就意味着不是一个解码的音频数据,也就不是我们所需要的,反之,就是我们需要连接的:

copy
/*Attemptthelink*/
  • ret=gst_pad_link(new_pad,sink_pad);
  • if(GST_PAD_LINK_FAILED(ret)){
  • g_print("Typeis'%s'butlinkfailed.\n",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> }else{
  • g_print("Linksucceeded(type'%s').\n",51); font-family:Arial; font-size:14px; line-height:26px">gst_pad_link()方法会把两个pad连接起来。
    ​就像gst_element_link()这个方法一样,连接必须是从source到sink,连接的两个pad必须在同一个bin里面。

    到这儿我们的任务就完成了,当一个合适的pad出现后,它会和后面的audio处理部分相连,然后继续运行直到ERROR或者EOS。
    ​下面,我们再介绍一点点GStreamer状态的概念。

    状态

    我们介绍过不把pipeline置成PLAYING状态,播放是不会开始的。
    ​这里我们继续介绍一下其他的几种状态,在GStreamer里面有4种状态:


    NULL NULL状态或者初始化状态
    READY element已经READY或者PAUSED
    PAUSED element已经PAUSED,准备接受数据
    PLAYING element在PLAYING,时钟在运行数据
    状态迁移只能在相邻的状态里迁移,也就是说,你不能从NULL一下跳到PLAYING。
    ​你必须经过READY和PAUSED状态。
    ​如果你把pipeline设到PLAYING状态,GStreamer自动会经过中间状态的过渡。
  • /*Weareonlyinterestedinstate-changedmessagesfromthepipeline*/
  • if(GST_MESSAGE_SRC(msg)==GST_OBJECT(data.pipeline)){
  • GstStateold_state,pending_state;
  • gst_message_parse_state_changed(msg,&pending_state);
  • g_print("Pipelinestatechangedfrom%sto%s:\n",
  • gst_element_state_get_name(old_state),gst_element_state_get_name(new_state));
  • break;
  • 我们增加这段代码来监听总线上状态变化的情况,并且打印出相应的内容
    ​虽然每个element都会把它的消息放到总线上,但我们只监听pipeline本身的。
    ​绝大多数应用都是在PLAYING状态开始播放, ​然后跳转到PAUSE状态来提供暂停功能, ​最后在退出时退到NULL状态。

    相关文章

    迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图...
    高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,...
    策略模式(Strategy)策略模式(Strategy)[Policy]意图:定...
    访问者模式(Visitor)访问者模式(Visitor)意图:表示一个...
    命令模式(Command)命令模式(Command)[Action/Transactio...
    生成器模式(Builder)生成器模式(Builder)意图:将一个对...