FFMpeg 作为音视频领域的开源工具,它几乎可以实现所有针对音视频的处理,本文主要利用 FFMpeg 官方提供的 SDK 实现音视频最简单的几个实例:编码、解码、封装、解封装、转码、缩放以及添加水印。
接下来会由发现问题->分析问题->解决问题->实现方案,循序渐进的完成。
FFMpeg 编码实现
本例子实现的是将视频域 YUV 数据编码为压缩域的帧数据,编码格式包含了 H.264/H.265/MPEG1/MPEG2 四种 CODEC 类型。
实现的过程,可以大致用如下图表示:
从图中可以大致看出视频编码的流程:
-
首先要有未压缩的 YUV 原始数据。
-
其次要根据想要编码的格式选择特定的编码器。
-
最后编码器的输出即为编码后的视频帧。
根据流程可以推倒出大致的代码实现:
-
存放待压缩的 YUV 原始数据。此时可以利用 FFMpeg 提供的 AVFrame 结构体,并根据 YUV 数据来填充 AVFrame 结构的视频宽高、像素格式;根据视频宽高、像素格式可以分配存放数据的内存大小,以及字节对齐情况。
-
获取编码器。利用想要压缩的格式,比如 H.264/H.265/MPEG1/MPEG2 等,来获取注册的编解码器,编解码器在 FFMpeg 中用 AVCodec 结构体表示,对于编解码器,肯定要对其进行配置,包括待压缩视频的宽高、像素格式、比特率等等信息,这些信息,FFMpeg 提供了一个专门的结构体 AVCodecContext 结构体。
-
存放编码后压缩域的视频帧。FFMpeg 中用来存放压缩编码数据相关信息的结构体为 AVPacket。最后将 AVPacket 存储的压缩数据写入文件即可。
AVFrame 结构体的分配使用av_frame_alloc()函数,该函数会对 AVFrame 结构体的某些字段设置默认值,它会返回一个指向 AVFrame 的指针或 NULL指针(失败)。
AVFrame 结构体的释放只能通过av_frame_free()来完成。
注意,该函数只能分配 AVFrame 结构体本身,不能分配它的 data buffers 字段指向的内容,该字段的指向要根据视频的宽高、像素格式信息手动分配,本例使用的是av_image_alloc()函数。
代码实现大致如下:
//allocate AVFrame struct
AVFrame *frame = NULL;
frame = av_frame_alloc();
if(!frame){
printf("Alloc Frame Fail\n");
return -1;
}
//fill AVFrame struct fields
frame->width = width;
frame->height = height;
frame->pix_fmt = AV_PIX_FMT_YUV420P;
//allocate AVFrame data buffers field point
ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, frame->pix_fmt, 32);
if(ret < 0){
printf("Alloc Fail\n");
return -1;
}
//write input file data to frame->data buffer
fread(frame->data[0], 1, frame->width*frame->height, pInput_File);
...
av_frame_free(frame);
编解码器相关的 AVCodec 结构体的分配使用avcodec_find_encoder(enum AVCodecID id)完成,该函数的作用是找到一个与 AVCodecID 匹配的已注册过得编码器;成功则返回一个指向 AVCodec ID 的指针,失败返回 NULL 指针。
该函数的作用是确定系统中是否有该编码器,只是能够使用编码器进行特定格式编码的最基本的条件,要想