一开始学习ffmpeg的时候,看着示例代码中长长的c函数,心里总是莫名地升起几分烦躁,觉得代码梳理起来实在有点费劲。等到对ffmpeg的API有了一定的了解后,再看一些示例,又觉得其中重复地代码非常的多,于是有了用C++对ffmpeg进行组件化的想法,后来慢慢动手,一点点添加代码,修改代码,测试,终于磨出来了一个基本的实现。
首先,说明一下封装的目的:1、代码复用;2、组件化后,可以简化代码结构。
全部代码已上传CSDN,链接如下:
简单说明一下自己的实现,随后会有利用这些组件来处理媒体的例子。
首先说明几个准备文件:
第一个是FFmpegs.h文件,它用于引入要使用的ffmpeg头文件,然后其他文件引用它来间接引入ffmpeg。
#ifndef FFMPEGS_H
#define FFMPEGS_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavutil/audio_fifo.h"
#include "libavutil/avstring.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/dict.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/avassert.h"
#include "libavutil/time.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavcodec/avfft.h"
#include "libswresample/swresample.h"
#include <libavutil/channel_layout.h>
#include <libavutil/timestamp.h>
#include <libavutil/avutil.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
typedef uint64_t u_int64_t;
#ifdef __cplusplus
}
#endif
#endif // FFMPEGS_H
第二个是ScaleResampleHelpFunc.h,它对视频帧的格式转化和音频帧的重采样进行了封装,非常简单,代码中有相关注释。
#ifndef AVSWSSWR_H
#define AVSWSSWR_H
#include "FFmpegs.h"
#ifdef __cplusplus
extern "C"
{
#endif
/*
* c接口,对音频帧和视频帧以及其转化和重采样提供了方便函数。
*/
/*
* 这里将视频帧和音频帧所需的基本参数进行抽象,定义了VideoFrameBaseParam和AudioFrameBaseParam。
* 接着进一步对视频帧转化和音频帧重采样进行了抽象。
*/
typedef struct VideoFrameBaseParam
{
uint32_t nWidth;
uint32_t nHeight;
enum AVPixelFormat sPixFmt;
}VideoFrameBaseParam;
typedef struct VideoFrameScaler
{
VideoFrameBaseParam sSrcParam;
VideoFrameBaseParam sDstParam;
struct SwsContext *pSwsContext;
}VideoFrameScaler;
typedef struct AudioFrameBaseParam
{
enum AVSampleFormat sSampleFmt;
uint64_t sChannelLayout;
uint32_t nSampleRate;
uint32_t nSampleNum;
}AudioFrameBaseParam;
typedef struct AudioFrameResampler
{
AudioFrameBaseParam sSrcParam;
AudioFrameBaseParam sDstParam;
struct SwrContext *pSwrContext;
}AudioFrameResampler;
//提供了一组返回BaseParam的方便函数
VideoFrameBaseParam CreateVideoFrameBaseParam(uint32_t nWidth,uint32_t nHeight,enum AVPixelFormat sPixFmt);
AudioFrameBaseParam CreateAudioFrameBaseParam(enum AVSampleFormat sSampleFmt,uint64_t sChannelLayout,
uint32_t nSampleRate,uint32_t nSampleNum);
VideoFrameBaseParam CreateVideoFrameBaseParamByAVFrame(AVFrame *piFrame);
AudioFrameBaseParam CreateAudioFrameBaseParamByAVFrame(AVFrame *piFrame);
VideoFrameBaseParam CreateVideoFrameBaseParamByCoderCxt(AVCodecContext *piCodecCxt);
AudioFrameBaseParam CreateAudioFrameBaseParamByCoderCxt(AVCodecContext *piCodecCxt);
/*
* 在有些时候,有这样的需要:我们要将一个视频帧的像素格式转化为YUV420P格式,其他的参数则不变,
* 这个时候,我们不需要知道源视频帧的详细参数,仅需指定想要修改的部分参数即可:
*
* 即,如果sDst中的字段未指定,那么使用sSrc中对应字段的值
*
* sSrc:源参数,这个参数里必须指定全部字段
* dDst:目标参数,这个参数里仅需指定部分字段,其他的设置为默认值,表示它使用sSrc对应的值
* 返回值:对dDst参数进行填充后的结果值
*
* VideoFrameBaseParam中,按照字段顺序,默认值为 0,0,AV_PIX_FMT_NONE
* AudioFrameBaseParam中,按照字段顺序,默认值为AV_SAMPLE_FMT_NONE,0,0,0
*
*/
VideoFrameBaseParam GetRealVideoParamByFirst(VideoFrameBaseParam sSrc,VideoFrameBaseParam sDst);
AudioFrameBaseParam GetRealAudioParamByFirst(AudioFrameBaseParam sSrc,AudioFrameBaseParam sDst);
/*
* 按照指定参数,分配AVFrame并为其开辟相关的内存
*/
AVFrame *CreateAudioFrameAndBuffered(AudioFrameBaseParam sParam);
AVFrame *CreateVideoFrameAndBuffered(VideoFrameBaseParam sParam);
//scaler和resampler的接口,非常简单
VideoFrameScaler *CreateVideoFrameScaler(VideoFrameBaseParam sSrcParam,VideoFrameBaseParam sDstParam);
AudioFrameResampler *CreateAudioFrameResampler(AudioFrameBaseParam sSrcParam,AudioFrameBaseParam sDstParam);
void FreeVideoFrameScaler(VideoFrameScaler **piScaler);
void FreeAudioFrameResampler(AudioFrameResampler **piResampler);
int ScaleVideo(VideoFrameScaler *piScaler,AVFrame *pSrc,AVFrame *pDst);
int ResampleAudio(AudioFrameResampler *piResampler,AVFrame *pSrc,AVFrame *pDst);
#ifdef __cplusplus
}
#endif
#endif // AVSWSSWR_H
第三个文件是CodecHelpFunc.h,它针对编解码器封装了一些函数,代码中有解释。
#ifndef AVCODECSS_H
#define AVCODECSS_H
#include "FFmpegs.h"
#ifdef __cplusplus
extern "C"
{
#endif
/*
* c函数,为codec封装了一些操作。
*/
/*
* 以下五个函数是codec相关的参数查询函数:
*
* SampleFormatIsSupportedByCodec:codec是否支持某一个采样格式
* SampleRateIsSupportedByCodec:codec是否支持某一个采样频率
* SelectSampleFormat:从codec支持的采样格式中选择一个最合适的
* SelectSampleRate:从codec支持的采样频率中选择一个最合适的
* SelectChannelLayout:从codec支持的channelLayout中选择一个最合适的
*/
int SampleFormatIsSupportedByCodec(const AVCodec *codec, enum AVSampleFormat sample_fmt);
int SampleRateIsSupportedByCodec(const AVCodec *codec, const int sample_rate);
enum AVSampleFormat SelectSampleFormat(const AVCodec *codec);
int SelectSampleRate(const AVCodec *codec);
u_int64_t SelectChannelLayout(const AVCodec *codec);
//从一个AVStream中打开一个Decoder
AVCodecContext *CreateAndOpenDecoderByAVStream(AVStream *piStream,AVDictionary *piOpenCodecCtxDict);
/*
* 当从一个AVStream打开一个Decoder时,所需的信息大多数情况下都由AVStream提供,一般不需要我们指定额外参数
*
* 而当打开一个Encoder时,就需要我们手动指定很多的参数,因此这里将其整个过程拆分为了三个函数,按照以下顺序使用:
*
* 1. NewEncoderByID:分配一个指定类型的Encoder
* 2. SetEncoderParamByDefault:为新分配的Encoder设置所需的默认的参数
* 3. 如果有额外需要,可以修改第二步设置的默认参数
* 4. OpenEncoder:经过上述步骤配置好参数后,打开Encoder
*
* EncoderHandler类中使用了这些函数
*/
AVCodecContext *NewEncoderByID(enum AVCodecID sEncodeID);
void SetEncoderParamByDefault(AVCodecContext *piCodecCxt,AVFormatContext *piFmtCxt);
int OpenEncoder(AVCodecContext *piCodecCxt,AVDictionary *piOpenCodecCtxDict);
#ifdef __cplusplus
}
#endif
#endif // AVCODEC_H