FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/graph.c
Date: 2026-08-15 14:54:27
Exec Total Coverage
Lines: 447 560 79.8%
Functions: 35 38 92.1%
Branches: 231 350 66.0%

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 224858 int ff_sws_pass_aligned_width(const SwsPass *pass, int width)
47 {
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224858 times.
224858 if (!pass)
49 return width;
50
51 224858 size_t aligned_w = width;
52 224858 aligned_w = FFALIGN(aligned_w, pass->output->width_align);
53 224858 aligned_w += pass->output->width_pad;
54
1/2
✓ Branch 0 taken 224858 times.
✗ Branch 1 not taken.
224858 return aligned_w <= INT_MAX ? aligned_w : width;
55 }
56
57 /* Allocates (or refs) one buffer per plane */
58 33156 static int frame_alloc_planes_ref(AVFrame *dst, const AVFrame *src,
59 const int plane_copy[4])
60 {
61 33156 int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX,
62 33156 dst->format, 0, NULL);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
33156 if (ret < 0)
64 return ret;
65
66 33156 const int align = av_cpu_max_align();
67 33156 const int aligned_w = FFALIGN(dst->width + 1, align); /* add space for over-write */
68 33156 ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w);
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
33156 if (ret < 0)
70 return ret;
71
72 ptrdiff_t linesize1[4];
73
2/2
✓ Branch 0 taken 132624 times.
✓ Branch 1 taken 33156 times.
165780 for (int i = 0; i < 4; i++)
74 132624 linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align);
75
76 size_t sizes[4];
77 33156 ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1);
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
33156 if (ret < 0)
79 return ret;
80
81
2/2
✓ Branch 0 taken 120296 times.
✓ Branch 1 taken 3207 times.
123503 for (int i = 0; i < 4; i++) {
82
2/2
✓ Branch 0 taken 29949 times.
✓ Branch 1 taken 90347 times.
120296 if (!sizes[i])
83 29949 break;
84 90347 int src_idx = plane_copy[i];
85
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 90347 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
90347 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 90347 AVBufferRef *buf = av_buffer_alloc(sizes[i]);
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90347 times.
90347 if (!buf)
97 return AVERROR(ENOMEM);
98 90347 dst->data[i] = buf->data;
99 90347 dst->buf[i] = buf;
100 }
101
102 33156 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 126848 static int pass_alloc_output(SwsPass *pass)
131 {
132
3/4
✓ Branch 0 taken 33156 times.
✓ Branch 1 taken 93692 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33156 times.
126848 if (!pass || pass->output->avframe)
133 93692 return 0;
134
135 33156 SwsPassBuffer *buffer = pass->output;
136 33156 AVFrame *avframe = av_frame_alloc();
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
33156 if (!avframe)
138 return AVERROR(ENOMEM);
139 33156 avframe->width = buffer->width;
140 33156 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 33156 const AVFrame *src = NULL;
159
2/2
✓ Branch 0 taken 9638 times.
✓ Branch 1 taken 23518 times.
33156 if (pass->input)
160 9638 src = pass->input->output->avframe;
161
162 33156 avframe->format = pass->format;
163 33156 ret = frame_alloc_planes_ref(avframe, src, buffer->plane_copy);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
33156 if (ret < 0) {
165 av_frame_free(&avframe);
166 return ret;
167 }
168
169 #if CONFIG_VULKAN
170 done:
171 #endif
172 33156 buffer->avframe = avframe;
173 33156 ff_sws_frame_from_avframe(&buffer->frame, avframe);
174 33156 return 0;
175 }
176
177 126856 static void free_buffer(AVRefStructOpaque opaque, void *obj)
178 {
179 126856 SwsPassBuffer *buffer = obj;
180 126856 av_frame_free(&buffer->avframe);
181 126856 }
182
183 126856 static void pass_free(SwsPass *pass)
184 {
185
2/2
✓ Branch 0 taken 125400 times.
✓ Branch 1 taken 1456 times.
126856 if (pass->free)
186 125400 pass->free(pass->priv);
187 126856 av_refstruct_unref(&pass->output);
188 126856 av_free(pass);
189 126856 }
190
191 126856 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 126856 SwsPass *pass = av_mallocz(sizeof(*pass));
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
126856 if (!pass) {
201 if (free_cb)
202 free_cb(priv);
203 return AVERROR(ENOMEM);
204 }
205
206
1/2
✓ Branch 0 taken 126856 times.
✗ Branch 1 not taken.
126856 if (!lines)
207 126856 lines = height;
208
209 126856 pass->graph = graph;
210 126856 pass->run = run;
211 126856 pass->setup = setup;
212 126856 pass->priv = priv;
213 126856 pass->free = free_cb;
214 126856 pass->format = fmt;
215 126856 pass->lines = lines;
216 126856 pass->input = input;
217 126856 pass->output = av_refstruct_alloc_ext(sizeof(*pass->output), 0, NULL, free_buffer);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
126856 if (!pass->output) {
219 ret = AVERROR(ENOMEM);
220 goto fail;
221 }
222
223 126856 pass->output->height = height;
224 126856 pass->output->width = width;
225 126856 pass->output->width_align = 1;
226 126856 memset(pass->output->plane_copy, -1, sizeof(pass->output->plane_copy));
227
228
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 126837 times.
126856 if (!align) {
229 19 pass->slice_h = pass->lines;
230 19 pass->num_slices = 1;
231 } else {
232 126837 pass->slice_h = (pass->lines + graph->num_threads - 1) / graph->num_threads;
233 126837 pass->slice_h = FFALIGN(pass->slice_h, align);
234 126837 pass->num_slices = (pass->lines + pass->slice_h - 1) / pass->slice_h;
235 }
236
237 126856 ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
126856 if (ret < 0)
239 goto fail;
240
241 126856 *out_pass = pass;
242 126856 return 0;
243
244 fail:
245 pass_free(pass);
246 return ret;
247 }
248
249 85890 void ff_sws_pass_link_output(SwsPass *dst, const SwsPass *src)
250 {
251
4/6
✓ Branch 0 taken 85890 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8948 times.
✓ Branch 3 taken 76942 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8948 times.
85890 if (!dst || !src || dst == src)
252 76942 return;
253
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8948 times.
8948 av_assert0(dst->format == src->format);
255 8948 SwsPassBuffer *keep = src->output, *drop = dst->output;
256
257 av_assert1(keep->width == drop->width);
258 av_assert1(keep->height == drop->height);
259 8948 keep->width_align = FFMAX(keep->width_align, drop->width_align);
260 8948 keep->width_pad = FFMAX(keep->width_pad, drop->width_pad);
261
262
2/2
✓ Branch 0 taken 35792 times.
✓ Branch 1 taken 8948 times.
44740 for (int i = 0; i < FF_ARRAY_ELEMS(keep->plane_copy); i++) {
263
2/2
✓ Branch 0 taken 35784 times.
✓ Branch 1 taken 8 times.
35792 if (keep->plane_copy[i] < 0)
264 35784 keep->plane_copy[i] = drop->plane_copy[i];
265 8 else if (drop->plane_copy[i] >= 0)
266 av_assert1(keep->plane_copy[i] == drop->plane_copy[i]);
267 }
268
269 8948 av_refstruct_replace(&dst->output, src->output);
270 }
271
272 680762 static void frame_shift(const SwsFrame *f, const int y, uint8_t *data[4])
273 {
274
2/2
✓ Branch 0 taken 2723048 times.
✓ Branch 1 taken 680762 times.
3403810 for (int i = 0; i < 4; i++) {
275
2/2
✓ Branch 0 taken 1582347 times.
✓ Branch 1 taken 1140701 times.
2723048 if (f->data[i])
276 1582347 data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i];
277 else
278 1140701 data[i] = NULL;
279 }
280 680762 }
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 1213 static void run_rgb0(const SwsFrame *out, const SwsFrame *in, int y, int h,
309 const SwsPass *pass)
310 {
311 1213 SwsInternal *c = pass->priv;
312 1213 const int x0 = c->src0Alpha - 1;
313 1213 const int w4 = 4 * out->width;
314 1213 const int src_stride = in->linesize[0];
315 1213 const int dst_stride = out->linesize[0];
316 1213 const uint8_t *src = in->data[0] + y * src_stride;
317 1213 uint8_t *dst = out->data[0] + y * dst_stride;
318
319
2/2
✓ Branch 0 taken 116598 times.
✓ Branch 1 taken 1213 times.
117811 for (int y = 0; y < h; y++) {
320 116598 memcpy(dst, src, w4 * sizeof(*dst));
321
2/2
✓ Branch 0 taken 13606866 times.
✓ Branch 1 taken 116598 times.
13723464 for (int x = x0; x < w4; x += 4)
322 13606866 dst[x] = 0xFF;
323
324 116598 src += src_stride;
325 116598 dst += dst_stride;
326 }
327 1213 }
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 39510 static void free_legacy_swscale(void *priv)
354 {
355 39510 SwsContext *sws = priv;
356 39510 sws_free_context(&sws);
357 39510 }
358
359 140081 static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
360 const SwsPass *pass)
361 {
362 140081 SwsContext *sws = pass->priv;
363 140081 SwsInternal *c = sws_internal(sws);
364
5/6
✓ Branch 0 taken 132786 times.
✓ Branch 1 taken 7295 times.
✓ Branch 2 taken 883 times.
✓ Branch 3 taken 131903 times.
✓ Branch 4 taken 883 times.
✗ Branch 5 not taken.
140081 if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
365
2/2
✓ Branch 0 taken 3532 times.
✓ Branch 1 taken 883 times.
4415 for (int i = 0; i < 4; i++)
366 3532 memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
367 }
368
369
2/2
✓ Branch 1 taken 6214 times.
✓ Branch 2 taken 133867 times.
140081 if (usePal(sws->src_format))
370 6214 ff_update_palette(c, (const uint32_t *) in->data[1]);
371
372 140081 return 0;
373 }
374
375 680762 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
376 {
377 680762 SwsContext *sws = pass->priv;
378 680762 SwsInternal *parent = sws_internal(sws);
379
2/2
✓ Branch 0 taken 72356 times.
✓ Branch 1 taken 608406 times.
680762 if (pass->num_slices == 1)
380 72356 return sws;
381
382 av_assert1(parent->nb_slice_ctx == pass->num_slices);
383 608406 sws = parent->slice_ctx[y / pass->slice_h];
384
385
2/2
✓ Branch 1 taken 35126 times.
✓ Branch 2 taken 573280 times.
608406 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 608406 return sws;
392 }
393
394 97267 static void run_legacy_unscaled(const SwsFrame *out, const SwsFrame *in,
395 int y, int h, const SwsPass *pass)
396 {
397 97267 SwsContext *sws = slice_ctx(pass, y);
398 97267 SwsInternal *c = sws_internal(sws);
399 uint8_t *in_data[4];
400 97267 frame_shift(in, y, in_data);
401
402 97267 c->convert_unscaled(c, (const uint8_t *const *) in_data, in->linesize, y, h,
403 97267 out->data, out->linesize);
404 97267 }
405
406 583495 static void run_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
407 int y, int h, const SwsPass *pass)
408 {
409 583495 SwsContext *sws = slice_ctx(pass, y);
410 583495 SwsInternal *c = sws_internal(sws);
411 uint8_t *out_data[4];
412 583495 frame_shift(out, y, out_data);
413
414 583495 ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
415 583495 sws->src_h, out_data, out->linesize, y, h);
416 583495 }
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 158036 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 158036 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
158036 if (override == -513 || override == *chr_pos)
433 158036 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 39511 static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws,
447 SwsPass *input, SwsPass **output)
448 {
449 39511 SwsInternal *c = sws_internal(sws);
450 39511 const int src_w = sws->src_w, src_h = sws->src_h;
451 39511 const int dst_w = sws->dst_w, dst_h = sws->dst_h;
452
4/4
✓ Branch 0 taken 25462 times.
✓ Branch 1 taken 14049 times.
✓ Branch 2 taken 21078 times.
✓ Branch 3 taken 4384 times.
39511 const int unscaled = src_w == dst_w && src_h == dst_h;
453 39511 int align = c->dst_slice_align;
454 39511 SwsPass *pass = NULL;
455 int ret;
456
457
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39510 times.
39511 if (c->cascaded_context[0]) {
458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const int num_cascaded = c->cascaded_context[2] ? 3 : 2;
459
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int i = 0; i < num_cascaded; i++) {
460 2 const int is_last = i + 1 == num_cascaded;
461
462 /* Steal cascaded context, so we can manage its lifetime independently */
463 2 SwsContext *sub = c->cascaded_context[i];
464 2 c->cascaded_context[i] = NULL;
465
466
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input);
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
468 break;
469 }
470
471 1 sws_free_context(&sws);
472 1 return ret;
473 }
474
475
3/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 39491 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
39510 if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
476 19 align = 0; /* disable slice threading */
477
478
6/6
✓ Branch 0 taken 1136 times.
✓ Branch 1 taken 38374 times.
✓ Branch 2 taken 1132 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 1117 times.
✓ Branch 6 taken 15 times.
39510 if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
479 1117 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 1117 times.
1117 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 39496 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
39510 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 39510 ret = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, 0, align,
497
2/2
✓ Branch 0 taken 3767 times.
✓ Branch 1 taken 35743 times.
39510 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 39510 times.
39510 if (ret < 0)
500 return ret;
501 39510 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 2588 times.
✓ Branch 1 taken 36922 times.
39510 if (pass->num_slices > 1) {
511 2588 c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
512
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2588 times.
2588 if (!c->slice_ctx)
513 return AVERROR(ENOMEM);
514
515
2/2
✓ Branch 0 taken 23191 times.
✓ Branch 1 taken 2588 times.
25779 for (int i = 0; i < pass->num_slices; i++) {
516 SwsContext *slice;
517 SwsInternal *c2;
518 23191 slice = c->slice_ctx[i] = sws_alloc_context();
519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
23191 if (!slice)
520 return AVERROR(ENOMEM);
521 23191 c->nb_slice_ctx++;
522
523 23191 c2 = sws_internal(slice);
524 23191 c2->parent = sws;
525
526 23191 ret = av_opt_copy(slice, sws);
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
23191 if (ret < 0)
528 return ret;
529
530 23191 ret = ff_sws_init_single_context(slice, NULL, NULL);
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
23191 if (ret < 0)
532 return ret;
533
534 23191 sws_setColorspaceDetails(slice, c->srcColorspaceTable,
535 23191 slice->src_range, c->dstColorspaceTable,
536 slice->dst_range, c->brightness, c->contrast,
537 c->saturation);
538
539
2/2
✓ Branch 0 taken 92764 times.
✓ Branch 1 taken 23191 times.
115955 for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
540 92764 c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
541 92764 c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
542 }
543 }
544 }
545
546
5/6
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 39472 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
39510 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 39510 *output = pass;
554 39510 return 0;
555 }
556
557 static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src,
558 SwsPass *input, SwsPass **output);
559
560 73994 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 73994 int ret, warned = 0;
565 73994 SwsContext *const ctx = graph->ctx;
566 73994 const SwsBackend backend = ff_sws_enabled_backends(ctx);
567
2/2
✓ Branch 0 taken 32710 times.
✓ Branch 1 taken 41284 times.
73994 if (!(backend & SWS_BACKEND_LEGACY))
568 32710 return AVERROR(ENOTSUP);
569
2/4
✓ Branch 0 taken 41284 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 41284 times.
41284 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 39517 times.
✓ Branch 2 taken 1767 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 39509 times.
41284 if (!sws_isSupportedInput(src->format) || !sws_isSupportedOutput(dst->format))
575 1775 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 39509 times.
39509 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 39509 SwsContext *sws = sws_alloc_context();
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39509 times.
39509 if (!sws)
591 return AVERROR(ENOMEM);
592
593 39509 sws->flags = ctx->flags;
594 39509 sws->dither = ctx->dither;
595 39509 sws->alpha_blend = ctx->alpha_blend;
596 39509 sws->gamma_flag = ctx->gamma_flag;
597 39509 sws->scaler = ctx->scaler;
598 39509 sws->scaler_sub = ctx->scaler_sub;
599
600 39509 sws->src_w = src->width;
601 39509 sws->src_h = src->height;
602 39509 sws->src_format = src->format;
603 39509 sws->src_range = src->range == AVCOL_RANGE_JPEG;
604
605 39509 sws->dst_w = dst->width;
606 39509 sws->dst_h = dst->height;
607 39509 sws->dst_format = dst->format;
608 39509 sws->dst_range = dst->range == AVCOL_RANGE_JPEG;
609 39509 ff_sws_chroma_pos(src, &graph->incomplete, &sws->src_h_chr_pos, &sws->src_v_chr_pos);
610 39509 ff_sws_chroma_pos(dst, &graph->incomplete, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos);
611
612 39509 graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
613 39509 graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
614
615 /* Allow overriding chroma position with the legacy API */
616 39509 legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
617 39509 legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
618 39509 legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
619 39509 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 35462 times.
✓ Branch 1 taken 4047 times.
39509 if (!src->desc->log2_chroma_w)
624 35462 sws->src_h_chr_pos = -513;
625
2/2
✓ Branch 0 taken 35841 times.
✓ Branch 1 taken 3668 times.
39509 if (!src->desc->log2_chroma_h)
626 35841 sws->src_v_chr_pos = -513;
627
2/2
✓ Branch 0 taken 37452 times.
✓ Branch 1 taken 2057 times.
39509 if (!dst->desc->log2_chroma_w)
628 37452 sws->dst_h_chr_pos = -513;
629
2/2
✓ Branch 0 taken 38254 times.
✓ Branch 1 taken 1255 times.
39509 if (!dst->desc->log2_chroma_h)
630 38254 sws->dst_v_chr_pos = -513;
631
632
2/2
✓ Branch 0 taken 79018 times.
✓ Branch 1 taken 39509 times.
118527 for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
633 79018 sws->scaler_params[i] = ctx->scaler_params[i];
634
635 39509 ret = sws_init_context(sws, NULL, NULL);
636
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39509 times.
39509 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 39509 sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
646 (int **)&table, &out_full,
647 &brightness, &contrast, &saturation);
648
649 39509 inv_table = sws_getCoefficients(src->csp);
650 39509 table = sws_getCoefficients(dst->csp);
651
652
2/2
✓ Branch 0 taken 19659 times.
✓ Branch 1 taken 19850 times.
59168 graph->incomplete |= src->csp != dst->csp &&
653
2/2
✓ Branch 0 taken 17973 times.
✓ Branch 1 taken 1686 times.
19659 (src->csp == AVCOL_SPC_UNSPECIFIED ||
654
2/2
✓ Branch 0 taken 17962 times.
✓ Branch 1 taken 11 times.
17973 dst->csp == AVCOL_SPC_UNSPECIFIED);
655
656 39509 sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
657 brightness, contrast, saturation);
658 }
659
660 39509 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 45113 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 45113 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 45113 const SwsBackend backends = ff_sws_enabled_backends(ctx);
706
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 44973 times.
45113 if (backends == SWS_BACKEND_LEGACY)
707 140 return AVERROR(ENOTSUP);
708
709 SwsOpList *ops;
710 44973 int ret = ff_sws_op_list_generate(ctx, src, dst, lut3d, &ops, &graph->incomplete);
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44973 times.
44973 if (ret < 0)
712 return ret;
713
714 44973 av_log(ctx, AV_LOG_VERBOSE, "Conversion pass for %s -> %s:\n",
715 44973 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
716
717 44973 av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
718 44973 ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
719
720 44973 const int flags = SWS_OP_FLAG_OPTIMIZE | SWS_OP_FLAG_SPLIT_MEMCPY;
721 44973 return ff_sws_compile_pass(graph, NULL, &ops, flags, input, output);
722 #else
723 return AVERROR(ENOTSUP);
724 #endif
725 }
726
727 84482 static bool prefer_ops_backend(SwsContext *ctx, const SwsFormat *src, const SwsFormat *dst)
728 {
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84482 times.
84482 if (ctx->flags & SWS_UNSTABLE)
730 return true;
731
4/4
✓ Branch 1 taken 77285 times.
✓ Branch 2 taken 7197 times.
✓ Branch 4 taken 3431 times.
✓ Branch 5 taken 73854 times.
84482 if (isFloat(src->format) || isFloat(dst->format))
732 10628 return true; /* ops backend has better support for float formats */
733 73854 return false; /* default to legacy for stability reasons */
734 }
735
736 84482 static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
737 const SwsFormat *dst, const SwsLut3D *lut3d,
738 SwsPass *input, SwsPass **output)
739 {
740 84482 SwsContext *ctx = graph->ctx;
741 int ret;
742
743
2/2
✓ Branch 1 taken 10628 times.
✓ Branch 2 taken 73854 times.
84482 if (prefer_ops_backend(ctx, src, dst)) {
744 10628 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
745
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 10488 times.
10628 if (ret == AVERROR(ENOTSUP))
746 140 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
747 } else {
748 73854 ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output);
749
2/2
✓ Branch 0 taken 34485 times.
✓ Branch 1 taken 39369 times.
73854 if (ret == AVERROR(ENOTSUP))
750 34485 ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output);
751 }
752
753 84482 return ret;
754 }
755
756 /**************************
757 * Gamut and tone mapping *
758 **************************/
759
760 84748 static int generate_3dlut(SwsGraph *graph, SwsFormat *src, SwsFormat *dst)
761 {
762 84748 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 5820 times.
✓ Branch 2 taken 78928 times.
84748 if (isGray(dst->format)) {
771 5820 dst->color = src->color;
772
2/2
✓ Branch 1 taken 10070 times.
✓ Branch 2 taken 68858 times.
78928 } else if (isGray(src->format)) {
773 10070 src->color = dst->color;
774 }
775
776 /* Fully infer color spaces before color mapping logic */
777 84748 graph->incomplete |= ff_infer_colors(&src->color, &dst->color);
778
779 84748 map.intent = graph->ctx->intent;
780 84748 map.src = src->color;
781 84748 map.dst = dst->color;
782
783
1/2
✓ Branch 1 taken 84748 times.
✗ Branch 2 not taken.
84748 if (ff_sws_color_map_noop(&map))
784 84748 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 84748 static int init_passes(SwsGraph *graph)
801 {
802 84748 SwsFormat src = graph->src;
803 84748 SwsFormat dst = graph->dst;
804 84748 SwsPass *pass = NULL; /* read from main input image */
805 int ret;
806
807 84748 ret = generate_3dlut(graph, &src, &dst);
808
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
84748 if (ret < 0)
809 return ret;
810
811
3/4
✓ Branch 1 taken 266 times.
✓ Branch 2 taken 84482 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
84748 if (!ff_fmt_equal(&src, &dst) || graph->lut3d) {
812 84482 ret = add_convert_pass(graph, &src, &dst, graph->lut3d, pass, &pass);
813
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 84478 times.
84482 if (ret < 0)
814 4 return ret;
815 }
816
817
2/2
✓ Branch 0 taken 287 times.
✓ Branch 1 taken 84457 times.
84744 if (!pass) {
818 /* No passes were added, so no operations were necessary */
819 287 graph->noop = 1;
820
821 287 const int nb_planes = av_pix_fmt_count_planes(dst.format);
822
2/2
✓ Branch 0 taken 855 times.
✓ Branch 1 taken 287 times.
1142 for (int i = 0; i < nb_planes; i++)
823 855 graph->plane_copy[i] = i;
824
825 /* Add threaded memcpy pass */
826 287 return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
827 pass, 0, 1, run_copy, NULL, NULL, NULL, &pass);
828 }
829
830 /* Compute end-to-end plane copy map */
831
2/2
✓ Branch 0 taken 126561 times.
✓ Branch 1 taken 84457 times.
211018 for (int n = 0; n < graph->num_passes; n++) {
832 126561 const SwsPass *pass = graph->passes[n];
833 /* This pass writes to an output buffer other than the image
834 * output, or copies from the output of a different pass */
835
3/4
✓ Branch 0 taken 126561 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33156 times.
✓ Branch 3 taken 93405 times.
126561 if (pass->output->avframe || pass->input)
836 33156 continue;
837
2/2
✓ Branch 0 taken 373620 times.
✓ Branch 1 taken 93405 times.
467025 for (int i = 0; i < FF_ARRAY_ELEMS(graph->plane_copy); i++) {
838 373620 const int idx = pass->output->plane_copy[i];
839
2/2
✓ Branch 0 taken 373477 times.
✓ Branch 1 taken 143 times.
373620 if (idx < 0)
840 373477 continue;
841
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 8 times.
143 if (graph->plane_copy[i] < 0) {
842 135 graph->plane_copy[i] = idx;
843 135 av_log(graph->ctx, AV_LOG_DEBUG, "Plane %d passthrough from "
844 "plane %d\n", i, idx);
845 } else {
846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 av_assert0(graph->plane_copy[i] == idx);
847 }
848 }
849 }
850
851 84457 return 0;
852 }
853
854 613608 static int sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
855 int nb_threads)
856 {
857 613608 SwsGraph *graph = priv;
858 613608 const SwsPass *pass = graph->exec.pass;
859 613608 const int slice_y = jobnr * pass->slice_h;
860 613608 const int slice_h = FFMIN(pass->slice_h, pass->lines - slice_y);
861
862 613608 pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass);
863 613608 return 0;
864 }
865
866 6412 SwsGraph *ff_sws_graph_alloc(void)
867 {
868 6412 return av_mallocz(sizeof(SwsGraph));
869 }
870
871 91164 static void graph_uninit(SwsGraph *graph)
872 {
873 91164 avpriv_slicethread_free(&graph->slicethread);
874
875
2/2
✓ Branch 0 taken 126848 times.
✓ Branch 1 taken 91164 times.
218012 for (int i = 0; i < graph->num_passes; i++)
876 126848 pass_free(graph->passes[i]);
877 91164 av_free(graph->passes);
878
879 91164 av_refstruct_unref(&graph->lut3d);
880
881 91164 memset(graph, 0, sizeof(*graph));
882 91164 }
883
884 84748 int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
885 const SwsFormat *src)
886 {
887 int ret;
888
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
84748 if (graph->ctx) {
889 av_log(ctx, AV_LOG_ERROR, "Graph is already initialized\n");
890 return AVERROR(EINVAL);
891 }
892
893 84748 graph->ctx = ctx;
894 84748 graph->src = *src;
895 84748 graph->dst = *dst;
896 84748 graph->opts_copy = *ctx;
897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
84748 av_assert0(src->interlaced == dst->interlaced);
898
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
84748 av_assert0(src->field == dst->field);
899 84748 memset(graph->plane_copy, -1, sizeof(graph->plane_copy));
900
901
2/2
✓ Branch 0 taken 82155 times.
✓ Branch 1 taken 2593 times.
84748 if (ctx->threads == 1) {
902 82155 graph->num_threads = 1;
903 } else {
904 2593 ret = avpriv_slicethread_create2(&graph->slicethread, (void *) graph,
905 sws_graph_worker, NULL, ctx->threads);
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2593 times.
2593 if (ret == AVERROR(ENOSYS)) {
907 /* Fall back to single threaded operation */
908 graph->num_threads = 1;
909
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2593 times.
2593 } else if (ret < 0) {
910 goto error;
911 } else {
912 2593 graph->num_threads = ret;
913 }
914 }
915
916 84748 ret = init_passes(graph);
917
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 84744 times.
84748 if (ret < 0)
918 4 goto error;
919
920 /* Resolve output buffers for all intermediate passes */
921
2/2
✓ Branch 0 taken 126848 times.
✓ Branch 1 taken 84744 times.
211592 for (int i = 0; i < graph->num_passes; i++) {
922 126848 graph->backend |= graph->passes[i]->backend;
923 126848 ret = pass_alloc_output(graph->passes[i]->input);
924
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126848 times.
126848 if (ret < 0)
925 goto error;
926 }
927
928 84744 return 0;
929
930 4 error:
931 4 graph_uninit(graph);
932 4 return ret;
933 }
934
935 4 void ff_sws_graph_rollback(SwsGraph *graph, int since_idx)
936 {
937
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 for (int i = since_idx; i < graph->num_passes; i++)
938 8 pass_free(graph->passes[i]);
939 4 graph->num_passes = since_idx;
940 4 }
941
942 403553 void ff_sws_graph_free(SwsGraph **pgraph)
943 {
944 403553 SwsGraph *graph = *pgraph;
945
2/2
✓ Branch 0 taken 397141 times.
✓ Branch 1 taken 6412 times.
403553 if (!graph)
946 397141 return;
947
948 6412 graph_uninit(graph);
949 6412 av_free(graph);
950 6412 *pgraph = NULL;
951 }
952
953 /* Tests only options relevant to SwsGraph */
954 142055 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
955 {
956 282609 return c1->flags == c2->flags &&
957
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->threads == c2->threads &&
958
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->dither == c2->dither &&
959
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->alpha_blend == c2->alpha_blend &&
960
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->gamma_flag == c2->gamma_flag &&
961
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->src_h_chr_pos == c2->src_h_chr_pos &&
962
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->src_v_chr_pos == c2->src_v_chr_pos &&
963
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
964
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
965
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->intent == c2->intent &&
966
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->scaler == c2->scaler &&
967
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 c1->scaler_sub == c2->scaler_sub &&
968
3/4
✓ Branch 0 taken 140554 times.
✓ Branch 1 taken 1501 times.
✓ Branch 2 taken 140554 times.
✗ Branch 3 not taken.
423163 c1->backends == c2->backends &&
969
1/2
✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
140554 !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
970
971 }
972
973 225302 int ff_sws_graph_reinit(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
974 const SwsFormat *src)
975 {
976
6/6
✓ Branch 1 taken 180343 times.
✓ Branch 2 taken 44959 times.
✓ Branch 4 taken 142055 times.
✓ Branch 5 taken 38288 times.
✓ Branch 6 taken 140554 times.
✓ Branch 7 taken 1501 times.
367357 if (ff_fmt_equal(&graph->src, src) && ff_fmt_equal(&graph->dst, dst) &&
977 142055 opts_equal(ctx, &graph->opts_copy))
978 {
979 140554 ff_sws_graph_update_metadata(graph, &src->color);
980 140554 return 0;
981 }
982
983 84748 graph_uninit(graph);
984 84748 return ff_sws_graph_init(graph, ctx, dst, src);
985 }
986
987 140554 void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
988 {
989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 140554 times.
140554 if (!color)
990 return;
991
992 140554 ff_color_update_dynamic(&graph->src.color, color);
993
994
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 140554 times.
140554 if (graph->lut3d)
995 ff_sws_lut3d_update(graph->lut3d, &graph->src.color);
996 }
997
998 370414 static void get_field(SwsGraph *graph, const SwsFormat *fmt,
999 const AVFrame *avframe, SwsFrame *frame)
1000 {
1001 370414 ff_sws_frame_from_avframe(frame, avframe);
1002
1003
1/2
✓ Branch 0 taken 370414 times.
✗ Branch 1 not taken.
370414 if (!(avframe->flags & AV_FRAME_FLAG_INTERLACED)) {
1004 av_assert1(!fmt->field);
1005 370414 return;
1006 }
1007
1008 if (fmt->field == FIELD_BOTTOM) {
1009 /* Odd rows, offset by one line */
1010 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
1011 for (int i = 0; i < 4; i++) {
1012 if (frame->data[i])
1013 frame->data[i] += frame->linesize[i];
1014 if (desc->flags & AV_PIX_FMT_FLAG_PAL)
1015 break;
1016 }
1017 }
1018
1019 /* Take only every second line */
1020 for (int i = 0; i < 4; i++)
1021 frame->linesize[i] <<= 1;
1022
1023 frame->height = (frame->height + (fmt->field == FIELD_TOP)) >> 1;
1024 }
1025
1026 185207 int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
1027 {
1028
2/4
✓ Branch 0 taken 185207 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 185207 times.
185207 av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
1029
2/4
✓ Branch 0 taken 185207 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 185207 times.
185207 av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
1030
1031 SwsFrame src_field, dst_field;
1032 185207 get_field(graph, &graph->dst, dst, &dst_field);
1033 185207 get_field(graph, &graph->src, src, &src_field);
1034
1035
2/2
✓ Branch 0 taken 228363 times.
✓ Branch 1 taken 185207 times.
413570 for (int i = 0; i < graph->num_passes; i++) {
1036 228363 const SwsPass *pass = graph->passes[i];
1037 228363 graph->exec.pass = pass;
1038
2/2
✓ Branch 0 taken 34060 times.
✓ Branch 1 taken 194303 times.
228363 graph->exec.input = pass->input ? &pass->input->output->frame : &src_field;
1039
2/2
✓ Branch 0 taken 34060 times.
✓ Branch 1 taken 194303 times.
228363 graph->exec.output = pass->output->avframe ? &pass->output->frame : &dst_field;
1040
2/2
✓ Branch 0 taken 226551 times.
✓ Branch 1 taken 1812 times.
228363 if (pass->setup) {
1041 226551 int ret = pass->setup(graph->exec.output, graph->exec.input, pass);
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 226551 times.
226551 if (ret < 0)
1043 return ret;
1044 }
1045
1046
2/2
✓ Branch 0 taken 160060 times.
✓ Branch 1 taken 68303 times.
228363 if (pass->num_slices == 1) {
1047 160060 pass->run(graph->exec.output, graph->exec.input, 0, pass->lines, pass);
1048 } else {
1049 68303 avpriv_slicethread_execute2(graph->slicethread, pass->num_slices, 0);
1050 }
1051 }
1052
1053 185207 return 0;
1054 }
1055