1. 视频播放器基本原理
下图引用自“雷霄骅,视音频编解码技术零基础学习方法”,因原图太小,看不太清楚,故重新制作了一张图片。
如下内容引用自“雷霄骅,视音频编解码技术零基础学习方法”:
解协议 将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。 解封装 将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。 解码 将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。 音视频同步 根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。
2. 简易播放器的实现-音视频播放
2.1 实验平台
实验平台:openSUSE Leap 42.3
FFmpeg版本:4.1
SDL版本:2.0.9
FFmpeg开发环境搭建可参考“FFmpeg开发环境构建”
2.2 源码清单
代码已经变得挺长了,不贴完整源码了,源码参考:
https://github.com/leichn/exercises/blob/master/source/ffmpeg/player_avideo/ffplayer.c
源码清单中涉及的一些概念简述如下:
container:
对应数据结构AVFormatContext
封装器,将流数据封装为指定格式的文件,文件格式如AVI、MP4等。
FFmpeg可识别五种流类型:视频video(v)、音频audio(a)、attachment(t)、数据data(d)、字幕subtitle。
codec:
对应数据结构AVCodec
编解码器。编码器将未压缩的原始图像或音频数据编码为压缩数据。解码器与之相反。
codec context:
对应数据结构AVCodecContext
编解码器上下文。此为非常重要的一个数据结构,后文分析。各API大量使用AVCodecContext来引用编解码器。
codec par:
对应数据结构AVCodecParameters
编解码器参数。新版本增加的字段。新版本建议使用AVStream->codepar替代AVStream->codec。
packet:
对应数据结构AVPacket
经过编码的数据。通过av_read_frame()从媒体文件中获取得到的一个packet可能包含多个(整数个)音频帧或单个
视频帧,或者其他类型的流数据。
frame:
对应数据结构AVFrame
解码后的原始数据。解码器将packet解码后生成frame。
plane:
如YUV有Y、U、V三个plane,RGB有R、G、B三个plane
slice:
图像中一片连续的行,必须是连续的,顺序由顶部到底部或由底部到顶部
stride/pitch:
一行图像所占的字节数,Stride = BytesPerPixel × Width,x字节对齐待确认
sdl window:
对应数据结构SDL_Window
播放视频时弹出的窗口。在SDL1.x版本中,只可以创建一个窗口。在SDL2.0版本中,可以创建多个窗口。
sdl texture:
对应数据结构SDL_Texture
一个SDL_Texture对应一帧解码后的图像数据。
sdl renderer:
对应数据结构SDL_Renderer
渲染器。将SDL_Texture渲染至SDL_Window。
sdl rect:
对应数据结构SDL_Rect
SDL_Rect用于确定SDL_Texture显示的位置。一个SDL_Window上可以显示多个SDL_Rect。这样可以实现同一窗口的分屏显示。
2.3 源码流程分析
参考如下:
2.4 解复用线程
解复用线程就是main()函数所在的主线程。main()函数作一些必要的初始化工作后,创建音频处理线程和视频处理线程。
然后main()函数进入主循环,从输入文件中读取packet,并根据packet类型,将之放入视频packet队列或音频packet队列。
2.5 音频处理线程
音频处理线程是SDL库内建线程。用户提供回调函数供音频处理线程调用。实现过程参考:
“FFmpeg简易播放器的实现-音频播放”
2.6 视频处理线程
视频处理线程实现视频解码及播放。实现过程参考:
“FFmpeg简易播放器的实现-视频播放”
3. 编译与验证
3.1 编译
代码语言:javascript复制gcc -o ffplayer ffplayer.c -lavutil -lavformat -lavcodec -lavutil -lswscale -lswresample -lSDL2
3.2 验证
选用clock.avi测试文件,测试文件下载:clock.avi
查看视频文件格式信息:
代码语言:javascript复制ffprobe clock.avi
打印视频文件信息如下:
代码语言:javascript复制[avi @ 0x9286c0] non-interleaved AVI
Input #0, avi, from 'clock.avi':
Duration: 00:00:12.00, start: 0.000000, bitrate: 42 kb/s
Stream #0:0: Video: msrle ([1][0][0][0] / 0x0001), pal8, 320x320, 1 fps, 1 tbr, 1 tbn, 1 tbc
Stream #0:1: Audio: truespeech ([34][0][0][0] / 0x0022), 8000 Hz, mono, s16, 8 kb/s
运行测试命令:
代码语言:javascript复制./ffplayer clock.avi
可以听到每隔1秒播放一次“嘀”声,声音播放12次。时针每隔1秒跳动一格,跳动12次。声音播放正常,画面播放也正常,但是声音和画面不能对应,因为没有考虑音视频同步。下一次实验研究音视频同步问题。
4. 参考资料
1 雷霄骅,视音频编解码技术零基础学习方法
2 雷霄骅,最简单的基于FFMPEG SDL的视频播放器ver2(采用SDL2.0)
3 SDL WIKI, https://wiki.libsdl.org/
4 Martin Bohme, An ffmpeg and SDL Tutorial, Tutorial 03: Playing Sound
5. 修改记录
2018-12-06 V1.0 初稿