前言
ffmpeg内置了很多滤镜库,都封装在AVFilter模块中,通过这个滤镜模块可以用来更加方便的处理音视频。比如视频分辨率压缩滤镜scale(用来对视频的分辨率进行缩放),视频翻转滤镜transpose(对视频进行上下左右的翻转);音频格式转换滤镜aformat(它实际上最终是调用avresample滤镜实现的),volume(用来调整音量大小)等等。
-
关于ffmpeg的滤镜AVFilter源码及编译
1、默认情况下libavfilter模块会编译如下文件:
OBJS = allfilters.o
audio.o
avfilter.o
avfiltergraph.o
buffersink.o
buffersrc.o
drawutils.o
fifo.o
formats.o
framepool.o
framequeue.o
graphdump.o
graphparser.o
transform.o
video.o
以上来自libavfilter/Makefile,所以就算编译ffmpeg时加入--disable-filters 也会将这些文件编译进去生成libavfilter库;
如果要将整个libavfilter模块禁用掉,在根目录Makefile将如下语句注释掉即可:
FFLIBS-$(CONFIG_AVFILTER) += avfilter2、如果要使用音频格式转换滤镜时需要添加如下选项:
aformat滤镜用于音频格式(如采样率,采样格式,声道类型)的转换(相当于实现了SwrContext的功能),它内部最终调用的aresample滤镜,而aresample滤镜内部又是用libswresample模块
的SwrContext实现的
--enable-filter=aformat;--enable-filter=aresample
3、如果要使用音频声音变化滤镜时需要添加如下选项:
--enable-filter=volume
4、分别对应的滤镜名称为
ff_af_aformat;ff_af_aresample;ff_af_volume
5、ffmpeg所有的滤镜在libavfilter/filter_list.c文件下
本文目的
通过aformat滤镜和volume滤镜实现音频格式转换,音量调整功能来学习滤镜的使用流程,其实不管是音频还是视频滤镜使用流程都是一样的。
ffmpeg的音频滤镜是通过滤镜管道来进行管理的,滤镜管道可以将各个滤镜连接到一起,形成一个处理流水线,流程如下:
srcfilter-->volumefilter->aformat....->otherfilter->sinkfilter
- 1、一个音频滤镜管道必须要有一个输入滤镜(abuffer,用于接收要处理的数据),一个输出滤镜(abuffersink,用于提供处理好的数据)
- 2、每一个滤镜(AVFilter)都有一个滤镜上下文AVFilterContext(也称为滤镜实例)与之对应,滤镜的参数通过这个上下文来设置
- 3、输入滤镜的输出端口连接着volume滤镜的输入端口,volume滤镜的输出端口连接着aformat的输入端口,aformat的输出端口连接着输出滤镜的输入端口,
这样就形成了一个滤镜处理链
音频滤镜使用流程
音频滤镜的使用流程有两种方式,这里分别进行说明。
方式一:
image.png
- 总结:滤镜初始化流程为
1、创建滤镜管道
2、创建各个滤镜(其中输入滤镜,输出滤镜是必须的滤镜),每个滤镜的创建为固定的流程(创建滤镜,创建滤镜上下文,设置滤镜参数并初始化上下文)
3、连接各个滤镜(其中输入滤镜一定要放在第一个,输出滤镜一定要放到最后一个)
4、初始化滤镜管道
5、av_buffersrc_add_frame()函数添加要进行滤镜处理的数据
6、av_buffersink_get_frame()函数获取处理好了的音频数据
方式二:
image.png
- 总结起来使用步骤为:
1、创建滤镜管道
2、创建各个滤镜(其中输入滤镜,输出滤镜是必须的滤镜),每个滤镜的创建为固定的流程(创建滤镜,创建滤镜上下文,设置滤镜参数并初始化上下文)
3、连接各个滤镜(其中输入滤镜一定要放在第一个,输出滤镜一定要放到最后一个)
4、初始化滤镜管道
5、av_buffersrc_add_frame()函数添加要进行滤镜处理的数据
6、av_buffersink_get_frame()函数获取处理好了的音频数据
其中2、3步骤每一个滤镜的处理步骤都一样,只是参数不同而已。ffmpeg针对性的提出了另外一种初始化每个滤镜的简化的处理方式。它通过给定格式的滤镜字符串来解析出
并初始化每个滤镜,同时自动添加到滤镜管道中。滤镜字符串的格式如下:
filter_name1=par_name1=par_val1,filter_name2=par_name1=par_val1
如果滤镜只需要一个参数,可以简化为filter_name=par_val;如果滤镜有多个参数,则多个参数用":"分隔filter_name=par_name1=par_val1:par_name2=par_val2:
多个滤镜之间则用","分隔
如:
aresample=44100,aformat=sample_fmts=s32:channel_layouts=mono
实现代码
公共代码文件
#ifndef AudioVolume_hpp
#define AudioVolume_hpp
#include <stdio.h>
#include <inttypes.h>
#include <string>
using namespace std;
extern "C" {
#include "Common.h"
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavutil/opt.h>
#include <libavutil/macros.h>
#include <libavutil/timestamp.h>
}
class AudioVolume
{
public:
AudioVolume();
~AudioVolume();
/** 方式一:实现调整声音的大小以及改变音频数据的格式功能
* 1、可以调整声音的大小
* 2、可以改变音频的采样率,采样格式,声道类型等等格式
* 使用音频滤镜实现,改变后达到想要的效果
* 备注:以MP3音频为例;
*/
void doChangeAudioVolume();
/** 方式二:实现调整声音的大小以及改变音频数据的格式功能
* 1、可以调整声音的大小
* 2、可以改变音频的采样率,采样格式,声道类型等等格式
* 使用音频滤镜实现,改变后达到想要的效果
* 备注:以MP3音频为例;与方式一不同的地方在于滤镜管道的初始化方式更加简单
*/
void doChangeAudioVolume2();
private:
// 用于解析本地音频源文件
AVFormatContext *in_fmt;
// 用于写入改变后的音频数据到目标文件
AVFormatContext *ou_fmt;
// 用于解码的
AVCodecContext *de_ctx;
// 用于编码的
AVCodecContext *en_ctx;
AVFrame *de_frame;
AVFrame *en_frame;
// 处理音频转换的滤镜管道
AVFilterGraph *graph;
// 输入滤镜上下文(滤镜实例)
AVFilterContext *src_flt_ctx;
// 输出滤镜上下文(滤镜实例)
AVFilterContext *sink_flt_ctx;
// 输入滤镜
AVFilter *src_flt;
// 输出滤镜
AVFilter *sink_flt;
int next_audio_pts;
void doDecode(AVPacket *packt);
void doEncode(AVFrame *frame);
void releasesources();
};
#endif /* AudioVolume_hpp */
方式一实现代码:
void AudioVolume::doChangeAudioVolume()
{
string curFile(__FILE__);
unsigned long pos = curFile.find("2-video_audio_advanced");
if (pos == string::npos) {
LOGD("not find file");
return;
}
string srcDic = curFile.substr(