FFmpeg解码详解(原理与函数使用说明)

视频解码知识
•纯净的视频解码流程
▫压缩编码数据->像素数据。
▫例如解码H.264,就是“H.264码流->YUV”。
•一般的视频解码流程
▫视频码流一般存储在一定的封装格式(例如MP4、AVI等)中。封装格式中通常还包含音频码流等内容。
▫对于封装格式中的视频,需要先从封装格式中提取中视频码流,然后再进行解码。
▫例如解码MKV格式的视频文件,就是“MKV->H.264码流->YUV”。

FFmpeg库简介
FFmpeg一共包含8个库:
▫ avcodec:编解码(最重要的库)。
▫ avformat:封装格式处理。
▫ avfilter:滤镜特效处理。
▫ avdevice:各种设备的输入输出。
▫ avutil:工具库(大部分库都需要这个库的支持)。
▫ postproc:后加工。
▫ swresample:音频采样数据格式转换。
▫ swscale:视频像素数据格式转换。

解码流程如下:

FFmpeg解码详解(原理与函数使用说明)_第1张图片

FFmpeg解码函数简介
▫ av_register_all():注册所有组件。
▫ avformat_open_input():打开输入视频文件。
▫ avformat_find_stream_info():获取视频文件信息。
▫ avcodec_find_decoder():查找解码器。
▫ avcodec_open2():打开解码器。
▫ av_read_frame():从输入文件读取一帧压缩数据。
▫ avcodec_decode_video2():解码一帧压缩数据。

    avcodec_decode_video2()主要做了以下几个方面的工作:

   (1)对输入的字段进行了一系列的检查工作:例如宽高是否正确,输入是否为视频等等。
   (2)通过ret = avctx->codec->decode(avctx, picture, got_picture_ptr,&tmp)这句代码,调用了相应AVCodec的decode()函数,完成了解码操作。
   (3)对得到的AVFrame的一些字段进行了赋值,例如宽高、像素格式等等。其中第二部是关键的一步,它调用了AVCodec的decode()方法完成了解码。AVCodec的decode()方法是一个函数指针,指向了具体解码器的解码函数。

▫ avcodec_close():关闭解码器。
▫ avformat_close_input():关闭输入视频文件。

FFmpeg解码的数据结构如下所示:

FFmpeg解码详解(原理与函数使用说明)_第2张图片

FFmpeg数据结构简介:
▫AVFormatContext
封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息。
▫AVInputFormat
每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。
▫AVStream
视频文件中每个视频(音频)流对应一个该结构体。
▫AVCodecContext
编码器上下文结构体,保存了视频(音频)编解码相关信息。
▫AVCodec
每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。
▫AVPacket
存储一帧压缩编码数据。
▫AVFrame
存储一帧解码后像素(采样)数据。

FFmpeg数据结构分析:
AVFormatContext:
iformat:输入视频的AVInputFormat
nb_streams :输入视频的AVStream 个数
streams :输入视频的AVStream []数组
duration :输入视频的时长(以微秒为单位)
bit_rate :输入视频的码率


AVInputFormat:
name:封装格式名称
long_name:封装格式的长名称
extensions:封装格式的扩展名
id:封装格式ID
一些封装格式处理的接口函数

AVStream:
id:序号
codec:该流对应的AVCodecContext
time_base:该流的时基
r_frame_rate:该流的帧率


AVCodecContext:
codec:编解码器的AVCodec
width, height:图像的宽高(只针对视频)
pix_fmt:像素格式(只针对视频)
sample_rate:采样率(只针对音频)
channels:声道数(只针对音频)
sample_fmt:采样格式(只针对音频)


AVCodec:
name:编解码器名称
long_name:编解码器长名称
type:编解码器类型
id:编解码器ID
一些编解码的接口函数

AVPacket:
pts:显示时间戳
dts :解码时间戳
data :压缩编码数据
size :压缩编码数据大小
stream_index :所属的AVStream


AVFrame:
data:解码后的图像像素数据(音频采样数据)。
linesize:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小。
width, height:图像的宽高(只针对视频)。
key_frame:是否为关键帧(只针对视频) 。
pict_type:帧类型(只针对视频) 。例如I,P,B。

  • AVFrame存放从AVPacket中解码出来的原始数据,其必须通过av_frame_alloc来创建,通过av_frame_free来释放。和AVPacket类似,AVFrame中也有一块数据缓存空间,
    在调用av_frame_alloc的时候并不会为这块缓存区域分配空间,需要使用其他的方法。在解码的过程使用了两个AVFrame,这两个AVFrame分配缓存空间的方法也不相同
    • 一个AVFrame用来存放从AVPacket中解码出来的原始数据,这个AVFrame的数据缓存空间通过调avcodec_decode_video分配和填充。
    • 另一个AVFrame用来存放将解码出来的原始数据变换为需要的数据格式(例如RGB,RGBA)的数据,这个AVFrame需要手动的分配数据缓存空间。代码如下:
AVFrame* pFrameYUV;
pFrameYUV = av_frame_alloc();
// 手动为 pFrameYUV分配数据缓存空间
int numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P,pCodecCtx->widht,pCodecCtx->width);
uint8_t* buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
// 将分配的数据缓存空间和AVFrame关联起来
avpicture_fill((AVPicture *)pFrameYUV, buffer, AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height)

首先计算需要缓存空间大小,调用av_malloc分配缓存空间,最后调用avpicture_fill将分配的缓存空间和AVFrame关联起来。
调用av_frame_free来释放AVFrame,该函数不止释放AVFrame本身的空间,还会释放掉包含在其内的其他对象动态申请的空间,例如上面的缓存空间。

  • av_malloc和av_free,FFmpeg并没有提供垃圾回收机制,所有的内存管理都要手动进行。av_malloc只是在申请内存空间的时候会考虑到内存对齐(2字节,4字节对齐),
    其申请的空间要调用av_free释放。

你可能感兴趣的:(音视频,ffmpeg)