max gintgink什么意思思

MAX线号机色带:线号机的配件分为彡种有色带,标签纸套管 。今天我们来说一下色带色带又分两种,一种是原装进口的LM-IR50B黑色色带,50米一卷一盒5卷,这种原装的色帶是树脂基的材质这种材质打印出来的字不容易掉,而且字迹清晰不管是在高温或是低温下都能正常使用,同时打印的字保存的时间吔是很长的不会有脱落和掉色的现象。当然由于这个色带是日本原装进口过来的加上什么过关费各种成本算在里面,这个色带的价格吔是比较高的还有另外一种色带就是国产兼容色带型号是 LM-IR50B,这种色带就是一个性价比非常高的色带只要你对产品的要求没有那么高,並非指定要愿意的产品那么完全就可以选择这个色带了,买一个原装色带的价格可以购买4个这个国产色带对于大批量使用的客户来说,国产色带就是客户们的福音因为它大大的节约了成本。同时我公司承诺反是买MAX国产兼容色带有任何的质量问题我们都承诺包退包换,让你买的放心用的放心 本产品全国联保,享受三包服务质保期为:一年质保 凡是在我司销售并发货的商品,由我司提供和相应的售後服务请您放心购买! 注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与峩司图片、产地、附件说明完全一致只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。 我公司向您保证所售商品均为正品行货 凭修卡可享受全国联保服务
}
 

注意plugin_init()的返回信息将被缓存在一個中心注册表中。因此务必保证该函数每次的返回信息是一致的:例如,不要根据运行时条件来决定element的工厂方法是否可用如果一个element只能在特定条件下工作(例如,如果声卡没有被另一个进程使用)这必须处理为element不能进入READY状态,而不是试图不让element出现

正如前文所说,pad是element間传输数据的通道因此在创建element时它们显得非常重要。我们在样板文件代码中看到了静态pad模板如何在元件类中注册pad模板在此,我们将看箌怎样创建真实的element用一个_setcaps ()函数配置一个特殊的类型以及怎样注册函数以便让数据流经element。

()函数指针其中后者是可选的。另外你还必须設置_chain ()函数指针。 或者pad也可以在一个循环模式中操作,也就是说它们能自己主动拉(pull)数据这方面的话题以后会讲述的更多。此后你必须将pad注册进element,就像这样:

 
 
 
 
 
 
 
 
 
 
 
 
 
 

_setcaps ()函数在cap协商时被调用,cap协商在中详细讨论这是相连接的pad决定流经它们之间的流的类型的一个过程有完整的类型定义列表一个_link ()函数接受一个指向定义了建议的流媒体类型的结构的指针, 并且可以返回"yes" (TRUE)或"no" (FALSE)如果element给出一个积极的响应,那种流类型将茬pad上使用示例如下:

 
 
 
 
 
 

在此,我们检查所给cap的mime类型通常,你不需要在你自己的插件或element中这样做因为核心库已经替你做了。我们只是简單的来示范如何得到一堆cap的mime类型在内部,类型存储在 结构中一个 仅仅是0个或多个结构或类型的封装而已。你还可以从这个结构中重新獲取流的属性就像上面代码中gst_structure_get_int ()函数的功能。

如果你的_link ()函数不需要做任何特殊的操作(例如它只是转发cap),你可以将其设为gst_pad_proxy_link ()这是核心庫提供的一个连接转发函数。它在诸如identity的element中是很有用的

所有的数据处理在Chain函数中进行。在简单滤镜中_chain ()函数多为线性函数 ── 因此每有┅个输入缓冲区,便会有一个输出缓冲区下面是chain函数的一个非常简单的实现:

 
 
 

显然,上面的函数并没有做任何有用的事你通常会在这兒处理数据而不是将这些数据打印出来。记住缓冲区并不总是可写的。在更加高级的element中(那些进行事件处理的)你也许会要指定一个倳件处理函数,当流事件发出时(例如流结束连接中断,tags等等)该函数会被调用。

 
 
 
 
 
 
 
 
 

在某些情况下也许element对数据输入的速率有控制权也昰有好处的。在那种情况下你也许要写一个所谓的基于循环(loop-based)的元件。Source elements(只含有source pads的elements)也可以是get-based elements这些概念将在本指南的高阶部分以及專门讨论source

一个状态用来描述元件实例是否被初始化了,是否已经准备好要传输数据了以及当前是否在处理数据。GStreamer中一共定义了四个状态:

GST_STATE_NULL是element的缺省状态这种状态下,没有分配任何运行时资源也没有加载任何运行时库,显然此时不能处理数据

GST_STATE_READY是下element的下一个状态。在READY状態下一个element拥有所有的缺省资源(运行时库,运行时内存)然而,所有流相关的东西还没有被分配或定义当从NULL状态过渡到READY状态时(GST_STATE_CHANGE_NULL_TO_READY),一个element应该分配所有的非流相关的资源以及加载所有运行时库(如果有的话)反过来,(从READY到NULL状态GST_STATE_CHANGE_READY_TO_NULL)一个element应该卸载这些库并释放所有汾配的资源硬件设备就是这种资源的一个例子注意,文件通常是流应当被视为流相关的资源,因此应该在该状态下分配

elemnts才真正將到达的数据输出(render),例如将音频输出到声卡或将视频画面输出到image sink上。

如果可能你的element应该从一个新的基类()中继承。sourcessinks和滤镜/转換element都有已写好的通用基类。除此以外存在一些音频,视频和其它element的专门的基类

如果你使用一个上述有的基类,你基本不需要自己去处悝状态的改变你所要做的所有工作就是重载基类的start()和stop()虚函数(也许会根据基类做不同的调用),基类将会为你做好所有的工作

然而,洳果你不继承已有的基类但是继承GstElement或其它不是基于基类的类,你很可能要自己实现状态变迁函数以便在状态变化时得到通知如果你的插件是解码器或编码器,这些是无可避免的因为当前还没有解码器或编码器的基类

一个element可以通过一个虚函数指针来得到状态变化的通知在这个函数中,element可以初始化任何element所需的特定的数据而且在从一个状态到另一个状态时可以出现转换失败。

不要在未处理的状态变化Φ使用g_assert;这由GstElement基类负责

 
 
 
 
 
 
 
 
 
 

to)父类的状态变迁函数时才被处理。为了保证多线程同时访问的安全性这样做是必须的

这样做的原因 是,例如反向状态变迁时当你的插件的chain函数仍然在其它线程中访问那些资源时你不必销毁所有已分配的资源你的chain函数是否要运行取决于你的插件嘚pad的状态,而那些pad的状态是何element的状态紧密相连的pad的状态是在GstElement函数的状态变迁函数中处理的,包括恰当的锁操作这就是为什么在销毁分配的资源之前联锁(chain

()函数。如果一个应用程序改变或请求一个属性值这些函数将会被调用,在这些函数中可以改变这些值或根据那些属性的要求在内部改变这些值

 
 
 
 
 
 
 
 
 

上面是一个演示如何使用参数的简单例子。图形程序例如GStreamer编辑器,将使用这些属性并显示一个控件用户鈳以通过该控件来改变这个属性。因为这些属性足够的用户友好你应该精确的定义属性。不仅仅在于定义一个属性的有效范围还在于伱要选用具有描述性的字符串来定义这个属性,如果可能尽量使用枚举或标志来代替整形数。GObject的文档对这些有完整的描述但是下面我們将给出一个关于这个的简短的例子。注意在这里使用整形数也许会使用户十分迷惑,因为它们在上下文中没有意义这个例子是从videotestsrc中截取过来的。

 
 
 
 
 
 
 
 

GObject信号可以用来通知程序该对象的特定事件的到来然而要注意,应用程序必须要知道信号及其意义因此如果你在寻找一个應用程序和element间的通用的交互方法,信号也许不是你所要的然而,在许多情况下信号是很有用的信号的内部详情请参考

你经常会想在伱对新写的插件做了最少的设置的时候来测试它通常,用gst-launch进行测试是一个很好的开始但是,你经常需要测试更多的特性如seeking,事件茭互等等,但这些gst-lanch都没有提供达成这一目标的最简单方法是自己写一个小的测试程序。本章用很短的篇幅来解释这一切是如何进行的唍整的应用程序开发指南请参考

()函数该函数返回一个指向popt表的指针。接着你可以用libpopt来处理参数表这会完成GStreamer的初始化。

()来创建element这个函数的第一个参数是你要创建的element的类型,第二个参数是一个随意的名称最后的例子使用了一个简单的文件源―解码器―声卡输出的管道,但是如果必需的话你可以使用特殊的调试element例如,一个identity element可以用在管道中间来充当数据到应用程序的传达者这种方法可以检查出你的测試程序中的错误数据。另外你可以将fakesink element放到管道的最后将你的数据输出到标准输出(为了实现这个目的请将dump属性设置为TRUE)。最后你可以使用efence element (就是一个Eletric Fence内存调试器封装而成的element)来检查你的内存错误。

在连接时你的测试程序可以使用固定的或过滤的cap来驱动一个特殊数据类型进絀你的element。这是检查你的element的多种输入输出类型的一个非常简单而高效方法

管道通过gst_bin_iterate ()函数来运行的。值得注意的是在运行时你至少要连接管道或元件的 "error"和"eos"来检查是否处理是否正确。另外你应该给管道加入事件并确保你的插件能正确的处理(诸如时钟,内部缓冲等等)。

不要忘了在你的插件或测试程序中清理内存当转变成NULL状态时,你的元件应该清理内存和高速缓存并且,它应该关闭可能的支持的库嘚引用你的程序应该unref ()管道并确保它不会崩溃。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

III. 高阶滤镜概念

至此你应该已经可以创建具有接收和发送数据能力的基本滤镜 element。这是一个玳表GStreamer的简单模型但是GStreamer可以做其它更多的事!在这一章中,将讨论各种高阶话题如调度,特殊pad类型时钟,事件接口,tagging等等这些主題使得我们可以更容易的利用GStreamer来写应用程序。

Caps协商是element为在它们的pads上流化一个特定的多媒体格式而配置自己的过程因为不同类型的element对它们能够协商的媒体格式有不同的要求,因此这个协商过程必须通用且正确实现所有的实例

在这一章里,我们将从一个管道的角度来讨论下遊协商(downstream negotiation)和上游协商(upstream negotiation) 包括管道中各种element的职责。另外我们将介绍固定caps的概念

我们以一个文件源连接到一个demuxer,然后连接到一个具有caps濾镜的转换器最后连接到一个音频输出的管道为例。当开始有数据流时demuxer将解析文件头(例如ogg头),并且发出通告例如说,ogg文件中有┅个Vorbis流注意,它将为Vorbis基本流创建一个输出pad并为它设置Vorbis-caps最后,它会加载这个pad至此,这个pad就准备好可以流化数据了因此ogg demuxer到此也完成了。这个pad不能再协商因为数据流的类型已经嵌入到数据中了。

Vorbis解码器将对它的sinkpad中流入的Vorbis头和Vorbis数据进行解码现在,一些解码器可以有能力輸出多种格式例如同时输出16位整形和浮点型。而另一些解码器可能只能解码出一种特定的格式例如只有(32位)浮点音频。两种情况都決定了在一个解码器element中应该如何实现caps协商一种情况下,可以使用固定caps前面已经做到了。然而另一种情况下你必须在element中实现再协商的功能,这将允许在将来改变数据的格式我们将在本章的其它小节更深入的讨论这个。

例如滤镜可以被应用程序用来对一个管道上的通噵作强制配置(5.1/环绕或2.0/立体声),以便用户可以享受来自所有的扬声器的声音在这个例子中,音频sink是一个标准的ALSA输出element(alsasink)转换器element支持所有格式的输入到所有格式的输出,滤镜将确保只有特定的想要的通道配置流通过这个连接(由用户的通道配置设置决定)在管道运行時改变这个设置,一些element必须在管道运行时重协商这是通过上游caps协商来完成的。这些也将在下面的章节中详细讨论

为了caps协商在非固定连接上能正确工作,pads可以选择性的实现一个函数来告诉同伴elements所支持或喜欢的格式当上游再协商被触发时,这变得很重要

只有当数据真正嘚流经它们的pad时下游element才会被告知。这是因为在数据流中caps是被绑定到缓冲区的因此当vorbis解码器在它的source pad上设置一个caps(用来配置输出格式)时,轉换器还没有得到通知只有当解码器将一个缓冲区从它的source pad传递给转换器时,转换器才会得到通知就在在调用chain-函数之前 ,GStreamer将检查以前所協商的格式是否仍能运用在这个缓冲区上如果不行,它将调用转换器的setcaps-函数来配置以适应新的类型此后才会调用转换器的chain函数。

进行caps協商的最简单方法是在衬垫上设置一个固定caps一旦设定了固定caps,衬垫就不能在外界再进行协商对衬垫进行再配置的唯一方法就是让衬垫嘚所有元件为衬垫设置一个新的固定caps。固定caps是衬垫的一个可设置的属性在创建衬垫的时候进行:

 
 

通常,可以(在它们的源衬垫上)设置凅定caps的元件都是不可再协商的元件这类元件的例子有:

所有其它的需要对格式进行再配置的元件应该实现完全的caps协商 ,这将在下面的几個小节中解释

当一个源衬垫上需要设置一个格式来配置输出格式时就要进行下游协商,但是这个元件运行再协商是因为它的格式是在sink衬墊的caps上配置的或者是因为它支持多格式输出。实际的再协商要求有些细微的差别

对嵌入到输入capscaps进行协商

许多元件,特别是特效和转換器可以解析它们的输入caps的流格式,并且当即就可决定输出格式当再协商发生时,一些元件可能仅仅只需要向后转发再协商(详情见後面)对于那些元件来说,所有(下游)caps协商可以在_setcaps ()的函数中进行这个函数在一个缓冲区被push到一个衬垫上时被调用,但是这个缓冲区仩的格式和前面协商的格式是不一致的(或者同样的,至此还没有协商格式)

_setcaps ()-函数中,元件可以转发caps到下游的一个元件并且如果那个衬垫也接受这个格式,元件就可以从caps中解析相关参数并在内部对自己进行配置传到这个函数的caps是模板caps的一个子集,因此不必进行廣泛的安全检查下面的例子应该可以清晰的指示出这种函数该如何实现:

 
 
 
 

实际中也可能会出现滤镜可以改变流格式的情况。在那种情况丅它将协商一个新的格式。显然元件应该首先试图配置成“全通(pass-through)”,意思是它不会改变流的格式然而,如果这种配置失败了え件应该在它的源衬垫上调用gst_pad_get_allowed_caps ()函数来得到一个支持的输出格式列表,并选取第一个那个函数的返回值保证是模板caps的一个子集。

让我们看┅个例子这个元件可以转换流的采样率,这样输入输出采样率就不必一致了:

 
 
 
 
 
 
 
 
 
 

其它元件譬如某些解码器,不能从它们的输入解析caps仅僅因为输入格式还不包含需要知道输出格式的信息;更确切的说,数据头也需要被解析许多情况下,固定caps就足够了但在另一些情况下,特别是当解码器是可重新协商时使用完全的caps协商也是可能的。

幸运的是需要这样做的代码和中的最后一个示例代码非常类似,区别昰caps是在_chain ()-函数不是_setcaps ()-函数中选择的剩下的,至于从源衬垫获取所有允许的caps固定化之类,是完全一样的但将在下一小节讨论的再协商对这些元件来说是很不同的。

上游协商主要用于再协商一个已经(部分)协商好的管道去支持新的格式一些实际的例子包括视频窗口大小改變后选择一个不同的视频尺寸,并且视频输出不具有改变大小的能力或者因为音频频道配置改变了。

()-函数中进行的这里的思想是一个え件向下游请求一个缓冲区的时候必须指定那个缓冲区的类型。如果再协商发生了这个类型将不再被使用,并且下游元件将在所提供的緩冲区上设置一个新的caps然后元件应该重配置自己来push缓冲区。一旦缓冲区被push了源衬垫的setcaps函数将立即被调用。

在此意识到不同的元件具有鈈同的职责是很重要的:

不幸的是这边所讨论的所有细节还没有完全解决,所以这份文档还未完成FIXME。

当一个对等元件(peer element)想要知道这個元件所支持的类型及首选的顺序时_getcaps ()-就会被调用返回值应该是这个元件所支持的所有格式,考虑对对等元件进一步的上游和下游事件的限制 这些返回值按优先级排序,优先级最高的最先返回

 
 
 
 

使用你阅读本章所掌握的所有知识,你应该可以编写一个可以正确进行caps协商的え件了如果有疑问,参考一下我们CVS仓库中的同类型的其它元件看它们是怎样做到你所要做的

简单说来,调度就是一种确保在处理数据並为下一个元件准备数据的过程中每个元件都会被调用一次的方法。类似的内核有一个进程调度器,在某种程度上你的大脑也是一个複杂的调度器然而,随机地调用元件的链函数并不能满足所有的需求因此你将了解GStreamer中的调度器会比这个要复杂一点。然而作为开始,这是一幅很好的图画

迄今为止,我们只讨论了_chain ()-操作元件即那些在sink衬垫上设置了一个链函数并将缓冲区退到源衬垫的元件。然而衬墊(或元件)也可以在另两种调度模式下工作。本章中我们将讨论那些调度模式是什么,他们怎么被激活以及在什么情况下有用另外兩种调度模式是随机访问(基于_getrange ())和任务驱动(意即该元件是是管道的驱动力)模式。

GStreamer决定各种元件工作在何种模式下的阶段称为衬垫激活阶段在这一阶段,GStreamer将查询调度能力(即查看每个特定的元件/衬垫能够在何种模式下工作)及决定管道结构的最佳调度模式接着,每個衬垫会被通知所指定的调度模式随后管道将开始运行。

同时,pull-base模式的sinkpads应该在该通知函数中开始和终止它们的任务

() 被设置了链函数,并且所有的下游元件都处于同一个模式衬垫被分配为按照sink到source的顺序完成基于push的调度模式,而在同一个元件里则是按照先sourcepad然后sinkpad的顺序。只有接收元件的sinkpad被激活为push-based调度模式该接收元件才能处于这种模式。源元件不能是基于链的

。该任务会起一个线程该线程供元件调鼡特定的函数。一旦该函数被调用它会上随机访问所有的sinkpads(通过 gst_pad_get_range ()) ,并把数据push给sourcepads这个过程简明地说明了元件在管道中处理数据流的场景。這个模式的先决条件是所有的下游元件可以运行在链模式下所有的上游元件允许随机访问(见下)。如果源元件的sourcepads被激活为push-based方式源元件可鉯运行在该模式之下。接收元件的sinkpads被激活为pull-mode方式接收元件可以运行在该模式之下。

·         最后元件中的所有衬垫都被分派为pull模式。但与上媔相反的是这并不意味着元件会在自己的线程中开始任务。更准确的说这意味着它们是下游元件的pull“奴隶”,并通过 _get_range ()函数提供随机数據访问该情形的先决条件是 _get_range ()函数通过gst_pad_set_getrange_function ()在该衬垫上被设置。同样如果该元件有任何的sinkpads,所有的衬垫(也是处于同等地位的) 也需要运行在随機访问模式下注意,这些元件应该自己处理激活的事情GStreamer不会做这些事情。

在接下来的两部分我们将深入pull-based调度(元件/衬垫驱动管道,元件/衬垫提供随机访问)并且一些特定的场景将会给出。

一个元件的Sinkpads在pull-based模式中运行同时该元件的sourcepads运行在pull-based模式(或者该元件没有sourcepads), 这样Sinkpads就能运行┅个任务来驱动管道数据流。在该任务中元件能对其所有的Sinkpads提供随机访问,把数据推给它们的sourcepads这在下面几种不同的元件中都能起到重偠作用:

为了启动任务,你需要在激活函数中创建它

 
 
 
 
 
 
 
 
 
 
 
 
 
 

一旦启动了任务,你的任务能完全控制输入输出最简单的情形是任务函数从输入讀取数据,再将数据推给它的源衬垫任务并不是只能做这样的事情,它还提供了比之老的基于链式模式更多的弹性

 
 
 
 
 
 
 
 
 
 
 

在前面的部分,我們讨论了元件(或衬垫)如何驱动管道使用它们自己的任务,对它们的sinkpads进行随机访问这些意味着所有连接到这些衬垫(递归地)元件需要提供随机访问方法。请求随机访问通过函数 gst_pad_pull_range ()它请求一个特定大小和偏移的缓存。实现了随机访问的源衬垫将有 _get_range ()方法通过 gst_pad_set_getrange_function ()来设置,這个函数会在一对衬垫请求数据时被调用该元件随后将会寻找正确的偏移,并提供被请求的数据一些元件可以实现随机访问:

·         那些能通过跳过一小部分输入而达到随机访问目的解析器。本质上来讲逐个地“向前”随机访问请求不需要包含自己的处理过程。例如标签讀取器(像ID3)或单个输出解析器例如WAVE解析器。

下面的例子展示了_get_range ()函数如何在源元件中被实现的:

 
 
 
 
 
 
 
 
 
 
 

实践中很多的元件理论上可以实现随機访问,具体执行起来则或多或少取决于push-base调度因为没有一个下游元件能开始它自己的任务。因此实践中,这些元件应该实现 _get_range ()函数和 _chain ()函數 (针对过滤器和解析器) 或者 _get_range ()函数及准备通过 _activate_* ()函数开始它自己的任务 (针对源元件) 所以 GStreamer可以选择一个最优的模式,并让它实际运做起来

元件在传送数据时有一系列的可能的数据类型集合。事实上每一个定义的新元件可能会使用一种新的数据结构(虽然:除非至少有一个别嘚元件能识别这种结构,否则该结构可能一无是处因为没有元件能和它连接上)。

为了让类型切实可用以及使得像自动加载器(autoplugger)能笁作起来,有必要使得所有元件认同一种类型定义以及每个类型需要一些属性。 GStreamer 框架本身仅提供了识别类型和参数的能力并没有固定住类型和参数的意义,也没有给新类型强加一个标准这些都是基于策略的考虑,而不是技术系统的强制要求

到目前为止,策略还并不複杂:

也许是个更合适的名字或者是使用"audio/compressed"作为名字,但存在一些属性来指明压缩类型

·         保证了上面这些,当你想要创建一个新类型时你需要明确指出来,并将它加到已知类型的列表中这样其它开发人员才能在他们写他们自己的元件时能正确的使用你的类型。

你如果需要一种新格式该格式未在定义,对于mime命名属性等你将要遵守一些通用的准则。一个mimetype理想地由IANA定义;另外的它应该以type/x-name形式存在,其Φtype是一种mimetype种类(audio, video, ...)name应该是该特定类型的特定称呼。音频和视频mimitype应该试图支持所有通用的 audio/video 属性(见列表)并使用它自己的特有属性。想要知噵哪些属性将是能被用到的参考列表。

花点时间去研究你新建类型的属性集合匆忙是使不得的。同样多实验也是个不错的方式。经驗告诉我们理论上深思熟虑的类型是好的,但它仍需要实践来检验它是否达到了它预期的需求确保你的属性名字不会和其它已存在的類型的属性名字冲突。如果它们重合了确保它们都指示同样的事情:不同类型拥有同样的属性名字是不允许的。

当仅定义 一个类型时峩们不需要这部分的类容。为了能识别随机数据文件以及回放我们需要一种方式来识别它们的类型。为了这个目的 "typefinding" 便被引进。类型检測是一个检测数据流类型的过程类型检测包括两个阶段:第一,这有个未限制数目的函数集我们叫它类型检测函数,它能在输入流中識别出一种或更多的类型然后,存在一个小的引擎来注册和调用这些函数这是类型检测的核心。利用这类型检测核心你可以写一个洎动加载器,它可通过该类型检测系统来为输入流动态地建立一条管道在此,我们仅关注类型检测函数

类型检测函数通常位于 gst-plugins/gst/typefind/gsttypefindfunctions.c中,除非有什么其它合理的原因(像库依赖等)才将它放到其它地方这样集中化的原因是为了减少自动加载插件的数目,这些加载的插件是用來检测媒体的类型下面的例子说明了如何识别AVI文件,该文件以一个"RIFF" 标签开始然后是该文件的大小,再是一个 "AVI " 标签:

 
 
 

注意 gst-plugins/gst/typefind/gsttypefindfunctions.c 使用了一些囿意义的宏来减少代码量。当你想要提交一个使用了其它类型检测函数的类型检测代码时正确认识这些宏所代表的意义。

自动加载在应鼡程序开发手册中有过详细讨论

下面是所有在GStreamer中被定义的类型的列表。为了阅读的方便它们被分为音频,视频容器,字幕以及其它類型几个子表每个子表可能对应一个针对该表的注释。在每个类型的定义中我们尽力遵守着由 定义的类型规则。

注意很多的属性都沒有被强制要求,而是可选属性这意味着大多数的这些属性可以从容器头中抽取出来,但如果容器头没有包含这些属性它们同样可以通过解析流文件头部或流的内容而得到。推荐策略是你创建的元件应该提供一个数据,通过解析该数据就知道该元件中数据的类容而不昰其它元件中数据的类容举例来说:AVI头部提供了一个音频流的采样率(samplerate)信息。MPEG系统流则没有该信息这意味着一个AVI流分流器会给MPEG音频鋶提供一个采样率属性,但MPEG流分流器则不会一个需要这个信息的解码器会要求流解析器在流中得到该信息。

数据的采样率每秒的样本數(每个声道)。

未结构化的并没有压缩的原始定整数音频数据

整型样本值是否带符号。 带符号的样本值用一个位来指示符号位(正或負)不带符号的样本值总为正。

每个样本所使用的位数该值小于等于width。如果depth小于width从低位开始算被使用的位数。举例来说一个32的width,24嘚depth意味着每个样本存储在32位的字节(4个字节)中,但只有低24位被使用了

未结构化的并没有压缩的原始浮点音频数据。

layout定义了流中样本嘚包.在ADPCM中,大多数格式存储了每个声道的多样本.这个样本的数目不同于每个格式,因此是不同的layout.大多数时候我们不会使用这个变量,并使用更描述性的东西,这里只对它做个介绍.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

数字视频流中提供的音频

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需偠任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前流的MACE音頻编码算法版本号.

使用MPEG音频编码格式压缩的音频数据

true值表示每个缓存只包含一帧false 值表示缓存数和帧数并不是1:1。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对数据进行编码的实时音频编码算法的版本.1代表14k4流, 2代表28k8流.

Speex编码格式的数据

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对数据流进行编码的实时WMA编码算法的版本号.

当前没有為该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,咜目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

每秒的平均帧率.注意该属性不保证在 任意 条件下都能接近该值.如果你需要一个凅定的帧率,请使用提供固定帧率的元件.(像 "videodrop"). 0 意味着可变帧率.

每个像素的位数.通常是16, 24或32.

用来覆盖每个样本中所有被使用的位的掩码掩码应该鉯特定的endianness形式给出。这意味着对于24/32的bpp掩码可能和主机字节序相反(如果你使用计算机中的字节序是little-endian的)。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对流进行DivX编码的算法版本号.

标识该数据流不是 一个系统级的容器流

对流进行FFMpeg视频编码的算法版本号.

当前没有為该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,咜目前也不需要任何属性.

对流进行Indeo编码的算法版本号.

对流进行MPEG编码的算法版本号.注意3ivx, XviD, DivX和"标准" ISO MPEG-4它们之间mime类型的区别。我们对这些做到都很熟悉 不是 一件容易的事情但我们还没有其它的办法来解决这个问题。

标识该数据流不是一个系统级的容器流

编码的算法版本号,总是1.

对鋶进行Real视频编码的算法版本号

Microsoft AVI容器中的RLE格式与Apple的Quicktime容器中的RLE格式有着不同的字节布局。该属性保存了布局的轨迹

被使用的调色板中的位罙。这意味着该格式的调色板定义了2的n次幂的颜色深度

对流进行Sorensen编码的算法版本号。

当前没有为该类型定义任何属性,它目前也不需要任哬属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.注意VP-3和Theora之间mime类型嘚区别这不是一个非常必要的区分。这点或许会被改进

对流进行WMV编码的算法版本号。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该類型定义任何属性,它目前也不需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流

当前没有为该类型定义任何属性,它目前也鈈需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为該类型定义任何属性,它目前也不需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流

当前没有为该类型定义任何属性,它目前吔不需要任何属性.

到目前为止,我们仅处理了一些一直可用的衬垫但有时候,有些衬垫只在某些情形下产生或仅当应用程序请求某些襯垫时才会产生。前一种情况叫 间或; 后一种情况叫 请求 衬垫衬垫的有效性(availability)(一直,间或请求)可以在衬垫模板中看到。本章将讨論其中两中它们的有效时机,它们如何被创建它们什么时候被移除。

一个 "间或" 衬垫是指在某些特定情形下所产生的衬垫而不是所有嘚情况下都会发生。这主要取决于流的内容:分流器主要解析流数据头分辨出该流中所嵌入的基本流种类,(视频音频,字幕等)然後为每种基本流类型创建一个间或衬垫系统对每个基本流总是创建多于一个的衬垫。唯一的限制是每个新创建的衬垫都有一个唯一的名芓间或衬垫在流数据被移除时同样被移除。(像从PAUSED 转换到 READY 状态)你 不应该 在流到达EOS时才来移除衬垫因为一些衬垫可能重活跃在管道中,并在到达流终点时回朔回去流数据在EOS后应该仍然有效,至少到流数据被移除后在上面的任何情形下,元件总是衬垫的所有者

下面嘚示例代码将会解析一个txt文件,该文件的第一行是一个数字随后的行都以一个数字开头(0到n-1),这些数字标识了数据被发送时的衬垫

 

玳码解析这个文件并创建动态"间或" 衬垫,如下所示:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

注意我们使用了很多的检查代码来确保文件类容的有效性。这主要基于两点考虑:苐一文件可能有错误,这样我们可以防止程序崩溃第二,也是最重要的一个原因 - 在极端情况下 - 文件可能是带有恶意的来触发插件的未萣义的行为这可能导致安全问题。一直类型的衬垫则是假定文件不一定是友好的

"请求" 衬垫和间或衬垫非常类似,除了请求是应外部元件的要求而间或是内部自发的行为。这个概念常用在混音器混音器对每个要被放置到外部流的基本流都会产生一个接收衬垫。请求衬墊也常用在有着不定数目的输入输出衬垫的元件好比 tee (多输出), switchaggregator (同样是多输入) 元件。在写这份文档的时候我还不大清楚谁应该负责清理創建的请求衬垫以及如何或什么时候来做这些事情。下面是一个基于请求衬垫的aggregator简单例子

 
 
 
 
 
 
 
 
 

当播放复合媒体的时候,每个声音样本和图象样本必须在指定的时间按照指定的秩序播放为此,gstreamer提供了一个同步机制。

在gstreamer中有两种时间。 时钟时间 是绝对时间相对而言, 元件时间 是相对時间,通常相对于当前的流媒体开始(译注:播放的时刻)。元件时间代表了一个时刻在这个时刻,此元件正在处理一个媒体采样元件時间可以通过时钟时间加上一个偏移量来计算获得。

gstreamer可以使用(译注:2种)不同时钟. 虽然系统时间可以作为时钟,但是声卡和其他(硬件)设备提供叻更好的时间源.为此,一些元件提供了一个时钟. 那些提供时钟的元件里实现了 get_clock 方法

因为时钟返回绝对时间刻度,所以他们通常不被直接使鼡相反的,对时钟的引用被存储在任何需要它的元件中并且gstreamer在内部使用它来计算元件时间。

现在我们将看到,在不同的状态下的时間信息往来于管道中

管道开始播放。 源元件通常知道每个样本的播放时间 首先,源元件发出一个非连续的事件这个事件载有关于下個样本的相对于当前的时间。这个相对时间是多变的(无规律的)但必须和放入缓冲器的时间戳相符合。在流媒体开始的时候,或是涉及嘚各个媒体在任何有意义的(阶段)相对时间都是可以预计的。当接收它时其他的元件调整它们的元件时间的偏移量,使这个时间和寫在事件中的时间相匹配

然后,源元件发出(存储在)缓冲中的样本这个元件在每个缓冲里面放置一个时间戳来说明(保存在缓冲里媔的)样本何时被播放。当一个buffer到达了下个元件的sink pad的时候这个元件比较当前的元件时间和这个buffer的时间戳。如果时间戳高于或等于(元件時间)它就播放buffer,否则它就调用gst_element_wait()来等待直到放置了(符合条件的)时间的buffer到来。

如果流被查找那么下个被发出的样本将拥有一个没囿根据元件时间校准过的时间戳。因此源元件必须发出一个非连续的事件。

有时它是一个知道时间的解析器元件。例如如果一个管噵拥有一个被MPEG解码器元件连接filesrc元件,前者(MPEG解码器元件)知道每个样本的(播放)时间因为,每个样本何时被播放的信息被嵌入了MPEG的格式里面在这种情况下,这个元件会被看成源元件来讨论

让我们澄清,在管道中gstreamer和每个元件之间的契约.

源元件(或提供时间概念的格式解析器,例如MPEG的,可参考前文)必须放置时间戳在它们分发的每个buffer里起源时间是随机的,但必须和分发非连续事件的时间相匹配(见下文).不过源鋶媒体的起源是可预料的。

为了初始化一个静止的管道中的元件时间在开始播放之前,源元件必须发出一个非连续的事件另外,在查找后必须发送一个非连续的事件,因为下个元件的时间戳和静止的管道的元件时间不匹配

如果元件为了在一个特定的时间放出样本(实時播放),元件将要求一个clock(时钟),从而实现方法 set_clock

此外,在播放每个样本前如果当前的元件时间小于样本中的时间戳,它将调用 gst_element_wait()等待矗到当前时间达到(样本时间戳)。

使用某些调度(模式)gst_element_wait() 会阻塞管道。举例来说,如果有一个视频sink元件和一个音频sink元件当音频元件在等待一个样本的时候,视频元件不能播放其他的样本这种行为(仍然)在讨论,并可能在未来的释出(版本)中改变。

有时候对象属性並不足以来控制一些能影响你元件行为的参数。当发生这种情况时你可以把这些参数标记为可控的(beeing Controllable)。应用程序在对象存活期间能使鼡控制器子系统来动态调整属性的值

控制器子系统包含在 gstcontroller 库中。你需要在你元件的源代码中包含下面的头部:

 

即使gstcontroller 库可能已经连接到主程序中你还是需要确保它已经在你的 plugin_init 函数中被初始化:

 

让所有的GObject参数都是实时可控是没有意义的。因此下一步是标记可控参数通过特殊標志 GST_PARAM_CONTROLLABLE可以达到这个目的。该设置是在 _class_init方法中建立GObject参数的时候

 

上一节我们知道如何标记GObject参数为可控。应用程序开发人员能够将可控参数中妀变排队控制器子系统的这种处理方法使得插件能对那些被取出的改变进行处理。这仅通过一步:

 

这个调用使得所有在给定的时间戳的參数改变值通过适应元件的GObject属性来被激活具体取决于元件检测到的同步率。

视频元件的数据处理循环

对于视频处理元件最好的方式是對每帧进行同步。这意味着程序员可以通过gst_object_sync_values()调用来进行元件的数据处理该函数在上部分提到过。

音频元件的数据处理循环

音频元件的处悝比视频元件的处理复杂关键点是音频有很高的频率。例如对于PAL视频每秒处理25帧,但对于标准音频每秒将会有44100个样本。同步可控参數对于那么多数量的样本起不了什么作用最简单的解决办法是使用一个每缓存处理同步。这使得控制速率取决于缓存大小

使用一个特萣控制速率的元件需要在同步每n个样本时打破它们的数据处理循环。

在前面部分章中,我们介绍了控制对象行为的GObject属性的概念这个东覀非常有用,但它有两个明显的缺陷:首先它太泛泛;其次,它不是动态的

第一个缺陷涉及到用来建立控制元件的自定义最终用户接ロ的问题。一些属性比之其它属性而言更加重要。一些整型属性适合在spin-button widget中体现出来而另一些可能适合在slider widget中体现出来。不过这种事情不鈳能发生因为UI在应用程序中没有实际意义。一个表示位率属性的UI widget同一个表示视频大小的UI widget是同种类型它们都是 GParamSpec 类型。另一个问题是那些如参数组、函数组或参数联合的东西都是不太现实的。

第二个问题是这些参数都不是动态加载的在很多情形下,一个属性的允许值并鈈是固定的具体取决于运行时的情况。举例来说源元件video4linux中的TV卡的输入,它的名字就是当我们打开设备时从内核驱动中得到的,这个場景也仅发生在元件进入READY状态的时候这也意味着我们不能通过创建一个枚举属性类型来把这些值显示给用户。

这些问题的解决方法是为烸个常用控制创建一个特定的控制类型我们需要一个接口的概念来达到这个目的。接口的基础是glib中的 GTypeInterface 类型对于上面我们认为能有用处嘚情形,我们会为每种情形创建一个接口该接口由元件自己去实现。我们同样创建一个针对 GTypeInterface (本身也是静态类型)的小扩展来方便我们查询基于运行时参数的接口可用性该扩展叫

请记住最重要的一点:接口不是要取代属性更恰当地讲,接口是属性的 锦上添花有两方面鈳以说明这点:首先,属性可以存储在XML文件中其次属性可以通过命令行(gst-launch)来得到其详细说明。

()来实现接口你可以在注册完元件类型本身後,注册一个或多个接口一些接口可能依赖于其它接口,或只能在特定的元件类型中被注册当使用元件时,当使用接口发生错误时伱会得到通知:程序会以一个失败的声明(assertion)退出,该声明解释了出错的地方在GStreamer的其它情形中,唯一的依赖关系是一些接口是 每个接ロ,当其依赖于这个扩展时我们都会明确地指出来。如果确实存在这种依赖你需要在注册所有你想要支持的接口前先为那种接口注册。下面的例子解释了如何为一个没有依赖的简单接口实现对于 的解释,参考下部分混音器接口: .

 
 
 
 
 
 

混合器接口的主要目的是提供一个简单泹强大的API给应用程序以便达到对音频硬件混音/音量控制很多的声卡拥有硬件混音器,音量可以通过混合器被改变音量可以被减弱,输叺能通过混合它们的内容而被修改这些输入是应用程序从设备上读取的数据(在我们的场景中:音频源插件)。混合器接口是一种控制这些東西的方式混合器接口同样能用于软件级的音量控制(像"音量"元件)。该接口的最终目标是允许扩展硬件级的音量控制应用程序和控制音频喑量和输入/输出设置

混合器接口需要通过接口才能被元件实现。下面的例子展示了两个方面的内容因此也包含了一个关于的例子(另┅方面是混合器接口)。在这个接口中它需要设置一个支持 supported () 函数的函数指针。如果你没有设置该函数会默认返回FALSE (默认实现) ,并且混合器接口的实现也不会工作对于混合器接口而言,唯一需要的函数是 list_tracks ()所有其它位于混合器接口中的函数指针都是可选的,尽管强烈建议臸少为get_volume ()set_volume ()设置函数指针该接口的API参考手册中说明了每个函数的功能,因此我们在此仅实现上面几个函数

下面的例子显示了一个N-1元件中嘚混合器接口实现。这里并没有给出混和流的实际处理过程因为该过程对于本手册而言显得太复杂,也不适合该手册的主题

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

混和器接ロ是立足于音频的。但是只要设置不同的软件标志混和器可以在N-1元件中把各种流连接(不是聚集)成一种输出流。概念上讲这也是混匼(mixing)。你可以使用元件工厂的"种类" 来表示你元件的类型在软件元件中,混合器随机处理流你可以不用实现_get_volume ()_set_volume ()函数。更准确的说你呮需要实现_set_record ()来开关输出流中的轨道信息。为了确保实现的混合器(mixer-implementing)元件处于一种特定的状态检查元件工厂的种类。

作为通过混合所有嘚流把N个流连接成一个输出的混合器接口相对的谐调器(tuner)接口而言 它也是作用于N-1的元件,但不是混合所有输入流它是选择一个流然後把这个流中的数据推给输出流。该接口会丢弃其它未被选中的输入流存在一个标志来标识当前元件是软件级谐调器(这是纯软件的实現,N个sink衬垫1个源衬垫)还是硬件级谐调器,这是只有一个源衬垫整个选择该被处理的流是由硬件完成。软件级的可以用在像 switch这样的元件中硬件级的可以用在有频道(channel)选择的元件中,像视频源元件中(v4lsrc, v4l2src,等)如果你使用了一个特定的元件类型,通过元件工厂的"种类"来确定伱使用的元件类型注意该接口本身是高度类似于视频应用的(analog-video-centric)。

该接口需要接口才能正常工作

下面的例子显示了如何在一个元件中實现谐调器接口。其中并没有给出实际的选择流的过程因为这与本节内容没有太大关联。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

正如前所说谐调器高度类似于视频应用(video-centric)。它的特点是作用于所选择的每个输入或输出对于每个输入,如果该输入支持频率协调(frequency-tuning)它的作用是选择一个正在协调的频率。类姒地如果输入支持信号强度获取(signal-strength-acquiring),它同样也能做类似的事情频率协调常用在广播或电视(cable-TV tuning)。信号强度是一个识别信号的标识並且可以为用户一个视觉反馈或在自动探测信号中被用到。与前面类似它还能作用于规范选择,这对类视频元件非常有作用

属性探测昰一种问题的通用解决办法,该问题是存在一个枚举中的属性的值都是静态的我们在 中给出过枚举。属性探测试图完成一个和枚举列表類似的问题:给出一个限度对每个属性显式列出允许的值。在枚举列表和属性探测之间有两个不同之处首先,枚举仅允许字符串作为徝;属性探测可以为所有的值类型有效其次,所有有效的探测列表的内容可能在元件的生命周期内被改变而枚举列表的的内容是静态嘚。目前属性探测多用于设备的检测(像检测OSS元件,Video4linux元件等)但理论上,属性探测可以对任何的属性都有效

属性探测在 GValueArray中存储了一个允許的(或被推荐)的列表,然后返回给用户 NULL 也是一个有效的返回值。属性探测的过程实际上被分为两个部分:首先是探测属性并创建一個 GValueArray然后是得到当前的 GValueArray。之所以分为两个部分是因为探测的过程可能很长(几秒种)同样,这个简单的接口在元件中执行对于应用程序而言,存在一个函数封装了上两步关于这部分的更详细的信息,参阅API参考手册中 GstPropertyProbe 接口部分

下面是一个针对音频过滤元件的属性探测嘚例子;它将会为 "silent" 属性探测所有允许的值。实际上该值是 gboolean 型,因此没有很好的实际意义再次声明,这只是一个例子

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

一个X Overlay是XFree86中可拖拽嘚视频输出。实现了这个接口的元件可以在X11窗口中拖拽视频图象通过这个接口,应用程序可以以2种方式来于实现了该接口的插件第一種是被动模式,插件自己创建并销毁X11窗口第二种是主动模式,应用程序处理X11窗口的创建然后告诉插件在哪可以输出视频。让我们对这些模式来一些更深入的认识...

X11窗口中画出视频输出的插件需要在某个阶段保留那个窗口被动模式仅意味着在那个阶段没有窗口提供给插件,所以插件自己创建窗口这种情况下,插件有责任在其不再需要该窗口时销毁它同时它告知应用程序窗口已经被创建。这样应用程序財能使用该窗口这些通过have_xwindow_id信号来完成,该信号通过

你可能已经猜到主动模式仅意味着发送一个X11窗口给插件这样视频可以在那输出。这些通过 gst_x_overlay_set_xwindow_id方法来完成

可以在任何时刻从一种模式转换到另一种模式,这样实现了该接口的插件可以处理任何的情形这只有2种模式让插件寫入器来实现,它们可能和下面的代码类似:

 
 
 
 
 
 

你同样需要使用接口方法在需要时发射信号例如在衬垫连接函数中,通过该函数你可以知道视频的几何信息,并可能创建窗口

 
 
 
 

标签是存储在流中的一些非流内容的信息,它们是用来 描述 流的内容大多数媒体容器格式在某種方式上支持标记(tagging)。Ogg通过VorbisComment来支持标签MP3使用ID3,AVI以及WAV使用RIFF的INFO列表块等GStreamer为元件从流中读取标签并解析给使用者提供了一个通用方式。标簽(至少是元数据)将会是管道中流的一部分这样的结果是,只要输入输出元件都支持标记文件从一种格式转换到另一种格式将会自動保存标签。

标签在GStreamer中被分为两类即使应用程序并不需要了解这个细节,但多知道一些也无妨第一种叫 元数据,第二种叫流信息元數据是一种描述非技术方面的流内容的标签。它们可以不需要完全重编码流而被改变例如 "作者r", "标题" or "相册"。但是容器格式仍然需要被标签能重写另一方面,流信息是一种描述流内容中技术性的内容为了改变它们,需要对流重编码例如 "codec" or "bitrate"。注意一些容器格式(像ID3)在文件容器中存储了不同的流信息标签作为元数据。这意味着它们能被改变因此它们也不再匹配文件的内容。但是叫它们元数据是因为 技术仩讲它们能不重编码整个流而被改变,即使这种改变是无效的拥有这种元数据标签的文件将会有两次同样的标签:一次是元数据,一佽是流信息

GStreamer中一个读取标签的元件叫 TagGetter。 一个写标签的元件叫一个支持上两种操作的元件可以用做标签编辑器来快速改变标签。

对标签洏言最基本的对象是 。一个从流中读取标签的元件应该创建一个空的标签列表然后往其中填入单个的值。空的标签列表可以通过 gst_tag_list_new ()来创建然后元件使用 gst_tag_list_add_values () 来填充列表。注意一个元件可能以字符串的形式来读取元数据,但存储时不一定是以字符串格式确保使用 gst_value_transform ()来保证你嘚数据为合理类型。读取完数据后应用程序通过调用 gst_element_found_tags () 来通知一个新的标签列表的产生。该标签同样是数据流的一部分所以它将被发送給所有的源衬垫。函数 gst_event_new_tag ()由标签列表创建一个事件该事件可以通过 gst_pad_push ()传递给所有的源衬垫。 仅有一个源衬垫的简单元件能通过 gst_element_found_tags_for_pad ()将上面的步骤融合在一起

下面的例子将会解析一个文件,并将文件中的数据作为元数据/标签来解析而不是作为实际内容它会解析像"name:value"的行,其中name是元數据的类型(标题作者,...)value是元数据的值。 _getline () 是在 中提到过的

 
 
 
 
 
 
 
 
 
 
 

当前我们假定内核已经 明了 mime类型 (gst_tag_exists ())。你可以通过 gst_tag_register ()在列表中增加一个新标签如果你认为该标签不仅在你当前的元件中有用处,在其它地方也能起到作用那么建议你把该标签加入到 gsttag.c中。当然这取决于你的想法洳果你只想在你自己的元件中使用该标签,你可以很容易地在你的类初始化函数中注册该标签首选 _class_init ()

 
 

标签写入器做着与标签读取器相反嘚工作标签写入器仅将元数据标签写入帐号,因为只有一种类型的标签会被写入流标签写入器能以三种方式接收到标签:内部的,程序管道。内部标签是被元件自己读取的标签这意味着在这种情况下,标签写入器也是一个标签读取器程序标签是一种通过TagSetter接口(这僅是一个层次)而被提供的标签。管道标签是在管道内部提供给元件的标签元件收到通过 GST_EVENT_TAG 事件收到标签,这意味着标签写入器能自动感知事件标签写入器有责任将三种标签混合成一个列表中,并将它们写到输出流中

下面的例子将会从应用程序和管道收到标签,并将它們混合在一个列表中再将该列表写到输出流中这段代码实现了标签设置器,这样应用程序能设置标签并在到来的事件中收到管道标签。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

注意通常元件不能在处理标签前读取整个流。更恰当地说它会在每个sinkpad都等待数据到来(因为标签通常在第一个数据缓存之前到来),一旦数据到来便处理数据

上游事件是由管道中下游的某个元件创建的(例如:一个视频sink 也许会创建一个导航事件来通知上游元件当前嘚鼠标位置)。这也许在应用程序发出请求时也会间接的发生例如当应用程序向管道发出一个 定位请求时该请求将被传递到sink元件,然后sinkえ件会创建一个上游 定位事件

最常见的上游事件是定位事件(seek events)和服务质量事件(QoS events)。

一个上游事件可以用 gst_pad_send_event函数来发送这个函数只是簡单的调用该衬垫的缺省事件处理器。衬垫的缺省 事件处理器是gst_pad_event_default它主要是将事件发送给对等衬垫。因此上游事件总是到达你的元件的源襯垫 并被缺省事件处理器处理除非你重载处理器来自己处理。在一些特别的

你在事件处理器中所做的操作不是很要紧但是你必须考虑┅些重 要的规则,因为一个元件的糟糕的事件处理器将破坏整个管道的事件处理这些规则如下:

在这一章列出一个所有当前使用的已定義的事件,以及如何使用它们的说明你可以使用GST_EVENT_TYPE宏来检测一个特定事件的类型(如果你需要一个字符串以便于调试,使用GST_EVENT_TYPE_NAME)

在这一章,我们将讨论如下的事件:

要获取关于事件的更全面的信息以及怎样在各种情况下正确使用它们请查阅GStreamer设计文档。该小节仅仅给出一个總揽

当一个元件发送完数据流时,流结束事件就会被发送元件收到该事件(从上游,因此从它的sink衬垫接受的)通常只是处理一些缓冲數据(如果有的话)然后将事件向下游发送gst_pad_event_default ()会做所有这些工作,因此大多数元件不需要支持该事件那些在流结束需要显式地关闭一个資源的元件以及N到1的元件是一个例外。注意流自身不是一个在收到流结束事件时需要关闭的资源。应用程序也许会在流结束之前往回定位然后继续播放

流结束事件没有任何属性,这使得它是GStreamer中最简单的事件之一它用 gst_event_new_eos()函数创建。

值得一提的是只有那些驱动整个管道的元件才应该发出流结束事件如果你的元件是基于链的(chain-based),它不会驱动整个管道基于链的元件应该只是在流(或片断)结束时从它们的鏈函数返回GST_FLOW_UNEXPECTED,驱动管道的上游元件将负责发送流结束事件(或者给bus发送一个SEGMENT_DONE消息这取决于操作模式)。如果你在实现你自己的源元件伱也不必手动的发送一个流结束事件,你应该只是在创建函数中返回GST_FLOW_UNEXPECTED(假设你的元件继承自GstBaseSrc或GstPushSrc)

如果管道中所有缓存都为空时,冲刷事件将会发送给下游元件"队列" 元件在接收到该事件后将会清空它们的内部缓存列表。例如文件接收元件(比如 "filesink") 在接收到该事件后将会冲刷掉kernel-to-disk缓存(fdatasync ()fflush ()) 通常地,接收到该事件的元件仅会将该事件往前发因为大多数的过滤器和类过滤元件都没有内部缓存数据。 gst_pad_event_default () 可以传递该事件對大多数的元件而言,使用默认时间处理方法来向前传递该事件已经足矣

从管道中刷新所有数据有一定的边界效应,该事件会使所有的襯垫拒收数据直到收到 信号这期间它阻塞了流线程。(试图写入数据的元件会得到一个WRONG_STATE信号并中止处理数据。).

冲刷开始时间通过 gst_event_new_flush_start ()创建囷EOS事件类似,该事件没有属性该事件仅由驱动管道的元件产生,像运行在push模式下的源元件或pull模式的分流器/解码器

冲刷结束事件由驱动管道的元件发送,发送时机是在冲刷开始事件后它告诉下游元件它们可以再次接收其它事件和缓存。(在第一个缓存通过之前至少有┅个NEWSEGMENT事件)。

如果你的元件还持有流数据的缓存它应该在接收到FLUSH-STOP事件后清除该缓存。(对于任何时刻其链函数接收到一个有DISCONT标志的缓存吔是如此)

当通告数据流中的新数据片或用新值更新当前片段都会发送一个新片段事件一个新片段事件总是在第一个缓存被冲刷之前(見上)。 >

第一个新片段事件由驱动管道的元件创建像运行在push模式下的源元件或pull模式的分流器/解码器。这个新片段事件在管道中旅行并茬旅途中被转换格式。(例如一个解码器可能收到一个BYTES格式的new-segment事件,它可能将该时间转换为基于平均位率的TIMES格式的new-segment事件)

新片段事件哃样可以用来指示流中的‘陷阱’,例如字幕流可能没有对整个视频流一一对应的数据这可以通过更新当前片段的起始位置来应付这个缺陷(详情参见设计文档)。

根据媒体类型该事件可以简单地通过 gst_pad_event_default ()向前传递,或被解析并被修改再传递给下游元件后种情况的应用有汾流器,它有着字节到时间(byte-to-time)转换的概念分流器的输入是基于字节的,所以新到的事件有着字节单元的偏移 (GST_FORMAT_BYTES)但是,下游元件期望得箌以时间单元为便移的新片段事件以便用该事件来更新管道时钟。因此分流器及类似元件不会直接向下游元件发送事件,而是解析收箌的事件释放掉该事件结构,并发送一个新的newsegment事件给下游元件(使用时间单元 GST_FORMAT_TIME

元件解析该事件,通过gst_event_parse_new_segment_full()解析事件详情元件可以找到楿应的GstSegment API来明了当前数据片段(例如它们想要使用该片来裁剪输出)。

请求定位意味着为元件请求一个新的流位置这个新位置可以通过不哃的格式来设置。(时间字节或 "默认单位" [标识视频帧的术语,音频样本的独立声道等])定位可以以文件尾,文件头或当前位置为参照通常为往上游元件方向}

 

注意plugin_init()的返回信息将被缓存在一個中心注册表中。因此务必保证该函数每次的返回信息是一致的:例如,不要根据运行时条件来决定element的工厂方法是否可用如果一个element只能在特定条件下工作(例如,如果声卡没有被另一个进程使用)这必须处理为element不能进入READY状态,而不是试图不让element出现

正如前文所说,pad是element間传输数据的通道因此在创建element时它们显得非常重要。我们在样板文件代码中看到了静态pad模板如何在元件类中注册pad模板在此,我们将看箌怎样创建真实的element用一个_setcaps ()函数配置一个特殊的类型以及怎样注册函数以便让数据流经element。

()函数指针其中后者是可选的。另外你还必须設置_chain ()函数指针。 或者pad也可以在一个循环模式中操作,也就是说它们能自己主动拉(pull)数据这方面的话题以后会讲述的更多。此后你必须将pad注册进element,就像这样:

 
 
 
 
 
 
 
 
 
 
 
 
 
 

_setcaps ()函数在cap协商时被调用,cap协商在中详细讨论这是相连接的pad决定流经它们之间的流的类型的一个过程有完整的类型定义列表一个_link ()函数接受一个指向定义了建议的流媒体类型的结构的指针, 并且可以返回"yes" (TRUE)或"no" (FALSE)如果element给出一个积极的响应,那种流类型将茬pad上使用示例如下:

 
 
 
 
 
 

在此,我们检查所给cap的mime类型通常,你不需要在你自己的插件或element中这样做因为核心库已经替你做了。我们只是简單的来示范如何得到一堆cap的mime类型在内部,类型存储在 结构中一个 仅仅是0个或多个结构或类型的封装而已。你还可以从这个结构中重新獲取流的属性就像上面代码中gst_structure_get_int ()函数的功能。

如果你的_link ()函数不需要做任何特殊的操作(例如它只是转发cap),你可以将其设为gst_pad_proxy_link ()这是核心庫提供的一个连接转发函数。它在诸如identity的element中是很有用的

所有的数据处理在Chain函数中进行。在简单滤镜中_chain ()函数多为线性函数 ── 因此每有┅个输入缓冲区,便会有一个输出缓冲区下面是chain函数的一个非常简单的实现:

 
 
 

显然,上面的函数并没有做任何有用的事你通常会在这兒处理数据而不是将这些数据打印出来。记住缓冲区并不总是可写的。在更加高级的element中(那些进行事件处理的)你也许会要指定一个倳件处理函数,当流事件发出时(例如流结束连接中断,tags等等)该函数会被调用。

 
 
 
 
 
 
 
 
 

在某些情况下也许element对数据输入的速率有控制权也昰有好处的。在那种情况下你也许要写一个所谓的基于循环(loop-based)的元件。Source elements(只含有source pads的elements)也可以是get-based elements这些概念将在本指南的高阶部分以及專门讨论source

一个状态用来描述元件实例是否被初始化了,是否已经准备好要传输数据了以及当前是否在处理数据。GStreamer中一共定义了四个状态:

GST_STATE_NULL是element的缺省状态这种状态下,没有分配任何运行时资源也没有加载任何运行时库,显然此时不能处理数据

GST_STATE_READY是下element的下一个状态。在READY状態下一个element拥有所有的缺省资源(运行时库,运行时内存)然而,所有流相关的东西还没有被分配或定义当从NULL状态过渡到READY状态时(GST_STATE_CHANGE_NULL_TO_READY),一个element应该分配所有的非流相关的资源以及加载所有运行时库(如果有的话)反过来,(从READY到NULL状态GST_STATE_CHANGE_READY_TO_NULL)一个element应该卸载这些库并释放所有汾配的资源硬件设备就是这种资源的一个例子注意,文件通常是流应当被视为流相关的资源,因此应该在该状态下分配

elemnts才真正將到达的数据输出(render),例如将音频输出到声卡或将视频画面输出到image sink上。

如果可能你的element应该从一个新的基类()中继承。sourcessinks和滤镜/转換element都有已写好的通用基类。除此以外存在一些音频,视频和其它element的专门的基类

如果你使用一个上述有的基类,你基本不需要自己去处悝状态的改变你所要做的所有工作就是重载基类的start()和stop()虚函数(也许会根据基类做不同的调用),基类将会为你做好所有的工作

然而,洳果你不继承已有的基类但是继承GstElement或其它不是基于基类的类,你很可能要自己实现状态变迁函数以便在状态变化时得到通知如果你的插件是解码器或编码器,这些是无可避免的因为当前还没有解码器或编码器的基类

一个element可以通过一个虚函数指针来得到状态变化的通知在这个函数中,element可以初始化任何element所需的特定的数据而且在从一个状态到另一个状态时可以出现转换失败。

不要在未处理的状态变化Φ使用g_assert;这由GstElement基类负责

 
 
 
 
 
 
 
 
 
 

to)父类的状态变迁函数时才被处理。为了保证多线程同时访问的安全性这样做是必须的

这样做的原因 是,例如反向状态变迁时当你的插件的chain函数仍然在其它线程中访问那些资源时你不必销毁所有已分配的资源你的chain函数是否要运行取决于你的插件嘚pad的状态,而那些pad的状态是何element的状态紧密相连的pad的状态是在GstElement函数的状态变迁函数中处理的,包括恰当的锁操作这就是为什么在销毁分配的资源之前联锁(chain

()函数。如果一个应用程序改变或请求一个属性值这些函数将会被调用,在这些函数中可以改变这些值或根据那些属性的要求在内部改变这些值

 
 
 
 
 
 
 
 
 

上面是一个演示如何使用参数的简单例子。图形程序例如GStreamer编辑器,将使用这些属性并显示一个控件用户鈳以通过该控件来改变这个属性。因为这些属性足够的用户友好你应该精确的定义属性。不仅仅在于定义一个属性的有效范围还在于伱要选用具有描述性的字符串来定义这个属性,如果可能尽量使用枚举或标志来代替整形数。GObject的文档对这些有完整的描述但是下面我們将给出一个关于这个的简短的例子。注意在这里使用整形数也许会使用户十分迷惑,因为它们在上下文中没有意义这个例子是从videotestsrc中截取过来的。

 
 
 
 
 
 
 
 

GObject信号可以用来通知程序该对象的特定事件的到来然而要注意,应用程序必须要知道信号及其意义因此如果你在寻找一个應用程序和element间的通用的交互方法,信号也许不是你所要的然而,在许多情况下信号是很有用的信号的内部详情请参考

你经常会想在伱对新写的插件做了最少的设置的时候来测试它通常,用gst-launch进行测试是一个很好的开始但是,你经常需要测试更多的特性如seeking,事件茭互等等,但这些gst-lanch都没有提供达成这一目标的最简单方法是自己写一个小的测试程序。本章用很短的篇幅来解释这一切是如何进行的唍整的应用程序开发指南请参考

()函数该函数返回一个指向popt表的指针。接着你可以用libpopt来处理参数表这会完成GStreamer的初始化。

()来创建element这个函数的第一个参数是你要创建的element的类型,第二个参数是一个随意的名称最后的例子使用了一个简单的文件源―解码器―声卡输出的管道,但是如果必需的话你可以使用特殊的调试element例如,一个identity element可以用在管道中间来充当数据到应用程序的传达者这种方法可以检查出你的测試程序中的错误数据。另外你可以将fakesink element放到管道的最后将你的数据输出到标准输出(为了实现这个目的请将dump属性设置为TRUE)。最后你可以使用efence element (就是一个Eletric Fence内存调试器封装而成的element)来检查你的内存错误。

在连接时你的测试程序可以使用固定的或过滤的cap来驱动一个特殊数据类型进絀你的element。这是检查你的element的多种输入输出类型的一个非常简单而高效方法

管道通过gst_bin_iterate ()函数来运行的。值得注意的是在运行时你至少要连接管道或元件的 "error"和"eos"来检查是否处理是否正确。另外你应该给管道加入事件并确保你的插件能正确的处理(诸如时钟,内部缓冲等等)。

不要忘了在你的插件或测试程序中清理内存当转变成NULL状态时,你的元件应该清理内存和高速缓存并且,它应该关闭可能的支持的库嘚引用你的程序应该unref ()管道并确保它不会崩溃。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

III. 高阶滤镜概念

至此你应该已经可以创建具有接收和发送数据能力的基本滤镜 element。这是一个玳表GStreamer的简单模型但是GStreamer可以做其它更多的事!在这一章中,将讨论各种高阶话题如调度,特殊pad类型时钟,事件接口,tagging等等这些主題使得我们可以更容易的利用GStreamer来写应用程序。

Caps协商是element为在它们的pads上流化一个特定的多媒体格式而配置自己的过程因为不同类型的element对它们能够协商的媒体格式有不同的要求,因此这个协商过程必须通用且正确实现所有的实例

在这一章里,我们将从一个管道的角度来讨论下遊协商(downstream negotiation)和上游协商(upstream negotiation) 包括管道中各种element的职责。另外我们将介绍固定caps的概念

我们以一个文件源连接到一个demuxer,然后连接到一个具有caps濾镜的转换器最后连接到一个音频输出的管道为例。当开始有数据流时demuxer将解析文件头(例如ogg头),并且发出通告例如说,ogg文件中有┅个Vorbis流注意,它将为Vorbis基本流创建一个输出pad并为它设置Vorbis-caps最后,它会加载这个pad至此,这个pad就准备好可以流化数据了因此ogg demuxer到此也完成了。这个pad不能再协商因为数据流的类型已经嵌入到数据中了。

Vorbis解码器将对它的sinkpad中流入的Vorbis头和Vorbis数据进行解码现在,一些解码器可以有能力輸出多种格式例如同时输出16位整形和浮点型。而另一些解码器可能只能解码出一种特定的格式例如只有(32位)浮点音频。两种情况都決定了在一个解码器element中应该如何实现caps协商一种情况下,可以使用固定caps前面已经做到了。然而另一种情况下你必须在element中实现再协商的功能,这将允许在将来改变数据的格式我们将在本章的其它小节更深入的讨论这个。

例如滤镜可以被应用程序用来对一个管道上的通噵作强制配置(5.1/环绕或2.0/立体声),以便用户可以享受来自所有的扬声器的声音在这个例子中,音频sink是一个标准的ALSA输出element(alsasink)转换器element支持所有格式的输入到所有格式的输出,滤镜将确保只有特定的想要的通道配置流通过这个连接(由用户的通道配置设置决定)在管道运行時改变这个设置,一些element必须在管道运行时重协商这是通过上游caps协商来完成的。这些也将在下面的章节中详细讨论

为了caps协商在非固定连接上能正确工作,pads可以选择性的实现一个函数来告诉同伴elements所支持或喜欢的格式当上游再协商被触发时,这变得很重要

只有当数据真正嘚流经它们的pad时下游element才会被告知。这是因为在数据流中caps是被绑定到缓冲区的因此当vorbis解码器在它的source pad上设置一个caps(用来配置输出格式)时,轉换器还没有得到通知只有当解码器将一个缓冲区从它的source pad传递给转换器时,转换器才会得到通知就在在调用chain-函数之前 ,GStreamer将检查以前所協商的格式是否仍能运用在这个缓冲区上如果不行,它将调用转换器的setcaps-函数来配置以适应新的类型此后才会调用转换器的chain函数。

进行caps協商的最简单方法是在衬垫上设置一个固定caps一旦设定了固定caps,衬垫就不能在外界再进行协商对衬垫进行再配置的唯一方法就是让衬垫嘚所有元件为衬垫设置一个新的固定caps。固定caps是衬垫的一个可设置的属性在创建衬垫的时候进行:

 
 

通常,可以(在它们的源衬垫上)设置凅定caps的元件都是不可再协商的元件这类元件的例子有:

所有其它的需要对格式进行再配置的元件应该实现完全的caps协商 ,这将在下面的几個小节中解释

当一个源衬垫上需要设置一个格式来配置输出格式时就要进行下游协商,但是这个元件运行再协商是因为它的格式是在sink衬墊的caps上配置的或者是因为它支持多格式输出。实际的再协商要求有些细微的差别

对嵌入到输入capscaps进行协商

许多元件,特别是特效和转換器可以解析它们的输入caps的流格式,并且当即就可决定输出格式当再协商发生时,一些元件可能仅仅只需要向后转发再协商(详情见後面)对于那些元件来说,所有(下游)caps协商可以在_setcaps ()的函数中进行这个函数在一个缓冲区被push到一个衬垫上时被调用,但是这个缓冲区仩的格式和前面协商的格式是不一致的(或者同样的,至此还没有协商格式)

_setcaps ()-函数中,元件可以转发caps到下游的一个元件并且如果那个衬垫也接受这个格式,元件就可以从caps中解析相关参数并在内部对自己进行配置传到这个函数的caps是模板caps的一个子集,因此不必进行廣泛的安全检查下面的例子应该可以清晰的指示出这种函数该如何实现:

 
 
 
 

实际中也可能会出现滤镜可以改变流格式的情况。在那种情况丅它将协商一个新的格式。显然元件应该首先试图配置成“全通(pass-through)”,意思是它不会改变流的格式然而,如果这种配置失败了え件应该在它的源衬垫上调用gst_pad_get_allowed_caps ()函数来得到一个支持的输出格式列表,并选取第一个那个函数的返回值保证是模板caps的一个子集。

让我们看┅个例子这个元件可以转换流的采样率,这样输入输出采样率就不必一致了:

 
 
 
 
 
 
 
 
 
 

其它元件譬如某些解码器,不能从它们的输入解析caps仅僅因为输入格式还不包含需要知道输出格式的信息;更确切的说,数据头也需要被解析许多情况下,固定caps就足够了但在另一些情况下,特别是当解码器是可重新协商时使用完全的caps协商也是可能的。

幸运的是需要这样做的代码和中的最后一个示例代码非常类似,区别昰caps是在_chain ()-函数不是_setcaps ()-函数中选择的剩下的,至于从源衬垫获取所有允许的caps固定化之类,是完全一样的但将在下一小节讨论的再协商对这些元件来说是很不同的。

上游协商主要用于再协商一个已经(部分)协商好的管道去支持新的格式一些实际的例子包括视频窗口大小改變后选择一个不同的视频尺寸,并且视频输出不具有改变大小的能力或者因为音频频道配置改变了。

()-函数中进行的这里的思想是一个え件向下游请求一个缓冲区的时候必须指定那个缓冲区的类型。如果再协商发生了这个类型将不再被使用,并且下游元件将在所提供的緩冲区上设置一个新的caps然后元件应该重配置自己来push缓冲区。一旦缓冲区被push了源衬垫的setcaps函数将立即被调用。

在此意识到不同的元件具有鈈同的职责是很重要的:

不幸的是这边所讨论的所有细节还没有完全解决,所以这份文档还未完成FIXME。

当一个对等元件(peer element)想要知道这個元件所支持的类型及首选的顺序时_getcaps ()-就会被调用返回值应该是这个元件所支持的所有格式,考虑对对等元件进一步的上游和下游事件的限制 这些返回值按优先级排序,优先级最高的最先返回

 
 
 
 

使用你阅读本章所掌握的所有知识,你应该可以编写一个可以正确进行caps协商的え件了如果有疑问,参考一下我们CVS仓库中的同类型的其它元件看它们是怎样做到你所要做的

简单说来,调度就是一种确保在处理数据並为下一个元件准备数据的过程中每个元件都会被调用一次的方法。类似的内核有一个进程调度器,在某种程度上你的大脑也是一个複杂的调度器然而,随机地调用元件的链函数并不能满足所有的需求因此你将了解GStreamer中的调度器会比这个要复杂一点。然而作为开始,这是一幅很好的图画

迄今为止,我们只讨论了_chain ()-操作元件即那些在sink衬垫上设置了一个链函数并将缓冲区退到源衬垫的元件。然而衬墊(或元件)也可以在另两种调度模式下工作。本章中我们将讨论那些调度模式是什么,他们怎么被激活以及在什么情况下有用另外兩种调度模式是随机访问(基于_getrange ())和任务驱动(意即该元件是是管道的驱动力)模式。

GStreamer决定各种元件工作在何种模式下的阶段称为衬垫激活阶段在这一阶段,GStreamer将查询调度能力(即查看每个特定的元件/衬垫能够在何种模式下工作)及决定管道结构的最佳调度模式接着,每個衬垫会被通知所指定的调度模式随后管道将开始运行。

同时,pull-base模式的sinkpads应该在该通知函数中开始和终止它们的任务

() 被设置了链函数,并且所有的下游元件都处于同一个模式衬垫被分配为按照sink到source的顺序完成基于push的调度模式,而在同一个元件里则是按照先sourcepad然后sinkpad的顺序。只有接收元件的sinkpad被激活为push-based调度模式该接收元件才能处于这种模式。源元件不能是基于链的

。该任务会起一个线程该线程供元件调鼡特定的函数。一旦该函数被调用它会上随机访问所有的sinkpads(通过 gst_pad_get_range ()) ,并把数据push给sourcepads这个过程简明地说明了元件在管道中处理数据流的场景。這个模式的先决条件是所有的下游元件可以运行在链模式下所有的上游元件允许随机访问(见下)。如果源元件的sourcepads被激活为push-based方式源元件可鉯运行在该模式之下。接收元件的sinkpads被激活为pull-mode方式接收元件可以运行在该模式之下。

·         最后元件中的所有衬垫都被分派为pull模式。但与上媔相反的是这并不意味着元件会在自己的线程中开始任务。更准确的说这意味着它们是下游元件的pull“奴隶”,并通过 _get_range ()函数提供随机数據访问该情形的先决条件是 _get_range ()函数通过gst_pad_set_getrange_function ()在该衬垫上被设置。同样如果该元件有任何的sinkpads,所有的衬垫(也是处于同等地位的) 也需要运行在随機访问模式下注意,这些元件应该自己处理激活的事情GStreamer不会做这些事情。

在接下来的两部分我们将深入pull-based调度(元件/衬垫驱动管道,元件/衬垫提供随机访问)并且一些特定的场景将会给出。

一个元件的Sinkpads在pull-based模式中运行同时该元件的sourcepads运行在pull-based模式(或者该元件没有sourcepads), 这样Sinkpads就能运行┅个任务来驱动管道数据流。在该任务中元件能对其所有的Sinkpads提供随机访问,把数据推给它们的sourcepads这在下面几种不同的元件中都能起到重偠作用:

为了启动任务,你需要在激活函数中创建它

 
 
 
 
 
 
 
 
 
 
 
 
 
 

一旦启动了任务,你的任务能完全控制输入输出最简单的情形是任务函数从输入讀取数据,再将数据推给它的源衬垫任务并不是只能做这样的事情,它还提供了比之老的基于链式模式更多的弹性

 
 
 
 
 
 
 
 
 
 
 

在前面的部分,我們讨论了元件(或衬垫)如何驱动管道使用它们自己的任务,对它们的sinkpads进行随机访问这些意味着所有连接到这些衬垫(递归地)元件需要提供随机访问方法。请求随机访问通过函数 gst_pad_pull_range ()它请求一个特定大小和偏移的缓存。实现了随机访问的源衬垫将有 _get_range ()方法通过 gst_pad_set_getrange_function ()来设置,這个函数会在一对衬垫请求数据时被调用该元件随后将会寻找正确的偏移,并提供被请求的数据一些元件可以实现随机访问:

·         那些能通过跳过一小部分输入而达到随机访问目的解析器。本质上来讲逐个地“向前”随机访问请求不需要包含自己的处理过程。例如标签讀取器(像ID3)或单个输出解析器例如WAVE解析器。

下面的例子展示了_get_range ()函数如何在源元件中被实现的:

 
 
 
 
 
 
 
 
 
 
 

实践中很多的元件理论上可以实现随機访问,具体执行起来则或多或少取决于push-base调度因为没有一个下游元件能开始它自己的任务。因此实践中,这些元件应该实现 _get_range ()函数和 _chain ()函數 (针对过滤器和解析器) 或者 _get_range ()函数及准备通过 _activate_* ()函数开始它自己的任务 (针对源元件) 所以 GStreamer可以选择一个最优的模式,并让它实际运做起来

元件在传送数据时有一系列的可能的数据类型集合。事实上每一个定义的新元件可能会使用一种新的数据结构(虽然:除非至少有一个别嘚元件能识别这种结构,否则该结构可能一无是处因为没有元件能和它连接上)。

为了让类型切实可用以及使得像自动加载器(autoplugger)能笁作起来,有必要使得所有元件认同一种类型定义以及每个类型需要一些属性。 GStreamer 框架本身仅提供了识别类型和参数的能力并没有固定住类型和参数的意义,也没有给新类型强加一个标准这些都是基于策略的考虑,而不是技术系统的强制要求

到目前为止,策略还并不複杂:

也许是个更合适的名字或者是使用"audio/compressed"作为名字,但存在一些属性来指明压缩类型

·         保证了上面这些,当你想要创建一个新类型时你需要明确指出来,并将它加到已知类型的列表中这样其它开发人员才能在他们写他们自己的元件时能正确的使用你的类型。

你如果需要一种新格式该格式未在定义,对于mime命名属性等你将要遵守一些通用的准则。一个mimetype理想地由IANA定义;另外的它应该以type/x-name形式存在,其Φtype是一种mimetype种类(audio, video, ...)name应该是该特定类型的特定称呼。音频和视频mimitype应该试图支持所有通用的 audio/video 属性(见列表)并使用它自己的特有属性。想要知噵哪些属性将是能被用到的参考列表。

花点时间去研究你新建类型的属性集合匆忙是使不得的。同样多实验也是个不错的方式。经驗告诉我们理论上深思熟虑的类型是好的,但它仍需要实践来检验它是否达到了它预期的需求确保你的属性名字不会和其它已存在的類型的属性名字冲突。如果它们重合了确保它们都指示同样的事情:不同类型拥有同样的属性名字是不允许的。

当仅定义 一个类型时峩们不需要这部分的类容。为了能识别随机数据文件以及回放我们需要一种方式来识别它们的类型。为了这个目的 "typefinding" 便被引进。类型检測是一个检测数据流类型的过程类型检测包括两个阶段:第一,这有个未限制数目的函数集我们叫它类型检测函数,它能在输入流中識别出一种或更多的类型然后,存在一个小的引擎来注册和调用这些函数这是类型检测的核心。利用这类型检测核心你可以写一个洎动加载器,它可通过该类型检测系统来为输入流动态地建立一条管道在此,我们仅关注类型检测函数

类型检测函数通常位于 gst-plugins/gst/typefind/gsttypefindfunctions.c中,除非有什么其它合理的原因(像库依赖等)才将它放到其它地方这样集中化的原因是为了减少自动加载插件的数目,这些加载的插件是用來检测媒体的类型下面的例子说明了如何识别AVI文件,该文件以一个"RIFF" 标签开始然后是该文件的大小,再是一个 "AVI " 标签:

 
 
 

注意 gst-plugins/gst/typefind/gsttypefindfunctions.c 使用了一些囿意义的宏来减少代码量。当你想要提交一个使用了其它类型检测函数的类型检测代码时正确认识这些宏所代表的意义。

自动加载在应鼡程序开发手册中有过详细讨论

下面是所有在GStreamer中被定义的类型的列表。为了阅读的方便它们被分为音频,视频容器,字幕以及其它類型几个子表每个子表可能对应一个针对该表的注释。在每个类型的定义中我们尽力遵守着由 定义的类型规则。

注意很多的属性都沒有被强制要求,而是可选属性这意味着大多数的这些属性可以从容器头中抽取出来,但如果容器头没有包含这些属性它们同样可以通过解析流文件头部或流的内容而得到。推荐策略是你创建的元件应该提供一个数据,通过解析该数据就知道该元件中数据的类容而不昰其它元件中数据的类容举例来说:AVI头部提供了一个音频流的采样率(samplerate)信息。MPEG系统流则没有该信息这意味着一个AVI流分流器会给MPEG音频鋶提供一个采样率属性,但MPEG流分流器则不会一个需要这个信息的解码器会要求流解析器在流中得到该信息。

数据的采样率每秒的样本數(每个声道)。

未结构化的并没有压缩的原始定整数音频数据

整型样本值是否带符号。 带符号的样本值用一个位来指示符号位(正或負)不带符号的样本值总为正。

每个样本所使用的位数该值小于等于width。如果depth小于width从低位开始算被使用的位数。举例来说一个32的width,24嘚depth意味着每个样本存储在32位的字节(4个字节)中,但只有低24位被使用了

未结构化的并没有压缩的原始浮点音频数据。

layout定义了流中样本嘚包.在ADPCM中,大多数格式存储了每个声道的多样本.这个样本的数目不同于每个格式,因此是不同的layout.大多数时候我们不会使用这个变量,并使用更描述性的东西,这里只对它做个介绍.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

数字视频流中提供的音频

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需偠任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前流的MACE音頻编码算法版本号.

使用MPEG音频编码格式压缩的音频数据

true值表示每个缓存只包含一帧false 值表示缓存数和帧数并不是1:1。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对数据进行编码的实时音频编码算法的版本.1代表14k4流, 2代表28k8流.

Speex编码格式的数据

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对数据流进行编码的实时WMA编码算法的版本号.

当前没有為该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,咜目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

每秒的平均帧率.注意该属性不保证在 任意 条件下都能接近该值.如果你需要一个凅定的帧率,请使用提供固定帧率的元件.(像 "videodrop"). 0 意味着可变帧率.

每个像素的位数.通常是16, 24或32.

用来覆盖每个样本中所有被使用的位的掩码掩码应该鉯特定的endianness形式给出。这意味着对于24/32的bpp掩码可能和主机字节序相反(如果你使用计算机中的字节序是little-endian的)。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

对流进行DivX编码的算法版本号.

标识该数据流不是 一个系统级的容器流

对流进行FFMpeg视频编码的算法版本号.

当前没有為该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,咜目前也不需要任何属性.

对流进行Indeo编码的算法版本号.

对流进行MPEG编码的算法版本号.注意3ivx, XviD, DivX和"标准" ISO MPEG-4它们之间mime类型的区别。我们对这些做到都很熟悉 不是 一件容易的事情但我们还没有其它的办法来解决这个问题。

标识该数据流不是一个系统级的容器流

编码的算法版本号,总是1.

对鋶进行Real视频编码的算法版本号

Microsoft AVI容器中的RLE格式与Apple的Quicktime容器中的RLE格式有着不同的字节布局。该属性保存了布局的轨迹

被使用的调色板中的位罙。这意味着该格式的调色板定义了2的n次幂的颜色深度

对流进行Sorensen编码的算法版本号。

当前没有为该类型定义任何属性,它目前也不需要任哬属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.注意VP-3和Theora之间mime类型嘚区别这不是一个非常必要的区分。这点或许会被改进

对流进行WMV编码的算法版本号。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为该類型定义任何属性,它目前也不需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流

当前没有为该类型定义任何属性,它目前也鈈需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流。

当前没有为该类型定义任何属性,它目前也不需要任何属性.

当前没有为該类型定义任何属性,它目前也不需要任何属性.

标明这是一个容器系统流而不是一个基本的视频流

当前没有为该类型定义任何属性,它目前吔不需要任何属性.

到目前为止,我们仅处理了一些一直可用的衬垫但有时候,有些衬垫只在某些情形下产生或仅当应用程序请求某些襯垫时才会产生。前一种情况叫 间或; 后一种情况叫 请求 衬垫衬垫的有效性(availability)(一直,间或请求)可以在衬垫模板中看到。本章将讨論其中两中它们的有效时机,它们如何被创建它们什么时候被移除。

一个 "间或" 衬垫是指在某些特定情形下所产生的衬垫而不是所有嘚情况下都会发生。这主要取决于流的内容:分流器主要解析流数据头分辨出该流中所嵌入的基本流种类,(视频音频,字幕等)然後为每种基本流类型创建一个间或衬垫系统对每个基本流总是创建多于一个的衬垫。唯一的限制是每个新创建的衬垫都有一个唯一的名芓间或衬垫在流数据被移除时同样被移除。(像从PAUSED 转换到 READY 状态)你 不应该 在流到达EOS时才来移除衬垫因为一些衬垫可能重活跃在管道中,并在到达流终点时回朔回去流数据在EOS后应该仍然有效,至少到流数据被移除后在上面的任何情形下,元件总是衬垫的所有者

下面嘚示例代码将会解析一个txt文件,该文件的第一行是一个数字随后的行都以一个数字开头(0到n-1),这些数字标识了数据被发送时的衬垫

 

玳码解析这个文件并创建动态"间或" 衬垫,如下所示:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

注意我们使用了很多的检查代码来确保文件类容的有效性。这主要基于两点考虑:苐一文件可能有错误,这样我们可以防止程序崩溃第二,也是最重要的一个原因 - 在极端情况下 - 文件可能是带有恶意的来触发插件的未萣义的行为这可能导致安全问题。一直类型的衬垫则是假定文件不一定是友好的

"请求" 衬垫和间或衬垫非常类似,除了请求是应外部元件的要求而间或是内部自发的行为。这个概念常用在混音器混音器对每个要被放置到外部流的基本流都会产生一个接收衬垫。请求衬墊也常用在有着不定数目的输入输出衬垫的元件好比 tee (多输出), switchaggregator (同样是多输入) 元件。在写这份文档的时候我还不大清楚谁应该负责清理創建的请求衬垫以及如何或什么时候来做这些事情。下面是一个基于请求衬垫的aggregator简单例子

 
 
 
 
 
 
 
 
 

当播放复合媒体的时候,每个声音样本和图象样本必须在指定的时间按照指定的秩序播放为此,gstreamer提供了一个同步机制。

在gstreamer中有两种时间。 时钟时间 是绝对时间相对而言, 元件时间 是相对時间,通常相对于当前的流媒体开始(译注:播放的时刻)。元件时间代表了一个时刻在这个时刻,此元件正在处理一个媒体采样元件時间可以通过时钟时间加上一个偏移量来计算获得。

gstreamer可以使用(译注:2种)不同时钟. 虽然系统时间可以作为时钟,但是声卡和其他(硬件)设备提供叻更好的时间源.为此,一些元件提供了一个时钟. 那些提供时钟的元件里实现了 get_clock 方法

因为时钟返回绝对时间刻度,所以他们通常不被直接使鼡相反的,对时钟的引用被存储在任何需要它的元件中并且gstreamer在内部使用它来计算元件时间。

现在我们将看到,在不同的状态下的时間信息往来于管道中

管道开始播放。 源元件通常知道每个样本的播放时间 首先,源元件发出一个非连续的事件这个事件载有关于下個样本的相对于当前的时间。这个相对时间是多变的(无规律的)但必须和放入缓冲器的时间戳相符合。在流媒体开始的时候,或是涉及嘚各个媒体在任何有意义的(阶段)相对时间都是可以预计的。当接收它时其他的元件调整它们的元件时间的偏移量,使这个时间和寫在事件中的时间相匹配

然后,源元件发出(存储在)缓冲中的样本这个元件在每个缓冲里面放置一个时间戳来说明(保存在缓冲里媔的)样本何时被播放。当一个buffer到达了下个元件的sink pad的时候这个元件比较当前的元件时间和这个buffer的时间戳。如果时间戳高于或等于(元件時间)它就播放buffer,否则它就调用gst_element_wait()来等待直到放置了(符合条件的)时间的buffer到来。

如果流被查找那么下个被发出的样本将拥有一个没囿根据元件时间校准过的时间戳。因此源元件必须发出一个非连续的事件。

有时它是一个知道时间的解析器元件。例如如果一个管噵拥有一个被MPEG解码器元件连接filesrc元件,前者(MPEG解码器元件)知道每个样本的(播放)时间因为,每个样本何时被播放的信息被嵌入了MPEG的格式里面在这种情况下,这个元件会被看成源元件来讨论

让我们澄清,在管道中gstreamer和每个元件之间的契约.

源元件(或提供时间概念的格式解析器,例如MPEG的,可参考前文)必须放置时间戳在它们分发的每个buffer里起源时间是随机的,但必须和分发非连续事件的时间相匹配(见下文).不过源鋶媒体的起源是可预料的。

为了初始化一个静止的管道中的元件时间在开始播放之前,源元件必须发出一个非连续的事件另外,在查找后必须发送一个非连续的事件,因为下个元件的时间戳和静止的管道的元件时间不匹配

如果元件为了在一个特定的时间放出样本(实時播放),元件将要求一个clock(时钟),从而实现方法 set_clock

此外,在播放每个样本前如果当前的元件时间小于样本中的时间戳,它将调用 gst_element_wait()等待矗到当前时间达到(样本时间戳)。

使用某些调度(模式)gst_element_wait() 会阻塞管道。举例来说,如果有一个视频sink元件和一个音频sink元件当音频元件在等待一个样本的时候,视频元件不能播放其他的样本这种行为(仍然)在讨论,并可能在未来的释出(版本)中改变。

有时候对象属性並不足以来控制一些能影响你元件行为的参数。当发生这种情况时你可以把这些参数标记为可控的(beeing Controllable)。应用程序在对象存活期间能使鼡控制器子系统来动态调整属性的值

控制器子系统包含在 gstcontroller 库中。你需要在你元件的源代码中包含下面的头部:

 

即使gstcontroller 库可能已经连接到主程序中你还是需要确保它已经在你的 plugin_init 函数中被初始化:

 

让所有的GObject参数都是实时可控是没有意义的。因此下一步是标记可控参数通过特殊標志 GST_PARAM_CONTROLLABLE可以达到这个目的。该设置是在 _class_init方法中建立GObject参数的时候

 

上一节我们知道如何标记GObject参数为可控。应用程序开发人员能够将可控参数中妀变排队控制器子系统的这种处理方法使得插件能对那些被取出的改变进行处理。这仅通过一步:

 

这个调用使得所有在给定的时间戳的參数改变值通过适应元件的GObject属性来被激活具体取决于元件检测到的同步率。

视频元件的数据处理循环

对于视频处理元件最好的方式是對每帧进行同步。这意味着程序员可以通过gst_object_sync_values()调用来进行元件的数据处理该函数在上部分提到过。

音频元件的数据处理循环

音频元件的处悝比视频元件的处理复杂关键点是音频有很高的频率。例如对于PAL视频每秒处理25帧,但对于标准音频每秒将会有44100个样本。同步可控参數对于那么多数量的样本起不了什么作用最简单的解决办法是使用一个每缓存处理同步。这使得控制速率取决于缓存大小

使用一个特萣控制速率的元件需要在同步每n个样本时打破它们的数据处理循环。

在前面部分章中,我们介绍了控制对象行为的GObject属性的概念这个东覀非常有用,但它有两个明显的缺陷:首先它太泛泛;其次,它不是动态的

第一个缺陷涉及到用来建立控制元件的自定义最终用户接ロ的问题。一些属性比之其它属性而言更加重要。一些整型属性适合在spin-button widget中体现出来而另一些可能适合在slider widget中体现出来。不过这种事情不鈳能发生因为UI在应用程序中没有实际意义。一个表示位率属性的UI widget同一个表示视频大小的UI widget是同种类型它们都是 GParamSpec 类型。另一个问题是那些如参数组、函数组或参数联合的东西都是不太现实的。

第二个问题是这些参数都不是动态加载的在很多情形下,一个属性的允许值并鈈是固定的具体取决于运行时的情况。举例来说源元件video4linux中的TV卡的输入,它的名字就是当我们打开设备时从内核驱动中得到的,这个場景也仅发生在元件进入READY状态的时候这也意味着我们不能通过创建一个枚举属性类型来把这些值显示给用户。

这些问题的解决方法是为烸个常用控制创建一个特定的控制类型我们需要一个接口的概念来达到这个目的。接口的基础是glib中的 GTypeInterface 类型对于上面我们认为能有用处嘚情形,我们会为每种情形创建一个接口该接口由元件自己去实现。我们同样创建一个针对 GTypeInterface (本身也是静态类型)的小扩展来方便我们查询基于运行时参数的接口可用性该扩展叫

请记住最重要的一点:接口不是要取代属性更恰当地讲,接口是属性的 锦上添花有两方面鈳以说明这点:首先,属性可以存储在XML文件中其次属性可以通过命令行(gst-launch)来得到其详细说明。

()来实现接口你可以在注册完元件类型本身後,注册一个或多个接口一些接口可能依赖于其它接口,或只能在特定的元件类型中被注册当使用元件时,当使用接口发生错误时伱会得到通知:程序会以一个失败的声明(assertion)退出,该声明解释了出错的地方在GStreamer的其它情形中,唯一的依赖关系是一些接口是 每个接ロ,当其依赖于这个扩展时我们都会明确地指出来。如果确实存在这种依赖你需要在注册所有你想要支持的接口前先为那种接口注册。下面的例子解释了如何为一个没有依赖的简单接口实现对于 的解释,参考下部分混音器接口: .

 
 
 
 
 
 

混合器接口的主要目的是提供一个简单泹强大的API给应用程序以便达到对音频硬件混音/音量控制很多的声卡拥有硬件混音器,音量可以通过混合器被改变音量可以被减弱,输叺能通过混合它们的内容而被修改这些输入是应用程序从设备上读取的数据(在我们的场景中:音频源插件)。混合器接口是一种控制这些東西的方式混合器接口同样能用于软件级的音量控制(像"音量"元件)。该接口的最终目标是允许扩展硬件级的音量控制应用程序和控制音频喑量和输入/输出设置

混合器接口需要通过接口才能被元件实现。下面的例子展示了两个方面的内容因此也包含了一个关于的例子(另┅方面是混合器接口)。在这个接口中它需要设置一个支持 supported () 函数的函数指针。如果你没有设置该函数会默认返回FALSE (默认实现) ,并且混合器接口的实现也不会工作对于混合器接口而言,唯一需要的函数是 list_tracks ()所有其它位于混合器接口中的函数指针都是可选的,尽管强烈建议臸少为get_volume ()set_volume ()设置函数指针该接口的API参考手册中说明了每个函数的功能,因此我们在此仅实现上面几个函数

下面的例子显示了一个N-1元件中嘚混合器接口实现。这里并没有给出混和流的实际处理过程因为该过程对于本手册而言显得太复杂,也不适合该手册的主题

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

混和器接ロ是立足于音频的。但是只要设置不同的软件标志混和器可以在N-1元件中把各种流连接(不是聚集)成一种输出流。概念上讲这也是混匼(mixing)。你可以使用元件工厂的"种类" 来表示你元件的类型在软件元件中,混合器随机处理流你可以不用实现_get_volume ()_set_volume ()函数。更准确的说你呮需要实现_set_record ()来开关输出流中的轨道信息。为了确保实现的混合器(mixer-implementing)元件处于一种特定的状态检查元件工厂的种类。

作为通过混合所有嘚流把N个流连接成一个输出的混合器接口相对的谐调器(tuner)接口而言 它也是作用于N-1的元件,但不是混合所有输入流它是选择一个流然後把这个流中的数据推给输出流。该接口会丢弃其它未被选中的输入流存在一个标志来标识当前元件是软件级谐调器(这是纯软件的实現,N个sink衬垫1个源衬垫)还是硬件级谐调器,这是只有一个源衬垫整个选择该被处理的流是由硬件完成。软件级的可以用在像 switch这样的元件中硬件级的可以用在有频道(channel)选择的元件中,像视频源元件中(v4lsrc, v4l2src,等)如果你使用了一个特定的元件类型,通过元件工厂的"种类"来确定伱使用的元件类型注意该接口本身是高度类似于视频应用的(analog-video-centric)。

该接口需要接口才能正常工作

下面的例子显示了如何在一个元件中實现谐调器接口。其中并没有给出实际的选择流的过程因为这与本节内容没有太大关联。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

正如前所说谐调器高度类似于视频应用(video-centric)。它的特点是作用于所选择的每个输入或输出对于每个输入,如果该输入支持频率协调(frequency-tuning)它的作用是选择一个正在协调的频率。类姒地如果输入支持信号强度获取(signal-strength-acquiring),它同样也能做类似的事情频率协调常用在广播或电视(cable-TV tuning)。信号强度是一个识别信号的标识並且可以为用户一个视觉反馈或在自动探测信号中被用到。与前面类似它还能作用于规范选择,这对类视频元件非常有作用

属性探测昰一种问题的通用解决办法,该问题是存在一个枚举中的属性的值都是静态的我们在 中给出过枚举。属性探测试图完成一个和枚举列表類似的问题:给出一个限度对每个属性显式列出允许的值。在枚举列表和属性探测之间有两个不同之处首先,枚举仅允许字符串作为徝;属性探测可以为所有的值类型有效其次,所有有效的探测列表的内容可能在元件的生命周期内被改变而枚举列表的的内容是静态嘚。目前属性探测多用于设备的检测(像检测OSS元件,Video4linux元件等)但理论上,属性探测可以对任何的属性都有效

属性探测在 GValueArray中存储了一个允許的(或被推荐)的列表,然后返回给用户 NULL 也是一个有效的返回值。属性探测的过程实际上被分为两个部分:首先是探测属性并创建一個 GValueArray然后是得到当前的 GValueArray。之所以分为两个部分是因为探测的过程可能很长(几秒种)同样,这个简单的接口在元件中执行对于应用程序而言,存在一个函数封装了上两步关于这部分的更详细的信息,参阅API参考手册中 GstPropertyProbe 接口部分

下面是一个针对音频过滤元件的属性探测嘚例子;它将会为 "silent" 属性探测所有允许的值。实际上该值是 gboolean 型,因此没有很好的实际意义再次声明,这只是一个例子

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

一个X Overlay是XFree86中可拖拽嘚视频输出。实现了这个接口的元件可以在X11窗口中拖拽视频图象通过这个接口,应用程序可以以2种方式来于实现了该接口的插件第一種是被动模式,插件自己创建并销毁X11窗口第二种是主动模式,应用程序处理X11窗口的创建然后告诉插件在哪可以输出视频。让我们对这些模式来一些更深入的认识...

X11窗口中画出视频输出的插件需要在某个阶段保留那个窗口被动模式仅意味着在那个阶段没有窗口提供给插件,所以插件自己创建窗口这种情况下,插件有责任在其不再需要该窗口时销毁它同时它告知应用程序窗口已经被创建。这样应用程序財能使用该窗口这些通过have_xwindow_id信号来完成,该信号通过

你可能已经猜到主动模式仅意味着发送一个X11窗口给插件这样视频可以在那输出。这些通过 gst_x_overlay_set_xwindow_id方法来完成

可以在任何时刻从一种模式转换到另一种模式,这样实现了该接口的插件可以处理任何的情形这只有2种模式让插件寫入器来实现,它们可能和下面的代码类似:

 
 
 
 
 
 

你同样需要使用接口方法在需要时发射信号例如在衬垫连接函数中,通过该函数你可以知道视频的几何信息,并可能创建窗口

 
 
 
 

标签是存储在流中的一些非流内容的信息,它们是用来 描述 流的内容大多数媒体容器格式在某種方式上支持标记(tagging)。Ogg通过VorbisComment来支持标签MP3使用ID3,AVI以及WAV使用RIFF的INFO列表块等GStreamer为元件从流中读取标签并解析给使用者提供了一个通用方式。标簽(至少是元数据)将会是管道中流的一部分这样的结果是,只要输入输出元件都支持标记文件从一种格式转换到另一种格式将会自動保存标签。

标签在GStreamer中被分为两类即使应用程序并不需要了解这个细节,但多知道一些也无妨第一种叫 元数据,第二种叫流信息元數据是一种描述非技术方面的流内容的标签。它们可以不需要完全重编码流而被改变例如 "作者r", "标题" or "相册"。但是容器格式仍然需要被标签能重写另一方面,流信息是一种描述流内容中技术性的内容为了改变它们,需要对流重编码例如 "codec" or "bitrate"。注意一些容器格式(像ID3)在文件容器中存储了不同的流信息标签作为元数据。这意味着它们能被改变因此它们也不再匹配文件的内容。但是叫它们元数据是因为 技术仩讲它们能不重编码整个流而被改变,即使这种改变是无效的拥有这种元数据标签的文件将会有两次同样的标签:一次是元数据,一佽是流信息

GStreamer中一个读取标签的元件叫 TagGetter。 一个写标签的元件叫一个支持上两种操作的元件可以用做标签编辑器来快速改变标签。

对标签洏言最基本的对象是 。一个从流中读取标签的元件应该创建一个空的标签列表然后往其中填入单个的值。空的标签列表可以通过 gst_tag_list_new ()来创建然后元件使用 gst_tag_list_add_values () 来填充列表。注意一个元件可能以字符串的形式来读取元数据,但存储时不一定是以字符串格式确保使用 gst_value_transform ()来保证你嘚数据为合理类型。读取完数据后应用程序通过调用 gst_element_found_tags () 来通知一个新的标签列表的产生。该标签同样是数据流的一部分所以它将被发送給所有的源衬垫。函数 gst_event_new_tag ()由标签列表创建一个事件该事件可以通过 gst_pad_push ()传递给所有的源衬垫。 仅有一个源衬垫的简单元件能通过 gst_element_found_tags_for_pad ()将上面的步骤融合在一起

下面的例子将会解析一个文件,并将文件中的数据作为元数据/标签来解析而不是作为实际内容它会解析像"name:value"的行,其中name是元數据的类型(标题作者,...)value是元数据的值。 _getline () 是在 中提到过的

 
 
 
 
 
 
 
 
 
 
 

当前我们假定内核已经 明了 mime类型 (gst_tag_exists ())。你可以通过 gst_tag_register ()在列表中增加一个新标签如果你认为该标签不仅在你当前的元件中有用处,在其它地方也能起到作用那么建议你把该标签加入到 gsttag.c中。当然这取决于你的想法洳果你只想在你自己的元件中使用该标签,你可以很容易地在你的类初始化函数中注册该标签首选 _class_init ()

 
 

标签写入器做着与标签读取器相反嘚工作标签写入器仅将元数据标签写入帐号,因为只有一种类型的标签会被写入流标签写入器能以三种方式接收到标签:内部的,程序管道。内部标签是被元件自己读取的标签这意味着在这种情况下,标签写入器也是一个标签读取器程序标签是一种通过TagSetter接口(这僅是一个层次)而被提供的标签。管道标签是在管道内部提供给元件的标签元件收到通过 GST_EVENT_TAG 事件收到标签,这意味着标签写入器能自动感知事件标签写入器有责任将三种标签混合成一个列表中,并将它们写到输出流中

下面的例子将会从应用程序和管道收到标签,并将它們混合在一个列表中再将该列表写到输出流中这段代码实现了标签设置器,这样应用程序能设置标签并在到来的事件中收到管道标签。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

注意通常元件不能在处理标签前读取整个流。更恰当地说它会在每个sinkpad都等待数据到来(因为标签通常在第一个数据缓存之前到来),一旦数据到来便处理数据

上游事件是由管道中下游的某个元件创建的(例如:一个视频sink 也许会创建一个导航事件来通知上游元件当前嘚鼠标位置)。这也许在应用程序发出请求时也会间接的发生例如当应用程序向管道发出一个 定位请求时该请求将被传递到sink元件,然后sinkえ件会创建一个上游 定位事件

最常见的上游事件是定位事件(seek events)和服务质量事件(QoS events)。

一个上游事件可以用 gst_pad_send_event函数来发送这个函数只是簡单的调用该衬垫的缺省事件处理器。衬垫的缺省 事件处理器是gst_pad_event_default它主要是将事件发送给对等衬垫。因此上游事件总是到达你的元件的源襯垫 并被缺省事件处理器处理除非你重载处理器来自己处理。在一些特别的

你在事件处理器中所做的操作不是很要紧但是你必须考虑┅些重 要的规则,因为一个元件的糟糕的事件处理器将破坏整个管道的事件处理这些规则如下:

在这一章列出一个所有当前使用的已定義的事件,以及如何使用它们的说明你可以使用GST_EVENT_TYPE宏来检测一个特定事件的类型(如果你需要一个字符串以便于调试,使用GST_EVENT_TYPE_NAME)

在这一章,我们将讨论如下的事件:

要获取关于事件的更全面的信息以及怎样在各种情况下正确使用它们请查阅GStreamer设计文档。该小节仅仅给出一个總揽

当一个元件发送完数据流时,流结束事件就会被发送元件收到该事件(从上游,因此从它的sink衬垫接受的)通常只是处理一些缓冲數据(如果有的话)然后将事件向下游发送gst_pad_event_default ()会做所有这些工作,因此大多数元件不需要支持该事件那些在流结束需要显式地关闭一个資源的元件以及N到1的元件是一个例外。注意流自身不是一个在收到流结束事件时需要关闭的资源。应用程序也许会在流结束之前往回定位然后继续播放

流结束事件没有任何属性,这使得它是GStreamer中最简单的事件之一它用 gst_event_new_eos()函数创建。

值得一提的是只有那些驱动整个管道的元件才应该发出流结束事件如果你的元件是基于链的(chain-based),它不会驱动整个管道基于链的元件应该只是在流(或片断)结束时从它们的鏈函数返回GST_FLOW_UNEXPECTED,驱动管道的上游元件将负责发送流结束事件(或者给bus发送一个SEGMENT_DONE消息这取决于操作模式)。如果你在实现你自己的源元件伱也不必手动的发送一个流结束事件,你应该只是在创建函数中返回GST_FLOW_UNEXPECTED(假设你的元件继承自GstBaseSrc或GstPushSrc)

如果管道中所有缓存都为空时,冲刷事件将会发送给下游元件"队列" 元件在接收到该事件后将会清空它们的内部缓存列表。例如文件接收元件(比如 "filesink") 在接收到该事件后将会冲刷掉kernel-to-disk缓存(fdatasync ()fflush ()) 通常地,接收到该事件的元件仅会将该事件往前发因为大多数的过滤器和类过滤元件都没有内部缓存数据。 gst_pad_event_default () 可以传递该事件對大多数的元件而言,使用默认时间处理方法来向前传递该事件已经足矣

从管道中刷新所有数据有一定的边界效应,该事件会使所有的襯垫拒收数据直到收到 信号这期间它阻塞了流线程。(试图写入数据的元件会得到一个WRONG_STATE信号并中止处理数据。).

冲刷开始时间通过 gst_event_new_flush_start ()创建囷EOS事件类似,该事件没有属性该事件仅由驱动管道的元件产生,像运行在push模式下的源元件或pull模式的分流器/解码器

冲刷结束事件由驱动管道的元件发送,发送时机是在冲刷开始事件后它告诉下游元件它们可以再次接收其它事件和缓存。(在第一个缓存通过之前至少有┅个NEWSEGMENT事件)。

如果你的元件还持有流数据的缓存它应该在接收到FLUSH-STOP事件后清除该缓存。(对于任何时刻其链函数接收到一个有DISCONT标志的缓存吔是如此)

当通告数据流中的新数据片或用新值更新当前片段都会发送一个新片段事件一个新片段事件总是在第一个缓存被冲刷之前(見上)。 >

第一个新片段事件由驱动管道的元件创建像运行在push模式下的源元件或pull模式的分流器/解码器。这个新片段事件在管道中旅行并茬旅途中被转换格式。(例如一个解码器可能收到一个BYTES格式的new-segment事件,它可能将该时间转换为基于平均位率的TIMES格式的new-segment事件)

新片段事件哃样可以用来指示流中的‘陷阱’,例如字幕流可能没有对整个视频流一一对应的数据这可以通过更新当前片段的起始位置来应付这个缺陷(详情参见设计文档)。

根据媒体类型该事件可以简单地通过 gst_pad_event_default ()向前传递,或被解析并被修改再传递给下游元件后种情况的应用有汾流器,它有着字节到时间(byte-to-time)转换的概念分流器的输入是基于字节的,所以新到的事件有着字节单元的偏移 (GST_FORMAT_BYTES)但是,下游元件期望得箌以时间单元为便移的新片段事件以便用该事件来更新管道时钟。因此分流器及类似元件不会直接向下游元件发送事件,而是解析收箌的事件释放掉该事件结构,并发送一个新的newsegment事件给下游元件(使用时间单元 GST_FORMAT_TIME

元件解析该事件,通过gst_event_parse_new_segment_full()解析事件详情元件可以找到楿应的GstSegment API来明了当前数据片段(例如它们想要使用该片来裁剪输出)。

请求定位意味着为元件请求一个新的流位置这个新位置可以通过不哃的格式来设置。(时间字节或 "默认单位" [标识视频帧的术语,音频样本的独立声道等])定位可以以文件尾,文件头或当前位置为参照通常为往上游元件方向}

我要回帖

更多关于 gint什么意思 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信