西安網(wǎng)站開發(fā)哪家好教育機構(gòu)加盟
1 前言
上一篇文章<FFmpeg下載安裝及Windows開發(fā)環(huán)境設(shè)置>介紹了FFmpeg的下載安裝及環(huán)境配置,本文介紹最簡單的FFmpeg視頻解碼示例。
2 視頻解碼過程
本文只討論視頻解碼。
FFmpeg視頻解碼的過程比較簡單,實際就4步:
- 打開媒體流獲取編碼格式;
- 循環(huán)獲取解碼幀
- 顯示圖像
- 關(guān)閉流
實際上前兩步即已實現(xiàn)視頻解碼。
2.1 打開媒體流獲取編碼格式
1 打開流文件
這個函數(shù)avformat_open_input打開一個媒體流并讀取其頭信息,對于實時流或者不包含頭信息的視頻流,此函數(shù)通過幾幀數(shù)據(jù)分析以獲取其信息
此函數(shù)支持的媒體流非常廣泛,包括本地視頻文件、RTSP、TCP碼流、UDP碼流等等都支持。
m_pFmtCtx = nullptr;
ret = avformat_open_input(&m_pFmtCtx, sVideoUrl.c_str(), nullptr, nullptr);
2 在媒體流中尋找視頻流
一個媒體流中可能包含了視頻、音頻、字幕、文本等多個流,到底哪個是我們要的視頻流,需要首先確定,這個實際有兩種方法,方法1是遍歷媒體中所有的流,檢查流類型判斷哪個是視頻流,找到視頻流后獲取其解碼器:
m_nIndexVideo = -1;AVCodec* pAVCodec;//method 1for (i = 0; i < m_pFmtCtx->nb_streams; i++){if (m_pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){m_nIndexVideo = i;break;}}if (m_nIndexVideo < 0)return false;pAVCodec = (AVCodec*)avcodec_find_decoder(m_pFmtCtx->streams[m_nIndexVideo]->codecpar->codec_id);
方法2就更簡單,直接av_find_best_stream按視頻格式查找最符合的流,并直接返回視頻流序號及相應(yīng)的解碼器:
m_nIndexVideo = av_find_best_stream(m_pFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, (const AVCodec**)&pAVCodec, 0);
此兩種方法結(jié)果是同樣的,選擇其中一種方法使用即可。
3 分配解碼器
根據(jù)視頻流解碼格式分配及設(shè)置解碼器,此處得到的解碼器m_pAVCodecCtx即可用于后續(xù)的連續(xù)幀的解碼了:
m_pAVCodecCtx = avcodec_alloc_context3(pAVCodec);if (m_pAVCodecCtx == nullptr)return false;ret = avcodec_parameters_to_context(m_pAVCodecCtx, m_pFmtCtx->streams[m_nIndexVideo]->codecpar);
4 準(zhǔn)備解碼
調(diào)用函數(shù)avcodec_open2后,即可開始解碼:
ret = avcodec_open2(m_pAVCodecCtx, pAVCodec, nullptr);
至此,一個媒體流的視頻流解碼工作就準(zhǔn)備好了,可以進(jìn)行獲取和解碼視頻幀了。
2.2 獲取解碼幀
獲取解碼幀的過程是:取一個原始包(AVPacket),用以上的解碼器從這個包里解出視頻幀(AVFrame),具體過程如下:
1 用函數(shù)av_read_frame從流中取出一個幀包,此包為流中的原始數(shù)據(jù),未解碼的。
前面說過,一個媒體流中可能包含了多個流,所以av_read_frame獲取的數(shù)據(jù)包不一定是我們想要的視頻流包,需要根據(jù)這個包所在流的序號來判斷是不是屬于前面確定視頻流的包。
while (1)
{ret = av_read_frame(m_pFmtCtx, m_pPkt);if (ret < 0)return nullptr;if (m_pPkt->stream_index == m_nIndexVideo)break;
}
2 解碼這個包,獲取一幀解碼圖像
采用如下兩個函數(shù)的組合,用前面獲得的解碼器m_pAVCodecCtx對這個包進(jìn)行解碼,獲得AVFrame。
avcodec_send_packet(m_pAVCodecCtx, m_pPkt);avcodec_receive_frame(m_pAVCodecCtx, m_pFrame);
此時獲得的m_pFrame即為已解碼出的一幅視頻幀,為一個AVFrame結(jié)構(gòu),此結(jié)構(gòu)中包含了圖像數(shù)據(jù)、寬高、格式等等信息,可以用于顯示、存儲等后續(xù)工作。
2.3 顯示圖像幀
有很多軟件架構(gòu)支持直接對AVFrame結(jié)構(gòu)進(jìn)行顯示,如SDL、D3DX等等。
我們這里用最基本的RGB圖像方式來顯示這個AVFrame,但AVFrame的圖像數(shù)據(jù)大多數(shù)是YUV格式,需要做YUV->RGB轉(zhuǎn)換,當(dāng)然可以自己找公式轉(zhuǎn)換,實際上FFmpeg對此也提供了方便的轉(zhuǎn)換方法sws_scale:
int ret;int wid, hei;wid = pFrame->width;hei = pFrame->height;if (m_pSwsCtx == nullptr){m_pSwsCtx = sws_getContext(wid, hei, (AVPixelFormat)pFrame->format, wid, hei, AV_PIX_FMT_RGB24, SWS_POINT, nullptr, nullptr, nullptr);}uint8_t* data[1];data[0] = pDib;int lines[1] = { wid * 3 };ret = sws_scale(m_pSwsCtx, pFrame->data, pFrame->linesize, 0, hei, data, lines);
這樣轉(zhuǎn)出的pDib就是24位RGB的圖像了,之后的顯示此處就不再贅述了。
2.4 關(guān)閉流
以上打開的流,以及分配的各種資源,最后不用時記得要釋放,如
if (m_pFmtCtx != nullptr){avformat_close_input(&m_pFmtCtx);m_pFmtCtx = nullptr;}if (m_pAVCodecCtx != nullptr){avcodec_close(m_pAVCodecCtx);avcodec_free_context(&m_pAVCodecCtx);m_pAVCodecCtx = nullptr;}
3 示例
下圖為程序運行視頻解碼結(jié)果。
以上代碼的完整工程供參考:https://download.csdn.net/download/hangl_ciom/88152736