这篇文章介绍怎么实现视频解码,具体步骤为读取Sample.mkv视频文件,从中提取视频流,然后解码为YUV图像数据,把YUV数据存储为PGM灰度图像,或者存储为YUV420p RAW格式视频。
使用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;
}
首先使用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;
}