ffmpeg filter命令行及API调用

命令行语法

ffmpeg中定义了很多filter,例如视频缩放(scale)、叠加(overlay)、截取(trim)、裁剪(crop)、旋转(rotate)等,ffmpeg支持的filter可以用下面的命令查看:

ffmpeg -filters

ffmpeg中某个filter的使用方法可用下面的命令查看:

#filter_name:filter名,例如sclae、overlay
ffmpeg -h filter=$filter_name

 ffmpeg filter语法包含3个层次:filter->filterchain->filtergraph

filter语法

用一个字符串描述filter的组成,形式如下

[in_link_1]...[in_link_N]filter_name=parameters[out_link_1]...[out_link_N]

 [in_link_N][out_link_N]:标识输入和输出的标签名,可以自定义,需要用"[]"括起来。1个filter可能有多个输入和输出,没有输入的filter称为source filter,没有输出的filter称为sink filter。对输入和输出打标签是可选的,打标签是为了方便连接其他filter。

parameters:初始化filter的参数,有以下几种形式:

#1. 使用":"字符分割的1个"key=value"列表,例如下面的命令将视频宽高缩小1倍
ffmpeg -i input.mp4 -vf scale=w=iw/2:h=ih/2 output.mp4


#2. 使用":"字符分割的value列表,在这种情况下key按照声明的顺序来接收value的值,例如scale filter的前两个选项分别是w和h,下面的命令将iw/2赋值给w,ih/2赋值给h
ffmpeg -i input.mp4 -vf scale=iw/2:ih/2 output.mp4


#3. 使用":"字符分割混合key和key=value列表,key必须在key=value之前,遵循第2种形式的约束规则,例如:
ffmpeg -i input.mp4 -vf scale=iw/2:h=ih/2 output.mp4


#4. 如果选项的值本身就是1个列表,则这种列表通常使用"|"分割,例如:
ffmpeg -i input.mp4 -vf "format=pix_fmts=yuv420p|yuv444p|yuv410p" output.mp4

 filterchain语法

用1个字符串描述filterchain的组成,形式如下,filter之间以","隔开,前1个filter的输出连接后1个filter的输入。

filter1,filter2,...filterN-1,filterN

在下面的例子中,[tmp]crop=iw:ih/2:0:0,vfilp[flip]就是1个filterchain

#1. split filter将输入视频分离成两路完全相同的流main和tmp
#2. tmp流输入到crop filter中,crop filter将其下半部分裁剪掉,再输入到vflip filter中做垂直旋转,输出flip
#3. 将flip叠加在main的[0,H/2]位置处,最终输出的视频上半部分是原始的,下半部分是上半部分的倒影
ffmpeg -i input.mp4 -vf "split[main][tmp];[tmp]crop=iw:ih/2:0:0,vflip[flip];[main][flip]overlay=0:H/2" iutput.mp4

filtergraph语法

用1个字符串描述filtergraph的组成,形式如下,filterchain之间以";"隔开

filterchain1;filterchain2...filterchainN-1;filterchainN

例如split[main][tmp];[tmp]crop=iw:ih/2:0:0,vfilp[flip]就是1个filtergraph

关于","和";"的使用,可以这样理解:对同一路流做处理的filter用","隔开,对不同路流做处理的filter用";"隔开,例如[tmp]crop=iw:ih/2:0:0,vfilp[flip],crop和vflip都是对tmp做处理,所以用","隔开。

ffmpeg filter命令行有简单filter和复杂filter,简单filter只有1个输入流,使用-vf或-filter:v命令,例如:

ffmpeg -i input.mp4 -filter:v scale=iw/2:ih/2 output.mp4

ffmpeg -i input.mp4 -vf "split[main][tmp];[tmp]crop=iw:ih/2:0:0,vflip[flip];[main][flip]overlay=0:H/2" output.mp4

复杂filter有多个输入流,使用-filtercomplex或-lavfi命令,例如:

#[1:v]这个参数中1表示操作对象的编号,在本例中0是input.mp4,1是image1.png,2是image2.png,3是output.mp4。v表示操作的是对象里的视频信息。
#[1:v]scale=100:100[img1]表示调整image1.png的尺寸为100x100,输出img1
#[2:v]scale=200:200[img2]表示调整image2.png的尺寸为200x200,输出img2
#[0:v][img1]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2[bkg]表示将img1叠加到input.mp4的视频上,输出bkg。叠加位置为[(main_w-overlay_w)/2:(main_h-overlay_h)/2],其中main_w、main_h表示主视频input.mp4的宽和高,overlay_w、overlay_h表示水印img1的宽和高。
#[bkg][img2]overlay=0:0表示将img2叠加到bkg上,叠加位置为[0,0]。
#这个命令相当于给input.mp4添加了两个水印image1和image2

ffmpeg -i input.mp4 -i image1.png -i image2.png -filter_complex[1:v]scale=100:100[img1];[2:v]scale=200:200[img2];[0:v][img1]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2[bkg];[bkg][img2]overlay=0:0 -y output.mp4
#下面的命令实现了ffplay同时播放两个视频,可用于主观对比两个视频的质量
#1. color=s=2160*1920:c=black[base]:新建1块大小为2160x1920的黑色幕布,输出base
#2. [base][org1]overlay=0:0[out1]:将org1(input1.mp4)从位置[0,0]处叠加到base上,输出out1。
#3. [out1][org2]overlay=1080:0:将org2(input2.mp4)从位置[1080,0]处叠加到out1上
ffplay -f lavfi -i "color=s=2160*1920:c=black[base];movie=input1.mp4[org1];movie=input2.mp4[org2];[base][org1]overlay=0:0[out1];[out1][org2]overlay=1080:0"

API使用

ffmpeg filter graph有以下3种创建方式: 

1. 用avfilter_graph_create_filter函数一个一个创建滤镜(AVFilterContext),然后用avfilter_link函数把各个滤镜的输入输出连接起来。这种方式比较灵活,但是非常繁琐。例如创建1个scale滤镜:

static int init_filter_v1(int width, int height) {
    AVFilterGraph   *filter_graph;
    AVFilterContext *buffer_ctx;
    AVFilterContext *scale_ctx;
    AVFilterContext *buffersink_ctx;
    
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        printf("alloc filter graph failed");
        return -1;
    }

    int ret = 0;
    AVFilter *buffer = avfilter_get_by_name("buffer");
    AVFilter *scale = avfilter_get_by_name("scale");
    AVFilter *buffersink = avfilter_get_by_name("buffersink");

    enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};

    AVBPrint args;
    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
    av_bprintf(&args, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
               dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
               ifmt_ctx->streams[video_index]->time_base.num, ifmt_ctx->streams[video_index]->time_base.den,
               ifmt_ctx->streams[video_index]->sample_aspect_ratio.num, ifmt_ctx->streams[video_index]->sample_aspect_ratio.den);
    printf("buffer args=%s\n", args.str);

    ret = avfilter_graph_create_filter(&buffer_ctx, buffer, "in", args.str, NULL, filter_graph);
    if (ret < 0) {
        printf("create buffer ctx failed\n");
        return ret;
    }

    av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
    av_bprintf(&args, "w=%d:h=%d", width, height);
    printf("scale args=%s\n", args.str);
    ret = avfilter_graph_create_filter(&scale_ctx, scale, "scale", args.str, NULL, filter_graph);
    if (ret < 0) {
        printf("create scale ctx failed\n");
        return ret;
    }

    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph);
    if(ret < 0) {
        printf("create buffersink ctx failed\n");
        return ret;
    }

    ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        printf("Cannot set output pixel format\n");
        return ret;
    }

    //连接buffer滤镜和scale滤镜
    if ((ret = avfilter_link(buffer_ctx, 0, scale_ctx, 0)) < 0) {
        printf("link buffer ctx to scale ctx failed\n");
        return ret;
    }

    //连接scale滤镜和buffersink滤镜
    if ((ret = avfilter_link(scale_ctx, 0, buffersink_ctx, 0)) < 0) {
        printf("link scale ctx to buffersink ctx failed\n");
        return ret;
    }

    //正式打开滤镜
    if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
        printf("config filter graph failed\n");
        return ret;
    }

    printf("create filter graph success\n");
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值