先简单的了解一下FFmpeg 使用过程中需要用到的FFmpeg库、播放与解码流程、函数和相关的结构体。
FFmpeg 库简介
库 | 介绍 |
avcodec | 音视频编解码核心库 |
avformat | 音视频容器格式的封装和解析 |
avutil | 核心工具库 |
swscal | 图像格式转换模块 |
swresampel | 音频重采样 |
avfilter | 音视频滤镜库,例如视频添加水印、音频变声 |
avdevice | 输入输出设备库,提供设备数据的输入与输出 |
FFmpeg 就是依靠以上几个库,实现了强大的音视频编码、解码、编辑、转换、采集等能力。
FFmpeg 播放流程
通常情况下,视频文件如MP4、MKV、FLV 等都属于封装格式,也就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。当我们播放一个多媒体文件时,通常要经过一下几个步骤:
由上图可以看到这个视频播放主要涉及到以下内容:
- 解封装(Demuxing):就是将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。例如FLV 格式的数据,经过解封装操作后,输出 H.264编码的视频码流和 AAC 编码的音频码流。
- 软硬解码(Decode):就是将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。通过解码,将压缩编码的视频数据 H.264、MPEG2 解码成为非压缩的颜色数据,例如YUV 等等;将压缩编码的音频数据AAC、MP3解码成为非压缩的音频抽样数据,例如PCM 数据。解码分为硬编码和软编码。
- 像素格式转换:将YUV数据格式转换成RGB 数据格式。
- 重采样:对音频重新采样。
- dts/pts: dts 是解码的时间戳,而pts 是显示的时间戳。pts 用于获取当前的播放进度。进度条的移动需要用到 av_seek_frame 函数。
- 音视频同步:就是根据解封装模块处理过程中获取到的参数信息,同步解码出来的音频和视频数据,并将音视频数据送到系统的显卡和声卡播放出来(Render)。
FFmpeg解码流程
1、 av_register_all()意思为注册FFmpeg 的所有组件,在4.0 版本以后已经弃用;不加也是可以正常编码音视频的。2、avformat_alloc_context()用于初始化 AVFormatContext 对象,必须初始化为NULL或者用avformat_alloc_context() 进行初始化。
3、avformat_open_input()打开媒体文件,并获得解封装的上下文。4、avformat_find_streeam_info()探测获取流信息,例如探测文件的格式、编码、宽高、总时长等。5、avcodec_find_decoder()查找解码器,参数是所要用解码器的ID,成功返回解码器。6、avcodec_open2()用于初始化一个音视频编解码器的AVCodecContext ,声明位于libavcodecutils.c。7、av_read_frame()读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用av_read_frame() 获得一帧视频的压缩数据,然后才能对该数据进行解码。8、avcodec_decode_video2()解码一帧视频数据。输入一个压缩编码的结构体 AVPacket,输出一个解码后的结构体 AVFrame。9、avformat_close_input()关闭释放解封装上下文,并且设置为 0。
使用到的FFmpeg结构体说明
这些结构体之间的对应关系如下所示:
1、AVFormatContext
解封装上下文,是存储音视频封装格式包含信息的结构体。
char filename[1024] // 保存打开的文件名,一般用在 rtsp、rtmp 断开重连
unsigned int nb_streams // 音视频流的个数
AVStream **streams // 存储视频流、音频流、字幕流信息
int64_t duration // 媒体文件的总时长,单位是把 1 秒切成 AV_TIME_BASE(1000000)份,即单位。为 us,注意不一定每个视频都能获取到 duration
int64_t bit_rate // 比特率(单位bps,转换为kbps需要除以1000)
2、AVStream
AVStream 是存储每一个音频/视频流信息的结构体。
int index // 标识该视频/音频流
AVCodecContext *codec // 解码器,4.0 版本后已弃用
AVRational time_base // 时基。通过该值可以把PTS,DTS转化为实际的时间(单位为秒s)
int64_t duration // 该视频/音频流时长,单位为 ms
AVRational avg_frame_rate // 帧率(注:对视频来说,这个挺重要的)
AVPacket attached_pic // 附带的图片。比如说一些 MP3,AAC 音频文件附带的专辑封面
AVCodecParameters *codecpar // 音视频参数,新增用来替换AVCodecContext *codec
3、AVCodecContext/ AVCodecParameters
AVCodecContext 是一个描述编解码器上下文的结构体,包含了众多编解码器需要的参数信息。
enum AVMediaType codec_type // 编解码器的类型(视频,音频...)
struct AVCodec *codec // 采用的解码器AVCodec(H.264,MPEG2...)
enum AVCodecID codec_id // 标示特定的编解码器(H.264,MPEG2...)
int format // 视频像素格式/音频采样数据格式
int width, height // 表示视频的宽和高
int bit_rate // 平均比特率
int channels // 声道数(音频)
uint64_t channel_layout // 声道格式
int sample_rate // 采样率(音频)
AVRational time_base; // 时基。通过该值可以把PTS,DTS转化为实际的时间(单位为秒s)
uint8_t *extradata; int extradata_size; // 针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
4、AVCodec
AVCodec 是存储编码器信息的机构体。
const char *name; // 编解码器的名字的简称
const char *long_name; // 编解码器名字的全称
enum AVMediaType type; // 指明了类型,是视频,音频,还是字幕
enum AVCodecID id; // ID,不重复
const AVRational *supported_framerates; // 支持的帧率(仅视频)
const enum AVPixelFormat *pix_fmts; // 支持的像素格式(仅视频),如RGB24、YUV420P等。
const int *supported_samplerates; // 支持的采样率(仅音频)
const enum AVSampleFormat *sample_fmts; // 支持的采样格式(仅音频)
const uint64_t *channel_layouts; // 支持的声道数(仅音频)
int priv_data_size; // 私有数据的大小
5、AVPacket
AVPacket 是存储压缩编码数据相关信息的结构体。
uint8_t *data; // 压缩编码的数据。
/* 例如对于H.264来说。1个AVPacket的data通常对应一个NAL。
注意:在这里只是对应,而不是一模一样。他们之间有微小的差别:使用FFMPEG类库分离出多媒体文件中的H.264码流。因此在使用FFMPEG进行音视频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到音视频的码流文件。*/
int size; // data的大小
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int stream_index; // 标识该AVPacket所属的视频/音频流。
6、AVFrame
AVFrame 结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是 YUV,RGB,对音频来说是 PCM),此外还包含了一些相关的信息。比如,解码的时候存储了宏块类型表,QP 表,运动矢量表等数据。编码的时候也存储了相关的数据。因此在使用 FFmpeg 进行码流分析的时候,AVFrame 是一个很重要的结构体。
uint8_t *data[AV_NUM_DATA_POINTERS]; // 解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)
int linesize[AV_NUM_DATA_POINTERS]; // data中“一行”数据的大小。注意:未必等于图像的宽,一般大于图像的宽。
int width, height; // 视频帧宽和高(1920x1080,1280x720...)
int nb_samples; // 音频的一个AVFrame中可能包含多个音频帧,在此标记包含了几个
int format; // 解码后原始数据类型(YUV420,YUV422,RGB24...)
int key_frame; // 是否是关键帧
enum AVPictureType pict_type; // 帧类型(I,B,P...)
AVRational sample_aspect_ratio; // 宽高比(16:9,4:3...)
int64_t pts; // 显示时间戳
int coded_picture_number; // 编码帧序号
int display_picture_number; // 显示帧序号
解协议
在上面提到的解封装之前还有一个过程就是解协议,就是将流媒体协议的数据,解析为标准的响应的封装数据的格式。通常音视频在网络上播放的时候,常常会采用各种流媒体协议,例如HTTP、RTMP或者MMS等等。这些协议再传输音视频数据的同时,也会传输一些指令数据,这些指令数据就包括对播放器的控制(播放、暂停、停止),或者对网络状态的描述等。解协议的过程中会去除掉指令数据而只保留音视频数据。比如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。
常用的流媒体协议
流媒体协议是服务器与客户端之间通信遵循的规定。当前网络上主要的流媒体协议如下表所示:
名称 | 推出机构 | 传输层协议 | 客户端 | 使用领域 |
RTSP+RTP | IETF | TCP+UDP | VLC、WMP | IPTV |
RTMP | Adobe.Inc. | TCP | Flash | 互联网直播 |
RTMFP | Adobe.Inc. | UDP | Flash | 互联网直播 |
MMS | Microsoft Inc. | TCP/UDP | WMP | 互联网直播+点播 |
HTTP | WWW+IETF | TCP | Flash | 互联网直播 |
RTSP+RTP 经常用于 IPTV 领域。因为其采用 UDP 传输视音频,支持组播,效率较高。但其缺点是网络不好的情况下可能会丢包,影响视频观看质量。因为互联网网络环境的不稳定性,RTSP+RTP 较少用于互联网视音频传输。互联网视频服务通常采用 TCP 作为其流媒体的传输层协议,因而像 RTMP,MMS,HTTP 这类的协议广泛用于互联网视音频服务之中。这类协议不会发生丢包,因而保证了视频的质量,但是传输的效率会相对低一些。此外 RTMFP 是一种比较新的流媒体协议,特点是支持 P2P。
常用的封装格式
封装格式的主要作用就是把视频码流和音频码流按照一定的格式存储在一个文件中。当今比较流行的封装格式如下表所示:
名称 | 推出机构 | 流媒体 | 支持的视频编码 | 支持的音频编码 | 使用领域 |
AVI | Microsoft Inc. | 不支持 | 所有格式 | 所有格式 | BT下载影视 |
MP4 | MPEG | 支持 | MPEG-2,MPEG-4,H.264,H.263等 | AAC,MPEG-1 Layers I, II, III, AC-3等 | 互联网视频网站 |
TS | MPEG | 支持 | MPEG-1,MPEG-2,MPEG-4,H.264 | AAC,MPEG-1 Layers I, II, III, | IPTV,数字电视 |
FLV | Adobe Inc. | 支持 | Sorenson, VP6, H.264 | MP3, ADPCM, Linear PCM, AAC等 | 互联网视频网站 |
MKV | CoreCodec Inc. | 支持 | 所有格式 | 所有格式 | 互联网视频网站 |
RMVB | Real Networks Inc. | 支持 | RealVideo 8, 9, 10 | AAC, Cook Codec, RealAudio Lossless | BT下载影视 |
由上表可见,除了 AVI 之外,其他封装格式都支持流媒体,即可以 “边下边播”。有些格式更 “万能” 一些,支持的视音频编码标准多一些,比如 MKV。而有些格式则支持的相对比较少,比如说 RMVB。
作者:倪尛
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。