ffmpeg 使用滤镜——代码实现

本文详细介绍了FFmpeg中的滤镜系统实现原理及使用方法,包括滤镜链的初始化过程、参数配置等,并提供了关键代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


http://blog.youkuaiyun.com/dangxw_/article/details/48999109


ffmpeg 中滤镜的实现其实并不高明,但是功能强大,先给出过滤镜的代码,再翻译我认为有的玩的滤镜。


一帧通过滤镜的函数:

[cpp]  view plain  copy
  1. int Filter_One_Frame(FilterArgs *filter_args,AVFrame *frame, AVFrame *filt_frame,const char * filter_descr)  
  2. {  
  3.     int ret;  
  4.     avcodec_register_all();  
  5.     av_register_all();  
  6.     avfilter_register_all();//注册,假如没有调用会报出不能创建滤镜链  
  7.   
  8.     if (filter_args == NULL || filter_descr == NULL ||frame == NULL ||filt_frame == NULL )  
  9.     {      
  10.         printf("%s\nLine %d:%s : input_mp4 == NULL or filter_descr == NULL\n", __FILE__, __LINE__,__func__);  
  11.          goto end;  
  12.     }  
  13.     if ((ret = init_filters(filter_descr,filter_args)) < 0)//初始化滤镜链  
  14.         goto end;  
  15.   
  16.         frame->pts = av_frame_get_best_effort_timestamp(frame);          
  17.         /* push the decoded frame into the filtergraph */  
  18.         if (av_buffersrc_add_frame(buffersrc_ctx, frame) < 0)  
  19.     {  
  20.                 av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");  
  21.             goto end;  
  22.     }  
  23.         /* pull filtered pictures from the filtergraph */  
  24.      while (1)  
  25.     {  
  26.                 ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);//过滤镜  
  27.                 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)  
  28.                     break;  
  29.                 if (ret < 0)  
  30.                     goto end;          
  31.        }  
  32.      ret = 0;  
  33. end:  
  34.   
  35.     if (filter_graph != NULL)  
  36.         avfilter_graph_free(&filter_graph);//释放  
  37.     if (ret < 0 && ret != AVERROR_EOF) {  
  38.         char buf[1024];  
  39.         av_strerror(ret, buf, sizeof(buf));  
  40.         fprintf(stderr, "Error occurred: %s\n", buf);  
  41.         return -1;  
  42.     }  
  43.         return 0;  
  44. }  

FilterArgs 一个结构体:

[cpp]  view plain  copy
  1. typedef struct Filter_Args  
  2. {  
  3.     int width;  
  4.     int height;  
  5.      enum AVPixelFormat pix_fmt;  
  6.     AVRational time_base;  
  7.     AVRational sample_aspect_ratio;  
  8. }FilterArgs;  

这些参数在创建滤镜得时候比较重要,假如和过滤镜得帧不符和,或者参数不合法会导致通过滤镜失败。假如帧的yuv不是从视频中解码出来,也就是没有time_base 和sample_aspect_ratio,那么我一般会设置成{1,20};

函数init_filters:

[cpp]  view plain  copy
  1. int init_filters(const char *filters_descr,FilterArgs *filter_args)  
  2.   
  3. {  
  4.   
  5.     char args[512];  
  6.   
  7.     int ret = 0;  
  8.   
  9.     AVFilter *buffersrc  = avfilter_get_by_name("buffer");  
  10.     AVFilter *buffersink = avfilter_get_by_name("buffersink");  
  11.     AVFilterInOut *outputs = avfilter_inout_alloc();  
  12.     AVFilterInOut *inputs  = avfilter_inout_alloc();  
  13.   
  14.     enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };//modify by elesos.com ?OEAE????oe??????  
  15.     if (filters_descr == NULL )  
  16.     {     
  17.         printf("%s\nLine %d:%s : filter_args == NULL or filter_descr == NULL\n", __FILE__, __LINE__,__func__);  
  18.          goto end;  
  19.     }  
  20.     filter_graph = avfilter_graph_alloc();  
  21.     if (!outputs || !inputs || !filter_graph )  
  22.     {  
  23.         ret = AVERROR(ENOMEM);  
  24.         goto end;  
  25.     }  
  26.     /* buffer video source: the decoded frames from the decoder will be inserted here. */  
  27.     if (filter_args)  
  28.     {  
  29.           snprintf(args, sizeof(args),  
  30.                     "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",  
  31.                     filter_args->width, filter_args->height, filter_args->pix_fmt,  
  32.                     filter_args->time_base.num, filter_args->time_base.den,  
  33.                     filter_args->sample_aspect_ratio.num, filter_args->sample_aspect_ratio.den);  
  34.     }  
  35.     else  
  36.     {  
  37.           snprintf(args, sizeof(args),  
  38.                     "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",  
  39.                     pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,  
  40.                     pCodecCtx->time_base.num, pCodecCtx->time_base.den,  
  41.                     pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);  
  42.     }  
  43.     puts(args);  
  44.     ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",  
  45.   
  46.                                        args, NULL, filter_graph);  
  47.     if (ret < 0) {  
  48.   
  49.         av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");  
  50.   
  51.         goto end;  
  52.   
  53.     }  
  54.      /* buffer video sink: to terminate the filter chain. */  
  55.     ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",  
  56.                                      NULL, NULL, filter_graph);  
  57.     if (ret < 0) {  
  58.         av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");  
  59.         goto end;  
  60.   
  61.     }  
  62.     ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);  
  63.     if (ret < 0) {  
  64.         av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");  
  65.         goto end;  
  66.     }  
  67.   
  68.     /* Endpoints for the filter graph. */  
  69.     outputs->name       = av_strdup("in");  
  70.     outputs->filter_ctx = buffersrc_ctx;  
  71.     outputs->pad_idx    = 0;  
  72.     outputs->next       = NULL;   
  73.   
  74.     inputs->name       = av_strdup("out");  
  75.     inputs->filter_ctx = buffersink_ctx;  
  76.     inputs->pad_idx    = 0;  
  77.     inputs->next       = NULL;  
  78.   
  79.     if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,&inputs, &outputs, NULL)) < 0)  
  80.               goto end;  
  81.   
  82.     if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)  
  83.   
  84.         goto end;  
  85.     end:  
  86.     avfilter_inout_free(&inputs);  
  87.     avfilter_inout_free(&outputs);  
  88.     return ret;  
  89.   
  90. }  

下篇给出我尝试过的滤镜及其效果:http://blog.csdn.NET/dangxw_/article/details/49001413



### 开发 FFmpeg 滤镜 开发自定义 FFmpeg 滤镜涉及多个方面,包括理解 FFmpeg 的架构、编写 C 代码以及编译集成到 FFmpeg 中。以下是创建简单滤镜的过程。 #### 创建简单的灰度转换滤镜 为了实现一个将彩色图像转为灰度图的滤镜,可以按照如下方式操作: 1. **初始化项目结构** 建立一个新的目录用于存放源码文件,并下载 FFmpeg 源码作为参考和依赖库。 2. **编写滤镜核心逻辑** 下面是一个简化版本的灰度化滤镜 `vf_grayscale.c` 文件的内容示例[^1]: ```c #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffer.h" typedef struct GrayscaleContext { const AVClass *class; } GrayscaleContext; #define OFFSET(x) offsetof(GrayscaleContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption grayscale_options[] = { { NULL } }; AVFILTER_DEFINE_CLASS(grayscale); // 初始化函数 static int init(AVFilterContext *ctx){ // 可以在这里设置一些默认参数或分配资源 return 0; } // 执行过滤器的核心算法 static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref){ AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; /* 获取输出缓冲区 */ AVFrame *outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!outpicref) return AVERROR(ENOMEM); uint8_t *src = inpicref->data[0]; // 输入数据指针 uint8_t *dst = outpicref->data[0]; // 输出数据指针 int linesize_in = inpicref->linesize[0]; int linesize_out = outpicref->linesize[0]; for (int y=0; y<inpicref->height; ++y){ for(int x=0; x<inpicref->width*3; x+=3){ // 假设RGB24格式 unsigned char r = src[y*linesize_in+x]; unsigned char g = src[y*linesize_in+x+1]; unsigned char b = src[y*linesize_in+x+2]; // 计算亮度值 Y = 0.299R + 0.587G + 0.114B float gray_value = 0.299*r + 0.587*g + 0.114*b; dst[y*linesize_out+(x/3)] = (unsigned char)(gray_value); } } av_frame_free(&inpicref); return ff_filter_frame(outlink, outpicref); } // 定义静态常量描述符 static const AVFilterPad inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .filter_frame = filter_frame, }, {NULL} }; static const AVFilterPad outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, }, {NULL} }; // 注册此滤镜FFmpeg框架内 AVFilter ff_vf_grayscale = { .name = "grayscale", .description = NULL_IF_CONFIG_SMALL("Convert video to grayscale."), .init = init, .uninit = uninit, .query_formats = query_formats, .priv_size = sizeof(GrayscaleContext), .inputs = inputs, .outputs = outputs, .priv_class=&grayscale_class, }; ``` 这段代码实现了基本的功能——接收输入帧,在内存中将其颜色空间由 RGB 转换为单通道灰度表示形式后再传递给下一个环节。注意这里假设输入像素是以 RGB24 格式存储;实际应用时可能需要考虑更多种情况下的兼容性处理[^3]。 3. **配置与构建** 完成上述工作之后,还需要修改 FFmpeg 构建系统的 Makefile 和其他必要的配置文件来包含新添加的模块。具体来说就是编辑 `filters/filters.c`, 添加一行注册语句以便让整个程序知道有这样一个新的外部插件存在。 最后运行标准流程重新编译整个工程即可使新增加的效果生效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值