-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathvmaf_calculator.cpp
More file actions
154 lines (117 loc) · 6.1 KB
/
vmaf_calculator.cpp
File metadata and controls
154 lines (117 loc) · 6.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "vmaf_calculator.h"
#include <iostream>
#include "filtered_logger.h"
#include "string_utils.h"
extern "C" {
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}
static const std::string VMAF_SCORE_STRING("VMAF score:");
static const std::regex VMAF_REGEX(VMAF_SCORE_STRING + "\\s(\\d+\\.\\d+)");
VMAFCalculator& VMAFCalculator::instance() {
static VMAFCalculator instance;
return instance;
}
VMAFCalculator::VMAFCalculator() {
FilteredLogger::instance().install(VMAF_SCORE_STRING);
}
void VMAFCalculator::set_libvmaf_options(const std::string& options) {
libvmaf_options_ = options;
}
std::string VMAFCalculator::compute(const AVFrame* distorted_frame, const AVFrame* reference_frame) {
std::string result = "n/a";
if (!disabled_) {
// libvmaf (via FFmpeg filter) errors out on very small frames and our catch path
// would permanently disable VMAF until restart. Avoid running it in that case.
static constexpr int kMinDim = 32;
if (distorted_frame->width < kMinDim || distorted_frame->height < kMinDim || reference_frame->width < kMinDim || reference_frame->height < kMinDim) {
const int dw = distorted_frame->width;
const int dh = distorted_frame->height;
const int rw = reference_frame->width;
const int rh = reference_frame->height;
std::cerr << "Warning: skipping VMAF computation for small frame(s). "
<< "distorted=" << dw << "x" << dh << ", reference=" << rw << "x" << rh << " (minimum " << kMinDim << "x" << kMinDim << ")." << std::endl;
return result;
}
try {
FilteredLogger::instance().reset();
run_libvmaf_filter(distorted_frame, reference_frame);
std::vector<std::string> vmaf_scores;
std::string buffered_logs = FilteredLogger::instance().get_buffered_logs();
std::string::const_iterator search_start(buffered_logs.cbegin());
std::smatch match;
while (std::regex_search(search_start, buffered_logs.cend(), match, VMAF_REGEX)) {
vmaf_scores.push_back(match[1].str());
search_start = match.suffix().first;
}
if (!vmaf_scores.empty()) {
result = string_join(vmaf_scores, "|");
} else {
std::cerr << "Failed to extract at least one VMAF score, disabling VMAF computation." << std::endl;
disabled_ = true;
}
} catch (const std::exception& e) {
std::cerr << "Failed to run libvmaf FFmpeg filter, disabling VMAF computation." << std::endl;
disabled_ = true;
}
}
return result;
}
void VMAFCalculator::run_libvmaf_filter(const AVFrame* distorted_frame, const AVFrame* reference_frame) {
if (!avfilter_get_by_name("libvmaf")) {
throw std::runtime_error("libvmaf filter not found");
}
auto format_filter_args = [](const AVFrame* frame) {
return
#if (LIBAVFILTER_VERSION_INT < AV_VERSION_INT(10, 1, 100))
string_sprintf("video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=0/1", frame->width, frame->height, frame->format);
#else
string_sprintf("video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=0/1:colorspace=%d:range=%d", frame->width, frame->height, frame->format, frame->colorspace, frame->color_range);
#endif
};
const AVFilter* buffersrc = avfilter_get_by_name("buffer");
const AVFilter* buffersink = avfilter_get_by_name("buffersink");
AVFilterGraphRAII filter_graph;
AVFilterContext* buffersrc_ctx_dist;
if (avfilter_graph_create_filter(&buffersrc_ctx_dist, buffersrc, "in_dist", format_filter_args(distorted_frame).c_str(), nullptr, filter_graph.get()) < 0) {
throw std::runtime_error("Cannot create buffer source for distorted frame");
}
AVFilterContext* buffersrc_ctx_ref;
if (avfilter_graph_create_filter(&buffersrc_ctx_ref, buffersrc, "in_ref", format_filter_args(reference_frame).c_str(), nullptr, filter_graph.get()) < 0) {
throw std::runtime_error("Cannot create buffer source for reference frame");
}
AVFilterContext* buffersink_ctx;
if (avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph.get()) < 0) {
throw std::runtime_error("Cannot create buffer sink");
}
std::string yuv_pixel_format = distorted_frame->format == AV_PIX_FMT_RGB24 ? "yuv444p" : "yuv444p16le";
std::string libvmaf_filter_options = libvmaf_options_.empty() ? "" : string_sprintf("=%s", libvmaf_options_.c_str());
std::string filter_description =
string_sprintf("[in_dist]setparams=colorspace=%d:range=%d,format=%s[in_dist_yuv],[in_ref]setparams=colorspace=%d:range=%d,format=%s[in_ref_yuv],[in_dist_yuv][in_ref_yuv]libvmaf%s[out]", distorted_frame->colorspace,
distorted_frame->color_range, yuv_pixel_format.c_str(), reference_frame->colorspace, reference_frame->color_range, yuv_pixel_format.c_str(), libvmaf_filter_options.c_str());
AVFilterInOutRAII outputs_ref(av_strdup("in_ref"), buffersrc_ctx_ref, nullptr, false);
AVFilterInOutRAII outputs_dist(av_strdup("in_dist"), buffersrc_ctx_dist, outputs_ref.get());
AVFilterInOutRAII inputs(av_strdup("out"), buffersink_ctx, nullptr);
if (avfilter_graph_parse_ptr(filter_graph.get(), filter_description.c_str(), inputs.get_pointer(), outputs_dist.get_pointer(), nullptr) < 0) {
throw std::runtime_error("Error parsing graph");
}
if (avfilter_graph_config(filter_graph.get(), nullptr) < 0) {
throw std::runtime_error("Error configuring graph");
}
if (av_buffersrc_add_frame(buffersrc_ctx_dist, const_cast<AVFrame*>(distorted_frame)) < 0) {
throw std::runtime_error("Error feeding distorted frame");
}
if (av_buffersrc_add_frame(buffersrc_ctx_ref, const_cast<AVFrame*>(reference_frame)) < 0) {
throw std::runtime_error("Error feeding reference frame");
}
if (av_buffersrc_close(buffersrc_ctx_dist, 0, AV_BUFFERSRC_FLAG_PUSH) < 0) {
throw std::runtime_error("Error closing distorted buffer source");
}
if (av_buffersrc_close(buffersrc_ctx_ref, 0, AV_BUFFERSRC_FLAG_PUSH) < 0) {
throw std::runtime_error("Error closing reference buffer source");
}
AVFrameRAII filtered_frame;
if (av_buffersink_get_frame(buffersink_ctx, filtered_frame.get()) < 0) {
throw std::runtime_error("Error getting filtered frame");
}
}