在之前的两篇文章中,我们熟悉了FFmpeg filter的基本概念,命令行用法和关键结构体、API使用流程
本篇文章我们在之前的Demo演示工程中通过调用libavfilter相关API来实现drawgrid filter效果
(实际敲一遍代码对自己的理解和记忆是非常有帮助的)
先通过命令行演示一下最终要实现的效果:3x3的白色网格
代码的实现并不复杂,在原有视频解码链路中添加filter初始化和后处理的流程即可,流程图如下
初始化filters
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处理
// 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运行效果如下