基础
- filter: 数据处理器
- filter_chain: 链表链接链接所有的filter
- filter_graph:数据处理管理器
- 查看filter的相关事项:ffmpeg -h filter=filter_name
| $ ffmpeg -h filter=crop |
| Filter crop |
| Crop the input video. |
| Inputs: |
| #0: default (video) |
| Outputs: |
| #0: default (video) |
| crop AVOptions: |
| out_w <string> ..FV..... set the width crop area expression (default "iw") |
| w <string> ..FV..... set the width crop area expression (default "iw") |
| out_h <string> ..FV..... set the height crop area expression (default "ih") |
| h <string> ..FV..... set the height crop area expression (default "ih") |
| x <string> ..FV..... set the x crop area expression (default "(in_w-out_w)/2") |
| y <string> ..FV..... set the y crop area expression (default "(in_h-out_h)/2") |
| keep_aspect <boolean> ..FV..... keep aspect ratio (default false) |
| exact <boolean> ..FV..... do exact cropping (default false) |
初始化过滤器
创建graph
| |
| AVFilterGraph *filter_graph = avfilter_graph_alloc(); |
| if (!filter_graph) { |
| printf("failed to create filter graph\n"); |
| return -1; |
| } |
创建chain
编码创建
创建输入buffer
| char args[512]; |
| memset(args, 0, 512); |
| sprintf(args, |
| "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", |
| in_width, |
| in_height, |
| AV_PIX_FMT_YUV420P, |
| 1, |
| 25, |
| 1, |
| 1); |
| AVFilter *buffser_src = |
| avfilter_get_by_name("buffer"); |
| AVFilterContext *buffer_src_ctx; |
| ret = avfilter_graph_create_filter( |
| &buffer_src_ctx, buffser_src, "in", args, NULL, filter_graph); |
| if (ret < 0) { |
| printf("failed to create filter buffersrc\n"); |
| return -1; |
| } |
输出buffersink
| |
| AVBufferSinkParams *buffer_sink_paras; |
| AVFilterContext *buffer_sink_ctx; |
| AVFilter *buffer_sink = avfilter_get_by_name("buffersink"); |
| enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }; |
| buffer_sink_paras = av_buffersink_params_alloc(); |
| buffer_sink_paras->pixel_fmts = pix_fmts; |
| ret = avfilter_graph_create_filter(&buffer_sink_ctx, |
| buffer_sink, |
| "out", |
| NULL, |
| buffer_sink_paras, |
| filter_graph); |
| if (ret < 0) { |
| printf("failed to create split filter\n"); |
| return -1; |
| } |
个人需要使用的filter
| |
| AVFilter *split_filter = avfilter_get_by_name("split"); |
| AVFilterContext *split_filter_ctx; |
| ret = avfilter_graph_create_filter(&split_filter_ctx, |
| split_filter, |
| "split", |
| "outputs=2", |
| NULL, |
| filter_graph); |
| if (ret < 0) { |
| printf("failed to create split filter\n"); |
| return -1; |
| } |
| |
| |
| AVFilter *crop_filter = avfilter_get_by_name("crop"); |
| AVFilterContext *crop_filter_ctx; |
| ret = avfilter_graph_create_filter(&crop_filter_ctx, |
| crop_filter, |
| "crop", |
| "out_w=iw:out_h=ih/2:x=0:y=0", |
| NULL, |
| filter_graph); |
| if (ret < 0) { |
| printf("failed to create crop filter\n"); |
| return -1; |
| } |
| |
| |
| AVFilter *vflip_filter = avfilter_get_by_name("vflip"); |
| AVFilterContext *vflip_filter_ctx; |
| ret = avfilter_graph_create_filter( |
| &vflip_filter_ctx, vflip_filter, "vflip", NULL, NULL, filter_graph); |
| if (ret < 0) { |
| printf("failed to create vflip filter\n"); |
| return -1; |
| } |
| |
| |
| AVFilter *overlay_filter = avfilter_get_by_name("overlay"); |
| AVFilterContext *overlay_filter_ctx; |
| ret = avfilter_graph_create_filter(&overlay_filter_ctx, |
| overlay_filter, |
| "overlay", |
| "y=0:H/2", |
| NULL, |
| filter_graph); |
| if (ret < 0) { |
| printf("failed to create overlay filter\n"); |
| return -1; |
| } |
link需要的filters
| |
| ret = avfilter_link(buffer_src_ctx, 0, split_filter_ctx, 0); |
| if (ret != 0) { |
| printf("failed to link src filter and split filter\n"); |
| return -1; |
| } |
| |
| ret = avfilter_link(split_filter_ctx, 0, overlay_filter_ctx, 0); |
| if (ret != 0) { |
| printf("failed to link split filter's first pad and overlay filter\n"); |
| return -1; |
| } |
| |
| ret = avfilter_link(split_filter_ctx, 1, crop_filter_ctx, 0); |
| if (ret != 0) { |
| printf("failed to link split filter's second pad and crop filter\n"); |
| return -1; |
| } |
| |
| ret = avfilter_link(crop_filter_ctx, 0, vflip_filter_ctx, 0); |
| if (ret != 0) { |
| printf("failed to link crop filter and vflip filter\n"); |
| return -1; |
| } |
| |
| ret = avfilter_link(vflip_filter_ctx, 0, overlay_filter_ctx, 1); |
| if (ret != 0) { |
| printf("failed to link vflip filter and overlay's second filter\n"); |
| return -1; |
| } |
| |
| ret = avfilter_link(overlay_filter_ctx, 0, buffer_sink_ctx, 0); |
| if (ret != 0) { |
| printf("failed to link overlay filter and sink filter\n"); |
| return -1; |
| } |
初始化graph
| |
| ret = avfilter_graph_config(filter_graph, NULL); |
| if (ret < 0) { |
| printf("failed in filter graph\n"); |
| return -1; |
| } |
命令创建
初始化命令行
| char filter_args[1024] = { 0 }; |
| snprintf( |
| filter_args, |
| sizeof(filter_args), |
| "buffer=video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d[v0];" |
| "[v0]split[main][tmp];" |
| "[tmp]crop=iw:ih/2:0:0,vflip[flip];" |
| "[main][flip]overlay=0:H/2[result];" |
| "[result]buffersink", |
| width, |
| height, |
| format, |
| 1, |
| 25, |
| 1, |
| 1); |
利用命令行初始化graph
| AVFilterInOut *inputs = NULL; |
| AVFilterInOut *outputs = NULL; |
| int ret = avfilter_graph_parse2(filter_graph, filter_args, &inputs, &outputs); |
| if (ret < 0) { |
| printf("cannot parse graph\n"); |
| return ret; |
| } |
| ret = avfilter_graph_config(filter_graph, NULL); |
| if (ret < 0) { |
| printf("cannot config graph\n"); |
| return ret; |
| } |
获取输入buffer和输出buffersink
| mainsrc_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_0"); |
| if (!mainsrc_ctx) { |
| printf("avfilter_graph_get_filter Parsed_buffer_0 failed\n"); |
| return -1; |
| } |
| resultsink_ctx = |
| avfilter_graph_get_filter(filter_graph, "Parsed_buffersink_5"); |
| if (!resultsink_ctx) { |
| printf("avfilter_graph_get_filter Parsed_buffer_5 failed\n"); |
| return -1; |
| } |
处理数据
向buffer中添加数据
| if (av_buffersrc_add_frame(mainsrc_ctx, frame_in) < 0) { |
| printf("error while add frame\n"); |
| break; |
| } |
从buffersink中获取数据
| ret = av_buffersink_get_frame(resultsink_ctx, frame_out); |
| if (ret < 0) |
| break; |
其他业务
| |
| if (frame_out->format == AV_PIX_FMT_YUV420P) { |
| for (int i = 0; i < frame_out->height; ++i) |
| fwrite(frame_out->data[0] + frame_out->linesize[0] * i, |
| 1, |
| frame_out->width, |
| outFile); |
| for (int i = 0; i < frame_out->height / 2; ++i) |
| fwrite(frame_out->data[1] + frame_out->linesize[1] * i, |
| 1, |
| frame_out->width / 2, |
| outFile); |
| for (int i = 0; i < frame_out->height / 2; ++i) |
| fwrite(frame_out->data[2] + frame_out->linesize[2] * i, |
| 1, |
| frame_out->width / 2, |
| outFile); |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)