想要从事音视频相关的工作,FFMPEG是绕不开的一道坎,就像吉他里面的大横按,既然我能给征服大横按,相信我也能征服你,FFMPEG!!!
自学音视频相关的内容也有一段时间啦,比如之前的H264,RTMP,FLV,还有最近的AudioUint和AudioQueue,通过这段时间的学习,我总结出来的方法就是,先将他们的数据结构分析清楚,了解主要的api,然后从简单的api开始实践。
所以我们先来看一下FFMPEG的主要数据结构和api…
数据结构
文件读取相关
AVIOContext
AVIOContext是FFMPEG管理输入输出数据的结构体
比较重要的属性:
unsigned char *buffer:缓存读取的数据
int buffer_size:缓存大小(默认32768)
unsigned char *buf_ptr:当前指针读取到的位置
unsigned char *buf_end:缓存结束的位置
void *opaque:URLContext结构体
协议相关
URLContext、URLProtocol
|
|
URLContext结构体中还有一个结构体URLProtocol,每种协议(rtp,rtmp,file等)对应一个URLProtocol。
封装格式相关
AVFormatContext
这个结构体描述了一个媒体文件或媒体流的构成和基本信息
在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。
看几个主要变量的作用:
struct AVInputFormat *iformat:输入数据的封装格式
AVIOContext *pb:输入数据的缓存
unsigned int nb_streams:视音频流的个数
AVStream **streams:音视频流
char filename[1024]:文件名
int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata:元数据
AVInputFormat
作为输入容器,包含了输入文件的音视频流信息,程序从输入容器从读出音视频包进行解码处理
AVOutputFormat
作为输出容器,程序把编码好的音视频包写入到输出容器中
编解码相关
AVCodecContext
这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息
看一下关键属性:
|
|
AVCodec
AVCodec是存储编解码器信息的结构体,每一个编解码器对应一个该结构体。
下面说一下最主要的几个变量:
|
|
AVStream
AVStream是存储每一个视频/音频流信息的结构体
AVStream重要的变量如下所示:
|
|
AVPacket
AVPacket是存储压缩编码数据相关信息的结构体
重要的变量有以下几个:
|
|
AVFrame
AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。比如说,解码的时候存储了宏块类型表,QP表,运动矢量表等数据。编码的时候也存储了相关的数据。因此在使用FFMPEG进行码流分析的时候,AVFrame是一个很重要的结构体。
下面看几个主要变量的作用:
|
|
结构体之间的关系可以参考下图:
api
avcodec_init()
初始化libavcodec,一般最先调用该函数
该函数必须在调用libavcodec里的其它函数前调用,一般在程序启动或模块初始化时调用,如果你调用了多次也无所谓,因为后面的调用不会做任何事情.从函数的实现里你可以发现,代码中对多次调用进行了控制.
av_register_all()
初始化 libavformat和注册所有的muxers、demuxers和protocols,
一般在调用avcodec_init后调用该方法
其中会调用avcodec_register_all()注册多种音视频格式的编解码器,并注册各种文件的编解复用器
avformat_alloc_context()
分配一个AVFormatContext结构,负责申请一个AVFormatContext结构的内存,并进行简单初始化
avformat_open_input()
打开一个流媒体文件
avformat_close_input()
关闭一个流媒体文件
avformat_free_context()
释放一个AVFormatContext结构
使用 avformat_alloc_context()分配的结构,采用该函数进行释放,除释放AVFormatContext结构本身内存之外,AVFormatContext中指针所指向的内存也会一并释放
avio_alloc_context()
为I/0缓存申请并初始化一个AVIOContext结构,结束使用时必须使用av_free()进行释放
av_open_input_file()
以输入方式打开一个媒体文件,也即源文件,codecs并没有打开,只读取了文件的头信息.
av_close_input_file()
关闭使用avformat_close_input()打开的输入文件容器,但并不关系它的codecs
使用 av_close_input_file 关闭后,就不再需要使用avformat_free_context 进行释放了
av_find_stream_info(AVFormatContext *ic)
通过读取媒体文件的中的包来获取媒体文件中的流信息,对于没有头信息的文件如(mpeg)是非常有用的
AVCodec *avcodec_find_decoder(enum CodecID id)
通过code ID查找一个已经注册的音视频解码器
查找解码器之前,必须先调用av_register_all注册所有支持的解码器
音视频解码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较解码器的ID来查找
AVCodec avcodec_find_decoder_by_name (constchar name)
通过一个指定的名称查找一个已经注册的音视频解码器
查找解码器之前,必须先调用av_register_all注册所有支持的解码器
音视频解码器保存在一个链表中,查找过程中,函数从头到尾遍历链表,通过比较解码器的name来查找
avcodec_find_encoder()
avcodec_find_encoder_by_name()
同上。。。
int avcodec_open(AVCodecContext avctx, AVCodec codec) / avcodec_open2()
使用给定的AVCodec初始化AVCodecContext
av_guess_format()
|
|
返回一个已经注册的最合适的输出格式
void av_init_packet(AVPacket *pkt);
使用默认值初始化AVPacket
定义AVPacket对象后,请使用av_init_packet进行初始化
int av_read_frame(AVFormatContext s, AVPacket pkt)
从输入源文件容器中读取一个AVPacket数据包
该函数读出的包并不每次都是有效的,对于读出的包我们都应该进行相应的解码(视频解码/音频解码),
在返回值>=0时,循环调用该函数进行读取,循环调用之前请调用av_free_packet函数清理AVPacket
avcodec_decode_video2()
|
|
avcodec_decode_audio3()/avcodec_decode_audio4()
|
|