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
- #include<gst/gst.h>
- /*Structuretocontainallourinformation,sowecanpassittocallbacks*/
- typedefstruct_CustomData{
- GstElement*pipeline;
- GstElement*source;
- GstElement*convert;
- GstElement*sink;
- }CustomData;
- /*Handlerforthepad-addedsignal*/
- staticvoidpad_added_handler(GstElement*src,GstPad*pad,153); font-weight:bold; background-color:inherit">CustomData*data);
- intmain(intargc,153); font-weight:bold; background-color:inherit">charchar*argv[]){
- CustomDatadata;
- GstBus*bus;
- GstMessage*msg;
- GstStateChangeReturnret;
- gbooleanterminate=FALSE;
- /*InitializeGStreamer*/
- gst_init(&argc,&argv);
- /*Createtheelements*/
- data.source=gst_element_factory_make("uridecodebin","source");
- data.convert=gst_element_factory_make("audioconvert","convert");
- data.sink=gst_element_factory_make("autoaudiosink","sink");
- /*Createtheemptypipeline*/
- data.pipeline=gst_pipeline_new("test-pipeline");
- if(!data.pipeline||!data.source||!data.convert||!data.sink){
- g_printerr("Notallelementscouldbecreated.\n");
- return-1;
- }
- /*Buildthepipeline.NotethatweareNOTlinkingthesourceatthis
- *point.Wewilldoitlater.*/
- gst_bin_add_many(GST_BIN(data.pipeline),data.source,data.convert,data.sink,153); font-weight:bold; background-color:inherit">NULL);
- if(!gst_element_link(data.convert,data.sink)){
- g_printerr("Elementscouldnotbelinked.\n");
- gst_object_unref(data.pipeline);
- return-1;
- }
- /*SettheURItoplay*/
- g_object_set(data.source,"uri","http://docs.gstreamer.com/media/sintel_trailer-480p.webm",0); background-color:inherit">/*Connecttothepad-addedsignal*/
- g_signal_connect(data.source,"pad-added",G_CALLBACK(pad_added_handler),&data);
- /*Startplaying*/
- ret=gst_element_set_state(data.pipeline,GST_STATE_PLAYING);
- if(ret==GST_STATE_CHANGE_FAILURE){
- g_printerr("Unabletosetthepipelinetotheplayingstate.\n");
- /*Listentothebus*/
- bus=gst_element_get_bus(data.pipeline);
- do{
- msg=gst_bus_timed_pop_filtered(bus,GST_CLOCK_TIME_NONE,
- GST_MESSAGE_STATE_CHANGED|GST_MESSAGE_ERROR|GST_MESSAGE_EOS);
- /*Parsemessage*/
- if(msg!=NULL){
- GError*err;
- gchar*debug_info;
- switch(GST_MESSAGE_TYPE(msg)){
- caseGST_MESSAGE_ERROR:
- gst_message_parse_error(msg,&err,&debug_info);
- g_printerr("Errorreceivedfromelement%s:%s\n",GST_OBJECT_NAME(msg->src),err->message);
- g_printerr("Debugginginformation:%s\n",debug_info?debug_info:"none");
- g_clear_error(&err);
- g_free(debug_info);
- terminate=TRUE;
- break;
- GST_MESSAGE_EOS:
- g_print("End-Of-Streamreached.\n");
- GST_MESSAGE_STATE_CHANGED:
- /*Weareonlyinterestedinstate-changedmessagesfromthepipeline*/
- if(GST_MESSAGE_SRC(msg)==GST_OBJECT(data.pipeline)){
- GstStateold_state,new_state,pending_state;
- gst_message_parse_state_changed(msg,&old_state,&new_state,&pending_state);
- g_print("Pipelinestatechangedfrom%sto%s:\n",
- gst_element_state_get_name(old_state),gst_element_state_get_name(new_state));
- break;
- default:
- /*Weshouldnotreachhere*/
- g_printerr("Unexpectedmessagereceived.\n");
- gst_message_unref(msg);
- }while(!terminate);
- /*Freeresources*/
- gst_object_unref(bus);
- gst_element_set_state(data.pipeline,GST_STATE_NULL);
- gst_object_unref(data.pipeline);
- return0;
- /*Thisfunctionwillbecalledbythepad-addedsignal*/
- GstPad*new_pad,153); font-weight:bold; background-color:inherit">CustomData*data){
- GstPad*sink_pad=gst_element_get_static_pad(data->convert,248)"> GstPadLinkReturnret;
- GstCaps*new_pad_caps=NULL;
- GstStructure*new_pad_struct=NULL;
- constgchar*new_pad_type= g_print("Receivednewpad'%s'from'%s':\n",GST_PAD_NAME(new_pad),GST_ELEMENT_NAME(src));
- /*Ifourconverterisalreadylinked,wehavenothingtodohere*/
- if(gst_pad_is_linked(sink_pad)){
- g_print("Wearealreadylinked.Ignoring.\n");
- gotoexit;
- /*Checkthenewpad'stype*/
- new_pad_caps=gst_pad_get_caps(new_pad);
- new_pad_struct=gst_caps_get_structure(new_pad_caps,0);
- new_pad_type=gst_structure_get_name(new_pad_struct);
- if(!g_str_has_prefix(new_pad_type,"audio/x-raw")){
- g_print("Ithastype'%s'whichisnotrawaudio.Ignoring.\n",new_pad_type);
- gotoexit;
- /*Attemptthelink*/
- ret=gst_pad_link(new_pad,sink_pad);
- if(GST_PAD_LINK_Failed(ret)){
- g_print("Typeis'%s'butlinkFailed.\n",new_pad_type);
- else{
- g_print("Linksucceeded(type'%s').\n",248)"> exit:
- /*Unreferencethenewpad'scaps,ifwegotthem*/
- if(new_pad_caps!=NULL)
- gst_caps_unref(new_pad_caps);
- /*Unreferencethesinkpad*/
- gst_object_unref(sink_pad);
- }
工作流程
我们像前面一样创建一个个element。
uridecodebin自己会在内部初始化必要的element,然后把一个URI变成一个原始音视频流输出,它差不多做了playbin2的一半工作。
因为它自己带着demuxer,所以它的source pad没有初始化,我们等会会用到。
audioconvert在不同的音频格式转换时很有用。这里用这个element是为了确保应用的平台无关性。
autoaudiosink和上一篇教程里面的autovideosink是非常相似的,只是操作的时音频流。
这个element的输出就是直接送往声卡的音频流。
注意,我们没有把source连接起来——因为这个时候还没有source pad。
我们把转换element和sink element连接起来后暂时就放在那里,等待后面在处理。
信号
信号是通过名字来区分的,每个GObject都有它自己的信号。
在这段代码里面,我们使用
g_signal_connect()
方法把
“pad-added”信号
和我们的源(uridecodebin)联系了起来,并且注册了一个回调函数。
GStreamer把&data这个指针的内容传给回调函数,这样CustomData这个数据结构中的数据也就传递了过去。
这个信号是有GstElement产生的,可以在相关的文档中找到或者用gst-inspect方法来查到。
我们现在准备开始运行了!
和前面的教程一样,把pipeline置成PLAYING状态,然后开始监听ERROR或者EOS。
回调函数
当我们的source element最后获得足够的数据时,它就会自动生成source pad,并且触发“pad-added”信号。
这样我们的回调就会被调用了:
在这个例子中,就是uridecodebin,也是我们唯一注册的一个信号。
new_pad是加到src上的pad。通常来说,是我们需要连接的pad。
data指针是跟随信号一起过来的参数,在这个例子中,我们传递的时CustomData指针。
copy