目录
前言
对于FFmpeg
的学习,感觉非常有趣,但是好像随着我的理解,有些概念有点模糊了!
基本流程
流概念
ES
ES
(Elementary
(初级)Streams
(原始流))是直接从编码器出来的数据流,可以是编码过的视频数据流(H264
等),音频数据流(AAC
等),或其他编码数据流的统称。ES
流经过PES
打包器之后,被转换成PES
包。
ES
是只包含一种内容的数据流,如只含视频或只含音频等,打包之后的PES
也是只含一种性质的ES
,如只含视频ES
的PES
,只含音频ES
的PES
等。每个ES
都有若干个存取单元(AU
)组成,每个视频AU
或音频AU
都是由头部和编码数据两部分组成,1个AU
相当于编码的1幅视频图像或1个音频帧。PES
PES
(Packetized Elementary Streams
(分组的ES
)),ES
形成的分组称为PES
分组,是用来传递ES
的一种数据结构。PES
流是ES
流经过PES
打包器处理后形成的数据流,在这个过程中完成了将ES
流分组、打包、加入包头信息等操作(对ES
流的第一次打包)。PES
流的基本单位是PES
包。PES
包由包头和payload
组成。PTS、DTS
PTS-Presentation Time Stamp
(显示时间标记),表示显示单元出现在系统目标解码器(H264
,MJPEG
等)的时间
DTS-Decoding Time Stamp
(解码时间标记),表示将存取单元全部字节从解码缓存器移走的时间
PTS/DTS
是打在PES
包的包头里面的两个参数,是解决音视频同步显示,防止解码器输入缓存上溢或下溢的关键。每一个I
(关键帧)、P
(预测帧)、B
(双向预测帧)帧的包头都有一个PTS
和DTS
,但PTS
和DTS
对于B
帧不一样,无需标出B
帧的DTS
,对于I
帧和P
帧,显示前一定要存储于视频解码器的重新排序缓存器,经过延迟(重新排序)后再显示,所以一定要分别标明PTS
和DTS
。TS
TS
(transport Stream
(传输流))由定长的TS
包组成(188字节),而TS
包是对PES
包的一个重新封装(也就是相对ES来说,封装了两次)。PES
包的包头信息依然存在于TS
包中。
TS
流和PS
流的区别在于TS
流的包结构是固定长度的,而PS
流的包结构是可变长度的。PS
包由于长度是变化的,一旦丢失某一PS
包的同步信息,接收机就会进入失步状态,从而导致严重的信息丢失事件。而TS
流由于采用固定长度的包结构,当传输误码破坏了某一TS
包的同步信息,接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免信息丢失。因此在信道环境较为恶劣、传输误码较高时一般采用TS
流,而在信道环境较好、传输误码较低时一般采用PS
码流。TS
单一码流、混合码流
单一性:TS
流的基本组成是长度为188字节的TS
包。
混合性:TS
流由多种数据组合而成,一个TS
包中的数据可以是视频数据,音频数据,填充数据,PSI/SI
表格数据等(唯一的PID
对应)。
FFmpeg
结构体和函数
FFmpeg
结构体
FFmpeg
函数
av_read_frame
av_read_frame
的作用是读取码流中的音频的若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用av_read_frame()
获得一帧视频的压缩数据,然后才能对该数据进行解码。av_packet_rescale_ts
转换时基,ffmpeg
存在多个时间基准,对应不同阶段(结构体),每个time_base
具体的值不一样。av_interleaved_write_frame
写一个packet
到输出媒体文件,确保正确的间隔。在有多个流的情况下要用av_interleaved_write_frame
,只有单一流2个函数都可以用。
ffmpeg源码跟踪笔记之av_write_frame 与 av_interleaved_write_frameavformat_alloc_output_context2
// avformat_alloc_output_context2可以初始化一个用于输出的AVFormatContext结构体。
int avformat_alloc_output_context2(AVFormatContext **ctx,
AVOutputFormat *oformat, const char *format_name, const char *filename);
/*
参数1:ctx 函数调用成功之后创建的AVFormatContext结构体
参数2:oformat 指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename),有FFmpeg自动确定输出格式。
参数3:format_name 指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,"mkv"等。
参数4:file_name 指定输出文件的名称。根据文件名称,FFmpeg会推出输出格式。文件名称可以是“xxx.flv”,"yy.mkv"
*/
// 例如:
avformat_alloc_output_context2(&outputContext, nullptr, "mpegts",
outputUrl.c_str());
这里设置的输出格式是mpegts,输出文件可以是本地文件也可以是网络文件
avio_open2
// 功能:avio_open2用于打开FFmpeg的输入输出文件。
int avio_open2(AVIOContext **s, const char *url, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options);
/*
s:函数调用成功之后创建的AVIOContext结构体
url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
flags:打开地址的方式。可以选择只读,只写,或者读写取值如下:
AVIO_FLAG_READ:只读。
AVIO_FLAG_WRITE:只写。
AVIO_FLAG_READ_WRITE:读写
*/
avformat_new_stream
avformat_new_stream
在AVFormatContext
中创建Stream
通道。之后,我们可以自行设置AVstream
的一些参数信息。例如:codec_id
,format
,bit_rate
,width
,height
等
ffmpeg——avformat_new_stream创建流通道avcodec_copy_context
avcodec_copy_context
可以将输入视频/音频的参数拷贝至输出视频/音频的AVCodecContext
结构体。avformat_write_header
// 功能:avformat_write_header用于写视频文件头
int avformat_write_header(AVFormatContext *s, AVDictionary **options);
// 参数1:s 用于输出的AVFormatContext
// 参数2:options 额外的选项 一般为NULL
avcodec_close
avcodec_close
用于关闭编码器avformat_close_input
avformat_close_input
用于关闭一个AVFormatContext
。
总结
以上的许多FFmpeg的概念仍然不是很清楚,但是简单说,这些函数是为了实现网络流的转发。根据我的理解,网络流的转发过程如下:
1) 打开输入文件,比如本地文件或者网络文件
2) 打开输出文件,比如本地文件或者网络文件
3) 打开输出文件之后,建立输入到输出的流通道,并且发送头信息
4) 读取输入文件的流信息,通过建立好的流通道转发到输出文件
因为博主对于视音频的编解码还有许多不懂的地方,如果错误,请指出,谢谢指教!