FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/tests/swscale.c
Date: 2026-05-02 03:33:10
Exec Total Coverage
Lines: 276 575 48.0%
Functions: 17 21 81.0%
Branches: 145 402 36.1%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2024 Niklas Haas
3 * Copyright (C) 2003-2011 Michael Niedermayer <michaelni@gmx.at>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <inttypes.h>
26 #include <stdarg.h>
27 #include <signal.h>
28
29 #undef HAVE_AV_CONFIG_H
30 #include "libavutil/cpu.h"
31 #include "libavutil/parseutils.h"
32 #include "libavutil/pixdesc.h"
33 #include "libavutil/lfg.h"
34 #include "libavutil/sfc64.h"
35 #include "libavutil/frame.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/time.h"
38 #include "libavutil/pixfmt.h"
39 #include "libavutil/avassert.h"
40 #include "libavutil/macros.h"
41 #include "libavutil/hwcontext.h"
42
43 #include "libswscale/swscale.h"
44
45 struct options {
46 enum AVPixelFormat src_fmt;
47 enum AVPixelFormat dst_fmt;
48 double prob;
49 int w, h;
50 int threads;
51 int iters;
52 int bench;
53 int flags;
54 int dither;
55 int unscaled;
56 int legacy;
57 int pretty;
58 };
59
60 struct mode {
61 SwsFlags flags;
62 SwsDither dither;
63 };
64
65 struct test_results {
66 float ssim[4];
67 float loss;
68 int64_t time;
69 int iters;
70 };
71
72 const SwsFlags flags[] = {
73 0, // test defaults
74 SWS_FAST_BILINEAR,
75 SWS_BILINEAR,
76 SWS_BICUBIC,
77 SWS_X | SWS_BITEXACT,
78 SWS_POINT,
79 SWS_AREA | SWS_ACCURATE_RND,
80 SWS_BICUBIC | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP,
81 };
82
83 static FFSFC64 prng_state;
84
85 /* reused between tests for efficiency */
86 static SwsContext *sws_ref_src;
87 static SwsContext *sws_src_dst;
88 static SwsContext *sws_dst_out;
89
90 static AVBufferRef *hw_device_ctx = NULL;
91 static AVHWFramesConstraints *hw_device_constr = NULL;
92
93 static double speedup_logavg;
94 static double speedup_min = 1e10;
95 static double speedup_max = 0;
96 static int speedup_count;
97
98 static const char *speedup_color(double ratio)
99 {
100 return ratio > 10.00 ? "\033[1;94m" : /* bold blue */
101 ratio > 2.00 ? "\033[1;32m" : /* bold green */
102 ratio > 1.02 ? "\033[32m" : /* green */
103 ratio > 0.98 ? "" : /* default */
104 ratio > 0.90 ? "\033[33m" : /* yellow */
105 ratio > 0.75 ? "\033[31m" : /* red */
106 "\033[1;31m"; /* bold red */
107 }
108
109 1 static void exit_handler(int sig)
110 {
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (speedup_count) {
112 double ratio = exp(speedup_logavg / speedup_count);
113 fprintf(stderr, "Overall speedup=%.3fx %s%s\033[0m, min=%.3fx max=%.3fx\n", ratio,
114 speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower",
115 speedup_min, speedup_max);
116 }
117
118 1 exit(sig);
119 }
120
121 /* Estimate luma variance assuming uniform dither noise distribution */
122 50700 static float estimate_quantization_noise(enum AVPixelFormat fmt)
123 {
124 50700 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
125 50700 float variance = 1.0 / 12;
126
2/2
✓ Branch 0 taken 4680 times.
✓ Branch 1 taken 46020 times.
50700 if (desc->comp[0].depth < 8) {
127 /* Extra headroom for very low bit depth output */
128 4680 variance *= (8 - desc->comp[0].depth);
129 }
130
131
2/2
✓ Branch 0 taken 1560 times.
✓ Branch 1 taken 49140 times.
50700 if (desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
132 1560 return 0.0;
133
2/2
✓ Branch 0 taken 15600 times.
✓ Branch 1 taken 33540 times.
49140 } else if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
134 15600 const float r = 0.299 / (1 << desc->comp[0].depth);
135 15600 const float g = 0.587 / (1 << desc->comp[1].depth);
136 15600 const float b = 0.114 / (1 << desc->comp[2].depth);
137 15600 return (r * r + g * g + b * b) * variance;
138 } else {
139 33540 const float y = 1.0 / (1 << desc->comp[0].depth);
140 33540 return y * y * variance;
141 }
142 }
143
144 33800 static int fmt_comps(enum AVPixelFormat fmt)
145 {
146 33800 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
147
2/2
✓ Branch 0 taken 29120 times.
✓ Branch 1 taken 4680 times.
33800 int comps = desc->nb_components >= 3 ? 0x7 : 0x1;
148
2/2
✓ Branch 0 taken 9360 times.
✓ Branch 1 taken 24440 times.
33800 if (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
149 9360 comps |= 0x8;
150 33800 return comps;
151 }
152
153 16900 static void get_ssim(float ssim[4], const AVFrame *out, const AVFrame *ref, int comps)
154 {
155 av_assert1(out->format == AV_PIX_FMT_YUVA444P);
156 av_assert1(ref->format == out->format);
157 av_assert1(ref->width == out->width && ref->height == out->height);
158
159
2/2
✓ Branch 0 taken 67600 times.
✓ Branch 1 taken 16900 times.
84500 for (int p = 0; p < 4; p++) {
160 67600 const int stride_a = out->linesize[p];
161 67600 const int stride_b = ref->linesize[p];
162 67600 const int w = out->width;
163 67600 const int h = out->height;
164
165
4/4
✓ Branch 0 taken 50700 times.
✓ Branch 1 taken 16900 times.
✓ Branch 2 taken 16900 times.
✓ Branch 3 taken 33800 times.
67600 const int is_chroma = p == 1 || p == 2;
166
2/2
✓ Branch 0 taken 33800 times.
✓ Branch 1 taken 33800 times.
67600 const uint8_t def = is_chroma ? 128 : 0xFF;
167 67600 const int has_ref = comps & (1 << p);
168 67600 double sum = 0;
169 67600 int count = 0;
170
171 /* 4x4 SSIM */
172
2/2
✓ Branch 0 taken 1622400 times.
✓ Branch 1 taken 67600 times.
1690000 for (int y = 0; y < (h & ~3); y += 4) {
173
2/2
✓ Branch 0 taken 38937600 times.
✓ Branch 1 taken 1622400 times.
40560000 for (int x = 0; x < (w & ~3); x += 4) {
174 38937600 const float c1 = .01 * .01 * 255 * 255 * 64;
175 38937600 const float c2 = .03 * .03 * 255 * 255 * 64 * 63;
176 38937600 int s1 = 0, s2 = 0, ss = 0, s12 = 0, var, covar;
177
178
2/2
✓ Branch 0 taken 155750400 times.
✓ Branch 1 taken 38937600 times.
194688000 for (int yy = 0; yy < 4; yy++) {
179
2/2
✓ Branch 0 taken 623001600 times.
✓ Branch 1 taken 155750400 times.
778752000 for (int xx = 0; xx < 4; xx++) {
180 623001600 int a = out->data[p][(y + yy) * stride_a + x + xx];
181
2/2
✓ Branch 0 taken 398905344 times.
✓ Branch 1 taken 224096256 times.
623001600 int b = has_ref ? ref->data[p][(y + yy) * stride_b + x + xx] : def;
182 623001600 s1 += a;
183 623001600 s2 += b;
184 623001600 ss += a * a + b * b;
185 623001600 s12 += a * b;
186 }
187 }
188
189 38937600 var = ss * 64 - s1 * s1 - s2 * s2;
190 38937600 covar = s12 * 64 - s1 * s2;
191 38937600 sum += (2 * s1 * s2 + c1) * (2 * covar + c2) /
192 38937600 ((s1 * s1 + s2 * s2 + c1) * (var + c2));
193 38937600 count++;
194 }
195 }
196
197
1/2
✓ Branch 0 taken 67600 times.
✗ Branch 1 not taken.
67600 ssim[p] = count ? sum / count : 0.0;
198 }
199 16900 }
200
201 33800 static float get_loss(const float ssim[4])
202 {
203 33800 const float weights[3] = { 0.8, 0.1, 0.1 }; /* tuned for Y'CbCr */
204
205 33800 float sum = 0;
206
2/2
✓ Branch 0 taken 101400 times.
✓ Branch 1 taken 33800 times.
135200 for (int i = 0; i < 3; i++)
207 101400 sum += weights[i] * ssim[i];
208 33800 sum *= ssim[3]; /* ensure alpha errors get caught */
209
210 33800 return 1.0 - sum;
211 }
212
213 16900 static void unref_buffers(AVFrame *frame)
214 {
215
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 for (int i = 0; i < FF_ARRAY_ELEMS(frame->buf); i++) {
216
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 if (!frame->buf[i])
217 16900 break;
218 av_buffer_unref(&frame->buf[i]);
219 }
220
221 16900 memset(frame->data, 0, sizeof(frame->data));
222 16900 memset(frame->linesize, 0, sizeof(frame->linesize));
223 16900 }
224
225 33931 static int checked_sws_scale_frame(SwsContext *c, AVFrame *dst, const AVFrame *src)
226 {
227 33931 int ret = sws_scale_frame(c, dst, src);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33931 times.
33931 if (ret < 0) {
229 av_log(NULL, AV_LOG_ERROR, "Failed %s ---> %s\n",
230 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
231 }
232 33931 return ret;
233 }
234
235 16900 static int scale_new(AVFrame *dst, const AVFrame *src,
236 const struct mode *mode, const struct options *opts,
237 int64_t *out_time)
238 {
239 16900 sws_src_dst->flags = mode->flags;
240 16900 sws_src_dst->dither = mode->dither;
241 16900 sws_src_dst->threads = opts->threads;
242
243 16900 int ret = sws_frame_setup(sws_src_dst, dst, src);
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0) {
245 av_log(NULL, AV_LOG_ERROR, "Failed to setup %s ---> %s\n",
246 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
247 return ret;
248 }
249
250 16900 int64_t time = av_gettime_relative();
251
3/4
✓ Branch 0 taken 33800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16900 times.
✓ Branch 3 taken 16900 times.
33800 for (int i = 0; ret >= 0 && i < opts->iters; i++) {
252 16900 unref_buffers(dst);
253 16900 ret = checked_sws_scale_frame(sws_src_dst, dst, src);
254 }
255 16900 *out_time = av_gettime_relative() - time;
256
257 16900 return ret;
258 }
259
260 static int scale_legacy(AVFrame *dst, const AVFrame *src,
261 const struct mode *mode, const struct options *opts,
262 int64_t *out_time)
263 {
264 SwsContext *sws_legacy;
265 int ret;
266
267 sws_legacy = sws_alloc_context();
268 if (!sws_legacy)
269 return -1;
270
271 sws_legacy->src_w = src->width;
272 sws_legacy->src_h = src->height;
273 sws_legacy->src_format = src->format;
274 sws_legacy->dst_w = dst->width;
275 sws_legacy->dst_h = dst->height;
276 sws_legacy->dst_format = dst->format;
277 sws_legacy->flags = mode->flags;
278 sws_legacy->dither = mode->dither;
279 sws_legacy->threads = opts->threads;
280
281 av_frame_unref(dst);
282 dst->width = sws_legacy->dst_w;
283 dst->height = sws_legacy->dst_h;
284 dst->format = sws_legacy->dst_format;
285 ret = av_frame_get_buffer(dst, 0);
286 if (ret < 0)
287 goto error;
288
289 ret = sws_init_context(sws_legacy, NULL, NULL);
290 if (ret < 0)
291 goto error;
292
293 int64_t time = av_gettime_relative();
294 for (int i = 0; ret >= 0 && i < opts->iters; i++)
295 ret = checked_sws_scale_frame(sws_legacy, dst, src);
296 *out_time = av_gettime_relative() - time;
297
298 error:
299 sws_freeContext(sws_legacy);
300 return ret;
301 }
302
303 static int scale_hw(AVFrame *dst, const AVFrame *src,
304 const struct mode *mode, const struct options *opts,
305 int64_t *out_time)
306 {
307 SwsContext *sws_hw = NULL;
308 AVBufferRef *in_ref = NULL;
309 AVBufferRef *out_ref = NULL;
310 AVHWFramesContext *in_ctx = NULL;
311 AVHWFramesContext *out_ctx = NULL;
312 AVFrame *in_f = NULL;
313 AVFrame *out_f = NULL;
314 int ret;
315
316 if (src->format == dst->format)
317 return AVERROR(ENOTSUP);
318
319 sws_hw = sws_alloc_context();
320 if (!sws_hw) {
321 ret = AVERROR(ENOMEM);
322 goto error;
323 }
324
325 sws_hw->flags = mode->flags;
326 sws_hw->dither = mode->dither;
327
328 in_ref = av_hwframe_ctx_alloc(hw_device_ctx);
329 if (!in_ref) {
330 ret = AVERROR(ENOMEM);
331 goto error;
332 }
333
334 in_ctx = (AVHWFramesContext *)in_ref->data;
335 in_ctx->format = AV_PIX_FMT_VULKAN;
336 in_ctx->sw_format = src->format;
337 in_ctx->width = src->width;
338 in_ctx->height = src->height;
339 ret = av_hwframe_ctx_init(in_ref);
340 if (ret < 0) {
341 if (ret != AVERROR(ENOTSUP))
342 av_log(NULL, AV_LOG_ERROR, "Failed to create input HW context: %s\n",
343 av_err2str(ret));
344 goto error;
345 }
346
347 out_ref = av_hwframe_ctx_alloc(hw_device_ctx);
348 if (!out_ref) {
349 ret = AVERROR(ENOMEM);
350 goto error;
351 }
352
353 out_ctx = (AVHWFramesContext *) out_ref->data;
354 out_ctx->format = AV_PIX_FMT_VULKAN;
355 out_ctx->sw_format = dst->format;
356 out_ctx->width = dst->width;
357 out_ctx->height = dst->height;
358 ret = av_hwframe_ctx_init(out_ref);
359 if (ret < 0) {
360 if (ret != AVERROR(ENOTSUP))
361 av_log(NULL, AV_LOG_ERROR, "Failed to create output HW context: %s\n",
362 av_err2str(ret));
363 goto error;
364 }
365
366 in_f = av_frame_alloc();
367 if (!in_f) {
368 ret = AVERROR(ENOMEM);
369 goto error;
370 }
371 in_f->width = src->width;
372 in_f->height = src->height;
373 in_f->format = AV_PIX_FMT_VULKAN;
374 ret = av_hwframe_get_buffer(in_ref, in_f, 0);
375 if (ret < 0) {
376 av_log(NULL, AV_LOG_ERROR, "Failed to allocate input HW frame\n");
377 goto error;
378 }
379
380 ret = av_hwframe_transfer_data(in_f, src, 0);
381 if (ret < 0) {
382 av_log(NULL, AV_LOG_ERROR, "Failed to upload HW frame\n");
383 goto error;
384 }
385
386 out_f = av_frame_alloc();
387 if (!out_f) {
388 ret = AVERROR(ENOMEM);
389 goto error;
390 }
391 out_f->width = dst->width;
392 out_f->height = dst->height;
393 out_f->format = AV_PIX_FMT_VULKAN;
394 ret = av_hwframe_get_buffer(out_ref, out_f, 0);
395 if (ret < 0) {
396 av_log(NULL, AV_LOG_ERROR, "Failed to allocate output HW frame\n");
397 goto error;
398 }
399
400 int64_t time = av_gettime_relative();
401 for (int i = 0; ret >= 0 && i < opts->iters; i++) {
402 ret = checked_sws_scale_frame(sws_hw, out_f, in_f);
403 if (ret < 0)
404 goto error;
405 }
406 *out_time = av_gettime_relative() - time;
407
408 ret = av_frame_get_buffer(dst, 0);
409 if (ret < 0)
410 goto error;
411
412 ret = av_hwframe_transfer_data(dst, out_f, 0);
413 if (ret < 0) {
414 av_log(NULL, AV_LOG_ERROR, "Failed to download HW frame\n");
415 goto error;
416 }
417
418 ret = 0;
419
420 error:
421 av_frame_free(&in_f);
422 av_frame_free(&out_f);
423 av_buffer_unref(&in_ref);
424 av_buffer_unref(&out_ref);
425 sws_free_context(&sws_hw);
426 return ret;
427 }
428
429 16900 static void print_results(const AVFrame *ref, const AVFrame *src, const AVFrame *dst,
430 int dst_w, int dst_h,
431 const struct mode *mode, const struct options *opts,
432 const struct test_results *r,
433 const struct test_results *ref_r,
434 float expected_loss)
435 {
436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16900 times.
16900 if (av_log_get_level() >= AV_LOG_INFO) {
437 printf("%-*s %*dx%*d -> %-*s %*dx%*d, flags=0x%0*x dither=%u",
438 opts->pretty ? 14 : 0, av_get_pix_fmt_name(src->format),
439 opts->pretty ? 4 : 0, src->width,
440 opts->pretty ? 4 : 0, src->height,
441 opts->pretty ? 14 : 0, av_get_pix_fmt_name(dst->format),
442 opts->pretty ? 4 : 0, dst->width,
443 opts->pretty ? 4 : 0, dst->height,
444 opts->pretty ? 8 : 0, mode->flags,
445 mode->dither);
446
447 if (!opts->bench || !ref_r) {
448 printf(", SSIM={Y=%f U=%f V=%f A=%f} loss=%e",
449 r->ssim[0], r->ssim[1], r->ssim[2], r->ssim[3],
450 r->loss);
451 if (ref_r)
452 printf(" (ref=%e)", ref_r->loss);
453 }
454
455 if (opts->bench) {
456 printf(", time=%*"PRId64"/%u us",
457 opts->pretty ? 7 : 0, r->time, opts->iters);
458 if (ref_r) {
459 double ratio = ((double) ref_r->time / ref_r->iters)
460 / ((double) r->time / opts->iters);
461 if (FFMIN(r->time, ref_r->time) > 100 /* don't pollute stats with low precision */) {
462 speedup_min = FFMIN(speedup_min, ratio);
463 speedup_max = FFMAX(speedup_max, ratio);
464 speedup_logavg += log(ratio);
465 speedup_count++;
466 }
467
468 printf(" (ref=%*"PRId64"/%u us), speedup=%*.3fx %s%s\033[0m",
469 opts->pretty ? 7 : 0, ref_r->time, ref_r->iters,
470 opts->pretty ? 6 : 0, ratio,
471 speedup_color(ratio), ratio >= 1.0 ? "faster" : "slower");
472 }
473 }
474 printf("\n");
475
476 fflush(stdout);
477 }
478
479
4/6
✓ Branch 0 taken 876 times.
✓ Branch 1 taken 16024 times.
✓ Branch 2 taken 876 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 876 times.
✗ Branch 5 not taken.
16900 if (r->loss - expected_loss > 1e-4 && dst_w >= ref->width && dst_h >= ref->height) {
480 876 const int bad = r->loss - expected_loss > 1e-2;
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 876 times.
876 const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 876 times.
876 const char *worse_str = bad ? "WORSE" : "worse";
483 876 av_log(NULL, level,
484 " loss %e is %s by %e, expected loss %e\n",
485 876 r->loss, worse_str, r->loss - expected_loss, expected_loss);
486 }
487
488
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16900 if (ref_r && r->loss - ref_r->loss > 1e-4) {
489 /**
490 * The new scaling code does not (yet) perform error diffusion for
491 * low bit depth output, which impacts the SSIM score slightly for
492 * very low bit-depth formats (e.g. monow, monob). Since this is an
493 * expected result, drop the badness from an error to a warning for
494 * such cases. This can be removed again once error diffusion is
495 * implemented in the new ops code.
496 */
497 const int dst_bits = av_pix_fmt_desc_get(dst->format)->comp[0].depth;
498 const int bad = r->loss - ref_r->loss > 1e-2 && dst_bits > 1;
499 const int level = bad ? AV_LOG_ERROR : AV_LOG_WARNING;
500 const char *worse_str = bad ? "WORSE" : "worse";
501 av_log(NULL, level,
502 " loss %e is %s by %e, ref loss %e SSIM={Y=%f U=%f V=%f A=%f}\n",
503 r->loss, worse_str, r->loss - ref_r->loss, ref_r->loss,
504 ref_r->ssim[0], ref_r->ssim[1], ref_r->ssim[2], ref_r->ssim[3]);
505 }
506 16900 }
507
508 33800 static int init_frame(AVFrame **pframe, const AVFrame *ref,
509 int width, int height, enum AVPixelFormat format)
510 {
511 33800 AVFrame *frame = av_frame_alloc();
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33800 times.
33800 if (!frame)
513 return AVERROR(ENOMEM);
514 33800 av_frame_copy_props(frame, ref);
515 33800 frame->width = width;
516 33800 frame->height = height;
517 33800 frame->format = format;
518 33800 *pframe = frame;
519 33800 return 0;
520 }
521
522 /* Runs a series of ref -> src -> dst -> out, and compares out vs ref */
523 16900 static int run_test(enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt,
524 int dst_w, int dst_h,
525 const struct mode *mode, const struct options *opts,
526 const AVFrame *ref, AVFrame *src,
527 const struct test_results *ref_r)
528 {
529 16900 AVFrame *dst = NULL, *out = NULL;
530 16900 const int comps = fmt_comps(src_fmt) & fmt_comps(dst_fmt);
531 int ret;
532
533 /* Estimate the expected amount of loss from bit depth reduction */
534 16900 const float c1 = 0.01 * 0.01; /* stabilization constant */
535 16900 const float ref_var = 1.0 / 12.0; /* uniformly distributed signal */
536 16900 const float src_var = estimate_quantization_noise(src_fmt);
537 16900 const float dst_var = estimate_quantization_noise(dst_fmt);
538 16900 const float out_var = estimate_quantization_noise(ref->format);
539 16900 const float total_var = src_var + dst_var + out_var;
540 16900 const float ssim_luma = (2 * ref_var + c1) / (2 * ref_var + total_var + c1);
541 16900 const float ssim_expected[4] = { ssim_luma, 1, 1, 1 }; /* for simplicity */
542 16900 const float expected_loss = get_loss(ssim_expected);
543
544 16900 struct test_results r = { 0 };
545
546
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 16770 times.
16900 if (src->format != src_fmt) {
547 130 av_frame_unref(src);
548 130 av_frame_copy_props(src, ref);
549 130 src->width = ref->width;
550 130 src->height = ref->height;
551 130 src->format = src_fmt;
552 130 ret = checked_sws_scale_frame(sws_ref_src, src, ref);
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (ret < 0)
554 goto error;
555 }
556
557 16900 ret = init_frame(&dst, ref, dst_w, dst_h, dst_fmt);
558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0)
559 goto error;
560
561 ret = opts->legacy ? scale_legacy(dst, src, mode, opts, &r.time)
562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
33800 : hw_device_ctx ? scale_hw(dst, src, mode, opts, &r.time)
563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 : scale_new(dst, src, mode, opts, &r.time);
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0) {
565 if (ret == AVERROR(ENOTSUP))
566 ret = 0;
567 goto error;
568 }
569
570 16900 ret = init_frame(&out, ref, ref->width, ref->height, ref->format);
571
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0)
572 goto error;
573
574 16900 ret = checked_sws_scale_frame(sws_dst_out, out, dst);
575
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0)
576 goto error;
577
578 16900 get_ssim(r.ssim, out, ref, comps);
579
580
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (opts->legacy) {
581 /* Legacy swscale does not perform bit accurate upconversions of low
582 * bit depth RGB. This artificially improves the SSIM score because the
583 * resulting error deletes some of the input dither noise. This gives
584 * it an unfair advantage when compared against a bit exact reference.
585 * Work around this by ensuring that the resulting SSIM score is not
586 * higher than it theoretically "should" be. */
587 if (src_var > dst_var) {
588 const float src_loss = (2 * ref_var + c1) / (2 * ref_var + src_var + c1);
589 r.ssim[0] = FFMIN(r.ssim[0], src_loss);
590 }
591 }
592
593 16900 r.loss = get_loss(r.ssim);
594
2/8
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16900 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
16900 if (!opts->legacy && r.loss - expected_loss > 1e-2 && dst_w >= ref->width && dst_h >= ref->height) {
595 ret = -1;
596 goto bad_loss;
597 }
598
599
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16900 if (ref_r && r.loss - ref_r->loss > 1e-2) {
600 ret = -1;
601 goto bad_loss;
602 }
603
604 16900 ret = 0; /* fall through */
605
606 16900 bad_loss:
607 16900 print_results(ref, src, dst,
608 dst_w, dst_h,
609 mode, opts,
610 &r, ref_r,
611 expected_loss);
612
613 16900 error:
614 16900 av_frame_free(&dst);
615 16900 av_frame_free(&out);
616 16900 return ret;
617 }
618
619 34977 static inline int fmt_is_subsampled(enum AVPixelFormat fmt)
620 {
621
2/2
✓ Branch 1 taken 25283 times.
✓ Branch 2 taken 9694 times.
60260 return av_pix_fmt_desc_get(fmt)->log2_chroma_w != 0 ||
622
2/2
✓ Branch 1 taken 786 times.
✓ Branch 2 taken 24497 times.
25283 av_pix_fmt_desc_get(fmt)->log2_chroma_h != 0;
623 }
624
625 34977 static inline int fmt_is_supported_by_hw(enum AVPixelFormat fmt)
626 {
627
1/2
✓ Branch 0 taken 34977 times.
✗ Branch 1 not taken.
34977 if (!hw_device_constr)
628 34977 return 1;
629
630 /* Semi-planar formats are only supported by the legacy path, which
631 * does not support hardware frames. */
632 if (fmt == AV_PIX_FMT_NV24 || fmt == AV_PIX_FMT_P410 ||
633 fmt == AV_PIX_FMT_P412 || fmt == AV_PIX_FMT_P416)
634 return 0;
635 for (int i = 0;
636 hw_device_constr->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) {
637 if (hw_device_constr->valid_sw_formats[i] == fmt)
638 return 1;
639 }
640 return 0;
641 }
642
643 1 static int run_self_tests(const AVFrame *ref, const struct options *opts)
644 {
645 1 const int dst_w[] = { opts->w, opts->w - opts->w / 3, opts->w + opts->w / 3 };
646 1 const int dst_h[] = { opts->h, opts->h - opts->h / 3, opts->h + opts->h / 3 };
647
648 enum AVPixelFormat src_fmt, dst_fmt,
649 1 src_fmt_min = 0,
650 1 dst_fmt_min = 0,
651 1 src_fmt_max = AV_PIX_FMT_NB - 1,
652 1 dst_fmt_max = AV_PIX_FMT_NB - 1;
653
654 1 AVFrame *src = av_frame_alloc();
655
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!src)
656 return AVERROR(ENOMEM);
657
658 1 int ret = 0;
659
660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (opts->src_fmt != AV_PIX_FMT_NONE)
661 src_fmt_min = src_fmt_max = opts->src_fmt;
662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (opts->dst_fmt != AV_PIX_FMT_NONE)
663 dst_fmt_min = dst_fmt_max = opts->dst_fmt;
664
665
2/2
✓ Branch 0 taken 267 times.
✓ Branch 1 taken 1 times.
268 for (src_fmt = src_fmt_min; src_fmt <= src_fmt_max; src_fmt++) {
666
1/2
✓ Branch 1 taken 267 times.
✗ Branch 2 not taken.
267 if ((!fmt_is_supported_by_hw(src_fmt)) ||
667
3/4
✓ Branch 0 taken 267 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 80 times.
✓ Branch 4 taken 187 times.
267 (opts->unscaled && fmt_is_subsampled(src_fmt)))
668 80 continue;
669
4/4
✓ Branch 1 taken 159 times.
✓ Branch 2 taken 28 times.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 130 times.
187 if (!sws_test_format(src_fmt, 0) || !sws_test_format(src_fmt, 1))
670 57 continue;
671
2/2
✓ Branch 0 taken 34710 times.
✓ Branch 1 taken 130 times.
34840 for (dst_fmt = dst_fmt_min; dst_fmt <= dst_fmt_max; dst_fmt++) {
672
1/2
✓ Branch 1 taken 34710 times.
✗ Branch 2 not taken.
34710 if ((!fmt_is_supported_by_hw(dst_fmt)) ||
673
3/4
✓ Branch 0 taken 34710 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10400 times.
✓ Branch 4 taken 24310 times.
34710 (opts->unscaled && fmt_is_subsampled(dst_fmt)))
674 10400 continue;
675
4/4
✓ Branch 1 taken 20670 times.
✓ Branch 2 taken 3640 times.
✓ Branch 4 taken 3770 times.
✓ Branch 5 taken 16900 times.
24310 if (!sws_test_format(dst_fmt, 0) || !sws_test_format(dst_fmt, 1))
676 7410 continue;
677
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 for (int h = 0; h < FF_ARRAY_ELEMS(dst_h); h++) {
678
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 for (int w = 0; w < FF_ARRAY_ELEMS(dst_w); w++) {
679
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 for (int f = 0; f < FF_ARRAY_ELEMS(flags); f++) {
680 33800 struct mode mode = {
681
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 .flags = opts->flags >= 0 ? opts->flags : flags[f],
682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 .dither = opts->dither >= 0 ? opts->dither : SWS_DITHER_AUTO,
683 };
684
685
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16900 times.
16900 if (ff_sfc64_get(&prng_state) > UINT64_MAX * opts->prob)
686 continue;
687
688 16900 ret = run_test(src_fmt, dst_fmt, dst_w[w], dst_h[h],
689 &mode, opts, ref, src, NULL);
690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
16900 if (ret < 0)
691 goto error;
692
693
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 16900 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
16900 if (opts->flags >= 0 || opts->unscaled)
694 break;
695 }
696
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 if (opts->unscaled)
697 16900 break;
698 }
699
1/2
✓ Branch 0 taken 16900 times.
✗ Branch 1 not taken.
16900 if (opts->unscaled)
700 16900 break;
701 }
702 }
703 }
704
705 1 ret = 0;
706
707 1 error:
708 1 av_frame_free(&src);
709 1 return ret;
710 }
711
712 static int run_file_tests(const AVFrame *ref, FILE *fp, const struct options *opts)
713 {
714 char buf[256];
715 int ret = 0;
716
717 AVFrame *src = av_frame_alloc();
718 if (!src)
719 return AVERROR(ENOMEM);
720
721 for (int line = 1; fgets(buf, sizeof(buf), fp); line++) {
722 char src_fmt_str[21], dst_fmt_str[21];
723 enum AVPixelFormat src_fmt;
724 enum AVPixelFormat dst_fmt;
725 int sw, sh, dw, dh;
726 struct test_results r = { 0 };
727 struct mode mode;
728 int n = 0;
729
730 ret = sscanf(buf,
731 "%20s %dx%d -> %20s %dx%d, flags=0x%x dither=%u, "
732 "SSIM={Y=%f U=%f V=%f A=%f} loss=%e%n",
733 src_fmt_str, &sw, &sh, dst_fmt_str, &dw, &dh,
734 &mode.flags, &mode.dither,
735 &r.ssim[0], &r.ssim[1], &r.ssim[2], &r.ssim[3],
736 &r.loss, &n);
737 if (ret != 13) {
738 av_log(NULL, AV_LOG_FATAL,
739 "Malformed reference file in line %d\n", line);
740 goto error;
741 }
742 if (opts->bench) {
743 ret = sscanf(buf + n,
744 ", time=%"PRId64"/%u us",
745 &r.time, &r.iters);
746 if (ret != 2) {
747 av_log(NULL, AV_LOG_FATAL,
748 "Missing benchmarks from reference file in line %d\n",
749 line);
750 goto error;
751 }
752 }
753
754 src_fmt = av_get_pix_fmt(src_fmt_str);
755 dst_fmt = av_get_pix_fmt(dst_fmt_str);
756 if (src_fmt == AV_PIX_FMT_NONE || dst_fmt == AV_PIX_FMT_NONE) {
757 av_log(NULL, AV_LOG_FATAL,
758 "Unknown pixel formats (%s and/or %s) in line %d\n",
759 src_fmt_str, dst_fmt_str, line);
760 goto error;
761 }
762
763 if (sw != ref->width || sh != ref->height) {
764 av_log(NULL, AV_LOG_FATAL,
765 "Mismatching dimensions %dx%d (ref is %dx%d) in line %d\n",
766 sw, sh, ref->width, ref->height, line);
767 goto error;
768 }
769
770 if (opts->src_fmt != AV_PIX_FMT_NONE && src_fmt != opts->src_fmt ||
771 opts->dst_fmt != AV_PIX_FMT_NONE && dst_fmt != opts->dst_fmt)
772 continue;
773
774 ret = run_test(src_fmt, dst_fmt, dw, dh, &mode, opts, ref, src, &r);
775 if (ret < 0)
776 goto error;
777 }
778
779 ret = 0;
780
781 error:
782 av_frame_free(&src);
783 return ret;
784 }
785
786 1 static int init_ref(AVFrame *ref, const struct options *opts)
787 {
788 1 SwsContext *ctx = sws_alloc_context();
789 1 AVFrame *rgb = av_frame_alloc();
790 AVLFG rand;
791 1 int ret = -1;
792
793
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!ctx || !rgb)
794 goto error;
795
796
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 rgb->width = opts->w > 32 ? opts->w / 12 : opts->w;
797
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 rgb->height = opts->h > 32 ? opts->h / 12 : opts->h;
798 1 rgb->format = AV_PIX_FMT_RGBA;
799 1 ret = av_frame_get_buffer(rgb, 32);
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
801 goto error;
802
803 1 av_lfg_init(&rand, 1);
804
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (int y = 0; y < rgb->height; y++) {
805
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
72 for (int x = 0; x < rgb->width; x++) {
806
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 64 times.
320 for (int c = 0; c < 4; c++)
807 256 rgb->data[0][y * rgb->linesize[0] + x * 4 + c] = av_lfg_get(&rand);
808 }
809 }
810
811 1 ctx->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
812 1 ret = checked_sws_scale_frame(ctx, ref, rgb);
813
814 1 error:
815 1 sws_free_context(&ctx);
816 1 av_frame_free(&rgb);
817 1 return ret;
818 }
819
820 1 static int parse_options(int argc, char **argv, struct options *opts, FILE **fp)
821 {
822
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (int i = 1; i < argc; i += 2) {
823
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
824 fprintf(stderr,
825 "swscale [options...]\n"
826 " -help\n"
827 " This text\n"
828 " -ref <file>\n"
829 " Uses file as reference to compare tests against. Tests that have become worse will contain the string worse or WORSE\n"
830 " -p <number between 0.0 and 1.0>\n"
831 " The percentage of tests or comparisons to perform. Doing all tests will take long and generate over a hundred MB text output\n"
832 " It is often convenient to perform a random subset\n"
833 " -dst <pixfmt>\n"
834 " Only test the specified destination pixel format\n"
835 " -src <pixfmt>\n"
836 " Only test the specified source pixel format\n"
837 " -s <size>\n"
838 " Set frame size (WxH or abbreviation)\n"
839 " -bench <iters>\n"
840 " Run benchmarks with the specified number of iterations. This mode also sets the frame size to 1920x1080 (unless -s is specified)\n"
841 " -flags <flags>\n"
842 " Test with a specific combination of flags\n"
843 " -dither <mode>\n"
844 " Test with a specific dither mode\n"
845 " -unscaled <1 or 0>\n"
846 " If 1, test only conversions that do not involve scaling\n"
847 " -legacy <1 or 0>\n"
848 " If 1, force using legacy swscale for the main conversion\n"
849 " -hw <device>\n"
850 " Use Vulkan hardware acceleration on the specified device for the main conversion\n"
851 " -threads <threads>\n"
852 " Use the specified number of threads\n"
853 " -cpuflags <cpuflags>\n"
854 " Uses the specified cpuflags in the tests\n"
855 " -pretty <1 or 0>\n"
856 " Align fields while printing results\n"
857 " -v <level>\n"
858 " Enable log verbosity at given level\n"
859 );
860 exit(0);
861 }
862
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (argv[i][0] != '-' || i + 1 == argc)
863 goto bad_option;
864
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!strcmp(argv[i], "-ref")) {
865 *fp = fopen(argv[i + 1], "r");
866 if (!*fp) {
867 fprintf(stderr, "could not open '%s'\n", argv[i + 1]);
868 return -1;
869 }
870
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strcmp(argv[i], "-cpuflags")) {
871 unsigned flags = av_get_cpu_flags();
872 int res = av_parse_cpu_caps(&flags, argv[i + 1]);
873 if (res < 0) {
874 fprintf(stderr, "invalid cpu flags %s\n", argv[i + 1]);
875 return -1;
876 }
877 av_force_cpu_flags(flags);
878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strcmp(argv[i], "-src")) {
879 opts->src_fmt = av_get_pix_fmt(argv[i + 1]);
880 if (opts->src_fmt == AV_PIX_FMT_NONE) {
881 fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]);
882 return -1;
883 }
884
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strcmp(argv[i], "-dst")) {
885 opts->dst_fmt = av_get_pix_fmt(argv[i + 1]);
886 if (opts->dst_fmt == AV_PIX_FMT_NONE) {
887 fprintf(stderr, "invalid pixel format %s\n", argv[i + 1]);
888 return -1;
889 }
890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strcmp(argv[i], "-s")) {
891 if (av_parse_video_size(&opts->w, &opts->h, argv[i + 1]) < 0) {
892 fprintf(stderr, "invalid frame size %s\n", argv[i + 1]);
893 return -1;
894 }
895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 } else if (!strcmp(argv[i], "-bench")) {
896 int iters = atoi(argv[i + 1]);
897 if (iters <= 0) {
898 opts->bench = 0;
899 opts->iters = 1;
900 } else {
901 opts->bench = 1;
902 opts->iters = iters;
903 }
904
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (!strcmp(argv[i], "-flags")) {
905 1 SwsContext *dummy = sws_alloc_context();
906 1 const AVOption *flags_opt = av_opt_find(dummy, "sws_flags", NULL, 0, 0);
907 1 int ret = av_opt_eval_flags(dummy, flags_opt, argv[i + 1], &opts->flags);
908 1 sws_free_context(&dummy);
909
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0) {
910 fprintf(stderr, "invalid flags %s\n", argv[i + 1]);
911 return -1;
912 }
913
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (!strcmp(argv[i], "-dither")) {
914 opts->dither = atoi(argv[i + 1]);
915
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 } else if (!strcmp(argv[i], "-unscaled")) {
916 1 opts->unscaled = atoi(argv[i + 1]);
917
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strcmp(argv[i], "-legacy")) {
918 opts->legacy = atoi(argv[i + 1]);
919
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strcmp(argv[i], "-hw")) {
920 int ret = av_hwdevice_ctx_create(&hw_device_ctx,
921 AV_HWDEVICE_TYPE_VULKAN,
922 argv[i + 1], NULL, 0);
923 if (ret < 0) {
924 fprintf(stderr, "Failed to create Vulkan device '%s'\n",
925 argv[i + 1]);
926 return -1;
927 }
928 hw_device_constr = av_hwdevice_get_hwframe_constraints(hw_device_ctx,
929 NULL);
930 if (!hw_device_constr) {
931 fprintf(stderr, "Failed to retrieve Vulkan device constraints '%s'\n",
932 argv[i + 1]);
933 return -1;
934 }
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strcmp(argv[i], "-threads")) {
936 opts->threads = atoi(argv[i + 1]);
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strcmp(argv[i], "-p")) {
938 opts->prob = atof(argv[i + 1]);
939
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (!strcmp(argv[i], "-pretty")) {
940 opts->pretty = atoi(argv[i + 1]);
941
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 } else if (!strcmp(argv[i], "-v")) {
942 1 av_log_set_level(atoi(argv[i + 1]));
943 } else {
944 bad_option:
945 fprintf(stderr, "bad option or argument missing (%s) see -help\n", argv[i]);
946 return -1;
947 }
948 }
949
950
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (opts->w < 0 || opts->h < 0) {
951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 opts->w = opts->bench ? 1920 : 96;
952
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 opts->h = opts->bench ? 1080 : 96;
953 }
954
955 1 return 0;
956 }
957
958 1 int main(int argc, char **argv)
959 {
960 1 struct options opts = {
961 .src_fmt = AV_PIX_FMT_NONE,
962 .dst_fmt = AV_PIX_FMT_NONE,
963 .w = -1,
964 .h = -1,
965 .threads = 1,
966 .iters = 1,
967 .prob = 1.0,
968 .flags = -1,
969 .dither = -1,
970 };
971
972 1 AVFrame *ref = NULL;
973 1 FILE *fp = NULL;
974 1 int ret = -1;
975
976
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (parse_options(argc, argv, &opts, &fp) < 0)
977 goto error;
978
979 1 ff_sfc64_init(&prng_state, 0, 0, 0, 12);
980 1 signal(SIGINT, exit_handler);
981
982 1 sws_ref_src = sws_alloc_context();
983 1 sws_src_dst = sws_alloc_context();
984 1 sws_dst_out = sws_alloc_context();
985
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (!sws_ref_src || !sws_src_dst || !sws_dst_out)
986 goto error;
987 1 sws_ref_src->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
988 1 sws_dst_out->flags = SWS_BILINEAR | SWS_BITEXACT | SWS_ACCURATE_RND;
989
990 1 ref = av_frame_alloc();
991
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ref)
992 goto error;
993 1 ref->width = opts.w;
994 1 ref->height = opts.h;
995 1 ref->format = AV_PIX_FMT_YUVA444P;
996
997 1 ret = init_ref(ref, &opts);
998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
999 goto error;
1000
1001 1 ret = fp ? run_file_tests(ref, fp, &opts)
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 : run_self_tests(ref, &opts);
1003
1004 /* fall through */
1005 1 error:
1006 1 sws_free_context(&sws_ref_src);
1007 1 sws_free_context(&sws_src_dst);
1008 1 sws_free_context(&sws_dst_out);
1009 1 av_buffer_unref(&hw_device_ctx);
1010 1 av_hwframe_constraints_free(&hw_device_constr);
1011 1 av_frame_free(&ref);
1012
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (fp)
1013 fclose(fp);
1014 1 exit_handler(ret);
1015 }
1016