网站建设阿里云搭建个人网站,代理公司注册网站,自建设网站,海外免费云服务器前言 从本章开始我们将要学习嵌入式音视频的学习了 #xff0c;使用的瑞芯微的开发板 #x1f3ac; 个人主页#xff1a;ChenPi #x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ #x1f525; 推荐专栏2: 《Linux C应用编程#xff08;概念类#xff09;_C… 前言 从本章开始我们将要学习嵌入式音视频的学习了 使用的瑞芯微的开发板 个人主页ChenPi 推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ 推荐专栏2: 《Linux C应用编程概念类_ChenPi的博客-CSDN博客》✨✨✨ 推荐专栏3《嵌入式音视频_ChenPi的博客-CSDN博客》 本篇简介 本章主要讲解ffmpeg模块输出参数的初始化 今天讲解的部分就是继上章未讲完的部分继续讲解
首先我们看上图红框里面的函数函数内部就是实现ffmpeg初始化合成复合流以及推流
里面内容比价多我可能分为三章去分别讲解今天我们先讲一下ffmpeg的参数初始化
01 ffmpeg输出模块结构体
在FFMPEG输出模块中用到了很多个结构体分别是AVFormatContext、AVStream、AVCodec、AVCodecContext、AVIOContext。 AVFormatContext是存储音视频封装格式中包含的信息的结构体它是FFMPEG中的核心所有的文件封装、编码都是从它开始 AVStreamAVStream主要存储了音频流、视频流的具体信息 AVCodecAVCodec是存储编解码信息的结构体 AVCodecContextAVCodecContext 是编解码上下文它主要的功能是除了存储AVCodec的信息外还保存了额外的编码属性AVIOContext其中AVIOContext是FFMPEG管理输入输出数据的结构体 02 ffmpeg初始化代码分析 这个代我们要我们要先从这个结构体开始看
这个结构体会有些许复杂
typedef struct
{AVStream *stream; /*主要储存了音视频流的具体信息*/AVCodecContext *enc; //编解码上下文它主要的功能是除了存储 AVCodec 的信息外还保存了额外的编码属性int64_t next_timestamp; //int samples_count;AVPacket *packet; //压缩后的数据和关于这些数据的一些附加的信息如显示时间戳pts解码时间戳dts,数据时长duration所在流媒体的索引stream_index等等。
} OutputStream;typedef struct
{unsigned int config_id;int protocol_type; //流媒体TYPEchar network_addr[NETWORK_ADDR_LENGTH];//流媒体地址enum AVCodecID video_codec; //视频编码器IDenum AVCodecID audio_codec; //音频编码器IDOutputStream video_stream; //VIDEO的STREAM配置OutputStream audio_stream; //AUDIO的STREAM配置AVFormatContext *oc; //是存储音视频封装格式中包含的信息的结构体也是FFmpeg中统领全局的结构体对文件的封装、编码操作从这里开始。
} RKMEDIA_FFMPEG_CONFIG; //FFMPEG配置 这个结构体很重要这里面保存着配置相关的所有信息
其中最重要的结构体是AVFormatContext里面存储音视频封装格式中包含的信息的结构体
也是FFmpeg中统领全局的结构体对文件的封装、编码操作从这里开始。 将一些简单的参数配置完后我们看一下如何初始化FFmpeg ffmpeg输出模块初始化大概就是这八步我们一步一步解析
2.1 根据需要配置码流属性avformat_alloc_output_context2() 函数原型
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) 2.2配置推流器编码参数和AVStream结构体
AVStream主要是存储流信息结构体这个流信息包含音频流和视频流。
创建的API是avformat_new_stream如下 函数原型
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) 参数1AVFormatContext的结构体指针参数2AVDictionary结构体指针的指针返回值成功返回AVStream结构体指针失败返回NULL 创建输出码流AVStream, AVStream是存储每一个视频/音频流信息的结构体 2.3找到编码器avcodec_find_encoder
找到对应的编码ID
函数原型
AVCodec *avcodec_find_encoder(AVCodecID id) 参数1请求编码器的编号返回值返回查找到的解码器没有找到就返回NULL 2.4 avcodec_alloc_context3分配AVCodecContext结构体
函数原型
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) 功能 分配一个AVCodecContext并将其字段设置为默认值。结果由avcodec_free_context()释放 参数 如果非null分配私有数据并初始化给定编解码器的默认值。使用不同的编解码器调用avcodec_open2()是非法的。如果为NULL那么特定于编解码器的默认值将不会被初始化这可能会导致默认设置不理想这对于编码器例如libx264尤为重要。 返回值 一个用默认值填充的AVCodecContext失败时为NULL。 2.5 设置AVCodecContext的参数 FFMPEG的视频编码参数如分辨率(WIDTH、HEIGHT)、时间基(time_base)、 帧率(r_frame_rate)、GOP_SIZE等都需要和右边VENC的参数要一一对应起来。其中time_base的值要和视频帧率必须要一致。 FFMPEG的音频编码参数如音频采样格式(sample_fmt)、音频码率(bit_rate)、 音频采样率(sample_rate)、 音频布局(channel_layout)、 音频通道(channels)、 时间基(time_base)等都需要和右边AENC的参数一一对应。其中time_base的值必须要和音频采样率要一致。 AV_CODEC_FLAG_GLOBAL_HEADER发送视频数据的时候都会在关键帧前面添加SPS/PPS这个标识符在FFMPEG初始化的时候都需要添加。 2.6 使能推流编码器 这里的open_video、open_audio就是使能推流编码器其中avcodec_open2让编码器和编码器上下文进行关联。并用avcodec_parameters_from_context把刚初始化的上下文传输到AVStream的编解码器。 2.7打开IO文件操作 使用avio_open打开对应的文件注意这里的文件不仅是指本地的文件也指的是网络流媒体文件下面是avio_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.8 avformat_write_header对头部进行初始化 int avformat_write_header(AVFormatContext *s, AVDictionary **options); 第一个参数传递AVFormatContext结构体指针第二个参数传递AVDictionary结构体指针的指针