FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/graph.c
Date: 2026-11-04 10:59:43
Exec Total Coverage
Lines: 462 565 81.8%
Functions: 36 38 94.7%
Branches: 235 350 67.1%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2024 Niklas Haas
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/avassert.h"
22 #include "libavutil/cpu.h"
23 #include "libavutil/error.h"
24 #include "libavutil/hwcontext.h"
25 #include "libavutil/imgutils.h"
26 #include "libavutil/macros.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/pixdesc.h"
30 #include "libavutil/refstruct.h"
31 #include "libavutil/slicethread.h"
32
33 #include "libswscale/swscale.h"
34 #include "libswscale/format.h"
35
36 #include "cms.h"
37 #include "lut3d.h"
38 #include "swscale_internal.h"
39 #include "graph.h"
40 #include "ops.h"
41 #include "ops_dispatch.h"
42 #if CONFIG_SWSCALE_VULKAN
43 #include "vulkan/ops.h"
44 #endif
45
46 486403 int ff_sws_pass_aligned_width(const SwsPass *pass, int width)
47 {
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 486403 times.
486403 if (!pass)
49 ✗ return width;
50
51 486403 size_t aligned_w = width;
52 486403 aligned_w = FFALIGN(aligned_w, pass->output->width_align);
53 486403 aligned_w += pass->output->width_pad;
54
1/2
✓ Branch 0 taken 486403 times.
✗ Branch 1 not taken.
486403 return aligned_w <= INT_MAX ? aligned_w : width;
55 }
56
57 /* Allocates (or refs) one buffer per plane */
58 119511 static int frame_alloc_planes_ref(AVFrame *dst, const AVFrame *src,
59 const int plane_copy[4])
60 {
61 119511 int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX,
62 119511 dst->format, 0, NULL);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119511 times.
119511 if (ret < 0)
64 ✗ return ret;
65
66 119511 const int align = av_cpu_max_align();
67 119511 const int aligned_w = FFALIGN(dst->width + 1, align); /* add space for over-write */
68 119511 ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w);
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119511 times.
119511 if (ret < 0)
70 ✗ return ret;
71
72 ptrdiff_t linesize1[4];
73
2/2
✓ Branch 0 taken 478044 times.
✓ Branch 1 taken 119511 times.
597555 for (int i = 0; i < 4; i++)
74 478044 linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align);
75
76 size_t sizes[4];
77 119511 ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1);
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119511 times.
119511 if (ret < 0)
79 ✗ return ret;
80
81
2/2
✓ Branch 0 taken 433180 times.
✓ Branch 1 taken 11339 times.
444519 for (int i = 0; i < 4; i++) {
82
2/2
✓ Branch 0 taken 108172 times.
✓ Branch 1 taken 325008 times.
433180 if (!sizes[i])
83 108172 break;
84 325008 int src_idx = plane_copy[i];
85
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 325008 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
325008 if (src_idx >= 0 && src) {
86 /* Ref the source plane instead of allocating a new buffer */
87 ✗ dst->buf[i] = av_buffer_ref(src->buf[src_idx]);
88 ✗ if (!dst->buf[i])
89 ✗ return AVERROR(ENOMEM);
90 ✗ dst->data[i] = src->data[src_idx];
91 ✗ dst->linesize[i] = src->linesize[src_idx];
92 ✗ continue;
93 }
94
95 325008 AVBufferRef *buf = av_buffer_alloc(sizes[i]);
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 325008 times.
325008 if (!buf)
97 ✗ return AVERROR(ENOMEM);
98 325008 dst->data[i] = buf->data;
99 325008 dst->buf[i] = buf;
100 }
101
102 119511 return 0;
103 }
104
105 #if CONFIG_SWSCALE_VULKAN
106 static int pass_alloc_output_hw(SwsPass *pass, AVFrame *avframe,
107 AVBufferRef *dev_ref)
108 {
109 SwsPassBuffer *buffer = pass->output;
110 AVBufferRef *frames_ref = av_hwframe_ctx_alloc(dev_ref);
111 if (!frames_ref)
112 return AVERROR(ENOMEM);
113
114 AVHWFramesContext *hwfc = (AVHWFramesContext *)frames_ref->data;
115 hwfc->format = AV_PIX_FMT_VULKAN;
116 hwfc->sw_format = pass->format;
117 hwfc->width = buffer->width;
118 hwfc->height = buffer->height;
119
120 int ret = av_hwframe_ctx_init(frames_ref);
121 if (ret >= 0) {
122 avframe->format = AV_PIX_FMT_VULKAN;
123 ret = av_hwframe_get_buffer(frames_ref, avframe, 0);
124 }
125 av_buffer_unref(&frames_ref);
126 return ret;
127 }
128 #endif
129
130 395453 static int pass_alloc_output(SwsPass *pass)
131 {
132
3/4
✓ Branch 0 taken 119511 times.
✓ Branch 1 taken 275942 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 119511 times.
395453 if (!pass || pass->output->avframe)
133 275942 return 0;
134
135 119511 SwsPassBuffer *buffer = pass->output;
136 119511 AVFrame *avframe = av_frame_alloc();
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119511 times.
119511 if (!avframe)
138 ✗ return AVERROR(ENOMEM);
139 119511 avframe->width = buffer->width;
140 119511 avframe->height = buffer->height;
141
142 int ret;
143
144 #if CONFIG_SWSCALE_VULKAN
145 const SwsGraph *graph = pass->graph;
146 if (graph->src.hw_format == AV_PIX_FMT_VULKAN &&
147 graph->dst.hw_format == AV_PIX_FMT_VULKAN) {
148 AVBufferRef *dev_ref = ff_sws_vk_device_ref(graph->ctx);
149 if (dev_ref) {
150 ret = pass_alloc_output_hw(pass, avframe, dev_ref);
151 if (ret >= 0)
152 goto done;
153 av_frame_unref(avframe);
154 }
155 }
156 #endif
157
158 119511 const AVFrame *src = NULL;
159
2/2
✓ Branch 0 taken 35016 times.
✓ Branch 1 taken 84495 times.
119511 if (pass->input)
160 35016 src = pass->input->output->avframe;
161
162 119511 avframe->format = pass->format;
163 119511 ret = frame_alloc_planes_ref(avframe, src, buffer->plane_copy);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119511 times.
119511 if (ret < 0) {
165 ✗ av_frame_free(&avframe);
166 ✗ return ret;
167 }
168
169 #if CONFIG_SWSCALE_VULKAN
170 done:
171 #endif
172 119511 buffer->avframe = avframe;
173 119511 ff_sws_frame_from_avframe(&buffer->frame, avframe);
174 119511 return 0;
175 }
176
177 402834 static void free_buffer(AVRefStructOpaque opaque, void *obj)
178 {
179 402834 SwsPassBuffer *buffer = obj;
180 402834 av_frame_free(&buffer->avframe);
181 402834 }
182
183 402834 static void pass_free(SwsPass *pass)
184 {
185
2/2
✓ Branch 0 taken 397804 times.
✓ Branch 1 taken 5030 times.
402834 if (pass->free)
186 397804 pass->free(pass->priv);
187 402834 av_refstruct_unref(&pass->output);
188 402834 av_free(pass);
189 402834 }
190
191 402834 int ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
192 int width, int height, SwsPass *input,
193 int lines, int align,
194 SwsPassFunc run, SwsPassSetup setup,
195 void *priv, void (*free_cb)(void *priv),
196 SwsPass **out_pass)
197 {
198 int ret;
199 402834 SwsPass *pass = av_mallocz(sizeof(*pass));
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 402834 times.
402834 if (!pass) {
201 ✗ if (free_cb)
202 ✗ free_cb(priv);
203 ✗ return AVERROR(ENOMEM);
204 }
205
206
1/2
✓ Branch 0 taken 402834 times.
✗ Branch 1 not taken.
402834 if (!lines)
207 402834 lines = height;
208
209 402834 pass->graph = graph;
210 402834 pass->run = run;
211 402834 pass->setup = setup;
212 402834 pass->priv = priv;
213 402834 pass->free = free_cb;
214 402834 pass->format = fmt;
215 402834 pass->lines = lines;
216 402834 pass->input = input;
217 402834 pass->output = av_refstruct_alloc_ext(sizeof(*pass->output), 0, NULL, free_buffer);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 402834 times.
402834 if (!pass->output) {
219 ✗ ret = AVERROR(ENOMEM);
220 ✗ goto fail;
221 }
222
223 402834 pass->output->height = height;
224 402834 pass->output->width = width;
225 402834 pass->output->width_align = 1;
226 402834 memset(pass->output->plane_copy, -1, sizeof(pass->output->plane_copy));
227
228
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 402771 times.
402834 if (!align) {
229 63 pass->slice_h = pass->lines;
230 63 pass->num_slices = 1;
231 } else {
232 402771 pass->slice_h = (pass->lines + graph->num_threads - 1) / graph->num_threads;
233 402771 pass->slice_h = FFALIGN(pass->slice_h, align);
234 402771 pass->num_slices = (pass->lines + pass->slice_h - 1) / pass->slice_h;
235 }
236
237 402834 ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 402834 times.
402834 if (ret < 0)
239 ✗ goto fail;
240
241 402834 *out_pass = pass;
242 402834 return 0;
243
244 ✗ fail:
245 ✗ pass_free(pass);
246 ✗ return ret;
247 }
248
249 283122 void ff_sws_pass_link_output(SwsPass *dst, const SwsPass *src)
250 {
251
4/6
✓ Branch 0 taken 283122 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22889 times.
✓ Branch 3 taken 260233 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 22889 times.
283122 if (!dst || !src || dst == src)
252 260233 return;
253
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22889 times.
22889 av_assert0(dst->format == src->format);
255 22889 SwsPassBuffer *keep = src->output, *drop = dst->output;
256
257 av_assert1(keep->width == drop->width);
258 av_assert1(keep->height == drop->height);
259 22889 keep->width_align = FFMAX(keep->width_align, drop->width_align);
260 22889 keep->width_pad = FFMAX(keep->width_pad, drop->width_pad);
261
262
2/2
✓ Branch 0 taken 91556 times.
✓ Branch 1 taken 22889 times.
114445 for (int i = 0; i < FF_ARRAY_ELEMS(keep->plane_copy); i++) {
263
2/2
✓ Branch 0 taken 91532 times.
✓ Branch 1 taken 24 times.
91556 if (keep->plane_copy[i] < 0)
264 91532 keep->plane_copy[i] = drop->plane_copy[i];
265 24 else if (drop->plane_copy[i] >= 0)
266 av_assert1(keep->plane_copy[i] == drop->plane_copy[i]);
267 }
268
269 22889 av_refstruct_replace(&dst->output, src->output);
270 }
271
272 773948 static void frame_shift(const SwsFrame *f, const int y, uint8_t *data[4])
273 {
274
2/2
✓ Branch 0 taken 3095792 times.
✓ Branch 1 taken 773948 times.
3869740 for (int i = 0; i < 4; i++) {
275
2/2
✓ Branch 0 taken 1917212 times.
✓ Branch 1 taken 1178580 times.
3095792 if (f->data[i])
276 1917212 data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i];
277 else
278 1178580 data[i] = NULL;
279 }
280 773948 }
281
282 84 static void run_copy(const SwsFrame *out, const SwsFrame *in, int y, int h,
283 const SwsPass *pass)
284 {
285 uint8_t *in_data[4], *out_data[4];
286 84 frame_shift(in, y, in_data);
287 84 frame_shift(out, y, out_data);
288
289
4/4
✓ Branch 0 taken 276 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 210 times.
✓ Branch 3 taken 66 times.
294 for (int i = 0; i < 4 && out_data[i]; i++) {
290 210 const int lines = h >> ff_fmt_vshift(in->format, i);
291 av_assert1(in_data[i]);
292
293
3/4
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 156 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 54 times.
210 if (i == 1 && av_pix_fmt_desc_get(in->format)->flags & AV_PIX_FMT_FLAG_PAL) {
294 ✗ if (!y && in->data[1] != out->data[1])
295 ✗ memcpy(out->data[1], in->data[1], AVPALETTE_SIZE);
296 ✗ break;
297 }
298
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
210 if (in_data[i] == out_data[i]) {
300 ✗ av_assert0(in->linesize[i] == out->linesize[i]);
301 } else {
302 210 ff_copyPlane(in_data[i], in->linesize[i], 0, lines,
303 210 av_image_get_linesize(out->format, out->width, i),
304 210 out_data[i], out->linesize[i]);
305 }
306 }
307 84 }
308
309 4158 static void run_rgb0(const SwsFrame *out, const SwsFrame *in, int y, int h,
310 const SwsPass *pass)
311 {
312 4158 SwsInternal *c = pass->priv;
313 4158 const int x0 = c->src0Alpha - 1;
314 4158 const int w4 = 4 * out->width;
315 4158 const int src_stride = in->linesize[0];
316 4158 const int dst_stride = out->linesize[0];
317 4158 const uint8_t *src = in->data[0] + y * src_stride;
318 4158 uint8_t *dst = out->data[0] + y * dst_stride;
319
320
2/2
✓ Branch 0 taken 397622 times.
✓ Branch 1 taken 4158 times.
401780 for (int y = 0; y < h; y++) {
321 397622 memcpy(dst, src, w4 * sizeof(*dst));
322
2/2
✓ Branch 0 taken 40529874 times.
✓ Branch 1 taken 397622 times.
40927496 for (int x = x0; x < w4; x += 4)
323 40529874 dst[x] = 0xFF;
324
325 397622 src += src_stride;
326 397622 dst += dst_stride;
327 }
328 4158 }
329
330 2575 static void run_xyz2rgb(const SwsFrame *out, const SwsFrame *in, int y, int h,
331 const SwsPass *pass)
332 {
333 2575 const SwsInternal *c = pass->priv;
334 2575 c->xyz12Torgb48(c, out->data[0] + y * out->linesize[0], out->linesize[0],
335 2575 in->data[0] + y * in->linesize[0], in->linesize[0],
336 2575 out->width, h);
337 2575 }
338
339 2640 static void run_rgb2xyz(const SwsFrame *out, const SwsFrame *in, int y, int h,
340 const SwsPass *pass)
341 {
342 2640 const SwsInternal *c = pass->priv;
343 2640 c->rgb48Toxyz12(c, out->data[0] + y * out->linesize[0], out->linesize[0],
344 2640 in->data[0] + y * in->linesize[0], in->linesize[0],
345 2640 out->width, h);
346 2640 }
347
348 /***********************************************************************
349 * Internal ff_swscale() wrapper. This reuses the legacy scaling API. *
350 * This is considered fully deprecated, and will be replaced by a full *
351 * reimplementation ASAP. *
352 ***********************************************************************/
353
354 114682 static void free_legacy_swscale(void *priv)
355 {
356 114682 SwsContext *sws = priv;
357 114682 sws_free_context(&sws);
358 114682 }
359
360 225157 static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
361 const SwsPass *pass)
362 {
363 225157 SwsContext *sws = pass->priv;
364 225157 SwsInternal *c = sws_internal(sws);
365
5/6
✓ Branch 0 taken 215841 times.
✓ Branch 1 taken 9316 times.
✓ Branch 2 taken 931 times.
✓ Branch 3 taken 214910 times.
✓ Branch 4 taken 931 times.
✗ Branch 5 not taken.
225157 if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
366
2/2
✓ Branch 0 taken 3724 times.
✓ Branch 1 taken 931 times.
4655 for (int i = 0; i < 4; i++)
367 3724 memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
368 }
369
370
2/2
✓ Branch 1 taken 9915 times.
✓ Branch 2 taken 215242 times.
225157 if (usePal(sws->src_format))
371 9915 ff_update_palette(c, (const uint32_t *) in->data[1]);
372
373 225157 return 0;
374 }
375
376 773780 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
377 {
378 773780 SwsContext *sws = pass->priv;
379 773780 SwsInternal *parent = sws_internal(sws);
380
2/2
✓ Branch 0 taken 155097 times.
✓ Branch 1 taken 618683 times.
773780 if (pass->num_slices == 1)
381 155097 return sws;
382
383 av_assert1(parent->nb_slice_ctx == pass->num_slices);
384 618683 sws = parent->slice_ctx[y / pass->slice_h];
385
386
2/2
✓ Branch 1 taken 35126 times.
✓ Branch 2 taken 583557 times.
618683 if (usePal(sws->src_format)) {
387 35126 SwsInternal *sub = sws_internal(sws);
388 35126 memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv));
389 35126 memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb));
390 }
391
392 618683 return sws;
393 }
394
395 101712 static void run_legacy_unscaled(const SwsFrame *out, const SwsFrame *in,
396 int y, int h, const SwsPass *pass)
397 {
398 101712 SwsContext *sws = slice_ctx(pass, y);
399 101712 SwsInternal *c = sws_internal(sws);
400 uint8_t *in_data[4];
401 101712 frame_shift(in, y, in_data);
402
403 101712 c->convert_unscaled(c, (const uint8_t *const *) in_data, in->linesize, y, h,
404 101712 out->data, out->linesize);
405 101712 }
406
407 672068 static void run_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
408 int y, int h, const SwsPass *pass)
409 {
410 672068 SwsContext *sws = slice_ctx(pass, y);
411 672068 SwsInternal *c = sws_internal(sws);
412 uint8_t *out_data[4];
413 672068 frame_shift(out, y, out_data);
414
415 672068 ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
416 672068 sws->src_h, out_data, out->linesize, y, h);
417 672068 }
418
419 ✗ static void run_legacy_lut3d(const SwsFrame *out, const SwsFrame *in,
420 int y, int h, const SwsPass *pass)
421 {
422 ✗ const SwsLut3D *lut = pass->graph->lut3d;
423 uint8_t *in_data[4], *out_data[4];
424 ✗ frame_shift(in, y, in_data);
425 ✗ frame_shift(out, y, out_data);
426
427 ✗ ff_sws_lut3d_apply_rgba64(lut, in_data[0], in->linesize[0], out_data[0],
428 ✗ out->linesize[0], out->width, h);
429 ✗ }
430
431 458708 static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
432 {
433
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 458708 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
458708 if (override == -513 || override == *chr_pos)
434 458708 return;
435
436 ✗ if (!*warned) {
437 ✗ av_log(NULL, AV_LOG_WARNING,
438 "Setting chroma position directly is deprecated, make sure "
439 "the frame is tagged with the correct chroma location.\n");
440 ✗ *warned = 1;
441 }
442
443 ✗ *chr_pos = override;
444 }
445
446 /* Takes over ownership of `sws` */
447 114687 static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws,
448 SwsPass *input, SwsPass **output)
449 {
450 114687 SwsInternal *c = sws_internal(sws);
451 114687 const int src_w = sws->src_w, src_h = sws->src_h;
452 114687 const int dst_w = sws->dst_w, dst_h = sws->dst_h;
453 114687 int align = c->dst_slice_align;
454 114687 SwsPass *pass = NULL;
455 int ret;
456
457
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 114682 times.
114687 if (c->cascaded_context[0]) {
458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 const int num_cascaded = c->cascaded_context[2] ? 3 : 2;
459
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 for (int i = 0; i < num_cascaded; i++) {
460 10 const int is_last = i + 1 == num_cascaded;
461
462 /* Steal cascaded context, so we can manage its lifetime independently */
463 10 SwsContext *sub = c->cascaded_context[i];
464 10 c->cascaded_context[i] = NULL;
465
466
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input);
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (ret < 0)
468 ✗ break;
469 }
470
471 5 sws_free_context(&sws);
472 5 return ret;
473 }
474
475
3/4
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 114619 times.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
114682 if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
476 63 align = 0; /* disable slice threading */
477
478
6/6
✓ Branch 0 taken 4009 times.
✓ Branch 1 taken 110673 times.
✓ Branch 2 taken 4005 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 3990 times.
✓ Branch 6 taken 15 times.
114682 if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
479 3990 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGBA, src_w, src_h, input,
480 0, 1, run_rgb0, NULL, c, NULL, &input);
481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3990 times.
3990 if (ret < 0) {
482 ✗ sws_free_context(&sws);
483 ✗ return ret;
484 }
485 }
486
487
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 114668 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 2 times.
114682 if (c->srcXYZ && !c->dstXYZ) {
488 12 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, src_w, src_h, input,
489 0, 1, run_xyz2rgb, NULL, c, NULL, &input);
490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ret < 0) {
491 ✗ sws_free_context(&sws);
492 ✗ return ret;
493 }
494 }
495
496 114682 ret = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, 0, align,
497
2/2
✓ Branch 0 taken 8007 times.
✓ Branch 1 taken 106675 times.
114682 c->convert_unscaled ? run_legacy_unscaled : run_legacy_swscale,
498 setup_legacy_swscale, sws, free_legacy_swscale, &pass);
499
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114682 times.
114682 if (ret < 0)
500 ✗ return ret;
501 114682 pass->backend = SWS_BACKEND_LEGACY;
502
503 /**
504 * For slice threading, we need to create sub contexts, similar to how
505 * swscale normally handles it internally. The most important difference
506 * is that we handle cascaded contexts before threaded contexts; whereas
507 * context_init_threaded() does it the other way around.
508 */
509
510
2/2
✓ Branch 0 taken 4138 times.
✓ Branch 1 taken 110544 times.
114682 if (pass->num_slices > 1) {
511 4138 c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4138 times.
4138 if (!c->slice_ctx)
513 ✗ return AVERROR(ENOMEM);
514
515
2/2
✓ Branch 0 taken 31304 times.
✓ Branch 1 taken 4138 times.
35442 for (int i = 0; i < pass->num_slices; i++) {
516 SwsContext *slice;
517 SwsInternal *c2;
518 31304 slice = c->slice_ctx[i] = sws_alloc_context();
519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31304 times.
31304 if (!slice)
520 ✗ return AVERROR(ENOMEM);
521 31304 c->nb_slice_ctx++;
522
523 31304 c2 = sws_internal(slice);
524 31304 c2->parent = sws;
525
526 31304 ret = av_opt_copy(slice, sws);
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31304 times.
31304 if (ret < 0)
528 ✗ return ret;
529
530 31304 ret = ff_sws_init_single_context(slice, NULL, NULL);
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31304 times.
31304 if (ret < 0)
532 ✗ return ret;
533
534 31304 sws_setColorspaceDetails(slice, c->srcColorspaceTable,
535 31304 slice->src_range, c->dstColorspaceTable,
536 slice->dst_range, c->brightness, c->contrast,
537 c->saturation);
538
539
2/2
✓ Branch 0 taken 125216 times.
✓ Branch 1 taken 31304 times.
156520 for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
540 125216 c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
541 125216 c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
542 }
543
544 31304 c2->srcXYZ = c->srcXYZ;
545 31304 c2->dstXYZ = c->dstXYZ;
546 }
547 }
548
549
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 114644 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 2 times.
114682 if (c->dstXYZ && !c->srcXYZ) {
550 36 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, pass,
551 0, 1, run_rgb2xyz, NULL, c, NULL, &pass);
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (ret < 0)
553 ✗ return ret;
554 }
555
556 114682 *output = pass;
557 114682 return 0;
558 }
559
560 static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
561 SwsPass *input, SwsPass **output);
562
563 300512 static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src,
564 const SwsFormat *dst, const SwsLut3D *lut3d,
565 SwsPass *input, SwsPass **output)
566 {
567 300512 int ret, warned = 0;
568 300512 SwsContext *const ctx = graph->ctx;
569 300512 const SwsBackend backend = ff_sws_enabled_backends(ctx);
570
2/2
✓ Branch 0 taken 181940 times.
✓ Branch 1 taken 118572 times.
300512 if (!(backend & SWS_BACKEND_LEGACY))
571 181940 return AVERROR(ENOTSUP);
572
2/4
✓ Branch 0 taken 118572 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 118572 times.
118572 if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE)
573 ✗ return AVERROR(ENOTSUP);
574
575 /* Re-check this here because this might not be excluded if the caller was
576 * testing against multiple backends */
577
4/4
✓ Branch 1 taken 114725 times.
✓ Branch 2 taken 3847 times.
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 114677 times.
118572 if (!sws_isSupportedInput(src->format) || !sws_isSupportedOutput(dst->format))
578 3895 return AVERROR(ENOTSUP);
579
580 /* If we need to apply a 3D LUT, add it as an explicit input prepass */
581
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114677 times.
114677 if (lut3d) {
582 ✗ ret = add_legacy_3dlut_pass(graph, src, input, &input);
583 ✗ if (ret < 0)
584 ✗ return ret;
585
586 ✗ SwsFormat tmp = *src;
587 ✗ tmp.format = input->format;
588 ✗ tmp.color = lut3d->map.dst;
589 ✗ return add_legacy_sws_pass(graph, &tmp, dst, NULL, input, output);
590 }
591
592 114677 SwsContext *sws = sws_alloc_context();
593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114677 times.
114677 if (!sws)
594 ✗ return AVERROR(ENOMEM);
595
596 114677 sws->flags = ctx->flags;
597 114677 sws->dither = ctx->dither;
598 114677 sws->alpha_blend = ctx->alpha_blend;
599 114677 sws->gamma_flag = ctx->gamma_flag;
600 114677 sws->scaler = ctx->scaler;
601 114677 sws->scaler_sub = ctx->scaler_sub;
602
603 114677 sws->src_w = src->width;
604 114677 sws->src_h = src->height;
605 114677 sws->src_format = src->format;
606 114677 sws->src_range = src->range == AVCOL_RANGE_JPEG;
607
608 114677 sws->dst_w = dst->width;
609 114677 sws->dst_h = dst->height;
610 114677 sws->dst_format = dst->format;
611 114677 sws->dst_range = dst->range == AVCOL_RANGE_JPEG;
612 114677 ff_sws_chroma_pos(src, &graph->incomplete, &sws->src_h_chr_pos, &sws->src_v_chr_pos);
613 114677 ff_sws_chroma_pos(dst, &graph->incomplete, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos);
614
615 114677 graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
616 114677 graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
617
618 /* Allow overriding chroma position with the legacy API */
619 114677 legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
620 114677 legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
621 114677 legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
622 114677 legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned);
623
624 /* Explicitly strip chroma offsets when not subsampling, because it
625 * interferes with the operation of flags like SWS_FULL_CHR_H_INP */
626
2/2
✓ Branch 0 taken 108585 times.
✓ Branch 1 taken 6092 times.
114677 if (!src->desc->log2_chroma_w)
627 108585 sws->src_h_chr_pos = -513;
628
2/2
✓ Branch 0 taken 109342 times.
✓ Branch 1 taken 5335 times.
114677 if (!src->desc->log2_chroma_h)
629 109342 sws->src_v_chr_pos = -513;
630
2/2
✓ Branch 0 taken 112460 times.
✓ Branch 1 taken 2217 times.
114677 if (!dst->desc->log2_chroma_w)
631 112460 sws->dst_h_chr_pos = -513;
632
2/2
✓ Branch 0 taken 113266 times.
✓ Branch 1 taken 1411 times.
114677 if (!dst->desc->log2_chroma_h)
633 113266 sws->dst_v_chr_pos = -513;
634
635
2/2
✓ Branch 0 taken 229354 times.
✓ Branch 1 taken 114677 times.
344031 for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
636 229354 sws->scaler_params[i] = ctx->scaler_params[i];
637
638 114677 ret = sws_init_context(sws, NULL, NULL);
639
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114677 times.
114677 if (ret < 0) {
640 ✗ sws_free_context(&sws);
641 ✗ return ret;
642 }
643
644 /* Set correct color matrices */
645 {
646 int in_full, out_full, brightness, contrast, saturation;
647 const int *inv_table, *table;
648 114677 sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
649 (int **)&table, &out_full,
650 &brightness, &contrast, &saturation);
651
652 114677 inv_table = sws_getCoefficients(src->csp);
653 114677 table = sws_getCoefficients(dst->csp);
654
655
2/2
✓ Branch 0 taken 60098 times.
✓ Branch 1 taken 54579 times.
174775 graph->incomplete |= src->csp != dst->csp &&
656
2/2
✓ Branch 0 taken 55912 times.
✓ Branch 1 taken 4186 times.
60098 (src->csp == AVCOL_SPC_UNSPECIFIED ||
657
2/2
✓ Branch 0 taken 55900 times.
✓ Branch 1 taken 12 times.
55912 dst->csp == AVCOL_SPC_UNSPECIFIED);
658
659 114677 sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
660 brightness, contrast, saturation);
661 }
662
663 114677 return init_legacy_subpass(graph, sws, input, output);
664 }
665
666 ✗ static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
667 SwsPass *input, SwsPass **output)
668 {
669 int ret;
670
671 ✗ const SwsLut3D *lut3d = graph->lut3d;
672 ✗ if (!lut3d)
673 ✗ return 0;
674
675 ✗ const enum AVPixelFormat fmt = AV_PIX_FMT_RGBA64;
676 ✗ if (src->format != fmt) {
677 ✗ SwsFormat tmp = *src;
678 ✗ tmp.format = fmt;
679 ✗ ret = add_legacy_sws_pass(graph, src, &tmp, NULL, input, &input);
680 ✗ if (ret < 0)
681 ✗ return ret;
682 }
683
684 ✗ ret = ff_sws_graph_add_pass(graph, fmt, src->width, src->height,
685 input, 0, 1, run_legacy_lut3d, NULL, NULL, NULL,
686 output);
687 ✗ if (ret < 0)
688 ✗ return ret;
689
690 ✗ return 0;
691 }
692
693 /*********************************
694 * Format conversion and scaling *
695 *********************************/
696
697 216906 static int add_ops_convert_pass(SwsGraph *graph, const SwsFormat *src,
698 const SwsFormat *dst, const SwsLut3D *lut3d,
699 SwsPass *input, SwsPass **output)
700 {
701 216906 SwsContext *ctx = graph->ctx;
702
703 /* Preemptively skip the ops list generation if the backend was
704 * constrained to the legacy implementation only. This would
705 * normally also fail in ff_sws_compile_pass() with the same
706 * error, but this way saves a bit of unnecessary overhead */
707 216906 const SwsBackend backends = ff_sws_enabled_backends(ctx);
708
2/2
✓ Branch 0 taken 151 times.
✓ Branch 1 taken 216755 times.
216906 if (backends == SWS_BACKEND_LEGACY)
709 151 return AVERROR(ENOTSUP);
710
711 SwsOpList *ops;
712 216755 int ret = ff_sws_op_list_generate(ctx, src, dst, lut3d, &ops, &graph->incomplete);
713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 216755 times.
216755 if (ret < 0)
714 ✗ return ret;
715
716 216755 av_log(ctx, AV_LOG_VERBOSE, "Conversion pass for %s -> %s:\n",
717 216755 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
718
719 216755 av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
720 216755 ff_sws_op_list_update(ops);
721 216755 ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
722
723 216755 const int flags = SWS_OP_FLAG_OPTIMIZE | SWS_OP_FLAG_SPLIT_MEMCPY;
724 216755 return ff_sws_compile_pass(graph, NULL, &ops, flags, input, output);
725 }
726
727 331432 static bool prefer_ops_backend(SwsContext *ctx, const SwsFormat *src, const SwsFormat *dst)
728 {
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 331432 times.
331432 if (ctx->flags & SWS_UNSTABLE)
730 ✗ return true;
731
4/4
✓ Branch 1 taken 303089 times.
✓ Branch 2 taken 28343 times.
✓ Branch 4 taken 17388 times.
✓ Branch 5 taken 285701 times.
331432 if (isFloat(src->format) || isFloat(dst->format))
732 45731 return true; /* ops backend has better support for float formats */
733 285701 return false; /* default to legacy for stability reasons */
734 }
735
736 331432 static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
737 const SwsFormat *dst, const SwsLut3D *lut3d,
738 SwsPass *input, SwsPass **output)
739 {
740 331432 SwsContext *ctx = graph->ctx;
741 int ret;
742
743
2/2
✓ Branch 1 taken 45731 times.
✓ Branch 2 taken 285701 times.
331432 if (prefer_ops_backend(ctx, src, dst)) {
744 45731 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
745
2/2
✓ Branch 0 taken 14811 times.
✓ Branch 1 taken 30920 times.
45731 if (ret == AVERROR(ENOTSUP))
746 14811 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
747 } else {
748 285701 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
749
2/2
✓ Branch 0 taken 171175 times.
✓ Branch 1 taken 114526 times.
285701 if (ret == AVERROR(ENOTSUP))
750 171175 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
751 }
752
753 331432 return ret;
754 }
755
756 /**************************
757 * Gamut and tone mapping *
758 **************************/
759
760 332349 static int generate_3dlut(SwsGraph *graph, SwsFormat *src, SwsFormat *dst)
761 {
762 332349 SwsColorMap map = {0};
763
764 /**
765 * Grayspace does not really have primaries, so just force the use of
766 * the equivalent other primary set to avoid a conversion. Technically,
767 * this does affect the weights used for the Grayscale conversion, but
768 * in practise, that should give the expected results more often than not.
769 */
770
2/2
✓ Branch 1 taken 28529 times.
✓ Branch 2 taken 303820 times.
332349 if (isGray(dst->format)) {
771 28529 dst->color = src->color;
772
2/2
✓ Branch 1 taken 40965 times.
✓ Branch 2 taken 262855 times.
303820 } else if (isGray(src->format)) {
773 40965 src->color = dst->color;
774 }
775
776 /* Fully infer color spaces before color mapping logic */
777 332349 graph->incomplete |= ff_infer_colors(&src->color, &dst->color);
778
779 332349 map.intent = graph->ctx->intent;
780 332349 map.src = src->color;
781 332349 map.dst = dst->color;
782
783
1/2
✓ Branch 1 taken 332349 times.
✗ Branch 2 not taken.
332349 if (ff_sws_color_map_noop(&map))
784 332349 return 0;
785
786 ✗ if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE)
787 ✗ return AVERROR(ENOTSUP);
788
789 ✗ graph->lut3d = ff_sws_lut3d_alloc();
790 ✗ if (!graph->lut3d)
791 ✗ return AVERROR(ENOMEM);
792
793 ✗ return ff_sws_lut3d_generate(graph->lut3d, &map);
794 }
795
796 /***************************************
797 * Main filter graph construction code *
798 ***************************************/
799
800 332349 static int init_passes(SwsGraph *graph)
801 {
802 332349 SwsFormat src = graph->src;
803 332349 SwsFormat dst = graph->dst;
804 332349 SwsPass *pass = NULL; /* read from main input image */
805 int ret;
806
807 332349 graph->incomplete |= ff_infer_chroma_loc(&src, &dst);
808
809 332349 ret = generate_3dlut(graph, &src, &dst);
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332349 times.
332349 if (ret < 0)
811 ✗ return ret;
812
813
3/4
✓ Branch 1 taken 917 times.
✓ Branch 2 taken 331432 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 917 times.
332349 if (!ff_fmt_equal(&src, &dst) || graph->lut3d) {
814 331432 ret = add_convert_pass(graph, &src, &dst, graph->lut3d, pass, &pass);
815
2/2
✓ Branch 0 taken 79296 times.
✓ Branch 1 taken 252136 times.
331432 if (ret < 0)
816 79296 return ret;
817 }
818
819
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 252061 times.
253053 if (!pass) {
820 /* Plane passthrough is not implemented for hardware frames */
821
2/4
✓ Branch 0 taken 992 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 992 times.
992 if (src.hw_format != AV_PIX_FMT_NONE || dst.hw_format != AV_PIX_FMT_NONE)
822 ✗ return AVERROR(ENOTSUP);
823
824 /* No passes were added, so no operations were necessary */
825 992 graph->noop = 1;
826
827 992 const int nb_planes = av_pix_fmt_count_planes(dst.format);
828
2/2
✓ Branch 0 taken 2822 times.
✓ Branch 1 taken 992 times.
3814 for (int i = 0; i < nb_planes; i++)
829 2822 graph->plane_copy[i] = i;
830
831 /* Add threaded memcpy pass */
832 992 return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
833 pass, 0, 1, run_copy, NULL, NULL, NULL, &pass);
834 }
835
836 /* Compute end-to-end plane copy map */
837
2/2
✓ Branch 0 taken 394461 times.
✓ Branch 1 taken 252061 times.
646522 for (int n = 0; n < graph->num_passes; n++) {
838 394461 const SwsPass *pass = graph->passes[n];
839 /* This pass writes to an output buffer other than the image
840 * output, or copies from the output of a different pass */
841
3/4
✓ Branch 0 taken 394461 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 119511 times.
✓ Branch 3 taken 274950 times.
394461 if (pass->output->avframe || pass->input)
842 119511 continue;
843
2/2
✓ Branch 0 taken 1099800 times.
✓ Branch 1 taken 274950 times.
1374750 for (int i = 0; i < FF_ARRAY_ELEMS(graph->plane_copy); i++) {
844 1099800 const int idx = pass->output->plane_copy[i];
845
2/2
✓ Branch 0 taken 1099458 times.
✓ Branch 1 taken 342 times.
1099800 if (idx < 0)
846 1099458 continue;
847
2/2
✓ Branch 0 taken 318 times.
✓ Branch 1 taken 24 times.
342 if (graph->plane_copy[i] < 0) {
848 318 graph->plane_copy[i] = idx;
849 318 av_log(graph->ctx, AV_LOG_DEBUG, "Plane %d passthrough from "
850 "plane %d\n", i, idx);
851 } else {
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 av_assert0(graph->plane_copy[i] == idx);
853 }
854 }
855 }
856
857 252061 return 0;
858 }
859
860 623885 static int sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
861 int nb_threads)
862 {
863 623885 SwsGraph *graph = priv;
864 623885 const SwsPass *pass = graph->exec.pass;
865 623885 const int slice_y = jobnr * pass->slice_h;
866 623885 const int slice_h = FFMIN(pass->slice_h, pass->lines - slice_y);
867
868 623885 pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass);
869 623885 return 0;
870 }
871
872 87797 SwsGraph *ff_sws_graph_alloc(void)
873 {
874 87797 return av_mallocz(sizeof(SwsGraph));
875 }
876
877 499442 static void graph_uninit(SwsGraph *graph)
878 {
879 499442 avpriv_slicethread_free(&graph->slicethread);
880
881
2/2
✓ Branch 0 taken 395453 times.
✓ Branch 1 taken 499442 times.
894895 for (int i = 0; i < graph->num_passes; i++)
882 395453 pass_free(graph->passes[i]);
883 499442 av_free(graph->passes);
884
885 499442 av_refstruct_unref(&graph->lut3d);
886
887 499442 memset(graph, 0, sizeof(*graph));
888 499442 }
889
890 332349 int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
891 const SwsFormat *src)
892 {
893 int ret;
894
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332349 times.
332349 if (graph->ctx) {
895 ✗ av_log(ctx, AV_LOG_ERROR, "Graph is already initialized\n");
896 ✗ return AVERROR(EINVAL);
897 }
898
899 332349 graph->ctx = ctx;
900 332349 graph->src = *src;
901 332349 graph->dst = *dst;
902 332349 graph->opts_copy = *ctx;
903
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332349 times.
332349 av_assert0(src->interlaced == dst->interlaced);
904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 332349 times.
332349 av_assert0(src->field == dst->field);
905 332349 memset(graph->plane_copy, -1, sizeof(graph->plane_copy));
906
907
2/2
✓ Branch 0 taken 328205 times.
✓ Branch 1 taken 4144 times.
332349 if (ctx->threads == 1) {
908 328205 graph->num_threads = 1;
909 } else {
910 4144 ret = avpriv_slicethread_create2(&graph->slicethread, (void *) graph,
911 sws_graph_worker, NULL, ctx->threads);
912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4144 times.
4144 if (ret == AVERROR(ENOSYS)) {
913 /* Fall back to single threaded operation */
914 ✗ graph->num_threads = 1;
915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4144 times.
4144 } else if (ret < 0) {
916 ✗ goto error;
917 } else {
918 4144 graph->num_threads = ret;
919 }
920 }
921
922 332349 ret = init_passes(graph);
923
2/2
✓ Branch 0 taken 79296 times.
✓ Branch 1 taken 253053 times.
332349 if (ret < 0)
924 79296 goto error;
925
926 /* Resolve output buffers for all intermediate passes */
927
2/2
✓ Branch 0 taken 395453 times.
✓ Branch 1 taken 253053 times.
648506 for (int i = 0; i < graph->num_passes; i++) {
928 395453 graph->backend |= graph->passes[i]->backend;
929 395453 ret = pass_alloc_output(graph->passes[i]->input);
930
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 395453 times.
395453 if (ret < 0)
931 ✗ goto error;
932 }
933
934 253053 return 0;
935
936 79296 error:
937 79296 graph_uninit(graph);
938 79296 return ret;
939 }
940
941 79296 void ff_sws_graph_rollback(SwsGraph *graph, int since_idx)
942 {
943
2/2
✓ Branch 0 taken 7381 times.
✓ Branch 1 taken 79296 times.
86677 for (int i = since_idx; i < graph->num_passes; i++)
944 7381 pass_free(graph->passes[i]);
945 79296 graph->num_passes = since_idx;
946 79296 }
947
948 1020841 void ff_sws_graph_free(SwsGraph **pgraph)
949 {
950 1020841 SwsGraph *graph = *pgraph;
951
2/2
✓ Branch 0 taken 933044 times.
✓ Branch 1 taken 87797 times.
1020841 if (!graph)
952 933044 return;
953
954 87797 graph_uninit(graph);
955 87797 av_free(graph);
956 87797 *pgraph = NULL;
957 }
958
959 /* Tests only options relevant to SwsGraph */
960 239835 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
961 {
962 474797 return c1->flags == c2->flags &&
963
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->threads == c2->threads &&
964
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->dither == c2->dither &&
965
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->alpha_blend == c2->alpha_blend &&
966
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->gamma_flag == c2->gamma_flag &&
967
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->src_h_chr_pos == c2->src_h_chr_pos &&
968
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->src_v_chr_pos == c2->src_v_chr_pos &&
969
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
970
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
971
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->intent == c2->intent &&
972
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->scaler == c2->scaler &&
973
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 c1->scaler_sub == c2->scaler_sub &&
974
3/4
✓ Branch 0 taken 234962 times.
✓ Branch 1 taken 4873 times.
✓ Branch 2 taken 234962 times.
✗ Branch 3 not taken.
709759 c1->backends == c2->backends &&
975
1/2
✓ Branch 0 taken 234962 times.
✗ Branch 1 not taken.
234962 !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
976
977 }
978
979 567311 int ff_sws_graph_reinit(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
980 const SwsFormat *src)
981 {
982
6/6
✓ Branch 1 taken 359428 times.
✓ Branch 2 taken 207883 times.
✓ Branch 4 taken 239835 times.
✓ Branch 5 taken 119593 times.
✓ Branch 6 taken 234962 times.
✓ Branch 7 taken 4873 times.
807146 if (ff_fmt_equal(&graph->src, src) && ff_fmt_equal(&graph->dst, dst) &&
983 239835 opts_equal(ctx, &graph->opts_copy))
984 {
985 234962 ff_sws_graph_update_metadata(graph, &src->color);
986 234962 return 0;
987 }
988
989 332349 graph_uninit(graph);
990 332349 return ff_sws_graph_init(graph, ctx, dst, src);
991 }
992
993 234962 void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
994 {
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234962 times.
234962 if (!color)
996 ✗ return;
997
998 234962 ff_color_update_dynamic(&graph->src.color, color);
999
1000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234962 times.
234962 if (graph->lut3d)
1001 ✗ ff_sws_lut3d_update(graph->lut3d, &graph->src.color);
1002 }
1003
1004 726258 static void get_field(SwsGraph *graph, const SwsFormat *fmt,
1005 const AVFrame *avframe, SwsFrame *frame)
1006 {
1007 726258 ff_sws_frame_from_avframe(frame, avframe);
1008
1009
1/2
✓ Branch 0 taken 726258 times.
✗ Branch 1 not taken.
726258 if (!(avframe->flags & AV_FRAME_FLAG_INTERLACED)) {
1010 av_assert1(!fmt->field);
1011 726258 return;
1012 }
1013
1014 ✗ if (fmt->field == FIELD_BOTTOM) {
1015 /* Odd rows, offset by one line */
1016 ✗ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
1017 ✗ for (int i = 0; i < 4; i++) {
1018 ✗ if (frame->data[i])
1019 ✗ frame->data[i] += frame->linesize[i];
1020 ✗ if (desc->flags & AV_PIX_FMT_FLAG_PAL)
1021 ✗ break;
1022 }
1023 }
1024
1025 /* Take only every second line */
1026 ✗ for (int i = 0; i < 4; i++)
1027 ✗ frame->linesize[i] <<= 1;
1028
1029 ✗ frame->height = (frame->height + (fmt->field == FIELD_TOP)) >> 1;
1030 }
1031
1032 363129 int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
1033 {
1034
2/4
✓ Branch 0 taken 363129 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 363129 times.
363129 av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
1035
2/4
✓ Branch 0 taken 363129 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 363129 times.
363129 av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
1036
1037 SwsFrame src_field, dst_field;
1038 363129 get_field(graph, &graph->dst, dst, &dst_field);
1039 363129 get_field(graph, &graph->src, src, &src_field);
1040
1041
2/2
✓ Branch 0 taken 507314 times.
✓ Branch 1 taken 363129 times.
870443 for (int i = 0; i < graph->num_passes; i++) {
1042 507314 const SwsPass *pass = graph->passes[i];
1043 507314 graph->exec.pass = pass;
1044
2/2
✓ Branch 0 taken 120827 times.
✓ Branch 1 taken 386487 times.
507314 graph->exec.input = pass->input ? &pass->input->output->frame : &src_field;
1045
2/2
✓ Branch 0 taken 120827 times.
✓ Branch 1 taken 386487 times.
507314 graph->exec.output = pass->output->avframe ? &pass->output->frame : &dst_field;
1046
2/2
✓ Branch 0 taken 502481 times.
✓ Branch 1 taken 4833 times.
507314 if (pass->setup) {
1047 502481 int ret = pass->setup(graph->exec.output, graph->exec.input, pass);
1048
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 502481 times.
502481 if (ret < 0)
1049 ✗ return ret;
1050 }
1051
1052
2/2
✓ Branch 0 taken 436676 times.
✓ Branch 1 taken 70638 times.
507314 if (pass->num_slices == 1) {
1053 436676 pass->run(graph->exec.output, graph->exec.input, 0, pass->lines, pass);
1054 } else {
1055 70638 avpriv_slicethread_execute2(graph->slicethread, pass->num_slices, 0);
1056 }
1057 }
1058
1059 363129 return 0;
1060 }
1061