1.本章节内容:
FFMPEG输出模块的最大作用是对音视频推流模块进行初始化让其能够正常工作起来,RV1126的码流通过FFMPEG进行推流,输出模块一般由几个步骤。分别由avformat_alloc_output_context2分配AVFormatContext 、avformat_new_stream初始化AVStream结构体、avcodec_find_encoder找出对应的codec编码器、利用avcodec_alloc_context3分配AVCodecCotext、设置AVCodecContext结构体参数、利用avcodec_parameters_from_context把codec参数传输到AVStream里面的参数、avio_open初始化FFMPEG的IO结构体、avformat_write_header初始化AVFormatContext
2.FFMPEG 输出配置的框图 :

2.1. 分配F FMPEG AVFormatContext 输出的上下文结构体指针

int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename)
第一个传输参数: AVFormatContext结构体指针的指针,是存储音视频封装格式中包含的信息的结构体,所有对文件的封装、编码都是从这个结构体开始。
第二个传输参数: AVOutputFormat的结构体指针,它主要存储复合流信息的常规配置,默认为设置NULL。
第三个传输参数: format_name指的是复合流的格式,比方说:flv、ts、mp4等等
第四个传输参数: filename是输出地址,输出地址可以是本地文件(如:xxx.mp4、xxx.ts等等)。也可以是网络流地址(如:rtmp://xxx.xxx.xxx.xxx:1935/live/01)
上面这个API是根据我们流媒体类型去分配AVFormatContext 结构体。我们传进来的类型会分为FLV_PROTOCOL 和TS_PROTOCOL ,具体如何配置如下面:
若 TS_PROTOCOL 类型 :avformat_alloc_output_context2(&group->oc, NULL, "mpegts ", group->url_addr);
若 FLV_PROTOCOL 类型 :avformat_alloc_output_context2(&group->oc, NULL, "flv" , group->url_addr);
注意:TS格式分别可以适配以下流媒体复合流,包括:SRT、UDP、TS本地文件等。flv格式包括:RTMP、FLV本地文件等等。
2 .2. 配置推流器编码参数和A VStream 结构体
A VS tream 主要是存储流信息结构体,这个流信息包含音频流和视频流。创建的API是avformat_new_stream , 如下图****:****

AVS tream * avformat _new_stream(AVFormatContext *s, AVDictionary **options);
第一个传输参数: AVFormatContext的结构体指针
第二个传输参数: AVDictionary结构体指针的指针
返回值: AVStream结构体指针
2.3. 设置对应的推流器编码器参数

AVCodec *avcodec_find_encoder(enum AVCodecID id); / /
第一个传输参数: 传递参数AVCodecID
2.4. 根据编码器ID分配 AVCodecContext 结构体

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
****第一个参数:****传递AVCodec结构体指针
avcodec_find_encoder 的主要作用是通过codec_id (编码器id )找到对应的AVCodec 结构体。在RV1126推流项目中c odec_id 我们使用两种,分别是AV_CODEC_ID_H264 、AV_CODEC_ID_H265 。 并利用a vcodec_alloc_context3 去创建AVCodecContext上下文。
初始化完A VS tream和编码上下文结构体之后,我们就需要对这些参数进行配置。重点:推流编码器参数和RV1126编码器的参数要完全一样,否则可能会出问题,具体的如下图:
1920 * 1080编码器和FFMPEG推流器的配置

1280* 720编码器和FFMPEG推流器的配置

FFMPEG的视频编码参数如:分辨率(WIDTH 、H EIGHT )、时间基(time_base )、 帧率(r_frame_rate )、GOP_SIZE等都需要和右边VENC的参数要一一对应起来。其中time_base的值要和视频帧率必须要一致。如RV1126高编码器分辨率是1920 * 1080,则FFMPEG推流器的WIDTH = 1920,HEIGHT = 1080;若RV1126编码器的分辨率是1280 * 720,则FFMPEG推流器的WIDTH = 1280,HEIGHT = 720;若RV1126的GOP的值是25,那右边FFMPEG的gop_size 也等于25;time_base的数值和帧率保持一致

AV_CODEC_FLAG_GLOBAL_HEADER ****:****发送视频数据的时候都会在关键帧前面添加SPS/PPS,这个标识符在FFMPEG初始化的时候都需要添加。
2.5. 设置完上述参数之后, 拷贝参数到AVStream编解码器 ,具体的操作如下:
拷贝参数到AVStream,我们封装到open_video自定义函数里面,要先调用avcodec_open2打开编码器,然后再调用avcodec_parameters_from_context把编码器参数传输到AVStream里面

2.5.1.int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
这个函数的具体作用是, 打开编解码器
****第一个参数:****AVCodecContext结构体指针
****第二个参数:****AVCodec结构体指针
****第三个参数:****AVDictionary二级指针
2.5.2. int avcodec_parameters_from_context(AVCodecParameters *par, const AVCodecContext *codec);
这个函数的具体作用是,把 AVCodecContext 的参数拷贝到 AVCodecParameters 里面。
****第一个参数:****AVCodecParameters结构体指针
****第二个参数:****AVCodecContext结构体指针
2.6 . 打开I O 文件操作

使用avio_open 打开对应的文件,注意这里的文件不仅是指本地的文件也指的是网络流媒体文件,下面是a vio_open的定义。

int avio_open(AVIOContext **s, const char *url, int flags);
****第一个参数:****AVIOContext的结构体指针,它主要是管理数据输入输出的结构体
第二个参数: url地址,这个URL地址既包括本地文件如(xxx.ts、xxx.mp4),也可以是网络流媒体地址,如(rtmp://192.168.22.22:1935/live/01)等
****第三个参数:****flags标识符
#define AVIO_FLAG_READ 1 /**< read-only */
#define AVIO_FLAG_WRITE 2 /**< write-only */
#define AVIO_FLAG_READ_WRITE (AVIO_FLAG_READ|AVIO_FLAG_WRITE) /**< read-write pseudo flag */
2.7 . avformat_write_header 对头部进行初始化 , 输出模块头部进行初始化

int avformat_write_header(AVFormatContext *s, AVDictionary **options);
第一个参数: 传递AVFormatContext结构体指针
第二个参数: 传递AVDictionary结构体指针的指针