在ffmpeg中,進行反交錯需要用到avfilter,即圖像過濾器,ffmpeg中有很多過濾器,很強大,反交錯的過濾器是yadif.
基本的過濾器使用流程是:
解碼后的畫面--->buffer過濾器---->其他過濾器---->buffersink過濾器--->處理完的畫面
所有的過濾器形成了過濾器鏈,一定要的兩個過濾器是buffer過濾器和buffersink過濾器,前者的作用是將解碼后的畫面加載到過濾器鏈中,后者的作用是將處理好的畫面從過濾器鏈中讀取出來.那么進行反交錯的過濾器鏈應該是這樣的: buffer過濾器--->yadif過濾器--->buffersink過濾器
過濾器相關的結構體:
AVFilterGraph: 管理所有的過濾器圖像
AVFilterContext: 過濾器上下文
AVFilter: 過濾器
下面來看如何創建過濾器鏈:
第一步,創建AVFilterGraph
AVFilterGraph *filter_graph=avfilter_graph_alloc();
第二步,獲取要使用的過濾器:
AVFilter *filter_buffer=avfilter_get_by_name("buffer");
AVFilter *filter_yadif=avfilter_get_by_name("yadif");
AVFilter *filter_buffersink=avfilter_get_by_name("buffersink");
第三步,創建過濾器上下文,即AVFilterContext:
int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
const char *name, const char *args, void *opaque,
AVFilterGraph *graph_ctx);
參數說明:filt_ctx用來保存創建好的過濾器上下文,filt是過濾器,name是過濾器名稱(在過濾器鏈中應該唯一),args是傳給過濾器的參 數(每個過濾器不同,可以在相應的過濾器代碼找到),opaque在代碼中沒有被使用,graph_ctx是過濾器圖像管理指針.例:
AVFilterContext *filter_buffer_ctx,*filter_yadif_ctx,*filter_buffersink_ctx;
//創建buffer過濾器
snprintf(args, sizeof(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,
dec_ctx->time_base.num, dec_ctx->time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
avfilter_graph_create_filter(&filter_buffer_ctx, avfilter_get_by_name("buffer"), "in",
args, NULL, filter_graph);
//創建yadif過濾器
avfilter_graph_create_filter(&filter_yadif_ctx, avfilter_get_by_name("yadif"), "yadif",
"mode=send_frame:parity=auto:deint=interlaced", NULL, filter_graph);
//創建buffersink過濾器
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
avfilter_graph_create_filter(&filter_buffersink_ctx, avfilter_get_by_name("buffersink"), "out",
NULL, NULL,filter_graph);
av_opt_set_int_list(filter_buffersink_ctx, "pix_fmts", pix_fmts,
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
第四步,連接過濾器
avfilter_link(filter_buffer_ctx, 0, filter_yadif_ctx, 0);
avfilter_link(filter_yadif_ctx, 0, filter_buffersink_ctx, 0);
第五步,檢查所有配置是否正確:
if ((ret = avfilter_graph_config(player->filter_graph, NULL)) < 0){
LOGE(0,"avfilter_graph_config:%d\n",ret);
goto end;
}
注意上面所有的函數都應該檢查返回值,這里是略寫,到這里如果沒出錯的話,過濾器鏈就創建好了.
如何使用過濾器鏈進行過濾,主要是使用兩個函數:
//將解碼后的frame推送給過濾器鏈
int av_buffersrc_add_frame_flags(AVFilterContext *buffer_src,
AVFrame *frame, int flags);
//將處理完的frame拉取出來:
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
例如:
av_buffersrc_add_frame_flags(filter_buffer_ctx, orgin_frame, AV_BUFFERSRC_FLAG_KEEP_REF);
while(1){
ret = av_buffersink_get_frame(filter_buffersink_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
break;
}
display(frame);
};
反交錯前的圖像和反交錯后的圖像對比:
雖然比較模糊,但是橫紋確實去掉了.
找了一下,發現ffmpeg中還有一個反交錯的過濾器kerndeint,是GPL授權,使用時要配置打開GPL
傳入參數thresh=0:map=0:order=1:sharp=0:twoway=0后,看來起比yadif要好一些,效果如下圖:
ffmpeg還有一些deinterlace的過濾器,測試發現在過濾時間和畫面質量方面,比較好的是pp/lb過濾器。