相關函數流程獲取音視頻具體信息:
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))一直讀取知道沒有可渲染的幀
}