播放器一般都会包含传输(Transport),多路分解(Demux),解码(Decode),效果(Effect),输出(Output)等。结构大概如图所示:
+---------+ +---------+ +--------+ +--------+
+--------+ +-----------+ | |-->| Decoder |-->| Effect |-->| Output |
| Source |-->| Transport |-->| Demuxer | +---------+ +--------+ +--------+
+--------+ +-----------+ | |-->| Decoder |-->| Effect |-->| Output |
+---------+ +---------+ +--------+ +--------+
Source 指的就是媒体内容所在的地方,可以是文件或者实时产生的媒体流。Transport 指的是把指定的媒体内容读到本地,可以是本地的文件或者 HTTP 等。多媒体容器中可能包含多路媒体,比如一路音频一路视频,需要把这两者区分,Demuxer 就用在此处。Decoder 用来将压缩了的视频和音频进行解码。Effect 用来增加一些效果,比如将音频的音量统一化。最后是输出,输出到声卡,屏幕或者文件中。
上图说画的是多媒体的数据流向,不论如何肯定是从 Source 这端流向 Output 这端的,但是有个主动被动问题。当然对于 Source 来讲都是被动的,所以要有一个角度问题。对此一般有两种结构,推模式(PUSH Model)和拉模式(PULL Model)。对于一个模块来说,如果我主动去别的地方拿数据,可称之为拉模式,是主动的;如果是别人把数据塞给我让我处理,那就是推模式,是被动的。
先说说现在的两大多媒体框架 [GStreamer] 和 [Xine]。GStreamer 同时支持推模式和拉模式,一般情况下为推模式,不同的元素(Element)之间是直接相连的,数据也是在两者之间直接传递。Xine 使用的也是推模式,但是具体的实现和 GStreamer 不同,它在不同的层(Layer)之间尤其是在多路分解和解码器之间有一个 FIFO 的缓冲区队列,由框架把要解码的数据从队列中拿出来,交给解码器进行解码,然后解码器把数据交给输出。
[XMMS] 更简单,只有解码器和输出两个层次,选择某个文件开始播放,选择一个解码器插件,由解码器创建一个解码的线程,其他输入呀、暂停/搜索什么的,直接调用解码器的相关回调函数,所有的事情都由解码器来做。从某种程度来讲,是拉模式和推模式混合的一种实现。显然,其缺点比较多的,被淘汰也是必然的一个结果。再说说 [XMMS2],最开始的时候,设计就是开头所画的图,但是它是完全的拉模式,全部由输出这部分来控制整体的播放、停止,搜索等等。后来将传输、解码、效果合并成一个变换(Transform,简写为 xform),由一系列的变化插件完成各种功能,也可是说很有创新的一个地方,在[另外一篇文章]中略微详细地讨论了其实现方法。
单单对于各个编解码器而言,使用推模式比较多,这是显而易见的,因为这样不用考虑输入输出,专心做好解码工作就可以了。而且对于不同的多媒体播放器框架,都可以很方便地编写插件或者 wrapper。相比而言,用拉模式实现的编解码器,需要一个 IO 层,那不管上层的多媒体框架使用的是什么模式,它都必须要提供一个适配器(Adapter),让解码器的 IO 和框架的 IO 连起来。这样增加了一些工作,甚至有可能实现不了相应的功能。
很不幸,Monkey's Audio 的 SDK 就是完全的拉模式,自己控制 IO,在给别的多媒体框架写插件的过程中,有时候就感到力不从心,很难办。等有时间了,想把它的 SDK 重写一遍,使用推模式,能更好地适应更多的多媒体框架。




未完待改
占个坑先,不知道为什么,就想在昨天发出来,再慢慢改。
已经更新
昨天晚上添加了一些内容,不过仍没写完,有些东西是在看文档的基础上写的,感觉心里没底,等有空了把 GStreamer 和 Xine 的代码大概翻一下再说,还得添加 MPlayer 相关的东西。
感谢
谢谢讲解:)
Post new comment