FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/graph.c
Date: 2026-10-13 21:07:32
Exec Total Coverage
Lines: 450 563 79.9%
Functions: 35 38 92.1%
Branches: 234 354 66.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_VULKAN
43 #include "vulkan/ops.h"
44 #endif
45
46 343801 int ff_sws_pass_aligned_width(const SwsPass *pass, int width)
47 {
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 343801 times.
343801 if (!pass)
49 ✗ return width;
50
51 343801 size_t aligned_w = width;
52 343801 aligned_w = FFALIGN(aligned_w, pass->output->width_align);
53 343801 aligned_w += pass->output->width_pad;
54
1/2
✓ Branch 0 taken 343801 times.
✗ Branch 1 not taken.
343801 return aligned_w <= INT_MAX ? aligned_w : width;
55 }
56
57 /* Allocates (or refs) one buffer per plane */
58 60107 static int frame_alloc_planes_ref(AVFrame *dst, const AVFrame *src,
59 const int plane_copy[4])
60 {
61 60107 int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX,
62 60107 dst->format, 0, NULL);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60107 times.
60107 if (ret < 0)
64 ✗ return ret;
65
66 60107 const int align = av_cpu_max_align();
67 60107 const int aligned_w = FFALIGN(dst->width + 1, align); /* add space for over-write */
68 60107 ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w);
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60107 times.
60107 if (ret < 0)
70 ✗ return ret;
71
72 ptrdiff_t linesize1[4];
73
2/2
✓ Branch 0 taken 240428 times.
✓ Branch 1 taken 60107 times.
300535 for (int i = 0; i < 4; i++)
74 240428 linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align);
75
76 size_t sizes[4];
77 60107 ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1);
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60107 times.
60107 if (ret < 0)
79 ✗ return ret;
80
81
2/2
✓ Branch 0 taken 217420 times.
✓ Branch 1 taken 5618 times.
223038 for (int i = 0; i < 4; i++) {
82
2/2
✓ Branch 0 taken 54489 times.
✓ Branch 1 taken 162931 times.
217420 if (!sizes[i])
83 54489 break;
84 162931 int src_idx = plane_copy[i];
85
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 162931 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
162931 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 162931 AVBufferRef *buf = av_buffer_alloc(sizes[i]);
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 162931 times.
162931 if (!buf)
97 ✗ return AVERROR(ENOMEM);
98 162931 dst->data[i] = buf->data;
99 162931 dst->buf[i] = buf;
100 }
101
102 60107 return 0;
103 }
104
105 #if CONFIG_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 233359 static int pass_alloc_output(SwsPass *pass)
131 {
132
3/4
✓ Branch 0 taken 60107 times.
✓ Branch 1 taken 173252 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 60107 times.
233359 if (!pass || pass->output->avframe)
133 173252 return 0;
134
135 60107 SwsPassBuffer *buffer = pass->output;
136 60107 AVFrame *avframe = av_frame_alloc();
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60107 times.
60107 if (!avframe)
138 ✗ return AVERROR(ENOMEM);
139 60107 avframe->width = buffer->width;
140 60107 avframe->height = buffer->height;
141
142 int ret;
143
144 #if CONFIG_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 60107 const AVFrame *src = NULL;
159
2/2
✓ Branch 0 taken 17372 times.
✓ Branch 1 taken 42735 times.
60107 if (pass->input)
160 17372 src = pass->input->output->avframe;
161
162 60107 avframe->format = pass->format;
163 60107 ret = frame_alloc_planes_ref(avframe, src, buffer->plane_copy);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60107 times.
60107 if (ret < 0) {
165 ✗ av_frame_free(&avframe);
166 ✗ return ret;
167 }
168
169 #if CONFIG_VULKAN
170 done:
171 #endif
172 60107 buffer->avframe = avframe;
173 60107 ff_sws_frame_from_avframe(&buffer->frame, avframe);
174 60107 return 0;
175 }
176
177 237888 static void free_buffer(AVRefStructOpaque opaque, void *obj)
178 {
179 237888 SwsPassBuffer *buffer = obj;
180 237888 av_frame_free(&buffer->avframe);
181 237888 }
182
183 237888 static void pass_free(SwsPass *pass)
184 {
185
2/2
✓ Branch 0 taken 234535 times.
✓ Branch 1 taken 3353 times.
237888 if (pass->free)
186 234535 pass->free(pass->priv);
187 237888 av_refstruct_unref(&pass->output);
188 237888 av_free(pass);
189 237888 }
190
191 237888 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 237888 SwsPass *pass = av_mallocz(sizeof(*pass));
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 237888 times.
237888 if (!pass) {
201 ✗ if (free_cb)
202 ✗ free_cb(priv);
203 ✗ return AVERROR(ENOMEM);
204 }
205
206
1/2
✓ Branch 0 taken 237888 times.
✗ Branch 1 not taken.
237888 if (!lines)
207 237888 lines = height;
208
209 237888 pass->graph = graph;
210 237888 pass->run = run;
211 237888 pass->setup = setup;
212 237888 pass->priv = priv;
213 237888 pass->free = free_cb;
214 237888 pass->format = fmt;
215 237888 pass->lines = lines;
216 237888 pass->input = input;
217 237888 pass->output = av_refstruct_alloc_ext(sizeof(*pass->output), 0, NULL, free_buffer);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 237888 times.
237888 if (!pass->output) {
219 ✗ ret = AVERROR(ENOMEM);
220 ✗ goto fail;
221 }
222
223 237888 pass->output->height = height;
224 237888 pass->output->width = width;
225 237888 pass->output->width_align = 1;
226 237888 memset(pass->output->plane_copy, -1, sizeof(pass->output->plane_copy));
227
228
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 237853 times.
237888 if (!align) {
229 35 pass->slice_h = pass->lines;
230 35 pass->num_slices = 1;
231 } else {
232 237853 pass->slice_h = (pass->lines + graph->num_threads - 1) / graph->num_threads;
233 237853 pass->slice_h = FFALIGN(pass->slice_h, align);
234 237853 pass->num_slices = (pass->lines + pass->slice_h - 1) / pass->slice_h;
235 }
236
237 237888 ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 237888 times.
237888 if (ret < 0)
239 ✗ goto fail;
240
241 237888 *out_pass = pass;
242 237888 return 0;
243
244 ✗ fail:
245 ✗ pass_free(pass);
246 ✗ return ret;
247 }
248
249 161874 void ff_sws_pass_link_output(SwsPass *dst, const SwsPass *src)
250 {
251
4/6
✓ Branch 0 taken 161874 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14188 times.
✓ Branch 3 taken 147686 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14188 times.
161874 if (!dst || !src || dst == src)
252 147686 return;
253
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14188 times.
14188 av_assert0(dst->format == src->format);
255 14188 SwsPassBuffer *keep = src->output, *drop = dst->output;
256
257 av_assert1(keep->width == drop->width);
258 av_assert1(keep->height == drop->height);
259 14188 keep->width_align = FFMAX(keep->width_align, drop->width_align);
260 14188 keep->width_pad = FFMAX(keep->width_pad, drop->width_pad);
261
262
2/2
✓ Branch 0 taken 56752 times.
✓ Branch 1 taken 14188 times.
70940 for (int i = 0; i < FF_ARRAY_ELEMS(keep->plane_copy); i++) {
263
2/2
✓ Branch 0 taken 56734 times.
✓ Branch 1 taken 18 times.
56752 if (keep->plane_copy[i] < 0)
264 56734 keep->plane_copy[i] = drop->plane_copy[i];
265 18 else if (drop->plane_copy[i] >= 0)
266 av_assert1(keep->plane_copy[i] == drop->plane_copy[i]);
267 }
268
269 14188 av_refstruct_replace(&dst->output, src->output);
270 }
271
272 722528 static void frame_shift(const SwsFrame *f, const int y, uint8_t *data[4])
273 {
274
2/2
✓ Branch 0 taken 2890112 times.
✓ Branch 1 taken 722528 times.
3612640 for (int i = 0; i < 4; i++) {
275
2/2
✓ Branch 0 taken 1738534 times.
✓ Branch 1 taken 1151578 times.
2890112 if (f->data[i])
276 1738534 data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i];
277 else
278 1151578 data[i] = NULL;
279 }
280 722528 }
281
282 ✗ 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 ✗ frame_shift(in, y, in_data);
287 ✗ frame_shift(out, y, out_data);
288
289 ✗ for (int i = 0; i < 4 && out_data[i]; i++) {
290 ✗ const int lines = h >> ff_fmt_vshift(in->format, i);
291 av_assert1(in_data[i]);
292
293 ✗ if (in_data[i] == out_data[i]) {
294 ✗ av_assert0(in->linesize[i] == out->linesize[i]);
295 ✗ } else if (in->linesize[i] == out->linesize[i]) {
296 ✗ memcpy(out_data[i], in_data[i], lines * out->linesize[i]);
297 } else {
298 ✗ const int linesize = FFMIN(out->linesize[i], in->linesize[i]);
299 ✗ for (int j = 0; j < lines; j++) {
300 ✗ memcpy(out_data[i], in_data[i], linesize);
301 ✗ in_data[i] += in->linesize[i];
302 ✗ out_data[i] += out->linesize[i];
303 }
304 }
305 }
306 ✗ }
307
308 2604 static void run_rgb0(const SwsFrame *out, const SwsFrame *in, int y, int h,
309 const SwsPass *pass)
310 {
311 2604 SwsInternal *c = pass->priv;
312 2604 const int x0 = c->src0Alpha - 1;
313 2604 const int w4 = 4 * out->width;
314 2604 const int src_stride = in->linesize[0];
315 2604 const int dst_stride = out->linesize[0];
316 2604 const uint8_t *src = in->data[0] + y * src_stride;
317 2604 uint8_t *dst = out->data[0] + y * dst_stride;
318
319
2/2
✓ Branch 0 taken 249238 times.
✓ Branch 1 taken 2604 times.
251842 for (int y = 0; y < h; y++) {
320 249238 memcpy(dst, src, w4 * sizeof(*dst));
321
2/2
✓ Branch 0 taken 26383314 times.
✓ Branch 1 taken 249238 times.
26632552 for (int x = x0; x < w4; x += 4)
322 26383314 dst[x] = 0xFF;
323
324 249238 src += src_stride;
325 249238 dst += dst_stride;
326 }
327 2604 }
328
329 2579 static void run_xyz2rgb(const SwsFrame *out, const SwsFrame *in, int y, int h,
330 const SwsPass *pass)
331 {
332 2579 const SwsInternal *c = pass->priv;
333 2579 c->xyz12Torgb48(c, out->data[0] + y * out->linesize[0], out->linesize[0],
334 2579 in->data[0] + y * in->linesize[0], in->linesize[0],
335 2579 out->width, h);
336 2579 }
337
338 2644 static void run_rgb2xyz(const SwsFrame *out, const SwsFrame *in, int y, int h,
339 const SwsPass *pass)
340 {
341 2644 const SwsInternal *c = pass->priv;
342 2644 c->rgb48Toxyz12(c, out->data[0] + y * out->linesize[0], out->linesize[0],
343 2644 in->data[0] + y * in->linesize[0], in->linesize[0],
344 2644 out->width, h);
345 2644 }
346
347 /***********************************************************************
348 * Internal ff_swscale() wrapper. This reuses the legacy scaling API. *
349 * This is considered fully deprecated, and will be replaced by a full *
350 * reimplementation ASAP. *
351 ***********************************************************************/
352
353 72661 static void free_legacy_swscale(void *priv)
354 {
355 72661 SwsContext *sws = priv;
356 72661 sws_free_context(&sws);
357 72661 }
358
359 181013 static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
360 const SwsPass *pass)
361 {
362 181013 SwsContext *sws = pass->priv;
363 181013 SwsInternal *c = sws_internal(sws);
364
5/6
✓ Branch 0 taken 173701 times.
✓ Branch 1 taken 7312 times.
✓ Branch 2 taken 899 times.
✓ Branch 3 taken 172802 times.
✓ Branch 4 taken 899 times.
✗ Branch 5 not taken.
181013 if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
365
2/2
✓ Branch 0 taken 3596 times.
✓ Branch 1 taken 899 times.
4495 for (int i = 0; i < 4; i++)
366 3596 memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
367 }
368
369
2/2
✓ Branch 1 taken 7872 times.
✓ Branch 2 taken 173141 times.
181013 if (usePal(sws->src_format))
370 7872 ff_update_palette(c, (const uint32_t *) in->data[1]);
371
372 181013 return 0;
373 }
374
375 722528 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
376 {
377 722528 SwsContext *sws = pass->priv;
378 722528 SwsInternal *parent = sws_internal(sws);
379
2/2
✓ Branch 0 taken 112654 times.
✓ Branch 1 taken 609874 times.
722528 if (pass->num_slices == 1)
380 112654 return sws;
381
382 av_assert1(parent->nb_slice_ctx == pass->num_slices);
383 609874 sws = parent->slice_ctx[y / pass->slice_h];
384
385
2/2
✓ Branch 1 taken 35126 times.
✓ Branch 2 taken 574748 times.
609874 if (usePal(sws->src_format)) {
386 35126 SwsInternal *sub = sws_internal(sws);
387 35126 memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv));
388 35126 memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb));
389 }
390
391 609874 return sws;
392 }
393
394 100658 static void run_legacy_unscaled(const SwsFrame *out, const SwsFrame *in,
395 int y, int h, const SwsPass *pass)
396 {
397 100658 SwsContext *sws = slice_ctx(pass, y);
398 100658 SwsInternal *c = sws_internal(sws);
399 uint8_t *in_data[4];
400 100658 frame_shift(in, y, in_data);
401
402 100658 c->convert_unscaled(c, (const uint8_t *const *) in_data, in->linesize, y, h,
403 100658 out->data, out->linesize);
404 100658 }
405
406 621870 static void run_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
407 int y, int h, const SwsPass *pass)
408 {
409 621870 SwsContext *sws = slice_ctx(pass, y);
410 621870 SwsInternal *c = sws_internal(sws);
411 uint8_t *out_data[4];
412 621870 frame_shift(out, y, out_data);
413
414 621870 ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
415 621870 sws->src_h, out_data, out->linesize, y, h);
416 621870 }
417
418 ✗ static void run_legacy_lut3d(const SwsFrame *out, const SwsFrame *in,
419 int y, int h, const SwsPass *pass)
420 {
421 ✗ const SwsLut3D *lut = pass->graph->lut3d;
422 uint8_t *in_data[4], *out_data[4];
423 ✗ frame_shift(in, y, in_data);
424 ✗ frame_shift(out, y, out_data);
425
426 ✗ ff_sws_lut3d_apply_rgba64(lut, in_data[0], in->linesize[0], out_data[0],
427 ✗ out->linesize[0], out->width, h);
428 ✗ }
429
430 290624 static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
431 {
432
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 290624 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
290624 if (override == -513 || override == *chr_pos)
433 290624 return;
434
435 ✗ if (!*warned) {
436 ✗ av_log(NULL, AV_LOG_WARNING,
437 "Setting chroma position directly is deprecated, make sure "
438 "the frame is tagged with the correct chroma location.\n");
439 ✗ *warned = 1;
440 }
441
442 ✗ *chr_pos = override;
443 }
444
445 /* Takes over ownership of `sws` */
446 72666 static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws,
447 SwsPass *input, SwsPass **output)
448 {
449 72666 SwsInternal *c = sws_internal(sws);
450 72666 const int src_w = sws->src_w, src_h = sws->src_h;
451 72666 const int dst_w = sws->dst_w, dst_h = sws->dst_h;
452
4/4
✓ Branch 0 taken 46828 times.
✓ Branch 1 taken 25838 times.
✓ Branch 2 taken 38371 times.
✓ Branch 3 taken 8457 times.
72666 const int unscaled = src_w == dst_w && src_h == dst_h;
453 72666 int align = c->dst_slice_align;
454 72666 SwsPass *pass = NULL;
455 int ret;
456
457
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 72661 times.
72666 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 35 times.
✓ Branch 1 taken 72626 times.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
72661 if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
476 35 align = 0; /* disable slice threading */
477
478
6/6
✓ Branch 0 taken 2480 times.
✓ Branch 1 taken 70181 times.
✓ Branch 2 taken 2476 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 2461 times.
✓ Branch 6 taken 15 times.
72661 if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
479 2461 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 2461 times.
2461 if (ret < 0) {
482 ✗ sws_free_context(&sws);
483 ✗ return ret;
484 }
485 }
486
487
5/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 72647 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
72661 if (c->srcXYZ && !(c->dstXYZ && unscaled)) {
488 14 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 14 times.
14 if (ret < 0) {
491 ✗ sws_free_context(&sws);
492 ✗ return ret;
493 }
494 }
495
496 72661 ret = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, 0, align,
497
2/2
✓ Branch 0 taken 7058 times.
✓ Branch 1 taken 65603 times.
72661 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 72661 times.
72661 if (ret < 0)
500 ✗ return ret;
501 72661 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 2633 times.
✓ Branch 1 taken 70028 times.
72661 if (pass->num_slices > 1) {
511 2633 c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2633 times.
2633 if (!c->slice_ctx)
513 ✗ return AVERROR(ENOMEM);
514
515
2/2
✓ Branch 0 taken 23475 times.
✓ Branch 1 taken 2633 times.
26108 for (int i = 0; i < pass->num_slices; i++) {
516 SwsContext *slice;
517 SwsInternal *c2;
518 23475 slice = c->slice_ctx[i] = sws_alloc_context();
519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23475 times.
23475 if (!slice)
520 ✗ return AVERROR(ENOMEM);
521 23475 c->nb_slice_ctx++;
522
523 23475 c2 = sws_internal(slice);
524 23475 c2->parent = sws;
525
526 23475 ret = av_opt_copy(slice, sws);
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23475 times.
23475 if (ret < 0)
528 ✗ return ret;
529
530 23475 ret = ff_sws_init_single_context(slice, NULL, NULL);
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23475 times.
23475 if (ret < 0)
532 ✗ return ret;
533
534 23475 sws_setColorspaceDetails(slice, c->srcColorspaceTable,
535 23475 slice->src_range, c->dstColorspaceTable,
536 slice->dst_range, c->brightness, c->contrast,
537 c->saturation);
538
539
2/2
✓ Branch 0 taken 93900 times.
✓ Branch 1 taken 23475 times.
117375 for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
540 93900 c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
541 93900 c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
542 }
543 }
544 }
545
546
5/6
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 72623 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
72661 if (c->dstXYZ && !(c->srcXYZ && unscaled)) {
547 38 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, pass,
548 0, 1, run_rgb2xyz, NULL, c, NULL, &pass);
549
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (ret < 0)
550 ✗ return ret;
551 }
552
553 72661 *output = pass;
554 72661 return 0;
555 }
556
557 static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
558 SwsPass *input, SwsPass **output);
559
560 193446 static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src,
561 const SwsFormat *dst, const SwsLut3D *lut3d,
562 SwsPass *input, SwsPass **output)
563 {
564 193446 int ret, warned = 0;
565 193446 SwsContext *const ctx = graph->ctx;
566 193446 const SwsBackend backend = ff_sws_enabled_backends(ctx);
567
2/2
✓ Branch 0 taken 116419 times.
✓ Branch 1 taken 77027 times.
193446 if (!(backend & SWS_BACKEND_LEGACY))
568 116419 return AVERROR(ENOTSUP);
569
2/4
✓ Branch 0 taken 77027 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 77027 times.
77027 if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE)
570 ✗ return AVERROR(ENOTSUP);
571
572 /* Re-check this here because this might not be excluded if the caller was
573 * testing against multiple backends */
574
4/4
✓ Branch 1 taken 74573 times.
✓ Branch 2 taken 2454 times.
✓ Branch 4 taken 1917 times.
✓ Branch 5 taken 72656 times.
77027 if (!sws_isSupportedInput(src->format) || !sws_isSupportedOutput(dst->format))
575 4371 return AVERROR(ENOTSUP);
576
577 /* If we need to apply a 3D LUT, add it as an explicit input prepass */
578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72656 times.
72656 if (lut3d) {
579 ✗ ret = add_legacy_3dlut_pass(graph, src, input, &input);
580 ✗ if (ret < 0)
581 ✗ return ret;
582
583 ✗ SwsFormat tmp = *src;
584 ✗ tmp.format = input->format;
585 ✗ tmp.color = lut3d->map.dst;
586 ✗ return add_legacy_sws_pass(graph, &tmp, dst, NULL, input, output);
587 }
588
589 72656 SwsContext *sws = sws_alloc_context();
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72656 times.
72656 if (!sws)
591 ✗ return AVERROR(ENOMEM);
592
593 72656 sws->flags = ctx->flags;
594 72656 sws->dither = ctx->dither;
595 72656 sws->alpha_blend = ctx->alpha_blend;
596 72656 sws->gamma_flag = ctx->gamma_flag;
597 72656 sws->scaler = ctx->scaler;
598 72656 sws->scaler_sub = ctx->scaler_sub;
599
600 72656 sws->src_w = src->width;
601 72656 sws->src_h = src->height;
602 72656 sws->src_format = src->format;
603 72656 sws->src_range = src->range == AVCOL_RANGE_JPEG;
604
605 72656 sws->dst_w = dst->width;
606 72656 sws->dst_h = dst->height;
607 72656 sws->dst_format = dst->format;
608 72656 sws->dst_range = dst->range == AVCOL_RANGE_JPEG;
609 72656 ff_sws_chroma_pos(src, &graph->incomplete, &sws->src_h_chr_pos, &sws->src_v_chr_pos);
610 72656 ff_sws_chroma_pos(dst, &graph->incomplete, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos);
611
612 72656 graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
613 72656 graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
614
615 /* Allow overriding chroma position with the legacy API */
616 72656 legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
617 72656 legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
618 72656 legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
619 72656 legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned);
620
621 /* Explicitly strip chroma offsets when not subsampling, because it
622 * interferes with the operation of flags like SWS_FULL_CHR_H_INP */
623
2/2
✓ Branch 0 taken 68454 times.
✓ Branch 1 taken 4202 times.
72656 if (!src->desc->log2_chroma_w)
624 68454 sws->src_h_chr_pos = -513;
625
2/2
✓ Branch 0 taken 68836 times.
✓ Branch 1 taken 3820 times.
72656 if (!src->desc->log2_chroma_h)
626 68836 sws->src_v_chr_pos = -513;
627
2/2
✓ Branch 0 taken 70449 times.
✓ Branch 1 taken 2207 times.
72656 if (!dst->desc->log2_chroma_w)
628 70449 sws->dst_h_chr_pos = -513;
629
2/2
✓ Branch 0 taken 71255 times.
✓ Branch 1 taken 1401 times.
72656 if (!dst->desc->log2_chroma_h)
630 71255 sws->dst_v_chr_pos = -513;
631
632
2/2
✓ Branch 0 taken 145312 times.
✓ Branch 1 taken 72656 times.
217968 for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
633 145312 sws->scaler_params[i] = ctx->scaler_params[i];
634
635 72656 ret = sws_init_context(sws, NULL, NULL);
636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72656 times.
72656 if (ret < 0) {
637 ✗ sws_free_context(&sws);
638 ✗ return ret;
639 }
640
641 /* Set correct color matrices */
642 {
643 int in_full, out_full, brightness, contrast, saturation;
644 const int *inv_table, *table;
645 72656 sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
646 (int **)&table, &out_full,
647 &brightness, &contrast, &saturation);
648
649 72656 inv_table = sws_getCoefficients(src->csp);
650 72656 table = sws_getCoefficients(dst->csp);
651
652
2/2
✓ Branch 0 taken 37382 times.
✓ Branch 1 taken 35274 times.
110038 graph->incomplete |= src->csp != dst->csp &&
653
2/2
✓ Branch 0 taken 35443 times.
✓ Branch 1 taken 1939 times.
37382 (src->csp == AVCOL_SPC_UNSPECIFIED ||
654
2/2
✓ Branch 0 taken 35431 times.
✓ Branch 1 taken 12 times.
35443 dst->csp == AVCOL_SPC_UNSPECIFIED);
655
656 72656 sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
657 brightness, contrast, saturation);
658 }
659
660 72656 return init_legacy_subpass(graph, sws, input, output);
661 }
662
663 ✗ static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
664 SwsPass *input, SwsPass **output)
665 {
666 int ret;
667
668 ✗ const SwsLut3D *lut3d = graph->lut3d;
669 ✗ if (!lut3d)
670 ✗ return 0;
671
672 ✗ const enum AVPixelFormat fmt = AV_PIX_FMT_RGBA64;
673 ✗ if (src->format != fmt) {
674 ✗ SwsFormat tmp = *src;
675 ✗ tmp.format = fmt;
676 ✗ ret = add_legacy_sws_pass(graph, src, &tmp, NULL, input, &input);
677 ✗ if (ret < 0)
678 ✗ return ret;
679 }
680
681 ✗ ret = ff_sws_graph_add_pass(graph, fmt, src->width, src->height,
682 input, 0, 1, run_legacy_lut3d, NULL, NULL, NULL,
683 output);
684 ✗ if (ret < 0)
685 ✗ return ret;
686
687 ✗ return 0;
688 }
689
690 /*********************************
691 * Format conversion and scaling *
692 *********************************/
693
694 140074 static int add_ops_convert_pass(SwsGraph *graph, const SwsFormat *src,
695 const SwsFormat *dst, const SwsLut3D *lut3d,
696 SwsPass *input, SwsPass **output)
697 {
698 #if CONFIG_UNSTABLE
699 140074 SwsContext *ctx = graph->ctx;
700
701 /* Preemptively skip the ops list generation if the backend was
702 * constrained to the legacy implementation only. This would
703 * normally also fail in ff_sws_compile_pass() with the same
704 * error, but this way saves a bit of unnecessary overhead */
705 140074 const SwsBackend backends = ff_sws_enabled_backends(ctx);
706
2/2
✓ Branch 0 taken 151 times.
✓ Branch 1 taken 139923 times.
140074 if (backends == SWS_BACKEND_LEGACY)
707 151 return AVERROR(ENOTSUP);
708
709 SwsOpList *ops;
710 139923 int ret = ff_sws_op_list_generate(ctx, src, dst, lut3d, &ops, &graph->incomplete);
711
2/2
✓ Branch 0 taken 3852 times.
✓ Branch 1 taken 136071 times.
139923 if (ret < 0)
712 3852 return ret;
713
714 136071 av_log(ctx, AV_LOG_VERBOSE, "Conversion pass for %s -> %s:\n",
715 136071 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
716
717 136071 av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
718 136071 ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
719
720 136071 const int flags = SWS_OP_FLAG_OPTIMIZE | SWS_OP_FLAG_SPLIT_MEMCPY;
721 136071 return ff_sws_compile_pass(graph, NULL, &ops, flags, input, output);
722 #else
723 return AVERROR(ENOTSUP);
724 #endif
725 }
726
727 212579 static bool prefer_ops_backend(SwsContext *ctx, const SwsFormat *src, const SwsFormat *dst)
728 {
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212579 times.
212579 if (ctx->flags & SWS_UNSTABLE)
730 ✗ return true;
731
4/4
✓ Branch 1 taken 194656 times.
✓ Branch 2 taken 17923 times.
✓ Branch 4 taken 10824 times.
✓ Branch 5 taken 183832 times.
212579 if (isFloat(src->format) || isFloat(dst->format))
732 28747 return true; /* ops backend has better support for float formats */
733 183832 return false; /* default to legacy for stability reasons */
734 }
735
736 212579 static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
737 const SwsFormat *dst, const SwsLut3D *lut3d,
738 SwsPass *input, SwsPass **output)
739 {
740 212579 SwsContext *ctx = graph->ctx;
741 int ret;
742
743
2/2
✓ Branch 1 taken 28747 times.
✓ Branch 2 taken 183832 times.
212579 if (prefer_ops_backend(ctx, src, dst)) {
744 28747 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
745
2/2
✓ Branch 0 taken 9614 times.
✓ Branch 1 taken 19133 times.
28747 if (ret == AVERROR(ENOTSUP))
746 9614 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
747 } else {
748 183832 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
749
2/2
✓ Branch 0 taken 111327 times.
✓ Branch 1 taken 72505 times.
183832 if (ret == AVERROR(ENOTSUP))
750 111327 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
751 }
752
753 212579 return ret;
754 }
755
756 /**************************
757 * Gamut and tone mapping *
758 **************************/
759
760 213353 static int generate_3dlut(SwsGraph *graph, SwsFormat *src, SwsFormat *dst)
761 {
762 213353 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 17914 times.
✓ Branch 2 taken 195439 times.
213353 if (isGray(dst->format)) {
771 17914 dst->color = src->color;
772
2/2
✓ Branch 1 taken 25905 times.
✓ Branch 2 taken 169534 times.
195439 } else if (isGray(src->format)) {
773 25905 src->color = dst->color;
774 }
775
776 /* Fully infer color spaces before color mapping logic */
777 213353 graph->incomplete |= ff_infer_colors(&src->color, &dst->color);
778
779 213353 map.intent = graph->ctx->intent;
780 213353 map.src = src->color;
781 213353 map.dst = dst->color;
782
783
1/2
✓ Branch 1 taken 213353 times.
✗ Branch 2 not taken.
213353 if (ff_sws_color_map_noop(&map))
784 213353 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 213353 static int init_passes(SwsGraph *graph)
801 {
802 213353 SwsFormat src = graph->src;
803 213353 SwsFormat dst = graph->dst;
804 213353 SwsPass *pass = NULL; /* read from main input image */
805 int ret;
806
807 213353 graph->incomplete |= ff_infer_chroma_loc(&src, &dst);
808
809 213353 ret = generate_3dlut(graph, &src, &dst);
810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 213353 times.
213353 if (ret < 0)
811 ✗ return ret;
812
813
3/4
✓ Branch 1 taken 774 times.
✓ Branch 2 taken 212579 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 774 times.
213353 if (!ff_fmt_equal(&src, &dst) || graph->lut3d) {
814 212579 ret = add_convert_pass(graph, &src, &dst, graph->lut3d, pass, &pass);
815
2/2
✓ Branch 0 taken 54289 times.
✓ Branch 1 taken 158290 times.
212579 if (ret < 0)
816 54289 return ret;
817 }
818
819
2/2
✓ Branch 0 taken 840 times.
✓ Branch 1 taken 158224 times.
159064 if (!pass) {
820 /* Plane passthrough is not implemented for hardware frames */
821
2/4
✓ Branch 0 taken 840 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 840 times.
840 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 840 graph->noop = 1;
826
827 840 const int nb_planes = av_pix_fmt_count_planes(dst.format);
828
2/2
✓ Branch 0 taken 2313 times.
✓ Branch 1 taken 840 times.
3153 for (int i = 0; i < nb_planes; i++)
829 2313 graph->plane_copy[i] = i;
830
831 /* Add threaded memcpy pass */
832 840 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 232519 times.
✓ Branch 1 taken 158224 times.
390743 for (int n = 0; n < graph->num_passes; n++) {
838 232519 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 232519 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60107 times.
✓ Branch 3 taken 172412 times.
232519 if (pass->output->avframe || pass->input)
842 60107 continue;
843
2/2
✓ Branch 0 taken 689648 times.
✓ Branch 1 taken 172412 times.
862060 for (int i = 0; i < FF_ARRAY_ELEMS(graph->plane_copy); i++) {
844 689648 const int idx = pass->output->plane_copy[i];
845
2/2
✓ Branch 0 taken 689363 times.
✓ Branch 1 taken 285 times.
689648 if (idx < 0)
846 689363 continue;
847
2/2
✓ Branch 0 taken 267 times.
✓ Branch 1 taken 18 times.
285 if (graph->plane_copy[i] < 0) {
848 267 graph->plane_copy[i] = idx;
849 267 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 18 times.
18 av_assert0(graph->plane_copy[i] == idx);
853 }
854 }
855 }
856
857 158224 return 0;
858 }
859
860 615076 static int sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
861 int nb_threads)
862 {
863 615076 SwsGraph *graph = priv;
864 615076 const SwsPass *pass = graph->exec.pass;
865 615076 const int slice_y = jobnr * pass->slice_h;
866 615076 const int slice_h = FFMIN(pass->slice_h, pass->lines - slice_y);
867
868 615076 pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass);
869 615076 return 0;
870 }
871
872 60865 SwsGraph *ff_sws_graph_alloc(void)
873 {
874 60865 return av_mallocz(sizeof(SwsGraph));
875 }
876
877 328507 static void graph_uninit(SwsGraph *graph)
878 {
879 328507 avpriv_slicethread_free(&graph->slicethread);
880
881
2/2
✓ Branch 0 taken 233359 times.
✓ Branch 1 taken 328507 times.
561866 for (int i = 0; i < graph->num_passes; i++)
882 233359 pass_free(graph->passes[i]);
883 328507 av_free(graph->passes);
884
885 328507 av_refstruct_unref(&graph->lut3d);
886
887 328507 memset(graph, 0, sizeof(*graph));
888 328507 }
889
890 213353 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 213353 times.
213353 if (graph->ctx) {
895 ✗ av_log(ctx, AV_LOG_ERROR, "Graph is already initialized\n");
896 ✗ return AVERROR(EINVAL);
897 }
898
899 213353 graph->ctx = ctx;
900 213353 graph->src = *src;
901 213353 graph->dst = *dst;
902 213353 graph->opts_copy = *ctx;
903
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 213353 times.
213353 av_assert0(src->interlaced == dst->interlaced);
904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 213353 times.
213353 av_assert0(src->field == dst->field);
905 213353 memset(graph->plane_copy, -1, sizeof(graph->plane_copy));
906
907
2/2
✓ Branch 0 taken 210718 times.
✓ Branch 1 taken 2635 times.
213353 if (ctx->threads == 1) {
908 210718 graph->num_threads = 1;
909 } else {
910 2635 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 2635 times.
2635 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 2635 times.
2635 } else if (ret < 0) {
916 ✗ goto error;
917 } else {
918 2635 graph->num_threads = ret;
919 }
920 }
921
922 213353 ret = init_passes(graph);
923
2/2
✓ Branch 0 taken 54289 times.
✓ Branch 1 taken 159064 times.
213353 if (ret < 0)
924 54289 goto error;
925
926 /* Resolve output buffers for all intermediate passes */
927
2/2
✓ Branch 0 taken 233359 times.
✓ Branch 1 taken 159064 times.
392423 for (int i = 0; i < graph->num_passes; i++) {
928 233359 graph->backend |= graph->passes[i]->backend;
929 233359 ret = pass_alloc_output(graph->passes[i]->input);
930
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 233359 times.
233359 if (ret < 0)
931 ✗ goto error;
932 }
933
934 159064 return 0;
935
936 54289 error:
937 54289 graph_uninit(graph);
938 54289 return ret;
939 }
940
941 50437 void ff_sws_graph_rollback(SwsGraph *graph, int since_idx)
942 {
943
2/2
✓ Branch 0 taken 4529 times.
✓ Branch 1 taken 50437 times.
54966 for (int i = since_idx; i < graph->num_passes; i++)
944 4529 pass_free(graph->passes[i]);
945 50437 graph->num_passes = since_idx;
946 50437 }
947
948 699680 void ff_sws_graph_free(SwsGraph **pgraph)
949 {
950 699680 SwsGraph *graph = *pgraph;
951
2/2
✓ Branch 0 taken 638815 times.
✓ Branch 1 taken 60865 times.
699680 if (!graph)
952 638815 return;
953
954 60865 graph_uninit(graph);
955 60865 av_free(graph);
956 60865 *pgraph = NULL;
957 }
958
959 /* Tests only options relevant to SwsGraph */
960 189230 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
961 {
962 375351 return c1->flags == c2->flags &&
963
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->threads == c2->threads &&
964
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->dither == c2->dither &&
965
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->alpha_blend == c2->alpha_blend &&
966
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->gamma_flag == c2->gamma_flag &&
967
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->src_h_chr_pos == c2->src_h_chr_pos &&
968
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->src_v_chr_pos == c2->src_v_chr_pos &&
969
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
970
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
971
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->intent == c2->intent &&
972
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->scaler == c2->scaler &&
973
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 c1->scaler_sub == c2->scaler_sub &&
974
3/4
✓ Branch 0 taken 186121 times.
✓ Branch 1 taken 3109 times.
✓ Branch 2 taken 186121 times.
✗ Branch 3 not taken.
561472 c1->backends == c2->backends &&
975
1/2
✓ Branch 0 taken 186121 times.
✗ Branch 1 not taken.
186121 !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
976
977 }
978
979 399474 int ff_sws_graph_reinit(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
980 const SwsFormat *src)
981 {
982
6/6
✓ Branch 1 taken 264290 times.
✓ Branch 2 taken 135184 times.
✓ Branch 4 taken 189230 times.
✓ Branch 5 taken 75060 times.
✓ Branch 6 taken 186121 times.
✓ Branch 7 taken 3109 times.
588704 if (ff_fmt_equal(&graph->src, src) && ff_fmt_equal(&graph->dst, dst) &&
983 189230 opts_equal(ctx, &graph->opts_copy))
984 {
985 186121 ff_sws_graph_update_metadata(graph, &src->color);
986 186121 return 0;
987 }
988
989 213353 graph_uninit(graph);
990 213353 return ff_sws_graph_init(graph, ctx, dst, src);
991 }
992
993 186121 void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
994 {
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186121 times.
186121 if (!color)
996 ✗ return;
997
998 186121 ff_color_update_dynamic(&graph->src.color, color);
999
1000
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186121 times.
186121 if (graph->lut3d)
1001 ✗ ff_sws_lut3d_update(graph->lut3d, &graph->src.color);
1002 }
1003
1004 533838 static void get_field(SwsGraph *graph, const SwsFormat *fmt,
1005 const AVFrame *avframe, SwsFrame *frame)
1006 {
1007 533838 ff_sws_frame_from_avframe(frame, avframe);
1008
1009
1/2
✓ Branch 0 taken 533838 times.
✗ Branch 1 not taken.
533838 if (!(avframe->flags & AV_FRAME_FLAG_INTERLACED)) {
1010 av_assert1(!fmt->field);
1011 533838 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 266919 int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
1033 {
1034
2/4
✓ Branch 0 taken 266919 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266919 times.
266919 av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
1035
2/4
✓ Branch 0 taken 266919 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266919 times.
266919 av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
1036
1037 SwsFrame src_field, dst_field;
1038 266919 get_field(graph, &graph->dst, dst, &dst_field);
1039 266919 get_field(graph, &graph->src, src, &src_field);
1040
1041
2/2
✓ Branch 0 taken 342616 times.
✓ Branch 1 taken 266919 times.
609535 for (int i = 0; i < graph->num_passes; i++) {
1042 342616 const SwsPass *pass = graph->passes[i];
1043 342616 graph->exec.pass = pass;
1044
2/2
✓ Branch 0 taken 61206 times.
✓ Branch 1 taken 281410 times.
342616 graph->exec.input = pass->input ? &pass->input->output->frame : &src_field;
1045
2/2
✓ Branch 0 taken 61206 times.
✓ Branch 1 taken 281410 times.
342616 graph->exec.output = pass->output->avframe ? &pass->output->frame : &dst_field;
1046
2/2
✓ Branch 0 taken 339413 times.
✓ Branch 1 taken 3203 times.
342616 if (pass->setup) {
1047 339413 int ret = pass->setup(graph->exec.output, graph->exec.input, pass);
1048
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 339413 times.
339413 if (ret < 0)
1049 ✗ return ret;
1050 }
1051
1052
2/2
✓ Branch 0 taken 273679 times.
✓ Branch 1 taken 68937 times.
342616 if (pass->num_slices == 1) {
1053 273679 pass->run(graph->exec.output, graph->exec.input, 0, pass->lines, pass);
1054 } else {
1055 68937 avpriv_slicethread_execute2(graph->slicethread, pass->num_slices, 0);
1056 }
1057 }
1058
1059 266919 return 0;
1060 }
1061