当前页面访问量: 59

FFmpeg4入门系列教程20:视频添加滤镜

Posted by

索引地址:系列教程索引地址

上一篇:FFmpeg4入门系列教程19:实现简单音视频同步和简单视频播放器

音视频编解码部分介绍完了,接下来简单介绍一下视频处理。

因为视频处理有专业的软件,本文就简单介绍一下FFmpeg支持的视频处理滤镜。FFmpeg的滤镜是在解码之后在YUV数据之上直接处理的。

那么,滤镜主要包括两部分:解码、滤镜。

先看一下流程:

flow

解码视频

直接把FFmpeg4入门系列教程8:软解并使用QtWidget播放视频(YUV420P->RGB32)代码拿过来就可以了。

初始化滤镜

    /*
     * 开始初始化滤波器
    * Filter的具体定义,只要是libavfilter中已注册的filter,
    * 就可以直接通过查询filter名字的方法获得其具体定义,所谓
    * 定义即filter的名称、功能描述、输入输出pad、相关回调函数等
    */
    AVFilter *bufSrc = (AVFilter*)avfilter_get_by_name("buffer");/* 输入buffer filter */
    AVFilter *bufSink = (AVFilter*)avfilter_get_by_name("buffersink");/* 输出buffer filter */
    //AVFilterInOut对应buffer和buffersink这两个首尾端的filter的输入输出
    AVFilterInOut *outFilter = avfilter_inout_alloc();
    AVFilterInOut *inFilter = avfilter_inout_alloc();

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

    filterGraph = avfilter_graph_alloc();
    if(!outFilter||!inFilter||!filterGraph){
        ret = AVERROR(ENOMEM);
    }

    QString qargs=QString("video_size=%1x%2:pix_fmt=0:time_base=1/20")
            .arg(videoWidth)
            .arg(videoHeight);
    char* args=qargs.toLocal8Bit().data();

    //根据指定的Filter,这里就是buffer,构造对应的初始化参数args,二者结合即可创建Filter的示例,并放入filter_graph中
    int ret = avfilter_graph_create_filter(&bufSrcCtx,
                                           bufSrc,
                                           "in",
                                           args,NULL,
                                           filterGraph);
    if(ret<0){
        printf("Cannot create buffer source.\n");
    }

    /* buffer video sink: to terminate the filter chain. */
    ret = avfilter_graph_create_filter(&bufSinkCtx,
                                       bufSink,
                                       "out",
                                       NULL,
                                       NULL,
                                       filterGraph);
    if(ret<0){
        printf("Cannot creat buffer sink.\n");
    }

    ret = av_opt_set_int_list(bufSinkCtx, "pix_fmts", pixel_fmts,
                              AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
    }

    /* Endpoints for the filter graph. */
    outFilter->name = av_strdup("in");
    outFilter->filter_ctx = bufSrcCtx;
    outFilter->pad_idx =0;
    outFilter->next = NULL;

    inFilter->name = av_strdup("out");
    inFilter->filter_ctx = bufSinkCtx;
    inFilter->pad_idx = 0;
    inFilter->next = NULL;

    //filter_descr是一个filter命令,例如"overlay=iw:ih",该函数可以解析这个命令,
    //然后自动完成FilterGraph中各个Filter之间的联接
    ret = avfilter_graph_parse_ptr(filterGraph,
                                   //filterDesc,
                                   filterDescr.toStdString().data(),
                                   &inFilter,
                                   &outFilter,
                                   NULL);
    if(ret<0){
        printf("Cannot parse filter desc.\n");
    }

    //检查当前所构造的FilterGraph的完整性与可用性
    if(avfilter_graph_config(filterGraph,NULL)<0){
        printf("Check config failed.\n");
    }

filterDescr是有效的亮度对比度语句,如果此语句不变,那么只需要初始化一次,如果此语句变换(主要是参数),那么每次使用之前都需要初始化。

添加滤镜

原始数据帧经过解码后,在格式转换之前进行添加滤镜,代码为:

    AVFrame *filterFrame = av_frame_alloc();
    while(av_read_frame(fmtCtx,pkt)>=0){
        if(!runFlag){
            break;
        }
        initFilter();
        if(pkt->stream_index == videoStreamIndex){
            if(avcodec_send_packet(videoCodecCtx,pkt)>=0){
                int ret;
                while((ret=avcodec_receive_frame(videoCodecCtx,yuvFrame))>=0){
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        return;
                    else if (ret < 0) {
                        fprintf(stderr, "Error during decoding\n");
                        exit(1);
                    }

                    /* push the decoded frame into the filtergraph */
                    if (av_buffersrc_add_frame_flags(bufSrcCtx, yuvFrame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
                        av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
                        break;
                    }

                    //把滤波后的视频帧从filter graph取出来
                    if(av_buffersink_get_frame(bufSinkCtx,filterFrame)<0){
                        printf("Cannot get frame from bufSinkCtx.\n");
                        continue;
                    }

                    sws_scale(img_ctx,
                              filterFrame->data,filterFrame->linesize,
                              0,videoCodecCtx->height,
                              rgbFrame->data,rgbFrame->linesize);

                    QImage img(out_buffer,
                               videoCodecCtx->width,videoCodecCtx->height,
                               QImage::Format_RGB32);
                    emit sendQImage(img);
                    QThread::msleep(30);
                }
            }
            av_packet_unref(pkt);
        }
    }

效果

对比度(contrast)值必须是一个-2.0-2.0间的浮点数,默认为0。这个值不适合界面控制,我们将其扩展到0-10,5为默认值。

亮度(brightness)值必须是一个-1.0-1.0间的浮点数,默认为0。同样是扩展到0-10,5为默认值。

视频效果:FFmpeg解码添加滤镜Qt显示效果-B站

完整代码在ffmpeg_Beginner中的20.video_decode_add_filter_display_by_qwidget中。

下一篇:FFmpeg4入门系列教程21:音视频解混合(demuxer)为MP3和H264

赞赏

微信赞赏支付宝赞赏

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用*标注