GStreamer基础教程08——pipeline的快捷访问

前端之家收集整理的这篇文章主要介绍了GStreamer基础教程08——pipeline的快捷访问前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

目标

GStreamer建立的pipeline不需要完全关闭
​有多种方法可以让数据在任何时候送到pipeline中或者从pipeline中取出。
​本教程会展示:

如何把外部数据送到pipeline中

如何把数据从pipeline中取出

如何操作这些数据

介绍

有几种方法可以让应用通过pipeline和数据流交互。
​本教程讲述了最简单的一种,因为使用了专门为这个而创建的element。

appsrc,专门让应用可以往pipeline里面传入数据的element
​(https://www.freedesktop.org/software/gstreamer-sdk/data/docs/2012.5/gst-plugins-base-libs-0.10/gst-plugins-base-libs-appsrc.html#appsrc),
​appsink,就正好相反,让应用可以从pipeline中获得数据。

​为了避免混淆,我们可以这么来理解,
​appsrc是一个普通的source element,不过它的数据都是来自外太空,
​appsink是一个普通的sink element,数据从这里出去的就消失不见了。


​appsrc和appsink用得非常多,所以他们都自己提供API,你只要连接了gstreamer-app库,那么就可以访问到。
​在本教程里,我们会使用一种简单地方法通过信号来实现。


​appsrc可以有不同的工作模式:
​ 在pull模式,在需要时向应用请求数据;
​ 在push模式,应用根据自己的节奏把数据推送过来。

​而且,在push模式,如果已经有了足够的数据,应用可以在push时被阻塞,或者可以经由enough-data和need-data信号来控制。
​本教程中得例子就采用了这种信号控制的方式,其他没有提及的方法可以在appsrc的文档中查阅。


Buffers

通过pipeline传递的大块数据被称为buffers。
​因为本例子会制造数据同时也消耗数据,所以我们需要了解GstBuffer。

Source Pads负责制造buffer,这些buffer被sink pad消耗掉。GStreamer在一个个element之间传递这些buffer。

一个buffer只能简单地描述一小片数据,不要认为我们所有的buffer都是一样大小的。
而且,buffer有一个时间戳和有效期,这个就描述了什么时候buffer里的数据需要渲染出来。

​时间戳是个非常复杂和精深的话题,但目前这个简单地解释也足够了。

作为一个例子,一个filesrc会提供“ANY”属性的buffers并且没有时间戳信息。
​在demux(《GStreamer基础教程03——动态pipeline》)之后,buffers会有一些特定的cap了,比如"video/x-h264",
​在解码后,每一个buffer都会包含一帧有原始caps的视频帧(比如:video/x-raw-yuv),
​并且有非常明确地时间戳用来指示这一帧在什么时候显示

教程

本教程是上一篇教程(《GStreamer基础教程07——多线程和Pad的有效性》)在两个方面的扩展:
​第一是用appsrc来取代audiotestsrc来生成音频数据;
​第二是在tee里新加了一个分支,这样流入audio sink和波形显示的数据同样复制了一份传给appsink。
​这个appsink就把信息回传给应用,应用就可以通知用户收到了数据或者做其他更复杂的工作。

一个粗糙的波形发生器


[objc] view plain copy
  1. #include<gst/gst.h>@H_404_98@
  2. #include<string.h>@H_404_98@
  3. #defineCHUNK_SIZE1024/*Amountofbyteswearesendingineachbuffer*/@H_404_98@
  4. #defineSAMPLE_RATE44100/*Samplespersecondwearesending*/@H_404_98@
  5. #defineAUdio_CAPS"audio/x-raw-int,channels=1,rate=%d,signed=(boolean)true,width=16,depth=16,endianness=BYTE_ORDER"@H_404_98@
  6. /*Structuretocontainallourinformation,sowecanpassittocallbacks*/@H_404_98@
  7. typedef@H_404_98@struct@H_404_98@_CustomData{
  8. GstElement@H_404_98@*pipeline,*app_source,*tee,*audio_queue,*audio_convert1@H_404_98@,*audio_resample,*audio_sink;
  9. GstElement@H_404_98@*video_queue,*audio_convert2@H_404_98@,*visual,*video_convert,*video_sink;
  10. GstElement@H_404_98@*app_queue,*app_sink;
  11. guint64@H_404_98@num_samples;/*Numberofsamplesgeneratedsofar(fortimestampgeneration)*/@H_404_98@
  12. gfloata,b,c,d;/*Forwaveformgeneration*/@H_404_98@
  13. guintsourceid;/*TocontroltheGSource*/@H_404_98@
  14. GMainLoop@H_404_98@*main_loop;/*GLib'sMainLoop*/@H_404_98@
  15. }CustomData;
  16. /*ThismethodiscalledbytheidleGSourceinthemainloop,toFeedCHUNK_SIZEbytesintoappsrc.
  17. *Theidlehandlerisaddedtothemainloopwhenappsrcrequestsustostartsendingdata(need-datasignal)
  18. *andisremovedwhenappsrchasenoughdata(enough-datasignal).
  19. */@H_404_98@
  20. static@H_404_98@gbooleanpush_data(CustomData@H_404_98@*data){
  21. GstBuffer@H_404_98@*buffer;
  22. GstFlowReturnret;
  23. int@H_404_98@i;
  24. gint16@H_404_98@*raw;
  25. gintnum_samples=CHUNK_SIZE/2@H_404_98@;/*Becauseeachsampleis16bits*/@H_404_98@
  26. gfloatfreq;
  27. /*Createanewemptybuffer*/@H_404_98@
  28. buffer=gst_buffer_new_and_alloc(CHUNK_SIZE);
  29. /*Setitstimestampandduration*/@H_404_98@
  30. GST_BUFFER_TIMESTAMP(buffer)=gst_util_uint64@H_404_98@_scale(data->num_samples,GST_SECOND,SAMPLE_RATE);
  31. GST_BUFFER_DURATION(buffer)=gst_util_uint64@H_404_98@_scale(CHUNK_SIZE,SAMPLE_RATE);
  32. /*Generatesomepsychodelicwaveforms*/@H_404_98@
  33. raw=(gint16@H_404_98@*)GST_BUFFER_DATA(buffer);
  34. data->c+=data->d;
  35. data->d-=data->c/1000@H_404_98@;
  36. freq=1100@H_404_98@+11000@H_404_98@*data->d;
  37. for@H_404_98@(i=0@H_404_98@;i<num_samples;i++){
  38. data->a+=data->b;
  39. data->b-=data->a/freq;
  40. raw[i]=(gint16@H_404_98@)(5500@H_404_98@*data->a);
  41. }
  42. data->num_samples+=num_samples;
  43. /*Pushthebufferintotheappsrc*/@H_404_98@
  44. g_signal_emit_by_name(data->app_source,"push-buffer"@H_404_98@,buffer,&ret);
  45. /*Freethebuffernowthatwearedonewithit*/@H_404_98@
  46. gst_buffer_unref(buffer);
  47. if@H_404_98@(ret!=GST_FLOW_OK){
  48. /*Wegotsomeerror,stopsendingdata*/@H_404_98@
  49. return@H_404_98@FALSE;
  50. return@H_404_98@TRUE;
  51. }
  52. /*Thissignalcallbacktriggerswhenappsrcneedsdata.Here,weaddanidlehandler
  53. *tothemainlooptostartpushingdataintotheappsrc*/@H_404_98@
  54. static@H_404_98@void@H_404_98@start_Feed(GstElement@H_404_98@*source,guintsize,CustomData@H_404_98@*data){
  55. if@H_404_98@(data->sourceid==0@H_404_98@){
  56. g_print("StartFeeding\n"@H_404_98@);
  57. data->sourceid=g_idle_add((GSourceFunc)push_data,data);
  58. /*Thiscallbacktriggerswhenappsrchasenoughdataandwecanstopsending.
  59. *Weremovetheidlehandlerfromthemainloop*/@H_404_98@
  60. void@H_404_98@stop_Feed(if@H_404_98@(data->sourceid!=0@H_404_98@){
  61. g_print("StopFeeding\n"@H_404_98@);
  62. g_source_remove(data->sourceid);
  63. data->sourceid=0@H_404_98@;
  64. /*Theappsinkhasreceivedabuffer*/@H_404_98@
  65. void@H_404_98@new_buffer(GstElement@H_404_98@*sink,153); font-weight:bold; background-color:inherit">GstBuffer@H_404_98@*buffer;
  66. /*Retrievethebuffer*/@H_404_98@
  67. g_signal_emit_by_name(sink,"pull-buffer"@H_404_98@,&buffer);
  68. if@H_404_98@(buffer){
  69. /*Theonlythingwedointhisexampleisprinta*toindicateareceivedbuffer*/@H_404_98@
  70. g_print("*"@H_404_98@);
  71. /*Thisfunctioniscalledwhenanerrormessageispostedonthebus*/@H_404_98@
  72. void@H_404_98@error_cb(GstBus@H_404_98@*bus,153); font-weight:bold; background-color:inherit">GstMessage@H_404_98@*msg,153); font-weight:bold; background-color:inherit">GError@H_404_98@*err;
  73. gchar@H_404_98@*debug_info;
  74. /*Printerrordetailsonthescreen*/@H_404_98@
  75. gst_message_parse_error(msg,&err,&debug_info);
  76. g_printerr("Errorreceivedfromelement%s:%s\n"@H_404_98@,GST_OBJECT_NAME(msg->src),err->message);
  77. g_printerr("Debugginginformation:%s\n"@H_404_98@,debug_info?debug_info:"none"@H_404_98@);
  78. g_clear_error(&err);
  79. g_free(debug_info);
  80. g_main_loop_quit(data->main_loop);
  81. int@H_404_98@main(int@H_404_98@argc,153); font-weight:bold; background-color:inherit">charchar@H_404_98@*argv[]){
  82. CustomDatadata;
  83. GstPadTemplate@H_404_98@*tee_src_pad_template;
  84. GstPad@H_404_98@*tee_audio_pad,*tee_video_pad,*tee_app_pad;
  85. GstPad@H_404_98@*queue_audio_pad,*queue_video_pad,*queue_app_pad;
  86. gchar@H_404_98@*audio_caps_text;
  87. GstCaps@H_404_98@*audio_caps;
  88. GstBus@H_404_98@*bus;
  89. /*Initializecumstomdatastructure*/@H_404_98@
  90. memset(&data,0@H_404_98@,153); font-weight:bold; background-color:inherit">sizeof@H_404_98@(data));
  91. data.b@H_404_98@=1@H_404_98@; data.d@H_404_98@=1@H_404_98@;
  92. /*InitializeGStreamer*/@H_404_98@
  93. gst_init(&argc,&argv);
  94. /*Createtheelements*/@H_404_98@
  95. data.app_source@H_404_98@=gst_element_factory_make("appsrc"@H_404_98@,"audio_source"@H_404_98@);
  96. data.tee@H_404_98@=gst_element_factory_make("tee"@H_404_98@,"tee"@H_404_98@);
  97. data.audio_queue@H_404_98@=gst_element_factory_make("queue"@H_404_98@,"audio_queue"@H_404_98@);
  98. data.audio_convert1@H_404_98@=gst_element_factory_make("audioconvert"@H_404_98@,"audio_convert1"@H_404_98@);
  99. data.audio_resample@H_404_98@=gst_element_factory_make("audioresample"@H_404_98@,"audio_resample"@H_404_98@);
  100. data.audio_sink@H_404_98@=gst_element_factory_make("autoaudiosink"@H_404_98@,"audio_sink"@H_404_98@);
  101. data.video_queue@H_404_98@=gst_element_factory_make("queue"@H_404_98@,"video_queue"@H_404_98@);
  102. data.audio_convert2@H_404_98@=gst_element_factory_make("audioconvert"@H_404_98@,"audio_convert2"@H_404_98@);
  103. data.visual@H_404_98@=gst_element_factory_make("wavescope"@H_404_98@,"visual"@H_404_98@);
  104. data.video_convert@H_404_98@=gst_element_factory_make("ffmpegcolorspace"@H_404_98@,"csp"@H_404_98@);
  105. data.video_sink@H_404_98@=gst_element_factory_make("autovideosink"@H_404_98@,"video_sink"@H_404_98@);
  106. data.app_queue@H_404_98@=gst_element_factory_make("queue"@H_404_98@,"app_queue"@H_404_98@);
  107. data.app_sink@H_404_98@=gst_element_factory_make("appsink"@H_404_98@,"app_sink"@H_404_98@);
  108. /*Createtheemptypipeline*/@H_404_98@
  109. data.pipeline@H_404_98@=gst_pipeline_new("test-pipeline"@H_404_98@);
  110. if@H_404_98@(!data.pipeline@H_404_98@||!data.app_source@H_404_98@||!data.tee@H_404_98@||!data.audio_queue@H_404_98@||!data.audio_convert1@H_404_98@||
  111. !data.audio_resample@H_404_98@||!data.audio_sink@H_404_98@||!data.video_queue@H_404_98@||!data.audio_convert2@H_404_98@||!data.visual@H_404_98@||
  112. !data.video_convert@H_404_98@||!data.video_sink@H_404_98@||!data.app_queue@H_404_98@||!data.app_sink@H_404_98@){
  113. g_printerr("Notallelementscouldbecreated.\n"@H_404_98@);
  114. return@H_404_98@-1@H_404_98@;
  115. /*Configurewavescope*/@H_404_98@
  116. g_object_set(data.visual@H_404_98@,"shader"@H_404_98@,0@H_404_98@,"style"@H_404_98@,153); font-weight:bold; background-color:inherit">NULL@H_404_98@);
  117. /*Configureappsrc*/@H_404_98@
  118. audio_caps_text=g_strdup_printf(AUdio_CAPS,SAMPLE_RATE);
  119. audio_caps=gst_caps_from_string(audio_caps_text);
  120. g_object_set(data.app_source@H_404_98@,"caps"@H_404_98@,audio_caps,153); font-weight:bold; background-color:inherit">NULL@H_404_98@);
  121. g_signal_connect(data.app_source@H_404_98@,"need-data"@H_404_98@,G_CALLBACK(start_Feed),&data);
  122. g_signal_connect(data.app_source@H_404_98@,"enough-data"@H_404_98@,G_CALLBACK(stop_Feed),&data);
  123. /*Configureappsink*/@H_404_98@
  124. g_object_set(data.app_sink@H_404_98@,"emit-signals"@H_404_98@,TRUE,248)"> g_signal_connect(data.app_sink@H_404_98@,"new-buffer"@H_404_98@,G_CALLBACK(new_buffer),108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> gst_caps_unref(audio_caps);
  125. g_free(audio_caps_text);
  126. /*Linkallelementsthatcanbeautomaticallylinkedbecausetheyhave"Always"pads*/@H_404_98@
  127. gst_bin_add_many(GST_BIN(data.pipeline@H_404_98@),data.app_source@H_404_98@,data.tee@H_404_98@,data.audio_queue@H_404_98@,data.audio_convert1@H_404_98@,data.audio_resample@H_404_98@,
  128. data.audio_sink@H_404_98@,data.video_queue@H_404_98@,data.audio_convert2@H_404_98@,data.visual@H_404_98@,data.video_convert@H_404_98@,data.video_sink@H_404_98@,data.app_queue@H_404_98@,
  129. data.app_sink@H_404_98@,153); font-weight:bold; background-color:inherit">if@H_404_98@(gst_element_link_many(data.app_source@H_404_98@,153); font-weight:bold; background-color:inherit">NULL@H_404_98@)!=TRUE||
  130. gst_element_link_many(data.audio_queue@H_404_98@,data.audio_sink@H_404_98@,153); font-weight:bold; background-color:inherit">NULL@H_404_98@)!=TRUE||
  131. gst_element_link_many(data.video_queue@H_404_98@,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> gst_element_link_many(data.app_queue@H_404_98@,data.app_sink@H_404_98@,153); font-weight:bold; background-color:inherit">NULL@H_404_98@)!=TRUE){
  132. g_printerr("Elementscouldnotbelinked.\n"@H_404_98@);
  133. gst_object_unref(data.pipeline@H_404_98@);
  134. return@H_404_98@-1@H_404_98@;
  135. /*ManuallylinktheTee,whichhas"Request"pads*/@H_404_98@
  136. tee_src_pad_template=gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(data.tee@H_404_98@),"src%d"@H_404_98@);
  137. tee_audio_pad=gst_element_request_pad(data.tee@H_404_98@,tee_src_pad_template,153); font-weight:bold; background-color:inherit">NULL@H_404_98@,248)"> g_print("Obtainedrequestpad%sforaudiobranch.\n"@H_404_98@,gst_pad_get_name(tee_audio_pad));
  138. queue_audio_pad=gst_element_get_static_pad(data.audio_queue@H_404_98@,"sink"@H_404_98@);
  139. tee_video_pad=gst_element_request_pad(data.tee@H_404_98@,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> g_print("Obtainedrequestpad%sforvideobranch.\n"@H_404_98@,gst_pad_get_name(tee_video_pad));
  140. queue_video_pad=gst_element_get_static_pad(data.video_queue@H_404_98@,"sink"@H_404_98@);
  141. tee_app_pad=gst_element_request_pad(data.tee@H_404_98@,248)"> g_print("Obtainedrequestpad%sforappbranch.\n"@H_404_98@,gst_pad_get_name(tee_app_pad));
  142. queue_app_pad=gst_element_get_static_pad(data.app_queue@H_404_98@,153); font-weight:bold; background-color:inherit">if@H_404_98@(gst_pad_link(tee_audio_pad,queue_audio_pad)!=GST_PAD_LINK_OK||
  143. gst_pad_link(tee_video_pad,queue_video_pad)!=GST_PAD_LINK_OK||
  144. gst_pad_link(tee_app_pad,queue_app_pad)!=GST_PAD_LINK_OK){
  145. g_printerr("Teecouldnotbelinked\n"@H_404_98@);
  146. gst_object_unref(data.pipeline@H_404_98@);
  147. gst_object_unref(queue_audio_pad);
  148. gst_object_unref(queue_video_pad);
  149. gst_object_unref(queue_app_pad);
  150. /*Instructthebustoemitsignalsforeachreceivedmessage,andconnecttotheinterestingsignals*/@H_404_98@
  151. bus=gst_element_get_bus(data.pipeline@H_404_98@);
  152. gst_bus_add_signal_watch(bus);
  153. g_signal_connect(G_OBJECT(bus),"message::error"@H_404_98@,(GCallback)error_cb,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> gst_object_unref(bus);
  154. /*Startplayingthepipeline*/@H_404_98@
  155. gst_element_set_state(data.pipeline@H_404_98@,GST_STATE_PLAYING);
  156. /*CreateaGLibMainLoopandsetittorun*/@H_404_98@
  157. data.main_loop@H_404_98@=g_main_loop_new( g_main_loop_run(data.main_loop@H_404_98@);
  158. /*ReleasetherequestpadsfromtheTee,andunrefthem*/@H_404_98@
  159. gst_element_release_request_pad(data.tee@H_404_98@,tee_audio_pad);
  160. gst_element_release_request_pad(data.tee@H_404_98@,tee_video_pad);
  161. gst_object_unref(tee_audio_pad);
  162. gst_object_unref(tee_video_pad);
  163. gst_object_unref(tee_app_pad);
  164. /*Freeresources*/@H_404_98@
  165. gst_element_set_state(data.pipeline@H_404_98@,GST_STATE_NULL);
  166. return@H_404_98@0@H_404_98@;
  167. }

工作流程

创建pipeline段的代码就是上一篇的教程中得例子的扩大版。
包括初始或所有的element,连接有Always Pad的element然后手动连接tee element的Request Pad。

下面我们关注一下appsrc和appsink这两个element的配置:

关于appsink的配置,我们连接了new-buffer的信号,这个信号在每次收到buffer的时候发出。
​当然,这个信号的发出需要emit-signals这个信号属性被开启(默认是关闭的)。
启动pipeline,等到消息和最后的清理资源都和以前的没什么区别。让我们关注我们刚刚注册的回调吧。

这个函数在appsrc内部队列将要空的时候调用,在这里我们做的事情仅仅是用g_idle_add()方法注册一个GLib的idle函数,
​这个函数会给appsrc输入数据知道内部队列满为止。
​一个GLib的idle函数是一个GLib在主循环在“idle”时会调用的方法,也就是说,当时没有更高优先级的任务运行。
这只是appsrc多种发出数据方法中的一个。
​特别需要指出的是,buffer不是必须要在主线程中用GLib方法来传递给appsrc的,
​你也不是一定要用need-data和enough-data信号来同步appsrc的(据说这样最方便)。

我们记录下g_idle_add()的返回的sourceid,这样后面可以关掉它。

copy

/*Thiscallbacktriggerswhenappsrchasenoughdataandwecanstopsending.
  • *Weremovetheidlehandlerfromthemainloop*/
  • if(data->sourceid!=0){
  • g_print("Stopfeeding\n");
  • g_source_remove(data->sourceid);
  • data->sourceid=0;
  • }

  • ​这个函数当appsrc内部的队列满的时候调用,所以我们需要停止发送数据。这里我们简单地用g_source_remove()来把idle函数移走。
    这个函数给appsrc发送数据。它被GLib调用的次数和频率我们不加以控制,但我们会在它任务完成时关闭它(appsrc内部队列满)。 这里第一步是用gst_buffer_new_and_alloc()方法和给定的大小创建一个新buffer(例子中是1024字节)。

    我们计算我们生成的采样数据的数据量,把数据存在CustomData.num_samples里面,
    ​这样我们可以用GstBuffer提供的GST_BUFFER_TIMESTAMP宏来生成buffer的时间戳。

    gst_util_uint64_scale是一个工具函数,用来缩放数据,确保不会溢出。

    这些给buffer的数据可以用GstBuffer提供的GST_BUFFER_DATA宏来访问。

    我们会跳过波形的生成部分,因为这不是本教程要讲述的内容。

    copy

    gst_buffer_unref(buffer);
    一旦我们的buffer已经准备好,我们把带着这个buffer的push-buffer信号传给appsrc,
    ​然后就调用gst_buffer_unref()方法,因为我们不会再用到它了。
    copy
    /*Theappsinkhasreceivedabuffer*/@H_404_98@
  • /*Retrievethebuffer*/@H_404_98@
  • g_signal_emit_by_name(sink,&buffer);
  • if@H_404_98@(buffer){
  • /*Theonlythingwedointhisexampleisprinta*toindicateareceivedbuffer*/@H_404_98@
  • g_print("*"@H_404_98@);
  • gst_buffer_unref(buffer);
  • 最后,这个函数在appsink收到buffer时被调用
    ​我们使用了pull-buffer的信号来重新获得buffer,因为是例子,所以仅仅在屏幕上打印一些内容
    ​我们可以用GstBuffer的GST_BUFFER_DATA宏来获得数据指针和用GST_BUFFER_SIZE宏来获得数据大小。
    ​请记住,这里的buffer不是一定要和我们在push_data函数里面创建的buffer完全一致的,
    ​在传输路径上得任何一个element都可能对buffer进行一些改变。
    ​(这个例子中仅仅是在appsrc和appsink中间通过一个tee element,所以buffer没有变化)。 请不要忘记调用gst_buffer_unref()来释放buffer,就讲这么多吧。

  • 猜你在找的设计模式相关文章