ffmpeg视频解码相关接口以及播放视频流程解析

相关函数流程获取音视频具体信息:

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))一直读取知道没有可渲染的帧
}

一个简单的播放器地址:

https://github.com/gt19930910/ffmpegSimpleVideoPlayer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章