在视频解码前,先了解以下几个基本的概念:

  • 编解码器(CODEC):能够进行视频和音频压缩(CO)与解压缩(DEC),是视频编解码的核心部分。
  • 容器/多媒体文件(Container/File):没有了解视频的编解码之前,总是错误的认为平常下载的电影的文件的后缀(avi,mkv,rmvb等)就是视频的编码方式。事实上,刚才提到的几种文件的后缀

    并不是视频的编码方式,只是其封装的方式。一个视频文件通常有视频数据、音频数据以及字幕等,封装的格式决定这些数据在文件中是如何的存放的,封装在一起音频、视频等数据组成的多媒体文件,也可以叫做容器(其中包含了视音频数据)。所以,只看多媒体文件的后缀名是难以知道视音频的编码方式的。
  • 流数据 Stream,例如视频流(Video Stream),音频流(Audio Stream)。流中的数据元素被称为帧Frame

FFmpeg视频解码过程

通常来说,FFmpeg的视频解码过程有以下几个步骤:

  1. 注册所支持的所有的文件(容器)格式及其对应的CODEC av_register_all()
  2. 打开文件 avformat_open_input()
  3. 从文件中提取流信息 avformat_find_stream_info()
  4. 在多个数据流中找到视频流 video stream(类型为 MEDIA_TYPE_VIDEO
  5. 查找video stream 相对应的解码器 avcodec_find_decoder
  6. 打开解码器 avcodec_open2()
  7. 为解码帧分配内存 av_frame_alloc()
  8. 从流中读取读取数据到Packet中 av_read_frame()
  9. 对video 帧进行解码,调用 avcodec_decode_video2()

解码过程的具体说明

1. 注册

av_register_all该函数注册支持的所有的文件格式(容器)及其对应的CODEC,只需要调用一次,故一般放在main函数中。也可以注册某个特定的容器格式,但通常来说不需要这么做。

2. 打开文件

avformat_open_input该函数读取文件的头信息,并将其信息保存到AVFormatContext结构体中。其调用如下

AVFormatContext* pFormatCtx = nullptr;
avformat_open_input(&pFormatCtx, filenName, nullptr, nullptr)

第一个参数是AVFormatContext结构体的指针,第二个参数为文件路径;第三个参数用来设定输入文件的格式,如果设为null,将自动检测文件格式;第四个参数用来填充AVFormatContext一些字段以及Demuxer的private选项。

AVFormatContext包含有较多的码流信息参数,通常由avformat_open_input创建并填充关键字段。

3. 获取必要的CODEC参数

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即可用获得这两个参数。

4. 打开解码器

经过上面的步骤,已经将文件格式信息读取到了AVFormatContext中,要打开流数据相应的CODEC需要经过下面几个步骤

  • 找到视频流 video stream

    一个多媒体文件包含有多个原始流,例如 movie.mkv这个多媒体文件可能包含下面的流数据
  • 原始流 1 h.264 video
  • 原始流 2 aac audio for Chinese
  • 原始流 3 aac audio for English
  • 原始流 4 Chinese Subtitle
  • 原始流 5 English Subtitle

要解码视频,首先要在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的流的下标。

  • 根据codec_id找到相应的CODEC,并打开

    结构体AVCodecContext描述了CODEC上下文,包含了众多CODEC所需要的参数信息。
AVCodecContext* pCodecCtxOrg = nullptr;
AVCodec* pCodec = nullptr;
pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context
// 找到video stream的 decoder
pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);
// open codec
if (avcodec_open2(pCodecCtxOrg , pCodec, nullptr) < 0)
return -1; // Could open codec

上述代码,首先通过codec_id找到相应的CODEC,然后调用avcodec_open2打开相应的CODEC。

5. 读取数据帧并解码

已经有了相应的解码器,下面的工作就是将数据从流中读出,并解码为没有压缩的原始数据

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对读取的数据进行解码。

6. 关闭

需要关闭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 <libavcodec\avcodec.h>
# include <libavformat\avformat.h>
# include <libswscale\swscale.h>
}

哎,博客园的Markdown用着好不方便啊,不知道怎么画流程图....

代码 FFmpeg0.cpp

FFmpeg学习1:视频解码的更多相关文章

  1. FFmpeg学习5:多线程播放视音频

    在前面的学习中,视频和音频的播放是分开进行的.这主要是为了学习的方便,经过一段时间的学习,对FFmpeg的也有了一定的了解,本文就介绍了 如何使用多线程同时播放音频和视频(未实现同步),并对前面的学习 ...

  2. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

  3. FFmpeg 学习(七):FFmpeg 学习整理总结

    一.FFmpeg 播放视频的基本流程整理 播放流程: video.avi(Container) -> 打开得到 Video_Stream -> 读取Packet -> 解析到 Fra ...

  4. FFmpeg学习起步 —— 环境搭建

    下面是我搭建FFmpeg学习环境的步骤. 一.在Ubuntu下 从http://www.ffmpeg.org/download.html下载最新的FFmpeg版本,我的版本是ffmpeg-2.7.2. ...

  5. FFMPEG学习----打印视频信息

    FFMPEG学习资料少之又少,在此推荐雷神的博客: http://blog.csdn.net/leixiaohua1020 在这里,我们把打印视频里的相关信息作为学习FFMPEG的 Hello Wor ...

  6. ffmpeg学习笔记-多线程音视频解码

    之前的视频解码仍然存在问题,那就是是在主线程中去完成解码的,会造成线程阻塞,这里将其改为多线程解码,使其主线程不被阻塞 前面介绍了音视频的主线程解码,那样会阻塞主线程,在前面学习了多线程以后,就可以对 ...

  7. FFmpeg学习2:解码数据结构及函数总结

    在上一篇文章中,对FFmpeg的视频解码过程做了一个总结.由于才接触FFmpeg,还是挺陌生的,这里就解码过程再做一个总结. 本文的总结分为以下两个部分: 数据读取,主要关注在解码过程中所用到的FFm ...

  8. FFmpeg学习4:音频格式转换

    前段时间,在学习试用FFmpeg播放音频的时候总是有杂音,网上的很多教程是基于之前版本的FFmpeg的,而新的FFmepg3中audio增加了平面(planar)格式,而SDL播放音频是不支持平面格式 ...

  9. ffmpeg学习笔记

           对于每一个刚開始学习的人,刚開始接触ffmpeg时,想必会有三个问题最为关心,即ffmpeg是什么?能干什么?怎么開始学习?本人前段时间開始接触ffmpeg,在刚開始学习过程中.这三个问 ...

随机推荐

  1. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  2. “.Net 社区虚拟大会”(dotnetConf) 2016 Day 2 Keynote: Miguel de Icaza

    美国时间 6月7日--9日,为期三天的微软.NET社区虚拟大会正式在 Channel9 上召开,美国时间6.8 是第二天, Miguel de Icaza 做Keynote,Miguel 在波士顿Xa ...

  3. hibernate多对多关联映射

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  4. 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])

    常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...

  5. .NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator

    去年,我在一篇文章用原始方法解析复杂字符串,json一定要用JsonMapper么?中介绍了简单的JSON解析的问题,那种方法在当时的环境是非常方便的,因为不需要生成实体类,结构很容易解析.但随着业务 ...

  6. PHP之时间和日期函数

    // 时间日期函数 Time <?php date_default_timezone_set('UTC'); // 获取当前时间的时间戳 $time0 = mktime(); $time1 = ...

  7. EF6 对多个数据库,多个DBContext的情况 进行迁移的方法。

    参见: http://stackoverflow.com/questions/21537558/multiple-db-contexts-in-the-same-db-and-application- ...

  8. ,net core mvc 文件上传

    工作用到文件上传的功能,在这个分享下 ~~ Controller: public class PictureController : Controller { private IHostingEnvi ...

  9. H3 BPM:为石化企业提供一个不一样的全停大修平台

    H3 BPM大型炼化企业装置全停检修管理平台(简称"全停大修")结合国际化的流程管理理念.成熟的系统技术架构.优秀的行业解决方案,为石油化工行业全停大修提供了卓越的信息化管理方案, ...

  10. Microsoft Visual Studio 2015 下载、注册、安装过程、功能列表、问题解决

    PS:请看看回复.可能会有文章里没有提到的问题.也许会对你有帮助哦~ 先上一张最终的截图吧: VS2015正式版出了,虽然没有Ultimate旗舰版,不过也是好激动的说.哈哈.可能有的小伙伴,由于工作 ...