-
Notifications
You must be signed in to change notification settings - Fork 161
播放器功能和原理
-
音视频回放
-
音频回放,要保证连续性
-
视频回放,要保证帧率稳定
-
音视频同时回放,要保证音视频同步
播放器的最最基本的功能和指标,就是音视频回放,连续性、稳定性、帧率控制、音视频同步。这些基本功能如果做不好,就不可能是一个好的播放器,也不要考虑其他附加功能的实现。
-
媒体总时长和播放进度显示
-
播放/暂停/seek 等播放控制功能
-
音频可视化效果(波形、频谱显示)
-
视频播放截图功能
-
媒体信息获取(视频宽高、帧率、采样率等)
-
音视频流选择功能
-
音视频渲染设备和渲染方式选择
-
变速播放功能
-
软件音量控制
-
歌词显示功能
-
字幕流渲染功能
-
硬件加速解码
在一些媒体文件中,可能存在多个音频流,比如有普通话的音频流、广东话音频流、英语音频流、...,所以我们在播放这些媒体文件的时候,是可以选择具体的音频流或视频流的。多个音频流的视频还是比较常见的,多个视频流的比较罕见,但还理论上也是可以存在的。这样,我们就需要去实现“音视频流选择”这样一个功能。
在具体的操作系统平台上,音视频的渲染有不同的方式,比如音频可以有 waveout、directsound,视频可以有 gdi、directdraw,direct3d。视频渲染,还可以有等比例缩放、填充整个窗口,这样的选择。不同的渲染设备和渲染方式,会提供不同的兼容性、用户体验。因此实现这样的功能也是必要的。
变速播放功能是指,我们以更快的或更慢的速度去播放一个媒体文件。比如 100% 的速度,表示按照正常速度播放,50% 速度表示放慢速度播放,正常速度如果需要 30 分钟播放完毕,那么 50% 的速度播放则需要 60 分钟的时间,而以 200% 速度播放,只需要 15 分钟时间。变速播放的原理在于,视频的帧率控制,增大或缩小帧率,以及音频在时间上的重采样。变速播放同时又受限于计算机的处理能力,如果要加快速度播放,那么必然需要计算机有更高的性能;反之降速播放,则可以节约性能。
软件音量控制,是独立于操作系统的音量控制,实际上是播放器内部,对音频解码数据的一个软件衰减或增益算法。多数时候,我们使用操作系统自身的音量控制即可,所以这是一个可选的附加功能。字幕流显示,是一些媒体文件内部,是存在字幕流的,而且可能存在多个语种的字幕流,播放器可以根据客户的选择,在播放时渲染出对应的字幕。
读取文件 -> 解复用(demux)-> 音视频解码(decode)-> 渲染
看起来很简单,许多人也知道,但是这仅仅是一个非常粗略的,非常基本的流程。如果只是懂了这个,距离实现一个播放器,远远不够。
问题就在于:
如何读文件,我们知道吗?
如何解复用,我们知道吗?
如何做解码,我们知道吗?
如何做渲染,我们知道吗?
说到实现细节,读文件我们大家都熟悉,fopen 这一组标准的 c 语言库函数就足够了。但是除了普通文件和本地文件,我们还有网络流媒体等等特殊的文件。要处理好差异性,抽象出统一的接口,还是挺难的。所谓解复用,基本上就可以理解为对不同媒体文件格式(比如 avi、mp4、rmvb)的解析,并从中分离出未解码的 audio & video 数据、字幕流数据,等等。要知道目前已有的媒体文件格式有多少种了,如果只是研究 avi 的格式,已经足够我们忙了,我们的播放器要支持多种格式,怎么办?再看看音视频解码,这个估计是最有技术含量,最复杂的东西了,地球上能懂的人估计也不到 1%,因为实在太难了,光一个 jpeg 解码就够你研究一阵子。对于很多人,真正熟悉的,估计也就是音视频的渲染技术,说白了,就是音频播放技术和图像显示技术。
那么我们如何解决,读文件、解复用、解码,这三大问题呢?还好我们有开源的 ffmpeg,读文件用 avio api,解复用 avformat 的相关 api,解码用 avcodec 的相关 api。
avformat_open_input 这个函数可以打开一个媒体文件(或流媒体 url)
avformat_find_stream_info 这个函数可以查找媒体文件中的流信息,调用后我们就知道媒体中有多少音频流、视频流、字幕流,还能知道视频帧率、视频宽高、视频像素格式、音频采样率、音频声道数、视频压缩方式、音频压缩方式、... 等等各种你需要的信息。
avcodec_find_decoder avcodec_open2 avcodec_close 这三个函数,用于查找、打开、关闭,音视频解码器。
av_read_frame 用于从媒体文件读入 frame,实际上读入的是一个 AVPacket,而读入的 AVPacket 的类型可能是 audio、video 或 subtitle(字幕)。这一步其实就是解复用了(demux)。
了解以上 ffmpeg 的接口函数,我大致可以想象自己如何构建一个播放器了,首先 avformat_open_input 打开一个文件,然后 avformat_find_stream_info 获取各种信息,然后根据获取的信息查找并打开对应的 decoder(解码器),然后用 av_read_frame 从打开的文件不停的读取 AVPacket,如果是音频,就用对应的音频解码器去解码,如果是视频,就用对应的视频解码器解码。解码出来的音视频,就扔给渲染器渲染。对的,基本流程就这样。(收尾的工作,比如关闭文件、关闭解码器,等等反初始化的工作,我们暂且不考虑。)
-
是否需要多线程?
-
到底多少个线程是必须的?
-
线程间如何同步?
-
如何渲染音频
-
如何渲染视频
-
如何实现帧率控制
-
如何实现音视频同步
-
如何实现 seek 操作
-
...
这些细节,都将在我们后续的章节里面讲到。
fanplayer wiki