相关函数流程获取音视频具体信息:
1、avformat_alloc_context(); 负责申请一个AVFormatContext结构的内存,并进行简单初始化
2、avformat_open_input(); 打开输入视频文件
3、avformat_find_stream_info(); 获取视频文件信息,这边找到的信息传输到AVStream里面去
4、av_read_frame(); 读取音视频包 avformat_seek_file(); 定位文件 av_seek_frame(); 定位文件
这里读取到的是AVPacket结构体
5、avformat_close_input(); 关闭解复用器会调用avformat_free_context();
avformat_free_context(); 释放什么的结构体
之后进行解码:
1、avcodec_alloc_context3(); 分配解码器上下文
2、发送上面avformat获取到的音视频信息输入到上下文
3、avcodec_find_decoder(); (通过id寻找,id一样找到的是首先注册的编解码器) avcodec_find_decoder_by_nam(); 能根据指定的名字找到编解码器
两种找法,有不同厂家的编码器——1、X264 2、open264,可以通过id和name寻找,id是一样的name不一样
4、avcodec_open2(); 打开编解码器
5、avcodec_parameters_to_context()——>将参数传输到AVCodecContext结构体中
6、avcodec_decode_video2(); avcodec_decode_audio2(); 解码一帧数据,这两个接口基本上已经抛弃了,使用下面的接口
7、avcodec_send_packet(); 发送需要编码数据包 avcodec_receive_packet(); 接收解码后数据
8、avcodec_free_context(); 释放上下文
9、avcodec_close(); 关闭解码器
相关结构体:
AVFormatContext保存了音视频文件封装格式相关信息,有几路流就会分配几路AVStream
AVInputFormat demuxer每种格式封装对应一个结构体包括FLV MP4 AVI MKV。。。
AVOutputFormat muxer这里相反
AVStream 对应每一路流(一般两路,音视频,字母也算一路),每一路码流都会有一个索引,里面有一个PTS,PTS*time_base等于真正的时间
PTS显示时间戳 DTS解码时间戳主要用前面一个
AVCodecContext 编解码器上下文结构体保存了编解码相关信息
AVCodec对应每一种音视频编解码器(h264解码器)
AVPacket存储一帧压缩编码数据
这里面有index会和AVStream里面的index进行对比判断这个包是属于哪一路码流,放入一个相应的解码器
AVFrame存储一帧解码后数据
数据, 类型等等,pts显示时间戳
视频解码播放流程
pFormatCtx = avformat_alloc_context();
首先给上下文分配内存
ret = avformat_open_input(&pFormatCtx, filePath, NULL, NULL);
之后打开媒体文件,这里面应该会将媒体格式信息存储在上下文中
av_dump_format(pFormatCtx, 0, filePath, 0);
这里可以打印出媒体格式的详细信息
ret = avformat_find_stream_info(pFormatCtx, NULL);
在上下文中寻找视频流信息
videoindex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
这里是找到视频流的index标志号
avStream = pFormatCtx->streams[videoindex];
设置avstream这个东西
g_frame_rate = avStream->avg_frame_rate.num / avStream->avg_frame_rate.den;
这里可以设置一下帧率
pCodecCtx = avcodec_alloc_context3(NULL);
这里打开解码器上下文
ret = avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
将媒体信息的参数传入解码器上下文
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
在解码器上下文中找到解码器
ret = avcodec_open2(pCodecCtx, pCodec, NULL);
打开解码器
screen_w = pCodecCtx->width;
这里可以从解码器上下文中获取到解码视频的宽
screen_h = pCodecCtx->height;
这里可以从解码器上下文中获取到解码视频的高可以给渲染窗口用
packet = av_packet_alloc();
av_init_packet(packet);
分配一下视频一帧的压缩包数据结构,并初始化
pFrame = av_frame_alloc();
分配解码后的一帧数据的数据结构
while(1)
{
//这里是不断读取需要解码的包送到解码器里面
ret = av_read_frame(pFormatCtx, packet); 这里while进行循环读取
1、ret < 0 没有包可以读,可以结束循环
2、ret == 0 && packet->stream_index != videoindex 这里表示没有读取到视频流
3、ret == 0 {
这里表示正常的视频包
avcodec_send_packet(pCodecCtx, packet); 这里发送要解码的数据到解码器上下文中
}else
{
av_packet_unref(packet); // 如果还占用内存则释放
ret = avcodec_send_packet(pCodecCtx, packet);这里如果不是0的话需要刷一个空包进去
}
//这里是读取解码器里面需要解码的包进行解码
do{
ret = avcodec_receive_frame(pCodecCtx, pFrame);
1、ret == 0 表示成功,可以从pFrame中读取到解码帧数据送到渲染器渲染
2、ret == AVERROR(EAGAIN) 说明没有可读的解完的帧数据
3、ret == AVERROR_EOF 说明没有帧了
avcodec_flush_buffers(pCodecCtx);
// YUV 1920*1080*1.5*5个buffer 这里需要做一个刷新
}while(ret != AVERROR(EAGAIN))一直读取知道没有可渲染的帧
}