1.获取AVFormatContext音视频格式上下文
根据后面的几个参数分配AVFormatContext(格式上下文)
参数1:AVFormatContext **ctx分配返回的地址
参数2:AVOutputFormat *oformat如果提供该参数则通过该参数分配结构体,如果是NULL则使用 const char *format_name, const char *filename分配结构体
参数3: const char *format_name音视频流的格式通常有flv与mpegts(ts)两种格式
参数4: const char *filename输出文件名称,这里分为输出到本地比如输出为ikun.ts ikun.flv文件,也可以输出到网络传输比如我们推流rtmp://192.168.1.21/live/mystream
返回值:函数返回一个整数值,用于指示操作的成功与否。如果成功,返回值大于或等于0;如果失败,返回一个负数的AVERROR
错误码。
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,
const char *format_name, const char *filename);
2.获取encoder编码器
这个api比较简单,就是根据enum AVCodecID id获取对应的编码器
如果成功则返回编码器地址,反之返回NULL
AVCodec *avcodec_find_encoder(enum AVCodecID id);
去库中可以查看AVCodecID的具体内容,其枚举了很多:
3.添加新的流AVStream
添加一个新的流,这个流可以是音频、视频、字幕流
参数1:AVFormatContext音视频上下文,这里是我要把流添加到哪个上下文
参数2:添加流的编码器,通过这个编码器找添加什么样的流(如果这里的编码器设置为NULL的时候不会设定编码器上下文的参数,需要后面再次设定编码器上下文的信息)
成功后返回流地址,反之返回NULL
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
4.分配编码器上下文
通过编码器const AVCodec *codec来分配编码器上下文,如果我们在上面添加流时候没有设置第二个参数,那么我们需要在这里手动配置流中的编码器上下文
成功返回编码器上下文地址,反之返回NULL
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
5.关联编码器和编码器上下文
初始化一个编解码器上下文(AVCodecContext
),使其准备好进行编码或解码操作。
通常编码器上下文会配置音频、视频的一些参数(采样率帧数等),如果我们绑定上下文和编码器,那么相当于告诉上下文以后的编码工作由指定的编码器来做
参数1:编码器上下文(配置参数)
参数2:编码器(find的)
参数3:可以填NULL
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
6.分配多媒体数据包AVPacket
类似于malloc,这里我们如何理解包和流的关系呢?
我认为包是一个处理音视频数据的基本元,比如我们接收一帧的数据是接收到一个packet即一个包,然后把包写入到流中,流相当于是很多个包,通过av_interleaved_write_frame
函数将包写入到流中
AVPacket *av_packet_alloc(void);
7.编码器上下文参数复制
如果配置好了上下文codec,但是并不是我们实际的编码器上下文使用该函数复制类似于mmcpy
int avcodec_parameters_from_context(AVCodecParameters *par,
const AVCodecContext *codec);
把codec的参数复制到par
使用实例如下: avcodec_parameters_from_context(videostream->codecpar, c);
即把配置好的编码器上下文参数复制到videostream->codecpar,这里videostream是AVStream类型。
8.输入输出日志打印
用于打印关于输入或输出格式的详细信息。这包括持续时间、比特率、流、容器格式、程序、元数据、边数据、编解码器和时间基准等信息。该函数通常用于调试或当用户需要详细了解媒体文件的情况。
参数1:打印哪个音视频上下文信息
参数2:打印什么信息-1是所有信息
参数3:音视频上下文设置的地址,即本地存储or网络传输
参数4:这个音视频上下文是输入还是输出,输入(0)还是输出(1)
void av_dump_format(AVFormatContext *ic,int index, const char *url,int is_output);
9.初始化配置
当前面的编码器、编码器上下文、等都配置好后使用该函数对整体进行初始化
参数1:初始化的音视频上下文
参数2:选项,可以填NULL
int avformat_write_header(AVFormatContext *s, AVDictionary **options);
实战合成音视频流程中,新建的音视频流的时间基准在这个函数后变为合成流时间基准
---------------------------上面的是一系列的配置函数,下面是实际的功能函数---------------------------------
10.时间戳比较
参数1:a的时间戳
参数2:a的时间基准
参数3:b的时间戳
参数4:b的时间基准
时间戳×时间基就是时间
返回值:-1,a在b前 ;0相同;1,a在b后
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);
11.变换时间戳
参数1:变换谁的时间戳
参数2:源时间戳
参数3:目标时间戳
void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
通常pkt的时间戳是音频or视频的,然后我们把其原时间戳转化为合成流时间戳
12.把处理好的包放入合成流
这一步是最重要的,是把pkt(转换好时间基的包)放入oc,oc就是我们合成流的上下文
参数1:写到哪?
参数2:写谁?
这个函数的好处是会交替的写
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
-----------------------------------------一些出口函数--------------------------------------------------------------
与linux驱动内容一样,我们定义了很多入口函数(初始化函数),那我们需要定义对应的出口函数(释放资源),避免内存的泄露
这些函数就不一一介绍了:
编码器上下文释放、buf释放、packet释放、音视频上下文释放、等等......