"); //-->
前面的多媒体处理框架已经介绍了Gstd的基本概念,我们再来介绍一下Pads作为完整性。
基础概念介绍---衬垫(Pads)
• 衬垫(Pads)在GStreamer中被用于多个元件的链接,从而让数据流能在这样的链接中流动。衬垫是元件对外的接口,可以被看作是一个元件的插座或者端口,元件之间的链接就是依靠着衬垫。数据流从一个元件的源衬垫(source pad)到另一个元件的接收衬垫(sink pad)。衬垫的功能(capabilities)决定了一个元件所能处理的媒体类型。
• 衬垫有处理特殊数据的能力:一个衬垫能够限制数据流类型的通过。
• 链接成功的条件是:只有在两个衬垫允许通过的数据类型一致的时候才被建立。数据类型的设定使用了一个叫做caps negotiation的方法。数据类型被为一个GstCaps变量所述。
这个特殊的元件同时拥有源衬垫和接收衬垫。接收输入数据的接收衬垫在元件的左端,源衬垫在右端。
它有多个输出衬垫。 Ogg分流器是个很好的实例。因为Ogg流包含了视频和音频。一个源衬垫可能包含视频元数据流,另一个则包含音频元数据流。
• 下面的这个比喻可能对你理解衬垫(Pads)有所帮助。一个衬垫(Pads)很像一个物理设备上的插头。 例如一个家庭影院系统。一个家庭影院系统由一个功放(amplifier),一个DVD机,还有一个无声的视频投影组成。 我们需要连接DVD机到功放(amplifier),因为两个设备都有音频插口;我们还需要连接投影机到DVD机上,因为两个设备都有视频处理插口。但我们很难将投影机与功放(amplifier)连接起来,因为他们之间处理的是不同的插口。 GStreamer衬垫(Pads)的作用跟家庭影院系统中的插口是一样的。
衬垫的特性---数据导向
• 一个衬垫的类型由2个特性决定:它的数据导向(direction)以及它的时效性(availability)。
• Gstreamer定义了2种衬垫的数据导向:源衬垫以及接收衬垫。衬垫的数据导向这个术语是从元件内部的角度给予定义的: 元件通过它们的接收衬垫接收数据,通过它们的源衬垫输出数据。如果通过一张图来形象地表述,接收衬垫画在元件的左侧,而源衬垫画在元件的右侧,数据从左向右流动。
衬垫的特性---时效性
• 衬垫的时效性比衬垫的数据导向复杂得多。一个衬垫可以拥有三种类型的时效性: 永久型(always)、随机型(sometimes)、请求型(onrequest)。三种时效性的意义顾名思义: 永久型的衬垫一直会存在,随机型的衬垫只在某种特定的条件下才存在(会随机消失的衬垫也属于随机型),请求型的衬垫只在应用程序明确发出请求时才出现。
• 通过gst-inspect可以查看一个元件所包含的pads信息。
• 下面列举一下:
随机型衬垫(sometimes)
• 一些元件在其被创建时不会立刻产生所有它将用到的衬垫。例如在一个Ogg demuxer的元件中可能发生这种情况。这个元件将会读取Ogg流,每当它在Ogg流中检测到一些元数据流时(例vorbis, theora ),它会为每个元数据流创建动态衬垫。同样,它也会在流终止时删除该衬垫。动态衬垫在demuxer这种元件中可以起到很大的作用。
• 以oggdemux为例,运行gst-inspect oggdemux会显示出一个名字叫做’sink’的永久型接收衬垫和名字叫做’src_%d’的随机型衬垫。可以从衬垫模板(Pad Templates)中的“Availability”
属性中看到这些信息。
衬垫(Pads)的性能(capabilities)
• 由于衬垫对于一个元件起了非常重要的作用,一个衬垫能够限制数据流类型的通过,因此就有了一个术语来᧿述能够通过衬垫或当前通过衬垫的数据流。这个术语就是性能 (capabilities)
• 衬垫的性能通过GstCaps 对象来进行描述。一个GstCaps对象会包含一个或多个GstStructure。一个 GstStructure描述一种媒体类型。
• 下面给出了一个例子,你可以通过运行 gstinspect vorbisdec 看到“vorbisdec” 元件的一些性能。你可能会看到2个衬垫: 源衬垫和接收衬垫, 2个衬垫的时效性都是永久型,并且每个衬垫都有相应的功能描述。接收衬垫将会接收vorbis编码的音频数据,其 mime-type显示为“audio/x-vorbis”。源衬垫可以将解码后的音频数据发送给下一个元件,其 mimetype显示为“audio/x-raw-float”。源衬垫的功能描述中还包含了一些其它的特性: 音频采样率(rate)、声道数(channels)、以及一些其他信息。
衬垫性能的用途
• 自动填充(Autoplugging): 根据元件的功能自动找到可连接的元件。
• 兼容性检测(Compatibility detection): 当两个个衬垫连接时,GStreamer 会验证它们是否采用的同样的数据流格式进行交互。连接并验证两个衬垫是否兼容的过程叫“功能谈判”.
• 元数据(Metadata): 通过读取衬垫的功能(capabilities),应用程序能够 供有关当前流经衬垫的正在播放的媒体类型信息。而这个信息我们叫做元数据(Metadata)。
• 过滤(Filtering): 应用程序可以通过衬垫的功能(capabilities)来给两个交互的衬垫之间的媒体类型加以限制,这些被限制的媒体类型的集合应该是两个交互的衬垫共同支持的格式集的子集。
动态创建pipeline
• 给出一张示意图,图中有一个demuxer和两个分支,一个处理音频一个处理视频。在一个容器文件中可能包含多个流(比如:一路视频,两路音频), demuxer会把他们分离开来,然后从不同的输出口送出来。
• 这里主要复杂在demuxer在没有看到容器文件之前无法确定需要做的工作,不能生成对应的内容。也就是说, demuxer开始时是没有source pad给其他element连接用的。
• 解决方法是只管建立pipeline,让source和demuxer连接起来,然后开始运行。当demuxer接收到数据之后它就有了足够的信息生成source pad。这时我们就可以继续把其他部分和demuxer新生成的pad连接起来,生成一个完整的pipeline。
核心代码
/* Initialize GStreamer */ gst_init (&argc, &argv); /* Create the elements */ data.source = gst_element_factory_make ("uridecodebin", "source"); data.convert = gst_element_factory_make ("audioconvert", "convert"); data.sink = gst_element_factory_make ("autoaudiosink", "sink"); /* Create the empty pipeline */ data.pipeline = gst_pipeline_new ("test-pipeline"); /* Build the pipeline. Note that we are NOT linking the source at this * point. We will do it later. */ gst_bin_add_many (GST_BIN (data.pipeline), data.source, data.convert , data.sink, NULL); if (!gst_element_link (data.convert, data.sink)) { g_printerr ("Elements could not be linked.\n"); gst_object_unref (data.pipeline); return -1; } /* Set the URI to play */ g_object_set (data.source, "uri", "http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL); /* Connect to the pad-added signal */ g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data); /* Start playing */ ret = gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
这里我们把三个元件都添加到bin中,但是我们只是把convert element和sink element连接起来———因为这时source element还没有source pad。我们把convert element和sink element连接起来后暂时就放在那里,等待后面再处理,我们把URI通过设置属性的方法设置好。
我们使用g_signal_connect()方法把“pad-added”信号和我们的源(uridecodebin)联系了起来,并且注册了一个回调函数。 GStreamer把&data这个指针的内容传给回调函数.
这样CustomData这个数据结构中的数据也就传递了过去。信号机制也是Gstreamer中的一个重要部分。
回调函数
当我们的 “pad-added” source element 信号。这样我们的回调就会被调用了 最后获得足够的数据时,它就会自动生成 。 source pad,并且触发
/* This function will be called by the pad-added signal */ static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) { GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink"); GstPadLinkReturn ret; GstCaps *new_pad_caps = NULL; GstStructure *new_pad_struct = NULL; const gchar *new_pad_type = NULL; g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src)); /* If our converter is already linked, we have nothing to do here */ if (gst_pad_is_linked (sink_pad)) { g_print (" We are already linked. Ignoring.\n"); goto exit; } /* Check the new pad's type */ 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 (" It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type); goto exit; } /* Attempt the link */ ret = gst_pad_link (new_pad, sink_pad); if (GST_PAD_LINK_FAILED (ret)) { g_print (" Type is '%s' but link failed.\n", new_pad_type); } else { g_print (" Link succeeded (type '%s').\n", new_pad_type); } exit: /* Unreference the new pad's caps, if we got them */ if (new_pad_caps != NULL) gst_caps_unref (new_pad_caps); /* Unreference the sink pad */ gst_object_unref (sink_pad); }
我们以audio数据为例,所以我们要检查new pad输出的数据类型。 gst_pad_get_caps()方法会获得pad的capability(也就是pad支持的数据类型),是被封装起来的GstCaps结构。一个pad可以有多个capability, GstCaps可以包含多个GstStructure,每个都描述了一个不同的capability。使用gst_caps_get_structure()方法来获得GstStructure。最后,我们用gst_structure_get_name()方法来获得structure的名字——最主要的描述部分。如果名字不是由audio/x-raw开始的,就意味着不是一个解码的音频数据,也就不是我们所需要的,反之,就是我们需要连接的。
gst_pad_link()方法会把两个pad连接起来。就像gst_element_link()这个方法一样,连接必须是从source到sink,连接的两个pad必须在同一个bin里面。到这儿我们的任务就完成了,当一个合适的pad出现后,它会和后面的audio处理部分相连,然后继续运行直到ERROR或者EOS。
以上便是Gst整体概念介绍,我们在后续的介绍中将开始Gst将对工具的使用进行进一步介绍。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。