【问题标题】:how to add reference movie into ffmpeg psnr filter in codes (computing psnr)如何在代码中将参考电影添加到ffmpeg psnr过滤器中(计算psnr)
【发布时间】:2025-12-23 04:50:11
【问题描述】:

我只是想了解如何在代码中使用ffmpeg中的psnr过滤器。

我已经按照https://ffmpeg.org/ffmpeg-filters.html#psnr:

sprintf(args, "movie=ref_movie.avi [main];[main][ref] psnr=\"stats_file=stats.log\" [out]");
err = avfilter_graph_create_filter(&psnrCtx,  psnrFilter, "psnr", args, NULL, m_filterGraph);
if ( err < 0 ) {
    avfilter_graph_free(&m_filterGraph);
    m_filterGraph = NULL;
    return false; }

但是错误信息返回给我:

Option 'movie' not found

我也试试这个:

sprintf(args, "stats_file=stats.log");
err = avfilter_graph_create_filter(&psnrCtx,  psnrFilter, "psnr", args, NULL, m_filterGraph);
if ( err < 0 ) {
   avfilter_graph_free(&m_filterGraph);
   m_filterGraph = NULL;
   return false; }
err = avfilter_link(last_ctx, 0, psnrCtx, 0);
if ( err < 0 ) {
   avfilter_graph_free(&m_filterGraph);
   m_filterGraph = NULL;
   return false;}

...  
err = avfilter_graph_config(m_filterGraph, NULL);
if ( err < 0 ) {
   avfilter_graph_free(&m_filterGraph);
   m_filterGraph = NULL;
   return false;
}

但错误信息返回给我:

Input pad "reference" with type video of the filter instance "psnr" of psnr not connected to any source

我在代码中搜索 psnr 过滤器,但没有太多关于它的信息。有人可以帮忙吗?

【问题讨论】:

    标签: filter ffmpeg


    【解决方案1】:

    您正在使用avfilter_graph_create_filter() 创建过滤器图而不是过滤器。请改用avfilter_graph_parse2() 创建整个过滤器图。

    过滤器图表与您在 ffmpeg 命令行中的 -filter_complex 中输入的内容相同。例如,请参阅此示例,说明您将使用什么字符串作为输入来获取电影元素:

    ffmpeg -filter_complex 'movie=file.mpg[in];movie=ref.mpg[ref];[in][ref]psnr[out]' -map '[out]' -f null -
    

    在解析过滤器图之前不要忘记调用avfilter_register_all()av_register_all(),否则电影元素将不会被注册。

    完整的示例代码:

    #include <assert.h>
    
    #include "libavcodec/avcodec.h"
    #include "libavfilter/avfilter.h"
    #include "libavfilter/buffersink.h"
    #include "libavformat/avformat.h"
    
    int main() {
        AVFilterGraph *filter_graph;
        AVFilterInOut *inputs = NULL, *outputs = NULL;
        AVFilterContext *buffersink_ctx;
        AVFrame *f;
    
        avfilter_register_all();
        av_register_all();
    
        filter_graph = avfilter_graph_alloc();
        int ret;
    
        ret = avfilter_graph_parse2(filter_graph,
                                    "movie=file.mpg[in];"
                                    "movie=ref.mpg[ref];"
                                    "[in][ref]psnr[out];"
                                    "[out]buffersink",
                                    &inputs, &outputs);
        if (ret < 0) return ret;
        ret = avfilter_graph_config(filter_graph, NULL);
        if (ret < 0) return ret;
        buffersink_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffersink_3");
        assert(buffersink_ctx != NULL);
        f = av_frame_alloc();
    
        for (;;) {
            ret = av_buffersink_get_frame(buffersink_ctx, f);
            if (ret < 0) break;
            av_frame_unref(f);
        }
    
        av_frame_free(&f);
        avfilter_graph_free(&filter_graph);
    
        return 0;
    }
    

    要从循环内的f 中提取每帧 PSN 值,请使用以下内容:

    static void parse_psnr(AVFrame *f) {
        AVDictionary *metadata = av_frame_get_metadata(f);
        float psnr[4];
    
    #define parse(a, b) \
        sscanf(av_dict_get(metadata, a, NULL, 0)->value, "%f", &b)
    
        parse("lavfi.psnr.psnr.y", psnr[0]);
        parse("lavfi.psnr.psnr.u", psnr[1]);
        parse("lavfi.psnr.psnr.v", psnr[2]);
        parse("lavfi.psnr.psnr_avg", psnr[3]);
    
    }
    

    要获得打印到标准输出的最终值(序列范围的 PSNR),您可以使用 av_log_set_callback() 设置日志回调并适当地解析字符串。

    【讨论】:

    • 谢谢你的回复,还有一个问题想问一下关于SSIM的问题,有哪些参数可以选择,就像PSNR中的lavfi.psnr.psnr.y一样
    • lavfi.ssim.dB.Y、lavfi.ssim.dB.U、lavfi.ssim.dB.V 和 lavfi.ssim.dB