zoukankan      html  css  js  c++  java
  • FFmpeg filter

    一、FFmpeg filter简介

    FFmpeg中的libavfilter提供了一整套的基于filter的机制。filter本身是一个插件的形式,可以快速的组装需要的效果。
    比如下面的filter,可以实现视频的水平镜像效果。
    ffplay.exe sample.rmvb -vf hflip

    FFmpeg定义的libavcodec接口已经成为在编解码领域的事实上的行业标准。但音视频filter并没有类似的标准,多个不同的多媒体项目(比如MPlayer、Xine、GStreamer等)都实现了自定义的filter系统。为了统一filter库API接口,FFmpeg提出了参考DirectDraw实现了高质量、高效、灵活的音视频filter接口。详细的文档资料可以参考FFmpeg filter。

    filter的分类

    按照处理数据的类型,通常多媒体的filter分为:

    ·                     音频filter

    ·                     视频filter

    ·                     字幕filter

    FFmpeg中filter分为:

    ·                     source filter (只有输出)

    ·                     audio filter

    ·                     video filter

    ·                     Multimedia filter

    ·                     sink filter (只有输入)

    除了source和sink filter,其他filter都至少有一个输入、至少一个输出。

    介绍了这么多,下面也是一个例子,使用filter实现宽高减半显示:

    ffplay.exe sample.rmvb -vf scale=iw/2:ih/2

    FFmpeg filter可以认为是一些预定义的范式,可以实现类似积木的多种功能的自由组合。每个filter都有固定数目的输入和输出,而且实际使用中不允许有空悬的输入输出端。使用文本描述时我们可以通过标识符指定输入和输出端口,将不同filter串联起来,构成更复杂的filter。这就形成了嵌套的filter。当然每个filter可以通过ffmpeg/ffplay命令行实现,但通常filter更方便。

    ffmpeg.exe、ffplay.exe能够通过filter处理原始的音视频数据。ffmpeg将filtergraph分为simple filtergraph和complex filtergraph。通常simple filtergraph只有一个输入和输出,ffmpeg命令行中使用-vf、-af识别,基本原理图如下:

         _________                        ______________
        |         |                      |              |
        | decoded |                      | encoded data |
        | frames  |                   _ | packets      |
        |_________|                  /||______________|
                        __________   /
          simple     _||          | /  encoder
          filtergraph   | filtered |/
                        | frames   |
                        |__________|
    complex filtergraph,通常是具有多个输入输出文件,并有多条执行路径;ffmpeg命令行中使用-lavfi、-filter_complex,基本原理图如下:

     _________
    |         |
    | input 0 |                    __________
    |_________|                  |          |
                    _________    /| output 0 |
                  |         |  / |__________|
     _________     | complex | /
    |         |     |         |/
    | input 1 |---->| filter  |
    |_________|     |         |   __________
                   /| graph   |   |          |
                  / |         |   | output 1 |
     _________   /  |_________|    |__________|
    |         | /
    | input 2 |/

    FFmpeg中filter包含三个层次,filter->filterchain->filtergraph。具体可以参考下图:


    filter是ffmpeg的libavfilter提供的基础单元。在同一个线性链中的filter使用逗号分隔,在不同线性链中的filter使用分号隔开,比如下面的例子:

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

    这里crop、vflip处于同一个线性链,split、overlay位于另一个线性链。二者连接通过命名的label实现(位于中括号中的是label的名字)。在上例中split filter有两个输出,依次命名为[main]和[tmp];[tmp]作为crop filter输入,之后通过vflip filter输出[flip];overlay的输入是[main]和[flilp]。如果filter需要输入参数,多个参数使用冒号分割。
    对于没有音频、视频输入的filter称为source filter,没有音频、视频输出的filter称为sink filter。

    经典的filter

    FFmpeg支持的所有filter可以通过filters查看。
    这里选几个相对经典的filter。

    音频filter

    adelay filter
    实现不同声道的延时处理。使用参数如下adelay=1500|0|500,这个例子中实现第一个声道的延迟1.5s,第三个声道延迟0.5s,第二个声道不做调整。

    aecho filter
    实现回声效果,具体参考http://ffmpeg.org/ffmpeg-filters.html#aecho。

    amerge filter
    将多个音频流合并成一个多声道音频流。具体参考http://ffmpeg.org/ffmpeg-filters.html#amerge-1。

    ashowinfo filter
    显示每一个audioframe的信息,比如时间戳、位置、采样格式、采样率、采样点数等。具体参考http://ffmpeg.org/ffmpeg-filters.html#ashowinfo。

    panfilter
    特定声道处理,比如立体声变为单声道,或者通过特定参数修改声道或交换声道。主要有两大类:
    混音处理,比如下面的例子pan=1c|c0=0.9*c0+0.1*c1,实现立体声到单声道的变换;
    声道变换,比如5.1声道顺序调整,pan="5.1| c0=c1 | c1=c0| c2=c2 | c3=c3 | c4=c4 | c5=c5"。

    silencedetect和silenceremove filter
    根据特定参数检测静音和移除静音。

    volume和volumedetect filter
    这两个filter分别实现音量调整和音量检测。

    audio source filter
    aevalsrc filter按照特定表达式生成音频信号。
    anullsrc filter生成特定的原始音频数据,用于模板或测试。
    anoisesrc filter生成噪声音频信号。
    sine filter生成正弦波音频信号。

    audio sink filter
    abuffersink filter和anullsink filter,这些filter只是用于特定情况下结束filterchain。

    视频filter

    blend和tblend filter
    将两帧视频合并为一帧。具体参数参考http://ffmpeg.org/ffmpeg-filters.html#blend_002c-tblend。

    crop filter
    按照特定分辨率裁剪输入视频,具体参数参考http://ffmpeg.org/ffmpeg-filters.html#crop。

    drawbox、drawgrid、drawtext filter
    绘制box(对话框)、grid(表格)、text(文本)。

    edgedetect filter
    边缘检测filter。

    fps filter
    按照指定帧率输出视频帧(丢帧或者复制)。具体参考http://ffmpeg.org/ffmpeg-filters.html#fps-1。

    hflip、vflip filter
    水平和垂直镜像。

    histogram filter
    生成每帧的各颜色分量的直方图。

    noise filter
    在输入视频帧中添加白噪声。

    overlay filter
    视频叠加。具体参考http://ffmpeg.org/ffmpeg-filters.html#overlay-1。

    pad filter
    视频边界填充。具体参考http://ffmpeg.org/ffmpeg-filters.html#pad-1。

    rotate filter
    视频任意角度旋转。具体参考http://ffmpeg.org/ffmpeg-filters.html#rotate。

    scale filter
    使用libswscale库完成视频缩放的filter。

    showinfo filter
    显示视频帧的参数信息,比如时间戳、采样格式、帧类型等。

    subtitles filter
    使用libass库绘制subtitle(字幕)。

    thumbnail filter
    提取缩略图的filter。

    transpose filter
    图像转置的filter。参数参考http://ffmpeg.org/ffmpeg-filters.html#transpose。

    source filter
    主要有cellatuo、coreimagesrc、mptestsrc、life等filter,具体效果建议参考ffmpeg用户手册。

    source sink
    主要有buffersink、nullsink两个filter。

    多媒体filter

    ahistogram filter
    将音频转化为视频输出,并显示为音量的直方图。

    concat filter
    将音频流、视频流拼接成一个。具体参考http://ffmpeg.org/ffmpeg-filters.html#concat。

    metadata、ametadata filter
    操作metadata信息。

    setpts、asetpts filter
    改变输入音频帧或视频帧的pts。

    showfreqs、showspectrum、showspertrumpic、showvolume、showwaves filter
    将输入音频转换为视频显示,并显示频谱、音量等信息

    split、asplit filter
    将输入切分为多个相同的输出。

    source filter
    主要是movie、amovie filter。从movie容器中读取音频或者视频帧。

    为ffmpeg添加自定义滤镜
    https://blog.csdn.net/xiaojun111111/article/details/50849182

    二、过滤器使用流程和经常函数

    基本的过滤器使用流程是:

         解码后的画面--->buffer过滤器---->其他过滤器---->buffersink过滤器--->处理完的画面

    所有的过滤器形成了过滤器链,一定要的两个过滤器是buffer过滤器和buffersink过滤器,前者的作用是将解码后的画面加载到过滤器链中,后者的作用是将处理好的画面从过滤器链中读取出来。

    过滤器相关的结构体:

    AVFilterGraph: 管理所有的过滤器图像

    AVFilterContext: 过滤器上下文

    AVFilter: 过滤器

    流程中的关键函数如下所示:

    avfilter_register_all():注册所有AVFilter。
    avfilter_graph_alloc():为FilterGraph分配内存。
    avfilter_graph_create_filter():创建并向FilterGraph中添加一个Filter。
    avfilter_graph_parse_ptr():将一串通过字符串描述的Graph添加到FilterGraph中。参数3,4结构中可以指定需要连接输入过滤器、输出过滤器。

    avfilter_link():连接过滤器。连接输入输出过滤器。比如:

    avfilter_link(filter_buffer_ctx, 0,filter_yadif_ctx, 0);
     
    avfilter_link(filter_yadif_ctx, 0,filter_buffersink_ctx, 0);
    avfilter_graph_config():检查FilterGraph的配置。

    av_buffersrc_add_frame():向FilterGraph中加入一个AVFrame。

    av_buffersink_get_frame():从FilterGraph中取出一个AVFrame。

    三、实例

    1、  ffmeg 反交错

    参考:https://blog.csdn.net/crazyman2010/article/details/42913055

    2、最简单的基于FFmpeg的AVfilter的例子

    // test_avfilter.cpp : 定义控制台应用程序的入口点。
    //
     
    #include "stdafx.h"
     
     
    /**
    * 最简单的基于FFmpeg的AVFilter例子 - 纯净版
    * Simplest FFmpeg AVfilter Example - Pure
    *
    * 雷霄骅 Lei Xiaohua
    * leixiaohua1020@126.com
    * 中国传媒大学/数字电视技术
    * Communication University of China / Digital TV Technology
    * http://blog.csdn.net/leixiaohua1020
    *
    * 本程序使用FFmpeg的AVfilter实现了YUV像素数据的滤镜处理功能。
    * 可以给YUV数据添加各种特效功能。
    * 是最简单的FFmpeg的AVFilter方面的教程。
    * 适合FFmpeg的初学者。
    *
    * This software uses FFmpeg's AVFilter to process YUV raw data.
    * It can add many excellent effect to YUV data.
    * It's the simplest example based on FFmpeg's AVFilter.
    * Suitable for beginner of FFmpeg
    *
    */
    #include <stdio.h>
     
    #define __STDC_CONSTANT_MACROS
     
    #ifdef _WIN32
    #define snprintf _snprintf
    //Windows
    extern "C"
    {
    #include "libavfilter/avfiltergraph.h"
    #include "libavfilter/buffersink.h"
    #include "libavfilter/buffersrc.h"
    #include "libavutil/avutil.h"
    #include "libavutil/imgutils.h"
    };
    #else
    //Linux...
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #include <libavfilter/avfiltergraph.h>
    #include <libavfilter/buffersink.h>
    #include <libavfilter/buffersrc.h>
    #include <libavutil/avutil.h>
    #include <libavutil/imgutils.h>
    #ifdef __cplusplus
    };
    #endif
    #endif
     
     
    #pragma comment(lib,"avcodec.lib")
    #pragma comment(lib,"avformat.lib")
    #pragma comment(lib,"avutil.lib")
    #pragma comment(lib,"avfilter.lib")
    #pragma comment(lib,"swscale.lib")
    #pragma comment(lib,"SDL2.lib")
    #pragma comment(lib,"SDL2main.lib")
     
    int main(int argc, char* argv[])
    {
        getchar();
     
        int ret;
        AVFrame *frame_in;
        AVFrame *frame_out;
        unsigned char *frame_buffer_in;
        unsigned char *frame_buffer_out;
     
        AVFilterContext *buffersink_ctx;
        AVFilterContext *buffersrc_ctx;
        AVFilterGraph *filter_graph;
        static int video_stream_index = -1;
     
        //Input YUV
        FILE *fp_in = fopen("trans.yuv", "rb+");
        if (fp_in == NULL) {
            printf("Error open input file. ");
            return -1;
        }
        int in_width = 1588;
        int in_height = 900;
     
        //Output YUV
        FILE *fp_out = fopen("output.yuv", "wb+");
        if (fp_out == NULL) {
            printf("Error open output file. ");
            return -1;
        }
     
        //const char *filter_descr = "lutyuv='u=128:v=128'";
        //const char *filter_descr = "boxblur";
        //const char *filter_descr = "hflip";
        //const char *filter_descr = "hue='h=60:s=-3'";
        //const char *filter_descr = "crop=2/3*in_w:2/3*in_h";
        //const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5";
        const char *filter_descr = "drawtext=fontfile=arial.ttf:fontcolor=red:fontsize=30:text='Lei Xiaohua':x=50:y=50";
     
        avfilter_register_all();
     
        char args[512];
        AVFilter *buffersrc = avfilter_get_by_name("buffer");
        AVFilter *buffersink = avfilter_get_by_name("buffersink");//老版本ffmpeg使用 avfilter_get_by_name("ffbuffersink")
        AVFilterInOut *outputs = avfilter_inout_alloc();
        AVFilterInOut *inputs = avfilter_inout_alloc();
        enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
        AVBufferSinkParams *buffersink_params;
     
        filter_graph = avfilter_graph_alloc();
     
        /* buffer video source: the decoded frames from the decoder will be inserted here. */
        snprintf(args, sizeof(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);
     
        ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
            args, NULL, filter_graph);
        if (ret < 0) {
            printf("Cannot create buffer source ");
            return ret;
        }
     
        /* buffer video sink: to terminate the filter chain. */
        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) {
            printf("Cannot create buffer sink ");
            return ret;
        }
     
        /* Endpoints for the filter graph. */
        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;
     
        if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_descr,
            &inputs, &outputs, NULL)) < 0)
            return ret;
     
        if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
            return ret;
     
        frame_in = av_frame_alloc();
        frame_buffer_in = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
        av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in,
            AV_PIX_FMT_YUV420P, in_width, in_height, 1);
     
        frame_out = av_frame_alloc();
        frame_buffer_out = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
        av_image_fill_arrays(frame_out->data, frame_out->linesize, frame_buffer_out,
            AV_PIX_FMT_YUV420P, in_width, in_height, 1);
     
        frame_in->width = in_width;
        frame_in->height = in_height;
        frame_in->format = AV_PIX_FMT_YUV420P;
     
        while (1) {
     
            if (fread(frame_buffer_in, 1, in_width*in_height * 3 / 2, fp_in) != in_width*in_height * 3 / 2) {
                break;
            }
            //input Y,U,V
            frame_in->data[0] = frame_buffer_in;
            frame_in->data[1] = frame_buffer_in + in_width*in_height;
            frame_in->data[2] = frame_buffer_in + in_width*in_height * 5 / 4;
     
            if (av_buffersrc_add_frame(buffersrc_ctx, frame_in) < 0) {
                printf("Error while add frame. ");
                break;
            }
     
            /* pull filtered pictures from the filtergraph */
            ret = av_buffersink_get_frame(buffersink_ctx, frame_out);
            if (ret < 0)
                break;
     
            //output Y,U,V
            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, fp_out);
                }
                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, fp_out);
                }
                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, fp_out);
                }
            }
            printf("Process 1 frame! ");
            av_frame_unref(frame_out);
        }
     
        fclose(fp_in);
        fclose(fp_out);
     
        av_frame_free(&frame_in);
        av_frame_free(&frame_out);
        avfilter_graph_free(&filter_graph);
     
        return 0;
    }

    3、最简单的基于FFmpeg的AVfilter例子(水印叠加)

    https://blog.csdn.net/leixiaohua1020/article/details/29368911

    源代码:https://github.com/leixiaohua1020/simplest_ffmpeg_video_filter

     四、注意事项

    1、 显示文字乱码问题,文字需要是utf-8格式,字体也必须存在。

    2、 调用avfilter_graph_parse_ptr解析字幕水印时返回-22,ffmpeg报错No suchfilter: 'drawtext',这是由于编译的时候没有开启FreeType字体引擎库,编译的时候./configure--enable-libfreetype。

    3、 调用avfilter_graph_parse_ptr解析参数时返回-2,drawtext=fontfile=arial.ttf:fontcolor=red:fontsize=30:text='LeiXiaohua':x=50:y=50里面的arial.ttf是字体文件,要保证字体文件在程序可以找到的目录,冒号在里面是分隔符,路径中不能带冒号。

    4、 编译完ffmpeg后可能没有libpostproc.so这个库,编译的时候加入开启这个库的编译选项--enable-postproc。

    参考资料:

    https://www.cnblogs.com/tocy/p/ffmpeg-filter-intro.html

    https://blog.csdn.net/shixin_0125/article/details/78400252
    ref:https://blog.csdn.net/byxdaz/article/details/80642614

  • 相关阅读:
    前端下载远程文件
    Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().
    前端内存泄漏检查判断及处理
    在vue中使用import()来代替require.ensure()实现代码打包分离
    微信小程序将view动态填满全屏
    H5背景音乐自动播放(兼容微信IOS,进程后台切换自动停止播放,本文例子为Vue写法)
    JS获取移动端系统信息(操作系统、操作系统版本、横竖屏状态、设备类型、网络状态、生成浏览器指纹)
    JS判断图片是否加载完毕
    JS深度合并对象
    jsonp跨域请求
  • 原文地址:https://www.cnblogs.com/lidabo/p/15040893.html
Copyright © 2011-2022 走看看