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

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