FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/graph.c
Date: 2026-04-21 03:24:50
Exec Total Coverage
Lines: 410 568 72.2%
Functions: 31 37 83.8%
Branches: 199 342 58.2%

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/imgutils.h"
25 #include "libavutil/macros.h"
26 #include "libavutil/mem.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/pixdesc.h"
29 #include "libavutil/refstruct.h"
30 #include "libavutil/slicethread.h"
31
32 #include "libswscale/swscale.h"
33 #include "libswscale/format.h"
34
35 #include "cms.h"
36 #include "lut3d.h"
37 #include "swscale_internal.h"
38 #include "graph.h"
39 #include "ops.h"
40
41 155951 int ff_sws_pass_aligned_width(const SwsPass *pass, int width)
42 {
43
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155951 times.
155951 if (!pass)
44 return width;
45
46 155951 size_t aligned_w = width;
47 155951 aligned_w = FFALIGN(aligned_w, pass->output->width_align);
48 155951 aligned_w += pass->output->width_pad;
49
1/2
✓ Branch 0 taken 155951 times.
✗ Branch 1 not taken.
155951 return aligned_w <= INT_MAX ? aligned_w : width;
50 }
51
52 /* Allocates one buffer per plane */
53 1353 static int frame_alloc_planes(AVFrame *dst)
54 {
55 1353 int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX,
56 1353 dst->format, 0, NULL);
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (ret < 0)
58 return ret;
59
60 1353 const int align = av_cpu_max_align();
61 1353 const int aligned_w = FFALIGN(dst->width, align);
62 1353 ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (ret < 0)
64 return ret;
65
66 ptrdiff_t linesize1[4];
67
2/2
✓ Branch 0 taken 5412 times.
✓ Branch 1 taken 1353 times.
6765 for (int i = 0; i < 4; i++)
68 5412 linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align);
69
70 size_t sizes[4];
71 1353 ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1);
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (ret < 0)
73 return ret;
74
75
1/2
✓ Branch 0 taken 2706 times.
✗ Branch 1 not taken.
2706 for (int i = 0; i < 4; i++) {
76
2/2
✓ Branch 0 taken 1353 times.
✓ Branch 1 taken 1353 times.
2706 if (!sizes[i])
77 1353 break;
78 1353 AVBufferRef *buf = av_buffer_alloc(sizes[i]);
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (!buf)
80 return AVERROR(ENOMEM);
81 1353 dst->data[i] = buf->data;
82 1353 dst->buf[i] = buf;
83 }
84
85 1353 return 0;
86 }
87
88 41660 static int pass_alloc_output(SwsPass *pass)
89 {
90
3/4
✓ Branch 0 taken 1353 times.
✓ Branch 1 taken 40307 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1353 times.
41660 if (!pass || pass->output->avframe)
91 40307 return 0;
92
93 1353 SwsPassBuffer *buffer = pass->output;
94 1353 AVFrame *avframe = av_frame_alloc();
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (!avframe)
96 return AVERROR(ENOMEM);
97 1353 avframe->format = pass->format;
98 1353 avframe->width = buffer->width;
99 1353 avframe->height = buffer->height;
100
101 1353 int ret = frame_alloc_planes(avframe);
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1353 times.
1353 if (ret < 0) {
103 av_frame_free(&avframe);
104 return ret;
105 }
106
107 1353 buffer->avframe = avframe;
108 1353 ff_sws_frame_from_avframe(&buffer->frame, avframe);
109 1353 return 0;
110 }
111
112 41660 static void free_buffer(AVRefStructOpaque opaque, void *obj)
113 {
114 41660 SwsPassBuffer *buffer = obj;
115 41660 av_frame_free(&buffer->avframe);
116 41660 }
117
118 41660 static void pass_free(SwsPass *pass)
119 {
120
2/2
✓ Branch 0 taken 40047 times.
✓ Branch 1 taken 1613 times.
41660 if (pass->free)
121 40047 pass->free(pass->priv);
122 41660 av_refstruct_unref(&pass->output);
123 41660 av_free(pass);
124 41660 }
125
126 41660 int ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
127 int width, int height, SwsPass *input,
128 int align, SwsPassFunc run, SwsPassSetup setup,
129 void *priv, void (*free_cb)(void *priv),
130 SwsPass **out_pass)
131 {
132 int ret;
133 41660 SwsPass *pass = av_mallocz(sizeof(*pass));
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41660 times.
41660 if (!pass) {
135 if (free_cb)
136 free_cb(priv);
137 return AVERROR(ENOMEM);
138 }
139
140 41660 pass->graph = graph;
141 41660 pass->run = run;
142 41660 pass->setup = setup;
143 41660 pass->priv = priv;
144 41660 pass->free = free_cb;
145 41660 pass->format = fmt;
146 41660 pass->width = width;
147 41660 pass->height = height;
148 41660 pass->input = input;
149 41660 pass->output = av_refstruct_alloc_ext(sizeof(*pass->output), 0, NULL, free_buffer);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41660 times.
41660 if (!pass->output) {
151 ret = AVERROR(ENOMEM);
152 goto fail;
153 }
154
155
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 41605 times.
41660 if (!align) {
156 55 pass->slice_h = pass->height;
157 55 pass->num_slices = 1;
158 } else {
159 41605 pass->slice_h = (pass->height + graph->num_threads - 1) / graph->num_threads;
160 41605 pass->slice_h = FFALIGN(pass->slice_h, align);
161 41605 pass->num_slices = (pass->height + pass->slice_h - 1) / pass->slice_h;
162 }
163
164 /* Align output buffer to include extra slice padding */
165 41660 pass->output->height = pass->slice_h * pass->num_slices;
166 41660 pass->output->width = pass->width;
167 41660 pass->output->width_align = 1;
168
169 41660 ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41660 times.
41660 if (ret < 0)
171 goto fail;
172
173 41660 *out_pass = pass;
174 41660 return 0;
175
176 fail:
177 pass_free(pass);
178 return ret;
179 }
180
181 665160 static void frame_shift(const SwsFrame *f, const int y, uint8_t *data[4])
182 {
183
2/2
✓ Branch 0 taken 2660640 times.
✓ Branch 1 taken 665160 times.
3325800 for (int i = 0; i < 4; i++) {
184
2/2
✓ Branch 0 taken 1517147 times.
✓ Branch 1 taken 1143493 times.
2660640 if (f->data[i])
185 1517147 data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i];
186 else
187 1143493 data[i] = NULL;
188 }
189 665160 }
190
191 static void run_copy(const SwsFrame *out, const SwsFrame *in, int y, int h,
192 const SwsPass *pass)
193 {
194 uint8_t *in_data[4], *out_data[4];
195 frame_shift(in, y, in_data);
196 frame_shift(out, y, out_data);
197
198 for (int i = 0; i < 4 && out_data[i]; i++) {
199 const int lines = h >> ff_fmt_vshift(in->format, i);
200 av_assert1(in_data[i]);
201
202 if (in_data[i] == out_data[i]) {
203 av_assert0(in->linesize[i] == out->linesize[i]);
204 } else if (in->linesize[i] == out->linesize[i]) {
205 memcpy(out_data[i], in_data[i], lines * out->linesize[i]);
206 } else {
207 const int linesize = FFMIN(out->linesize[i], in->linesize[i]);
208 for (int j = 0; j < lines; j++) {
209 memcpy(out_data[i], in_data[i], linesize);
210 in_data[i] += in->linesize[i];
211 out_data[i] += out->linesize[i];
212 }
213 }
214 }
215 }
216
217 574 static void run_rgb0(const SwsFrame *out, const SwsFrame *in, int y, int h,
218 const SwsPass *pass)
219 {
220 574 SwsInternal *c = pass->priv;
221 574 const int x0 = c->src0Alpha - 1;
222 574 const int w4 = 4 * pass->width;
223 574 const int src_stride = in->linesize[0];
224 574 const int dst_stride = out->linesize[0];
225 574 const uint8_t *src = in->data[0] + y * src_stride;
226 574 uint8_t *dst = out->data[0] + y * dst_stride;
227
228
2/2
✓ Branch 0 taken 54582 times.
✓ Branch 1 taken 574 times.
55156 for (int y = 0; y < h; y++) {
229 54582 memcpy(dst, src, w4 * sizeof(*dst));
230
2/2
✓ Branch 0 taken 7661522 times.
✓ Branch 1 taken 54582 times.
7716104 for (int x = x0; x < w4; x += 4)
231 7661522 dst[x] = 0xFF;
232
233 54582 src += src_stride;
234 54582 dst += dst_stride;
235 }
236 574 }
237
238 3095 static void run_xyz2rgb(const SwsFrame *out, const SwsFrame *in, int y, int h,
239 const SwsPass *pass)
240 {
241 3095 const SwsInternal *c = pass->priv;
242 3095 c->xyz12Torgb48(c, out->data[0] + y * out->linesize[0], out->linesize[0],
243 3095 in->data[0] + y * in->linesize[0], in->linesize[0],
244 3095 pass->width, h);
245 3095 }
246
247 2902 static void run_rgb2xyz(const SwsFrame *out, const SwsFrame *in, int y, int h,
248 const SwsPass *pass)
249 {
250 2902 const SwsInternal *c = pass->priv;
251 2902 c->rgb48Toxyz12(c, out->data[0] + y * out->linesize[0], out->linesize[0],
252 2902 in->data[0] + y * in->linesize[0], in->linesize[0],
253 2902 pass->width, h);
254 2902 }
255
256 /***********************************************************************
257 * Internal ff_swscale() wrapper. This reuses the legacy scaling API. *
258 * This is considered fully deprecated, and will be replaced by a full *
259 * reimplementation ASAP. *
260 ***********************************************************************/
261
262 25767 static void free_legacy_swscale(void *priv)
263 {
264 25767 SwsContext *sws = priv;
265 25767 sws_free_context(&sws);
266 25767 }
267
268 124903 static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
269 const SwsPass *pass)
270 {
271 124903 SwsContext *sws = pass->priv;
272 124903 SwsInternal *c = sws_internal(sws);
273
5/6
✓ Branch 0 taken 115139 times.
✓ Branch 1 taken 9764 times.
✓ Branch 2 taken 879 times.
✓ Branch 3 taken 114260 times.
✓ Branch 4 taken 879 times.
✗ Branch 5 not taken.
124903 if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
274
2/2
✓ Branch 0 taken 3516 times.
✓ Branch 1 taken 879 times.
4395 for (int i = 0; i < 4; i++)
275 3516 memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
276 }
277
278
2/2
✓ Branch 1 taken 5421 times.
✓ Branch 2 taken 119482 times.
124903 if (usePal(sws->src_format))
279 5421 ff_update_palette(c, (const uint32_t *) in->data[1]);
280
281 124903 return 0;
282 }
283
284 665160 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
285 {
286 665160 SwsContext *sws = pass->priv;
287 665160 SwsInternal *parent = sws_internal(sws);
288
2/2
✓ Branch 0 taken 57235 times.
✓ Branch 1 taken 607925 times.
665160 if (pass->num_slices == 1)
289 57235 return sws;
290
291 av_assert1(parent->nb_slice_ctx == pass->num_slices);
292 607925 sws = parent->slice_ctx[y / pass->slice_h];
293
294
2/2
✓ Branch 1 taken 35135 times.
✓ Branch 2 taken 572790 times.
607925 if (usePal(sws->src_format)) {
295 35135 SwsInternal *sub = sws_internal(sws);
296 35135 memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv));
297 35135 memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb));
298 }
299
300 607925 return sws;
301 }
302
303 97161 static void run_legacy_unscaled(const SwsFrame *out, const SwsFrame *in,
304 int y, int h, const SwsPass *pass)
305 {
306 97161 SwsContext *sws = slice_ctx(pass, y);
307 97161 SwsInternal *c = sws_internal(sws);
308 uint8_t *in_data[4];
309 97161 frame_shift(in, y, in_data);
310
311 97161 c->convert_unscaled(c, (const uint8_t *const *) in_data, in->linesize, y, h,
312 97161 out->data, out->linesize);
313 97161 }
314
315 567999 static void run_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
316 int y, int h, const SwsPass *pass)
317 {
318 567999 SwsContext *sws = slice_ctx(pass, y);
319 567999 SwsInternal *c = sws_internal(sws);
320 uint8_t *out_data[4];
321 567999 frame_shift(out, y, out_data);
322
323 567999 ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
324 567999 sws->src_h, out_data, out->linesize, y, h);
325 567999 }
326
327 51532 static void get_chroma_pos(SwsGraph *graph, int *h_chr_pos, int *v_chr_pos,
328 const SwsFormat *fmt)
329 {
330 51532 enum AVChromaLocation chroma_loc = fmt->loc;
331 51532 const int sub_x = fmt->desc->log2_chroma_w;
332 51532 const int sub_y = fmt->desc->log2_chroma_h;
333 int x_pos, y_pos;
334
335 /* Explicitly default to center siting for compatibility with swscale */
336
2/2
✓ Branch 0 taken 51531 times.
✓ Branch 1 taken 1 times.
51532 if (chroma_loc == AVCHROMA_LOC_UNSPECIFIED) {
337 51531 chroma_loc = AVCHROMA_LOC_CENTER;
338
4/4
✓ Branch 0 taken 45457 times.
✓ Branch 1 taken 6074 times.
✓ Branch 2 taken 240 times.
✓ Branch 3 taken 45217 times.
51531 graph->incomplete |= sub_x || sub_y;
339 }
340
341 /* av_chroma_location_enum_to_pos() always gives us values in the range from
342 * 0 to 256, but we need to adjust this to the true value range of the
343 * subsampling grid, which may be larger for h/v_sub > 1 */
344 51532 av_chroma_location_enum_to_pos(&x_pos, &y_pos, chroma_loc);
345 51532 x_pos *= (1 << sub_x) - 1;
346 51532 y_pos *= (1 << sub_y) - 1;
347
348 /* Fix vertical chroma position for interlaced frames */
349
3/4
✓ Branch 0 taken 4894 times.
✓ Branch 1 taken 46638 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4894 times.
51532 if (sub_y && fmt->interlaced) {
350 /* When vertically subsampling, chroma samples are effectively only
351 * placed next to even rows. To access them from the odd field, we need
352 * to account for this shift by offsetting the distance of one luma row.
353 *
354 * For 4x vertical subsampling (v_sub == 2), they are only placed
355 * next to every *other* even row, so we need to shift by three luma
356 * rows to get to the chroma sample. */
357 if (graph->field == FIELD_BOTTOM)
358 y_pos += (256 << sub_y) - 256;
359
360 /* Luma row distance is doubled for fields, so halve offsets */
361 y_pos >>= 1;
362 }
363
364 /* Explicitly strip chroma offsets when not subsampling, because it
365 * interferes with the operation of flags like SWS_FULL_CHR_H_INP */
366
2/2
✓ Branch 0 taken 6075 times.
✓ Branch 1 taken 45457 times.
51532 *h_chr_pos = sub_x ? x_pos : -513;
367
2/2
✓ Branch 0 taken 4894 times.
✓ Branch 1 taken 46638 times.
51532 *v_chr_pos = sub_y ? y_pos : -513;
368 51532 }
369
370 103064 static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
371 {
372
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 103064 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
103064 if (override == -513 || override == *chr_pos)
373 103064 return;
374
375 if (!*warned) {
376 av_log(NULL, AV_LOG_WARNING,
377 "Setting chroma position directly is deprecated, make sure "
378 "the frame is tagged with the correct chroma location.\n");
379 *warned = 1;
380 }
381
382 *chr_pos = override;
383 }
384
385 /* Takes over ownership of `sws` */
386 25768 static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws,
387 SwsPass *input, SwsPass **output)
388 {
389 25768 SwsInternal *c = sws_internal(sws);
390 25768 const int src_w = sws->src_w, src_h = sws->src_h;
391 25768 const int dst_w = sws->dst_w, dst_h = sws->dst_h;
392
4/4
✓ Branch 0 taken 25312 times.
✓ Branch 1 taken 456 times.
✓ Branch 2 taken 25302 times.
✓ Branch 3 taken 10 times.
25768 const int unscaled = src_w == dst_w && src_h == dst_h;
393 25768 int align = c->dst_slice_align;
394 25768 SwsPass *pass = NULL;
395 int ret;
396
397
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25767 times.
25768 if (c->cascaded_context[0]) {
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const int num_cascaded = c->cascaded_context[2] ? 3 : 2;
399
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int i = 0; i < num_cascaded; i++) {
400 2 const int is_last = i + 1 == num_cascaded;
401
402 /* Steal cascaded context, so we can manage its lifetime independently */
403 2 SwsContext *sub = c->cascaded_context[i];
404 2 c->cascaded_context[i] = NULL;
405
406
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input);
407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
408 break;
409 }
410
411 1 sws_free_context(&sws);
412 1 return ret;
413 }
414
415
3/4
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 25712 times.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
25767 if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
416 55 align = 0; /* disable slice threading */
417
418
6/6
✓ Branch 0 taken 585 times.
✓ Branch 1 taken 25182 times.
✓ Branch 2 taken 581 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 526 times.
✓ Branch 6 taken 55 times.
25767 if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
419 526 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGBA, src_w, src_h, input,
420 1, run_rgb0, NULL, c, NULL, &input);
421
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 526 times.
526 if (ret < 0) {
422 sws_free_context(&sws);
423 return ret;
424 }
425 }
426
427
6/6
✓ Branch 0 taken 532 times.
✓ Branch 1 taken 25235 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 528 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
25767 if (c->srcXYZ && !(c->dstXYZ && unscaled)) {
428 530 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, src_w, src_h, input,
429 1, run_xyz2rgb, NULL, c, NULL, &input);
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 530 times.
530 if (ret < 0) {
431 sws_free_context(&sws);
432 return ret;
433 }
434 }
435
436 25767 ret = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, align,
437
2/2
✓ Branch 0 taken 3848 times.
✓ Branch 1 taken 21919 times.
25767 c->convert_unscaled ? run_legacy_unscaled : run_legacy_swscale,
438 setup_legacy_swscale, sws, free_legacy_swscale, &pass);
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25767 times.
25767 if (ret < 0)
440 return ret;
441
442 /**
443 * For slice threading, we need to create sub contexts, similar to how
444 * swscale normally handles it internally. The most important difference
445 * is that we handle cascaded contexts before threaded contexts; whereas
446 * context_init_threaded() does it the other way around.
447 */
448
449
2/2
✓ Branch 0 taken 2579 times.
✓ Branch 1 taken 23188 times.
25767 if (pass->num_slices > 1) {
450 2579 c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
451
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2579 times.
2579 if (!c->slice_ctx)
452 return AVERROR(ENOMEM);
453
454
2/2
✓ Branch 0 taken 23118 times.
✓ Branch 1 taken 2579 times.
25697 for (int i = 0; i < pass->num_slices; i++) {
455 SwsContext *slice;
456 SwsInternal *c2;
457 23118 slice = c->slice_ctx[i] = sws_alloc_context();
458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23118 times.
23118 if (!slice)
459 return AVERROR(ENOMEM);
460 23118 c->nb_slice_ctx++;
461
462 23118 c2 = sws_internal(slice);
463 23118 c2->parent = sws;
464
465 23118 ret = av_opt_copy(slice, sws);
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23118 times.
23118 if (ret < 0)
467 return ret;
468
469 23118 ret = ff_sws_init_single_context(slice, NULL, NULL);
470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23118 times.
23118 if (ret < 0)
471 return ret;
472
473 23118 sws_setColorspaceDetails(slice, c->srcColorspaceTable,
474 23118 slice->src_range, c->dstColorspaceTable,
475 slice->dst_range, c->brightness, c->contrast,
476 c->saturation);
477
478
2/2
✓ Branch 0 taken 92472 times.
✓ Branch 1 taken 23118 times.
115590 for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
479 92472 c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
480 92472 c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
481 }
482 }
483 }
484
485
6/6
✓ Branch 0 taken 298 times.
✓ Branch 1 taken 25469 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 294 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
25767 if (c->dstXYZ && !(c->srcXYZ && unscaled)) {
486 296 ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, pass,
487 1, run_rgb2xyz, NULL, c, NULL, &pass);
488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 296 times.
296 if (ret < 0)
489 return ret;
490 }
491
492 25767 *output = pass;
493 25767 return 0;
494 }
495
496 25766 static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src,
497 const SwsFormat *dst, SwsPass *input,
498 SwsPass **output)
499 {
500 25766 int ret, warned = 0;
501 25766 SwsContext *const ctx = graph->ctx;
502
2/4
✓ Branch 0 taken 25766 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25766 times.
25766 if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE)
503 return AVERROR(ENOTSUP);
504
505 25766 SwsContext *sws = sws_alloc_context();
506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25766 times.
25766 if (!sws)
507 return AVERROR(ENOMEM);
508
509 25766 sws->flags = ctx->flags;
510 25766 sws->dither = ctx->dither;
511 25766 sws->alpha_blend = ctx->alpha_blend;
512 25766 sws->gamma_flag = ctx->gamma_flag;
513 25766 sws->scaler = ctx->scaler;
514 25766 sws->scaler_sub = ctx->scaler_sub;
515
516 25766 sws->src_w = src->width;
517 25766 sws->src_h = src->height;
518 25766 sws->src_format = src->format;
519 25766 sws->src_range = src->range == AVCOL_RANGE_JPEG;
520
521 25766 sws->dst_w = dst->width;
522 25766 sws->dst_h = dst->height;
523 25766 sws->dst_format = dst->format;
524 25766 sws->dst_range = dst->range == AVCOL_RANGE_JPEG;
525 25766 get_chroma_pos(graph, &sws->src_h_chr_pos, &sws->src_v_chr_pos, src);
526 25766 get_chroma_pos(graph, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos, dst);
527
528 25766 graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED;
529 25766 graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED;
530
531 /* Allow overriding chroma position with the legacy API */
532 25766 legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
533 25766 legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
534 25766 legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
535 25766 legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned);
536
537
2/2
✓ Branch 0 taken 51532 times.
✓ Branch 1 taken 25766 times.
77298 for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
538 51532 sws->scaler_params[i] = ctx->scaler_params[i];
539
540 25766 ret = sws_init_context(sws, NULL, NULL);
541
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25766 times.
25766 if (ret < 0) {
542 sws_free_context(&sws);
543 return ret;
544 }
545
546 /* Set correct color matrices */
547 {
548 int in_full, out_full, brightness, contrast, saturation;
549 const int *inv_table, *table;
550 25766 sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
551 (int **)&table, &out_full,
552 &brightness, &contrast, &saturation);
553
554 25766 inv_table = sws_getCoefficients(src->csp);
555 25766 table = sws_getCoefficients(dst->csp);
556
557
2/2
✓ Branch 0 taken 11870 times.
✓ Branch 1 taken 13896 times.
37636 graph->incomplete |= src->csp != dst->csp &&
558
2/2
✓ Branch 0 taken 9610 times.
✓ Branch 1 taken 2260 times.
11870 (src->csp == AVCOL_SPC_UNSPECIFIED ||
559
2/2
✓ Branch 0 taken 9600 times.
✓ Branch 1 taken 10 times.
9610 dst->csp == AVCOL_SPC_UNSPECIFIED);
560
561 25766 sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
562 brightness, contrast, saturation);
563 }
564
565 25766 return init_legacy_subpass(graph, sws, input, output);
566 }
567
568 /*********************************
569 * Format conversion and scaling *
570 *********************************/
571
572 #if CONFIG_UNSTABLE
573 static SwsScaler get_scaler_fallback(SwsContext *ctx)
574 {
575 if (ctx->scaler != SWS_SCALE_AUTO)
576 return ctx->scaler;
577
578 /* Backwards compatibility with legacy flags API */
579 if (ctx->flags & SWS_BILINEAR) {
580 return SWS_SCALE_BILINEAR;
581 } else if (ctx->flags & (SWS_BICUBIC | SWS_BICUBLIN)) {
582 return SWS_SCALE_BICUBIC;
583 } else if (ctx->flags & SWS_POINT) {
584 return SWS_SCALE_POINT;
585 } else if (ctx->flags & SWS_AREA) {
586 return SWS_SCALE_AREA;
587 } else if (ctx->flags & SWS_GAUSS) {
588 return SWS_SCALE_GAUSSIAN;
589 } else if (ctx->flags & SWS_SINC) {
590 return SWS_SCALE_SINC;
591 } else if (ctx->flags & SWS_LANCZOS) {
592 return SWS_SCALE_LANCZOS;
593 } else if (ctx->flags & SWS_SPLINE) {
594 return SWS_SCALE_SPLINE;
595 } else {
596 return SWS_SCALE_AUTO;
597 }
598 }
599
600 30960 static int add_filter(SwsContext *ctx, SwsPixelType type, SwsOpList *ops,
601 SwsOpType filter, int src_size, int dst_size)
602 {
603
1/2
✓ Branch 0 taken 30960 times.
✗ Branch 1 not taken.
30960 if (src_size == dst_size)
604 30960 return 0; /* no-op */
605
606 SwsFilterParams params = {
607 .scaler = get_scaler_fallback(ctx),
608 .src_size = src_size,
609 .dst_size = dst_size,
610 };
611
612 for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++)
613 params.scaler_params[i] = ctx->scaler_params[i];
614
615 SwsFilterWeights *kernel;
616 int ret = ff_sws_filter_generate(ctx, &params, &kernel);
617 if (ret == AVERROR(ENOTSUP)) {
618 /* Filter size exceeds limit; cascade with geometric mean size */
619 int mean = sqrt((int64_t) src_size * dst_size);
620 if (mean == src_size || mean == dst_size)
621 return AVERROR_BUG; /* sanity, prevent infinite loop */
622 ret = add_filter(ctx, type, ops, filter, src_size, mean);
623 if (ret < 0)
624 return ret;
625 return add_filter(ctx, type, ops, filter, mean, dst_size);
626 } else if (ret < 0) {
627 return ret;
628 }
629
630 return ff_sws_op_list_append(ops, &(SwsOp) {
631 .type = type,
632 .op = filter,
633 .filter.kernel = kernel,
634 });
635 }
636
637 40046 static int add_convert_pass(SwsGraph *graph, const SwsFormat *src,
638 const SwsFormat *dst, SwsPass *input,
639 SwsPass **output)
640 {
641 40046 const SwsPixelType type = SWS_PIXEL_F32;
642
643 40046 SwsContext *ctx = graph->ctx;
644 40046 SwsOpList *ops = NULL;
645 40046 int ret = AVERROR(ENOTSUP);
646
647 /* Mark the entire new ops infrastructure as experimental for now */
648
2/2
✓ Branch 0 taken 23276 times.
✓ Branch 1 taken 16770 times.
40046 if (!(ctx->flags & SWS_UNSTABLE))
649 23276 goto fail;
650
651 /* The new code does not yet support alpha blending */
652
2/2
✓ Branch 0 taken 4644 times.
✓ Branch 1 taken 12126 times.
16770 if (src->desc->flags & AV_PIX_FMT_FLAG_ALPHA &&
653
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4644 times.
4644 ctx->alpha_blend != SWS_ALPHA_BLEND_NONE)
654 goto fail;
655
656 16770 ops = ff_sws_op_list_alloc();
657
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16770 times.
16770 if (!ops)
658 return AVERROR(ENOMEM);
659 16770 ops->src = *src;
660 16770 ops->dst = *dst;
661
662 16770 ret = ff_sws_decode_pixfmt(ops, src->format);
663
2/2
✓ Branch 0 taken 1290 times.
✓ Branch 1 taken 15480 times.
16770 if (ret < 0)
664 1290 goto fail;
665 15480 ret = ff_sws_decode_colors(ctx, type, ops, src, &graph->incomplete);
666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15480 times.
15480 if (ret < 0)
667 goto fail;
668
669 /**
670 * Always perform horizontal scaling first, since it's much more likely to
671 * benefit from small integer optimizations; we should maybe flip the order
672 * here if we're downscaling the vertical resolution by a lot, though.
673 */
674 15480 ret = add_filter(ctx, type, ops, SWS_OP_FILTER_H, src->width, dst->width);
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15480 times.
15480 if (ret < 0)
676 goto fail;
677 15480 ret = add_filter(ctx, type, ops, SWS_OP_FILTER_V, src->height, dst->height);
678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15480 times.
15480 if (ret < 0)
679 goto fail;
680
681 15480 ret = ff_sws_encode_colors(ctx, type, ops, src, dst, &graph->incomplete);
682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15480 times.
15480 if (ret < 0)
683 goto fail;
684 15480 ret = ff_sws_encode_pixfmt(ops, dst->format);
685
2/2
✓ Branch 0 taken 1200 times.
✓ Branch 1 taken 14280 times.
15480 if (ret < 0)
686 1200 goto fail;
687
688 14280 av_log(ctx, AV_LOG_VERBOSE, "Conversion pass for %s -> %s:\n",
689 14280 av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format));
690
691 14280 av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
692 14280 ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
693
694 14280 ret = ff_sws_compile_pass(graph, &ops, SWS_OP_FLAG_OPTIMIZE, input, output);
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14280 times.
14280 if (ret < 0)
696 goto fail;
697
698 14280 ret = 0;
699 /* fall through */
700
701 40046 fail:
702 40046 ff_sws_op_list_free(&ops);
703
2/2
✓ Branch 0 taken 25766 times.
✓ Branch 1 taken 14280 times.
40046 if (ret == AVERROR(ENOTSUP))
704 25766 return add_legacy_sws_pass(graph, src, dst, input, output);
705 14280 return ret;
706 }
707 #else
708 #define add_convert_pass add_legacy_sws_pass
709 #endif
710
711
712 /**************************
713 * Gamut and tone mapping *
714 **************************/
715
716 static void free_lut3d(void *priv)
717 {
718 SwsLut3D *lut = priv;
719 ff_sws_lut3d_free(&lut);
720 }
721
722 static int setup_lut3d(const SwsFrame *out, const SwsFrame *in, const SwsPass *pass)
723 {
724 SwsLut3D *lut = pass->priv;
725
726 /* Update dynamic frame metadata from the original source frame */
727 ff_sws_lut3d_update(lut, &pass->graph->src.color);
728 return 0;
729 }
730
731 static void run_lut3d(const SwsFrame *out, const SwsFrame *in, int y, int h,
732 const SwsPass *pass)
733 {
734 SwsLut3D *lut = pass->priv;
735 uint8_t *in_data[4], *out_data[4];
736 frame_shift(in, y, in_data);
737 frame_shift(out, y, out_data);
738
739 ff_sws_lut3d_apply(lut, in_data[0], in->linesize[0], out_data[0],
740 out->linesize[0], pass->width, h);
741 }
742
743 40307 static int adapt_colors(SwsGraph *graph, SwsFormat src, SwsFormat dst,
744 SwsPass *input, SwsPass **output)
745 {
746 enum AVPixelFormat fmt_in, fmt_out;
747 40307 SwsColorMap map = {0};
748 SwsLut3D *lut;
749 int ret;
750
751 /**
752 * Grayspace does not really have primaries, so just force the use of
753 * the equivalent other primary set to avoid a conversion. Technically,
754 * this does affect the weights used for the Grayscale conversion, but
755 * in practise, that should give the expected results more often than not.
756 */
757
2/2
✓ Branch 1 taken 2439 times.
✓ Branch 2 taken 37868 times.
40307 if (isGray(dst.format)) {
758 2439 dst.color = src.color;
759
2/2
✓ Branch 1 taken 3999 times.
✓ Branch 2 taken 33869 times.
37868 } else if (isGray(src.format)) {
760 3999 src.color = dst.color;
761 }
762
763 /* Fully infer color spaces before color mapping logic */
764 40307 graph->incomplete |= ff_infer_colors(&src.color, &dst.color);
765
766 40307 map.intent = graph->ctx->intent;
767 40307 map.src = src.color;
768 40307 map.dst = dst.color;
769
770
1/2
✓ Branch 1 taken 40307 times.
✗ Branch 2 not taken.
40307 if (ff_sws_color_map_noop(&map))
771 40307 return 0;
772
773 if (src.hw_format != AV_PIX_FMT_NONE || dst.hw_format != AV_PIX_FMT_NONE)
774 return AVERROR(ENOTSUP);
775
776 lut = ff_sws_lut3d_alloc();
777 if (!lut)
778 return AVERROR(ENOMEM);
779
780 fmt_in = ff_sws_lut3d_pick_pixfmt(src, 0);
781 fmt_out = ff_sws_lut3d_pick_pixfmt(dst, 1);
782 if (fmt_in != src.format) {
783 SwsFormat tmp = src;
784 tmp.format = fmt_in;
785 ret = add_convert_pass(graph, &src, &tmp, input, &input);
786 if (ret < 0) {
787 ff_sws_lut3d_free(&lut);
788 return ret;
789 }
790 }
791
792 ret = ff_sws_lut3d_generate(lut, fmt_in, fmt_out, &map);
793 if (ret < 0) {
794 ff_sws_lut3d_free(&lut);
795 return ret;
796 }
797
798 return ff_sws_graph_add_pass(graph, fmt_out, src.width, src.height,
799 input, 1, run_lut3d, setup_lut3d, lut,
800 free_lut3d, output);
801 }
802
803 /***************************************
804 * Main filter graph construction code *
805 ***************************************/
806
807 40307 static int init_passes(SwsGraph *graph)
808 {
809 40307 SwsFormat src = graph->src;
810 40307 SwsFormat dst = graph->dst;
811 40307 SwsPass *pass = NULL; /* read from main input image */
812 int ret;
813
814 40307 ret = adapt_colors(graph, src, dst, pass, &pass);
815
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40307 times.
40307 if (ret < 0)
816 return ret;
817
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40307 times.
40307 src.format = pass ? pass->format : src.format;
818 40307 src.color = dst.color;
819
820
2/2
✓ Branch 1 taken 40046 times.
✓ Branch 2 taken 261 times.
40307 if (!ff_fmt_equal(&src, &dst)) {
821 40046 ret = add_convert_pass(graph, &src, &dst, pass, &pass);
822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40046 times.
40046 if (ret < 0)
823 return ret;
824 }
825
826
2/2
✓ Branch 0 taken 40046 times.
✓ Branch 1 taken 261 times.
40307 if (pass)
827 40046 return 0;
828
829 /* No passes were added, so no operations were necessary */
830 261 graph->noop = 1;
831
832 /* Add threaded memcpy pass */
833 261 return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
834 pass, 1, run_copy, NULL, NULL, NULL, &pass);
835 }
836
837 613127 static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
838 int nb_threads)
839 {
840 613127 SwsGraph *graph = priv;
841 613127 const SwsPass *pass = graph->exec.pass;
842 613127 const int slice_y = jobnr * pass->slice_h;
843 613127 const int slice_h = FFMIN(pass->slice_h, pass->height - slice_y);
844
845 613127 pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass);
846 613127 }
847
848 40307 int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src,
849 int field, SwsGraph **out_graph)
850 {
851 int ret;
852 40307 SwsGraph *graph = av_mallocz(sizeof(*graph));
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40307 times.
40307 if (!graph)
854 return AVERROR(ENOMEM);
855
856 40307 graph->ctx = ctx;
857 40307 graph->src = *src;
858 40307 graph->dst = *dst;
859 40307 graph->field = field;
860 40307 graph->opts_copy = *ctx;
861
862
2/2
✓ Branch 0 taken 37724 times.
✓ Branch 1 taken 2583 times.
40307 if (ctx->threads == 1) {
863 37724 graph->num_threads = 1;
864 } else {
865 2583 ret = avpriv_slicethread_create(&graph->slicethread, (void *) graph,
866 sws_graph_worker, NULL, ctx->threads);
867
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2583 times.
2583 if (ret == AVERROR(ENOSYS)) {
868 /* Fall back to single threaded operation */
869 graph->num_threads = 1;
870
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2583 times.
2583 } else if (ret < 0) {
871 goto error;
872 } else {
873 2583 graph->num_threads = ret;
874 }
875 }
876
877 40307 ret = init_passes(graph);
878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40307 times.
40307 if (ret < 0)
879 goto error;
880
881 /* Resolve output buffers for all intermediate passes */
882
2/2
✓ Branch 0 taken 41660 times.
✓ Branch 1 taken 40307 times.
81967 for (int i = 0; i < graph->num_passes; i++) {
883 41660 ret = pass_alloc_output(graph->passes[i]->input);
884
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41660 times.
41660 if (ret < 0)
885 goto error;
886 }
887
888 40307 *out_graph = graph;
889 40307 return 0;
890
891 error:
892 ff_sws_graph_free(&graph);
893 return ret;
894 }
895
896 void ff_sws_graph_rollback(SwsGraph *graph, int since_idx)
897 {
898 for (int i = since_idx; i < graph->num_passes; i++)
899 pass_free(graph->passes[i]);
900 graph->num_passes = since_idx;
901 }
902
903 349285 void ff_sws_graph_free(SwsGraph **pgraph)
904 {
905 349285 SwsGraph *graph = *pgraph;
906
2/2
✓ Branch 0 taken 308978 times.
✓ Branch 1 taken 40307 times.
349285 if (!graph)
907 308978 return;
908
909 40307 avpriv_slicethread_free(&graph->slicethread);
910
911
2/2
✓ Branch 0 taken 41660 times.
✓ Branch 1 taken 40307 times.
81967 for (int i = 0; i < graph->num_passes; i++)
912 41660 pass_free(graph->passes[i]);
913 40307 av_free(graph->passes);
914
915 40307 av_free(graph);
916 40307 *pgraph = NULL;
917 }
918
919 /* Tests only options relevant to SwsGraph */
920 116035 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
921 {
922 232070 return c1->flags == c2->flags &&
923
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->threads == c2->threads &&
924
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->dither == c2->dither &&
925
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->alpha_blend == c2->alpha_blend &&
926
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->gamma_flag == c2->gamma_flag &&
927
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->src_h_chr_pos == c2->src_h_chr_pos &&
928
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->src_v_chr_pos == c2->src_v_chr_pos &&
929
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
930
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
931
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->intent == c2->intent &&
932
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 c1->scaler == c2->scaler &&
933
2/4
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 116035 times.
✗ Branch 3 not taken.
348105 c1->scaler_sub == c2->scaler_sub &&
934
1/2
✓ Branch 0 taken 116035 times.
✗ Branch 1 not taken.
116035 !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
935
936 }
937
938 156342 int ff_sws_graph_reinit(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src,
939 int field, SwsGraph **out_graph)
940 {
941 156342 SwsGraph *graph = *out_graph;
942
6/6
✓ Branch 0 taken 149962 times.
✓ Branch 1 taken 6380 times.
✓ Branch 3 taken 132934 times.
✓ Branch 4 taken 17028 times.
✓ Branch 5 taken 116035 times.
✓ Branch 6 taken 16899 times.
289276 if (graph && ff_fmt_equal(&graph->src, src) &&
943
1/2
✓ Branch 1 taken 116035 times.
✗ Branch 2 not taken.
248969 ff_fmt_equal(&graph->dst, dst) &&
944 116035 opts_equal(ctx, &graph->opts_copy))
945 {
946 116035 ff_sws_graph_update_metadata(graph, &src->color);
947 116035 return 0;
948 }
949
950 40307 ff_sws_graph_free(out_graph);
951 40307 return ff_sws_graph_create(ctx, dst, src, field, out_graph);
952 }
953
954 116035 void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
955 {
956
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116035 times.
116035 if (!color)
957 return;
958
959 116035 ff_color_update_dynamic(&graph->src.color, color);
960 }
961
962 278362 static void get_field(SwsGraph *graph, const AVFrame *avframe, SwsFrame *frame)
963 {
964 278362 ff_sws_frame_from_avframe(frame, avframe);
965
966
1/2
✓ Branch 0 taken 278362 times.
✗ Branch 1 not taken.
278362 if (!(avframe->flags & AV_FRAME_FLAG_INTERLACED)) {
967 av_assert1(!graph->field);
968 278362 return;
969 }
970
971 if (graph->field == FIELD_BOTTOM) {
972 /* Odd rows, offset by one line */
973 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
974 for (int i = 0; i < 4; i++) {
975 if (frame->data[i])
976 frame->data[i] += frame->linesize[i];
977 if (desc->flags & AV_PIX_FMT_FLAG_PAL)
978 break;
979 }
980 }
981
982 /* Take only every second line */
983 for (int i = 0; i < 4; i++)
984 frame->linesize[i] <<= 1;
985
986 frame->height = (frame->height + (graph->field == FIELD_TOP)) >> 1;
987 }
988
989 139181 int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
990 {
991
2/4
✓ Branch 0 taken 139181 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 139181 times.
139181 av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
992
2/4
✓ Branch 0 taken 139181 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 139181 times.
139181 av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
993
994 SwsFrame src_field, dst_field;
995 139181 get_field(graph, dst, &dst_field);
996 139181 get_field(graph, src, &src_field);
997
998
2/2
✓ Branch 0 taken 141130 times.
✓ Branch 1 taken 139181 times.
280311 for (int i = 0; i < graph->num_passes; i++) {
999 141130 const SwsPass *pass = graph->passes[i];
1000 141130 graph->exec.pass = pass;
1001
2/2
✓ Branch 0 taken 1949 times.
✓ Branch 1 taken 139181 times.
141130 graph->exec.input = pass->input ? &pass->input->output->frame : &src_field;
1002
2/2
✓ Branch 0 taken 1949 times.
✓ Branch 1 taken 139181 times.
141130 graph->exec.output = pass->output->avframe ? &pass->output->frame : &dst_field;
1003
2/2
✓ Branch 0 taken 139183 times.
✓ Branch 1 taken 1947 times.
141130 if (pass->setup) {
1004 139183 int ret = pass->setup(graph->exec.output, graph->exec.input, pass);
1005
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 139183 times.
139183 if (ret < 0)
1006 return ret;
1007 }
1008
1009
2/2
✓ Branch 0 taken 72884 times.
✓ Branch 1 taken 68246 times.
141130 if (pass->num_slices == 1) {
1010 72884 pass->run(graph->exec.output, graph->exec.input, 0, pass->height, pass);
1011 } else {
1012 68246 avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
1013 }
1014 }
1015
1016 139181 return 0;
1017 }
1018