在视频解码前,先了解以下几个基本的概念:
FFmpeg视频解码过程
通常来说,FFmpeg的视频解码过程有以下几个步骤:
av_register_all该函数注册支持的所有的文件格式(容器)及其对应的CODEC,只需要调用一次,故一般放在main函数中。也可以注册某个特定的容器格式,但通常来说不需要这么做。
avformat_open_input该函数读取文件的头信息,并将其信息保存到AVFormatContext结构体中。其调用如下
AVFormatContext* pFormatCtx = nullptr;
avformat_open_input(&pFormatCtx, filenName, nullptr, nullptr)
第一个参数是AVFormatContext结构体的指针,第二个参数为文件路径;第三个参数用来设定输入文件的格式,如果设为null,将自动检测文件格式;第四个参数用来填充AVFormatContext一些字段以及Demuxer的private选项。
AVFormatContext包含有较多的码流信息参数,通常由avformat_open_input创建并填充关键字段。
avformat_open_input通过解析多媒体文件或流的头信息及其他的辅助数据,能够获取到足够多的关于文件、流和CODEC的信息,并将这些信息填充到AVFormatContext结构体中。但任何一种多媒体格式(容器)提供的信息都是有限的,而且不同的多媒体制作软件对头信息的设置也不尽相同,在制作多媒体文件的时候难免会引入一些错误。也就是说,仅仅通过avformat_open_input并不能保证能够获取所需要的信息,所以一般要使用
avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
avformat_find_stream_info主要用来获取必要的CODEC参数,设置到ic->streams[i]->codec。
在解码的过程中,首先要获取到各个stream所对应的CODEC类型和id,CODEC的类型和id是两个枚举值,其定义如下:
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1,
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA,
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT,
AVMEDIA_TYPE_NB
};
enum CodecID {
CODEC_ID_NONE, /* video codecs */
CODEC_ID_MPEG1VIDEO,
CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
CODEC_ID_MPEG2VIDEO_XVMC,
CODEC_ID_H261,
CODEC_ID_H263,
…
}
通常,如果多媒体文件具有完整而正确的头信息,通过avformat_open_input即可用获得这两个参数。
经过上面的步骤,已经将文件格式信息读取到了AVFormatContext中,要打开流数据相应的CODEC需要经过下面几个步骤
要解码视频,首先要在AVFormatContext包含的多个流中找到CODEC类型为AVMEDIA_TYPE_VIDEO,代码如下:
//查找视频流 video stream
int videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if (videoStream == -1)
return -1; // 没有找到视频流video stream
结构体AVFormatContext中的streams字段是一个AVStream指针的数组,包含了文件所有流的描述,上述上述代码在该数组中查找CODEC类型为
AVMEDIA_TYPE_VIDEO的流的下标。
return -1; // Could open codec
上述代码,首先通过codec_id找到相应的CODEC,然后调用avcodec_open2打开相应的CODEC。
已经有了相应的解码器,下面的工作就是将数据从流中读出,并解码为没有压缩的原始数据
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0)
{
if (packet.stream_index == videoStream)
{
int frameFinished = 0;
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (frameFinished)
{
doSomething();
}
}
}
上述代码调用av_read_frame将数据从流中读取数据到packet中,并调用avcodec_decode_video2对读取的数据进行解码。
需要关闭avformat_open_input打开的输入流,avcodec_open2打开的CODEC
avcodec_close(pCodecCtxOrg);
avformat_close_input(&pFormatCtx);
在配置好FFmpeg的开发环境后,在C++中使用FFmpeg的库函数,会出现解析不出函数的名称链接错误,这是由于FFmpeg库是C语言实现,要在C++调用C函数需要 extern "C"的声明。
extern "C"
{
# include
# include
# include
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章