使用libavfilter 为实时流添加滤镜
添加滤镜一般是使用FFMpeg
进行滤镜添加,在ffmpeg 中提供了libavfiler
库来完成该操作。
而添加滤镜的基本操作如下图(引用自雷神):
从上图我们可以清楚的看到,滤镜的加载和解码部分几乎是完全独立的,现在我大致的用文字描述一下步骤。
- 注册滤镜信息。
- 创建滤波器主结构体,用于整合所有的滤波过程。
- 创建滤波器输入、输出或者其他复杂滤波。
- 解析字符串,并构建该字符串所描述的滤波图
- 提交滤波器
接下来上代码:
// 1.初始化滤波器结构体
filter_graph = avfilter_graph_alloc();
// 2.获取两个特殊的滤波器,输入和输出(buffer/buffersink)
AVFilter *buffersrc = avfilter_get_by_name("buffer");
AVFilter *buffersink = avfilter_get_by_name("buffersink");
// 3.为滤波器添加像素等配置
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
pCodecContext->width, pCodecContext->height, AV_PIX_FMT_YUV420P,
1, 1,
16, 9);
// 4.创建输入滤波器
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);
if (ret < 0) {
NSLog(@"创建滤镜输入失败");
return NO;
}
// 5.创建输出滤波器的配置结构体
AVBufferSinkParams *buffersink_params;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
buffersink_params = av_buffersink_params_alloc();
buffersink_params->pixel_fmts = pix_fmts;
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, buffersink_params, filter_graph);
av_free(buffersink_params);
if (ret < 0) {
NSLog(@"创建buffersink失败");
return false;
}
// 6.创建输入输出的列表(包含名称及下一个输入或者输出)
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
// 7.解析字符串,构建滤波器
const char *filter_descr = "lutyuv='u=128:v=128'";
ret = avfilter_graph_parse(filter_graph, filter_descr, inputs, outputs, NULL);
if (ret < 0) {
NSLog(@"水印加载失败");
return NO;
}
// 8.上传滤波器
ret = avfilter_graph_config(filter_graph, NULL);
if (ret < 0) {
NSLog(@"水印连接存在错误显示");
return NO;
}
在构建完成滤波器之后,我们只需要把avcodec_receive_frame
或者avcodec_decode_video2
解码出来的AVFrame
添加到滤波器中,就完成了输入的动作,代码如下:
if (av_buffersrc_add_frame(buffersrc_ctx, pFrame) < 0) {
printf( "Error while feeding the filtergraph\n");
break;
}
上述操作之后,所有入队列的AVFrame
数据可以通过对应的方法获取出来,代码如下:
int ret = av_buffersink_get_frame(buffersink_ctx, outFrame);
if (ret < 0) {
NSLog(@"从滤镜获取avframe 数据失败");
break;
}
至此,整个的滤波器添加工作已经完成。
本文参考资料