FFmpeg filter浅析--下篇

2022-11-19 10:14:30 浏览数 (2)

在之前的两篇文章中,我们熟悉了FFmpeg filter的基本概念,命令行用法和关键结构体、API使用流程

FFmpeg filter浅析--上篇

FFmpeg filter浅析--中篇

本篇文章我们在之前的Demo演示工程中通过调用libavfilter相关API来实现drawgrid filter效果

(实际敲一遍代码对自己的理解和记忆是非常有帮助的)

先通过命令行演示一下最终要实现的效果:3x3的白色网格

代码的实现并不复杂,在原有视频解码链路中添加filter初始化和后处理的流程即可,流程图如下

初始化filters

代码语言:javascript复制
void VideoDecoder::initFilters() {
    const AVFilter *bufferSrc = avfilter_get_by_name("buffer");
    const AVFilter *bufferSink = avfilter_get_by_name("buffersink");

    mFilterOutputs = avfilter_inout_alloc();
    mFilterInputs = avfilter_inout_alloc();
    mFilterGraph = avfilter_graph_alloc();
    if (!mFilterOutputs || !mFilterInputs || !mFilterGraph) {
        LOGE("initFilters failed")
        return;
    }

    int ret;
    do {
        char args[512];
        snprintf(args, sizeof(args),
                 "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
                 mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt,
                 mTimeBase.num, mTimeBase.den,
                 mCodecContext->sample_aspect_ratio.num, mCodecContext->sample_aspect_ratio.den);
        LOGI("avfilter_graph_create_filter, args: %s", args)
        ret = avfilter_graph_create_filter(&mBufferScrCtx, bufferSrc, "in", args, nullptr, mFilterGraph);
        if (ret < 0) {
            LOGE("Cannot create buffer source, ret: %d", ret)
            break;
        }

        ret = avfilter_graph_create_filter(&mBufferSinkCtx, bufferSink, "out", nullptr, nullptr, mFilterGraph);
        if (ret < 0) {
            LOGE("Cannot create buffer sink, ret: %d", ret)
            break;
        }

        enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
        ret = av_opt_set_int_list(mBufferSinkCtx, "pix_fmts", pix_fmts,
                                  AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
        if(ret < 0) {
            LOGE("set output pixel format failed, err=%d", ret);
            break;
        }

        mFilterOutputs->name = av_strdup("in");
        mFilterOutputs->filter_ctx = mBufferScrCtx;
        mFilterOutputs->pad_idx = 0;
        mFilterOutputs->next = nullptr;

        mFilterInputs->name = av_strdup("out");
        mFilterInputs->filter_ctx = mBufferSinkCtx;
        mFilterInputs->pad_idx = 0;
        mFilterInputs->next = nullptr;

        // ffmpeg -h filter=drawgrid
        std::string filterDesc = "drawgrid=w=iw/3:h=ih/3:t=2:c=white@0.5";
        ret = avfilter_graph_parse_ptr(mFilterGraph, filterDesc.c_str(), &mFilterInputs, &mFilterOutputs, nullptr);
        LOGI("avfilter_graph_parse_ptr, ret: %d", ret)
        if (ret < 0) {
            break;
        }

        ret = avfilter_graph_config(mFilterGraph, nullptr);
        LOGI("avfilter_graph_config, ret: %d", ret)
    } while (false);

    if (ret < 0) {
        avfilter_inout_free(&mFilterInputs);
        mFilterInputs = nullptr;
        avfilter_inout_free(&mFilterOutputs);
        mFilterOutputs = nullptr;

        mBufferScrCtx = nullptr;
        mBufferSinkCtx = nullptr;

        avfilter_graph_free(&mFilterGraph);
        mFilterGraph = nullptr;
        LOGE("initFilters failed, clean ctx")
    }
}

filter处理

代码语言:javascript复制
// send decode frame to filter graph
int ret = av_buffersrc_add_frame_flags(mBufferScrCtx, mAvFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
LOGI("av_buffersrc_add_frame_flags, ret: %d", ret)

// get filter frame from filter graph
ret = av_buffersink_get_frame(mBufferSinkCtx, mFilterAvFrame);
LOGI("av_buffersink_get_frame, ret: %d, format: %d", ret, mFilterAvFrame->format)

再贴一下之前文章发过的FFmpeg过滤器整体处理流程图

Demo运行效果如下

0 人点赞