摘要
這篇文章介紹怎么實現視頻解碼,具體步驟為讀取Sample.mkv視頻文件,從中提取視頻流,然后解碼為YUV圖像數據,把YUV數據存儲為PGM灰度圖像,或者存儲為YUV420p RAW格式視頻。
初始化FFmepg和FormatContext
使用FFmpeg API第一個操作就是執行初始化函數:av_register_all注冊所有相關組件,然后使用avformat_open_input打開指定的媒體文件,并使用avformat_find_stream_info獲取媒體流相關信息,把這些格式信息映射到AVFormatContext *mFormatCtx這個結構中。
使用函數av_dump_format可以從控制臺輸出媒體文件相關信息。
bool VideoDecoding::init(const char * file)
{
av_register_all();
if ((avformat_open_input(&mFormatCtx, file, 0, 0)) < 0) {
printf("Failed to open input filen");
}
if ((avformat_find_stream_info(mFormatCtx, 0)) < 0) {
printf("Failed to retrieve input stream informationn");
}
av_dump_format(mFormatCtx, 0, file, 0);
return false;
}
查詢媒體流序號
多媒體文件一般都有一個視頻流和多個音頻流或者字幕流,每個媒體流都有序號Index。新版本的API使用av_find_best_stream函數查詢相應的媒體流,第一個參數為初始化后的媒體格式Context,第二個參數即為媒體類型:
- AVMEDIA_TYPE_VIDEO:視頻流
- AVMEDIA_TYPE_AUDIO:音頻流
- AVMEDIA_TYPE_SUBTITLE:字幕流
后面幾個參數是指定流特性的,如果從多個音頻流中選擇一個的話可以進行相關設置。此時只有一個視頻流,所以參數設為-1即可返回默認的媒體流Index,得到這個Index后,接下來可以根據這個Index讀取所需要的流。
bool VideoDecoding::findStreamIndex()
{
// Find video stream in the file
mVideoStreamIndex = av_find_best_stream(mFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (mVideoStreamIndex < 0) {
printf("Could not find stream in input filen");
return true;
}
return false;
}
配置編解碼器CodecContext
首先使用avcodec_find_decoder函數根據流Index查找相應的解碼器。
然后使用avcodec_alloc_context3函數根據解碼器申請一個CodecContext。
接著根據流數據填充CodecContext各項信息。
最后完成CodecContext初始化操作。
// Initialize the AVCodecContext to use the given AVCodec.
bool VideoDecoding::initCodecContext()
{
// Find a decoder with a matching codec ID
AVCodec *dec = avcodec_find_decoder(mFormatCtx->streams[mVideoStreamIndex]->codecpar->codec_id);
if (!dec) {
printf("Failed to find codec!n");
return true;
}
// Allocate a codec context for the decoder
if (!(mCodecCtx = avcodec_alloc_context3(dec))) {
printf("Failed to allocate the codec contextn");
return true;
}
// Fill the codec context based on the supplied codec parameters.
if (avcodec_parameters_to_context(mCodecCtx, mFormatCtx->streams[mVideoStreamIndex]->codecpar) < 0) {
printf("Failed to copy codec parameters to decoder context!n");
return true;
}
// Initialize the AVCodecContext to use the given Codec
if (avcodec_open2(mCodecCtx, dec, NULL) < 0) {
printf("Failed to open codecn");
return true;
}
return false;
}