Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.

解码框架讨论

microcai edited this page Dec 6, 2012 · 5 revisions

参考 ffmpeg/gstreamer/dshow ,设计了一个新的解码器框架。

框架由插件构成。每个插件挂到一条管线上即可。

这个是从 gstreamer/directshow 学来的。插件的作用就是处理数据。数据从一个插件流到另一个插件。每条管线都有至少一个提供数据的source,也至少一个消费数据的sink,并有且只有一个接收外界控制信息的插件:通常是解复用器。中间的都是 filter。数据是单向的source到sink传递,但是命令却是双向传递的。 比如解复用器可以向前一个插件,也就是媒体数据读取插件传递 seek信息,用于快进快退。媒体插件也可以向解复用器传递not ready/ready信息,用来暂停/恢复解码过程。

每个插件都运行在自己的独立线程里。

这个是和 gstreamer 最大的不同点。gstreamer依次调用管线上的插件。而avplayer则不是。每个插件都是独立运行在各自的线程中。 使用双向异步列队进行数据和命令传递。如果要确保数据和命令被同步接受,可以使用列队的同步原语。 更重要的是,视频渲染插件可以在自己的线程中执行窗口建立和视频渲染。

每个插件都有一个或多个输入列队。使用 get_date 获得输入数据。有一个或者多个输出列队,使用 put_data输出。还有一个信号发射器。使用 av_signal 发送信号,将被上下游都接受到。信号发生后,上下游必须是等待在 get_data 或者 wait_signal 上才能接受信号。信号处理函数被直接执行。wait_signal 执行好信号处理函数后会返回。如果要等待可以继续调用。 get_data 则依据参数是继续等待还是返回。

关于同步

一个典型文件的播放过程是

  文件读取插件->解复用器-> 视频解码器 -> 视频播放 -> 视频渲染
                    -> 音频解码器 -> 音频播放 -> 音频输出

也就是说,同步也是一个插件。同步主要是视频播放和音频播放之间的同步。视频播放插件和音频播放插件相互之间设置一个 master ,通常是音频播放插件。然后视频播放插件就会向音频播放插件获得时间戳信息来同步自己。

视频播放和音频播放插件,都必须使用”同步“的数据传递原语将数据交给渲染插件。视频渲染插件会将每帧的绘制时间都传回视频播放插件。同样音频输出插件会向播放插件回馈每个音频块被播放的时间。

播放过程解释

播放过程由 av_play() 开启。这个时候解复用器从GUI收到 play 命令。然后开始打开读取文件,将数据传递给解码器。

在文件读取过程中。复用器不停的使用 get_data 向上级插件请求数据,然后解码后 put_data 给解码器。get_data 有延时,超时后就进入暂停模式。向下级插件发送pause信号并等待上级插件的 ready 信号。如果数据准备好了(BT下载模式)解复用插件收到ready信号,继续处理。获得合理数据后向下级发送resume信号,继续工作。

解码器:解码器从上级获得数据后将编码的数据解码为未编码数据。向下级传递。解码器和播放插件之间可以加入多个filter。比如视觉效果增强啊,之类的。

播放插件:播放插件和同级的另一个播放插件执行时间戳同步。

音频播放插件的做法是这样的:从上级获得音频数据,然后推送给下级,并监控下级回馈的 played 信号。每次获得 played 信号就说明一个 data trunk 播放完毕。音频播放插件如果处于master模式,则向其他播放插件(视频播放插件)广播这个信息。每个音频输出插件都可以告诉音频播放插件自己支持的播放模式,比如wavout/OSS/同步的alsa 的模式是,写入一段就会播放一段,播放完成后返回。 dsound/pulseaudio/sdl/异步alsa 的模式是,播放引擎向程序请求数据,它要多少数据程序提供多少数据。区别就是 played 信号表示的是数据开始播放还是数据完成播放。

视频播放插件通过监听音频播放插件提供的played信号,获得音频的时间戳,然后等待当前帧对应的时间,到达对应的时间就调用视频渲染插件执行一次渲染。要结合视频渲染插件的渲染时间来相应的调前调用渲染器的时间。获得渲染时间通过调用 put_data 后等待 played 信号获得。视频播放插件事先会知道每个 played 信号的间隔。所以可以使用 sleep 或者等待下一个 played 信号来等待”时机“

如果视频播放器处于master模式,情况则反过来。

如果没有一个处于 master模式,则各自使用系统时钟进行同步。

拖动

快进由解复用器从 GUI 代码接收。av_seek() -> av_plug_demux_*_seek () 。 然后解复器向解码器发出discard(注意这个discard动作)指令。接着向 文件读取插件发出 seek 命令。然后等待文件读取插件的 ready 信号。等到后就可以继续读取了。等读取到正确的帧数据后,开始继续解码。视频渲染插件收到 discard指令可以显示一个缓冲符号:)。