FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/graph.c
Date: 2025-06-01 09:29:47
Exec Total Coverage
Lines: 309 400 77.2%
Functions: 24 28 85.7%
Branches: 143 218 65.6%

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/error.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/macros.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/pixdesc.h"
28 #include "libavutil/slicethread.h"
29
30 #include "libswscale/swscale.h"
31 #include "libswscale/format.h"
32
33 #include "cms.h"
34 #include "lut3d.h"
35 #include "swscale_internal.h"
36 #include "graph.h"
37
38 6225 static int pass_alloc_output(SwsPass *pass)
39 {
40
3/4
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 6166 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 59 times.
6225 if (!pass || pass->output.fmt != AV_PIX_FMT_NONE)
41 6166 return 0;
42 59 pass->output.fmt = pass->format;
43 59 return av_image_alloc(pass->output.data, pass->output.linesize, pass->width,
44 59 pass->num_slices * pass->slice_h, pass->format, 64);
45 }
46
47 6225 SwsPass *ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
48 int width, int height, SwsPass *input,
49 int align, void *priv, sws_filter_run_t run)
50 {
51 int ret;
52 6225 SwsPass *pass = av_mallocz(sizeof(*pass));
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6225 times.
6225 if (!pass)
54 return NULL;
55
56 6225 pass->graph = graph;
57 6225 pass->run = run;
58 6225 pass->priv = priv;
59 6225 pass->format = fmt;
60 6225 pass->width = width;
61 6225 pass->height = height;
62 6225 pass->input = input;
63 6225 pass->output.fmt = AV_PIX_FMT_NONE;
64
65 6225 ret = pass_alloc_output(input);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6225 times.
6225 if (ret < 0) {
67 av_free(pass);
68 return NULL;
69 }
70
71
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6214 times.
6225 if (!align) {
72 11 pass->slice_h = pass->height;
73 11 pass->num_slices = 1;
74 } else {
75 6214 pass->slice_h = (pass->height + graph->num_threads - 1) / graph->num_threads;
76 6214 pass->slice_h = FFALIGN(pass->slice_h, align);
77 6214 pass->num_slices = (pass->height + pass->slice_h - 1) / pass->slice_h;
78 }
79
80 6225 ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6225 times.
6225 if (ret < 0)
82 av_freep(&pass);
83 6225 return pass;
84 }
85
86 /* Wrapper around ff_sws_graph_add_pass() that chains a pass "in-place" */
87 58 static int pass_append(SwsGraph *graph, enum AVPixelFormat fmt, int w, int h,
88 SwsPass **pass, int align, void *priv, sws_filter_run_t run)
89 {
90 58 SwsPass *new = ff_sws_graph_add_pass(graph, fmt, w, h, *pass, align, priv, run);
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 if (!new)
92 return AVERROR(ENOMEM);
93 58 *pass = new;
94 58 return 0;
95 }
96
97 static void run_copy(const SwsImg *out_base, const SwsImg *in_base,
98 int y, int h, const SwsPass *pass)
99 {
100 SwsImg in = ff_sws_img_shift(in_base, y);
101 SwsImg out = ff_sws_img_shift(out_base, y);
102
103 for (int i = 0; i < FF_ARRAY_ELEMS(out.data) && out.data[i]; i++) {
104 const int lines = h >> ff_fmt_vshift(in.fmt, i);
105 av_assert1(in.data[i]);
106
107 if (in.linesize[i] == out.linesize[i]) {
108 memcpy(out.data[i], in.data[i], lines * out.linesize[i]);
109 } else {
110 const int linesize = FFMIN(out.linesize[i], in.linesize[i]);
111 for (int j = 0; j < lines; j++) {
112 memcpy(out.data[i], in.data[i], linesize);
113 in.data[i] += in.linesize[i];
114 out.data[i] += out.linesize[i];
115 }
116 }
117 }
118 }
119
120 54 static void run_rgb0(const SwsImg *out, const SwsImg *in, int y, int h,
121 const SwsPass *pass)
122 {
123 54 SwsInternal *c = pass->priv;
124 54 const int x0 = c->src0Alpha - 1;
125 54 const int w4 = 4 * pass->width;
126 54 const int src_stride = in->linesize[0];
127 54 const int dst_stride = out->linesize[0];
128 54 const uint8_t *src = in->data[0] + y * src_stride;
129 54 uint8_t *dst = out->data[0] + y * dst_stride;
130
131
2/2
✓ Branch 0 taken 4662 times.
✓ Branch 1 taken 54 times.
4716 for (int y = 0; y < h; y++) {
132 4662 memcpy(dst, src, w4 * sizeof(*dst));
133
2/2
✓ Branch 0 taken 2869202 times.
✓ Branch 1 taken 4662 times.
2873864 for (int x = x0; x < w4; x += 4)
134 2869202 dst[x] = 0xFF;
135
136 4662 src += src_stride;
137 4662 dst += dst_stride;
138 }
139 54 }
140
141 2577 static void run_xyz2rgb(const SwsImg *out, const SwsImg *in, int y, int h,
142 const SwsPass *pass)
143 {
144 2577 ff_xyz12Torgb48(pass->priv, out->data[0] + y * out->linesize[0], out->linesize[0],
145 2577 in->data[0] + y * in->linesize[0], in->linesize[0],
146 2577 pass->width, h);
147 2577 }
148
149 2618 static void run_rgb2xyz(const SwsImg *out, const SwsImg *in, int y, int h,
150 const SwsPass *pass)
151 {
152 2618 ff_rgb48Toxyz12(pass->priv, out->data[0] + y * out->linesize[0], out->linesize[0],
153 2618 in->data[0] + y * in->linesize[0], in->linesize[0],
154 2618 pass->width, h);
155 2618 }
156
157 /***********************************************************************
158 * Internal ff_swscale() wrapper. This re-uses the legacy scaling API. *
159 * This is considered fully deprecated, and will be replaced by a full *
160 * reimplementation ASAP. *
161 ***********************************************************************/
162
163 6167 static void free_legacy_swscale(void *priv)
164 {
165 6167 SwsContext *sws = priv;
166 6167 sws_free_context(&sws);
167 6167 }
168
169 99800 static void setup_legacy_swscale(const SwsImg *out, const SwsImg *in,
170 const SwsPass *pass)
171 {
172 99800 SwsContext *sws = pass->priv;
173 99800 SwsInternal *c = sws_internal(sws);
174
5/6
✓ Branch 0 taken 92559 times.
✓ Branch 1 taken 7241 times.
✓ Branch 2 taken 870 times.
✓ Branch 3 taken 91689 times.
✓ Branch 4 taken 870 times.
✗ Branch 5 not taken.
99800 if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
175
2/2
✓ Branch 0 taken 3480 times.
✓ Branch 1 taken 870 times.
4350 for (int i = 0; i < 4; i++)
176 3480 memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
177 }
178
179
2/2
✓ Branch 1 taken 4713 times.
✓ Branch 2 taken 95087 times.
99800 if (usePal(sws->src_format))
180 4713 ff_update_palette(c, (const uint32_t *) in->data[1]);
181 99800 }
182
183 621254 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
184 {
185 621254 SwsContext *sws = pass->priv;
186 621254 SwsInternal *parent = sws_internal(sws);
187
2/2
✓ Branch 0 taken 34491 times.
✓ Branch 1 taken 586763 times.
621254 if (pass->num_slices == 1)
188 34491 return sws;
189
190 av_assert1(parent->nb_slice_ctx == pass->num_slices);
191 586763 sws = parent->slice_ctx[y / pass->slice_h];
192
193
2/2
✓ Branch 1 taken 35117 times.
✓ Branch 2 taken 551646 times.
586763 if (usePal(sws->src_format)) {
194 35117 SwsInternal *sub = sws_internal(sws);
195 35117 memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv));
196 35117 memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb));
197 }
198
199 586763 return sws;
200 }
201
202 87786 static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in_base,
203 int y, int h, const SwsPass *pass)
204 {
205 87786 SwsContext *sws = slice_ctx(pass, y);
206 87786 SwsInternal *c = sws_internal(sws);
207 87786 const SwsImg in = ff_sws_img_shift(in_base, y);
208
209 87786 c->convert_unscaled(c, (const uint8_t *const *) in.data, in.linesize, y, h,
210 87786 out->data, out->linesize);
211 87786 }
212
213 533468 static void run_legacy_swscale(const SwsImg *out_base, const SwsImg *in,
214 int y, int h, const SwsPass *pass)
215 {
216 533468 SwsContext *sws = slice_ctx(pass, y);
217 533468 SwsInternal *c = sws_internal(sws);
218 533468 const SwsImg out = ff_sws_img_shift(out_base, y);
219
220 533468 ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
221 sws->src_h, out.data, out.linesize, y, h);
222 533468 }
223
224 12332 static void get_chroma_pos(SwsGraph *graph, int *h_chr_pos, int *v_chr_pos,
225 const SwsFormat *fmt)
226 {
227 12332 enum AVChromaLocation chroma_loc = fmt->loc;
228 12332 const int sub_x = fmt->desc->log2_chroma_w;
229 12332 const int sub_y = fmt->desc->log2_chroma_h;
230 int x_pos, y_pos;
231
232 /* Explicitly default to center siting for compatibility with swscale */
233
2/2
✓ Branch 0 taken 12331 times.
✓ Branch 1 taken 1 times.
12332 if (chroma_loc == AVCHROMA_LOC_UNSPECIFIED) {
234 12331 chroma_loc = AVCHROMA_LOC_CENTER;
235
4/4
✓ Branch 0 taken 6372 times.
✓ Branch 1 taken 5959 times.
✓ Branch 2 taken 240 times.
✓ Branch 3 taken 6132 times.
12331 graph->incomplete |= sub_x || sub_y;
236 }
237
238 /* av_chroma_location_enum_to_pos() always gives us values in the range from
239 * 0 to 256, but we need to adjust this to the true value range of the
240 * subsampling grid, which may be larger for h/v_sub > 1 */
241 12332 av_chroma_location_enum_to_pos(&x_pos, &y_pos, chroma_loc);
242 12332 x_pos *= (1 << sub_x) - 1;
243 12332 y_pos *= (1 << sub_y) - 1;
244
245 /* Fix vertical chroma position for interlaced frames */
246
3/4
✓ Branch 0 taken 4781 times.
✓ Branch 1 taken 7551 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4781 times.
12332 if (sub_y && fmt->interlaced) {
247 /* When vertically subsampling, chroma samples are effectively only
248 * placed next to even rows. To access them from the odd field, we need
249 * to account for this shift by offsetting the distance of one luma row.
250 *
251 * For 4x vertical subsampling (v_sub == 2), they are only placed
252 * next to every *other* even row, so we need to shift by three luma
253 * rows to get to the chroma sample. */
254 if (graph->field == FIELD_BOTTOM)
255 y_pos += (256 << sub_y) - 256;
256
257 /* Luma row distance is doubled for fields, so halve offsets */
258 y_pos >>= 1;
259 }
260
261 /* Explicitly strip chroma offsets when not subsampling, because it
262 * interferes with the operation of flags like SWS_FULL_CHR_H_INP */
263
2/2
✓ Branch 0 taken 5960 times.
✓ Branch 1 taken 6372 times.
12332 *h_chr_pos = sub_x ? x_pos : -513;
264
2/2
✓ Branch 0 taken 4781 times.
✓ Branch 1 taken 7551 times.
12332 *v_chr_pos = sub_y ? y_pos : -513;
265 12332 }
266
267 24664 static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
268 {
269
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 24664 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
24664 if (override == -513 || override == *chr_pos)
270 24664 return;
271
272 if (!*warned) {
273 av_log(NULL, AV_LOG_WARNING,
274 "Setting chroma position directly is deprecated, make sure "
275 "the frame is tagged with the correct chroma location.\n");
276 *warned = 1;
277 }
278
279 *chr_pos = override;
280 }
281
282 6168 static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws,
283 SwsPass *input, SwsPass **output)
284 {
285 6168 SwsInternal *c = sws_internal(sws);
286 6168 const int src_w = sws->src_w, src_h = sws->src_h;
287 6168 const int dst_w = sws->dst_w, dst_h = sws->dst_h;
288
4/4
✓ Branch 0 taken 5722 times.
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 5712 times.
✓ Branch 3 taken 10 times.
6168 const int unscaled = src_w == dst_w && src_h == dst_h;
289 6168 int align = c->dst_slice_align;
290 6168 SwsPass *pass = NULL;
291 int ret;
292
293
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6167 times.
6168 if (c->cascaded_context[0]) {
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 const int num_cascaded = c->cascaded_context[2] ? 3 : 2;
295
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int i = 0; i < num_cascaded; i++) {
296 2 SwsContext *sub = c->cascaded_context[i];
297 2 const int is_last = i + 1 == num_cascaded;
298
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input);
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
300 return ret;
301 /* Steal cascaded context, so we can free the parent */
302 2 c->cascaded_context[i] = NULL;
303 }
304
305 1 sws_free_context(&sws);
306 1 return 0;
307 }
308
309
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 6156 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
6167 if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
310 11 align = 0; /* disable slice threading */
311
312
6/6
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 6142 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 6 times.
✓ Branch 6 taken 15 times.
6167 if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
313 6 ret = pass_append(graph, AV_PIX_FMT_RGBA, src_w, src_h, &input, 1, c, run_rgb0);
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (ret < 0)
315 return ret;
316 }
317
318
5/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6153 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6167 if (c->srcXYZ && !(c->dstXYZ && unscaled)) {
319 14 ret = pass_append(graph, AV_PIX_FMT_RGB48, src_w, src_h, &input, 1, c, run_xyz2rgb);
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ret < 0)
321 return ret;
322 }
323
324 6167 pass = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, align, sws,
325
2/2
✓ Branch 0 taken 647 times.
✓ Branch 1 taken 5520 times.
6167 c->convert_unscaled ? run_legacy_unscaled : run_legacy_swscale);
326
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6167 times.
6167 if (!pass)
327 return AVERROR(ENOMEM);
328 6167 pass->setup = setup_legacy_swscale;
329 6167 pass->free = free_legacy_swscale;
330
331 /**
332 * For slice threading, we need to create sub contexts, similar to how
333 * swscale normally handles it internally. The most important difference
334 * is that we handle cascaded contexts before threaded contexts; whereas
335 * context_init_threaded() does it the other way around.
336 */
337
338
2/2
✓ Branch 0 taken 2486 times.
✓ Branch 1 taken 3681 times.
6167 if (pass->num_slices > 1) {
339 2486 c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2486 times.
2486 if (!c->slice_ctx)
341 return AVERROR(ENOMEM);
342
343
2/2
✓ Branch 0 taken 22290 times.
✓ Branch 1 taken 2486 times.
24776 for (int i = 0; i < pass->num_slices; i++) {
344 SwsContext *slice;
345 SwsInternal *c2;
346 22290 slice = c->slice_ctx[i] = sws_alloc_context();
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22290 times.
22290 if (!slice)
348 return AVERROR(ENOMEM);
349 22290 c->nb_slice_ctx++;
350
351 22290 c2 = sws_internal(slice);
352 22290 c2->parent = sws;
353
354 22290 ret = av_opt_copy(slice, sws);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22290 times.
22290 if (ret < 0)
356 return ret;
357
358 22290 ret = ff_sws_init_single_context(slice, NULL, NULL);
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22290 times.
22290 if (ret < 0)
360 return ret;
361
362 22290 sws_setColorspaceDetails(slice, c->srcColorspaceTable,
363 22290 slice->src_range, c->dstColorspaceTable,
364 slice->dst_range, c->brightness, c->contrast,
365 c->saturation);
366
367
2/2
✓ Branch 0 taken 89160 times.
✓ Branch 1 taken 22290 times.
111450 for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
368 89160 c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
369 89160 c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
370 }
371 }
372 }
373
374
5/6
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 6129 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6167 if (c->dstXYZ && !(c->srcXYZ && unscaled)) {
375 38 ret = pass_append(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, &pass, 1, c, run_rgb2xyz);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (ret < 0)
377 return ret;
378 }
379
380 6167 *output = pass;
381 6167 return 0;
382 }
383
384 6166 static int add_legacy_sws_pass(SwsGraph *graph, SwsFormat src, SwsFormat dst,
385 SwsPass *input, SwsPass **output)
386 {
387 6166 int ret, warned = 0;
388 6166 SwsContext *const ctx = graph->ctx;
389 6166 SwsContext *sws = sws_alloc_context();
390
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (!sws)
391 return AVERROR(ENOMEM);
392
393 6166 sws->flags = ctx->flags;
394 6166 sws->dither = ctx->dither;
395 6166 sws->alpha_blend = ctx->alpha_blend;
396 6166 sws->gamma_flag = ctx->gamma_flag;
397
398 6166 sws->src_w = src.width;
399 6166 sws->src_h = src.height;
400 6166 sws->src_format = src.format;
401 6166 sws->src_range = src.range == AVCOL_RANGE_JPEG;
402
403 6166 sws->dst_w = dst.width;
404 6166 sws->dst_h = dst.height;
405 6166 sws->dst_format = dst.format;
406 6166 sws->dst_range = dst.range == AVCOL_RANGE_JPEG;
407 6166 get_chroma_pos(graph, &sws->src_h_chr_pos, &sws->src_v_chr_pos, &src);
408 6166 get_chroma_pos(graph, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos, &dst);
409
410 6166 graph->incomplete |= src.range == AVCOL_RANGE_UNSPECIFIED;
411 6166 graph->incomplete |= dst.range == AVCOL_RANGE_UNSPECIFIED;
412
413 /* Allow overriding chroma position with the legacy API */
414 6166 legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
415 6166 legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
416 6166 legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
417 6166 legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned);
418
419 6166 sws->scaler_params[0] = ctx->scaler_params[0];
420 6166 sws->scaler_params[1] = ctx->scaler_params[1];
421
422 6166 ret = sws_init_context(sws, NULL, NULL);
423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret < 0) {
424 sws_free_context(&sws);
425 return ret;
426 }
427
428 /* Set correct color matrices */
429 {
430 int in_full, out_full, brightness, contrast, saturation;
431 const int *inv_table, *table;
432 6166 sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
433 (int **)&table, &out_full,
434 &brightness, &contrast, &saturation);
435
436 6166 inv_table = sws_getCoefficients(src.csp);
437 6166 table = sws_getCoefficients(dst.csp);
438
439
2/2
✓ Branch 0 taken 2114 times.
✓ Branch 1 taken 4052 times.
8280 graph->incomplete |= src.csp != dst.csp &&
440
2/2
✓ Branch 0 taken 628 times.
✓ Branch 1 taken 1486 times.
2114 (src.csp == AVCOL_SPC_UNSPECIFIED ||
441
2/2
✓ Branch 0 taken 619 times.
✓ Branch 1 taken 9 times.
628 dst.csp == AVCOL_SPC_UNSPECIFIED);
442
443 6166 sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
444 brightness, contrast, saturation);
445 }
446
447 6166 ret = init_legacy_subpass(graph, sws, input, output);
448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret < 0) {
449 sws_free_context(&sws);
450 return ret;
451 }
452
453 6166 return 0;
454 }
455
456 /**************************
457 * Gamut and tone mapping *
458 **************************/
459
460 static void free_lut3d(void *priv)
461 {
462 SwsLut3D *lut = priv;
463 ff_sws_lut3d_free(&lut);
464 }
465
466 static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
467 {
468 SwsLut3D *lut = pass->priv;
469
470 /* Update dynamic frame metadata from the original source frame */
471 ff_sws_lut3d_update(lut, &pass->graph->src.color);
472 }
473
474 static void run_lut3d(const SwsImg *out_base, const SwsImg *in_base,
475 int y, int h, const SwsPass *pass)
476 {
477 SwsLut3D *lut = pass->priv;
478 const SwsImg in = ff_sws_img_shift(in_base, y);
479 const SwsImg out = ff_sws_img_shift(out_base, y);
480
481 ff_sws_lut3d_apply(lut, in.data[0], in.linesize[0], out.data[0],
482 out.linesize[0], pass->width, h);
483 }
484
485 6166 static int adapt_colors(SwsGraph *graph, SwsFormat src, SwsFormat dst,
486 SwsPass *input, SwsPass **output)
487 {
488 enum AVPixelFormat fmt_in, fmt_out;
489 6166 SwsColorMap map = {0};
490 SwsLut3D *lut;
491 SwsPass *pass;
492 int ret;
493
494 /**
495 * Grayspace does not really have primaries, so just force the use of
496 * the equivalent other primary set to avoid a conversion. Technically,
497 * this does affect the weights used for the Grayscale conversion, but
498 * in practise, that should give the expected results more often than not.
499 */
500
2/2
✓ Branch 1 taken 342 times.
✓ Branch 2 taken 5824 times.
6166 if (isGray(dst.format)) {
501 342 dst.color = src.color;
502
2/2
✓ Branch 1 taken 95 times.
✓ Branch 2 taken 5729 times.
5824 } else if (isGray(src.format)) {
503 95 src.color = dst.color;
504 }
505
506 /* Fully infer color spaces before color mapping logic */
507 6166 graph->incomplete |= ff_infer_colors(&src.color, &dst.color);
508
509 6166 map.intent = graph->ctx->intent;
510 6166 map.src = src.color;
511 6166 map.dst = dst.color;
512
513
1/2
✓ Branch 1 taken 6166 times.
✗ Branch 2 not taken.
6166 if (ff_sws_color_map_noop(&map))
514 6166 return 0;
515
516 lut = ff_sws_lut3d_alloc();
517 if (!lut)
518 return AVERROR(ENOMEM);
519
520 fmt_in = ff_sws_lut3d_pick_pixfmt(src, 0);
521 fmt_out = ff_sws_lut3d_pick_pixfmt(dst, 1);
522 if (fmt_in != src.format) {
523 SwsFormat tmp = src;
524 tmp.format = fmt_in;
525 ret = add_legacy_sws_pass(graph, src, tmp, input, &input);
526 if (ret < 0)
527 return ret;
528 }
529
530 ret = ff_sws_lut3d_generate(lut, fmt_in, fmt_out, &map);
531 if (ret < 0) {
532 ff_sws_lut3d_free(&lut);
533 return ret;
534 }
535
536 pass = ff_sws_graph_add_pass(graph, fmt_out, src.width, src.height,
537 input, 1, lut, run_lut3d);
538 if (!pass) {
539 ff_sws_lut3d_free(&lut);
540 return AVERROR(ENOMEM);
541 }
542 pass->setup = setup_lut3d;
543 pass->free = free_lut3d;
544
545 *output = pass;
546 return 0;
547 }
548
549 /***************************************
550 * Main filter graph construction code *
551 ***************************************/
552
553 6166 static int init_passes(SwsGraph *graph)
554 {
555 6166 SwsFormat src = graph->src;
556 6166 SwsFormat dst = graph->dst;
557 6166 SwsPass *pass = NULL; /* read from main input image */
558 int ret;
559
560 6166 ret = adapt_colors(graph, src, dst, pass, &pass);
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret < 0)
562 return ret;
563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 src.format = pass ? pass->format : src.format;
564 6166 src.color = dst.color;
565
566
1/2
✓ Branch 1 taken 6166 times.
✗ Branch 2 not taken.
6166 if (!ff_fmt_equal(&src, &dst)) {
567 6166 ret = add_legacy_sws_pass(graph, src, dst, pass, &pass);
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret < 0)
569 return ret;
570 }
571
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (!pass) {
573 /* No passes were added, so no operations were necessary */
574 graph->noop = 1;
575
576 /* Add threaded memcpy pass */
577 pass = ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
578 pass, 1, NULL, run_copy);
579 if (!pass)
580 return AVERROR(ENOMEM);
581 }
582
583 6166 return 0;
584 }
585
586 626503 static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
587 int nb_threads)
588 {
589 626503 SwsGraph *graph = priv;
590 626503 const SwsPass *pass = graph->exec.pass;
591
2/2
✓ Branch 0 taken 5258 times.
✓ Branch 1 taken 621245 times.
626503 const SwsImg *input = pass->input ? &pass->input->output : &graph->exec.input;
592
2/2
✓ Branch 0 taken 5258 times.
✓ Branch 1 taken 621245 times.
626503 const SwsImg *output = pass->output.fmt != AV_PIX_FMT_NONE ? &pass->output : &graph->exec.output;
593 626503 const int slice_y = jobnr * pass->slice_h;
594 626503 const int slice_h = FFMIN(pass->slice_h, pass->height - slice_y);
595
596 626503 pass->run(output, input, slice_y, slice_h, pass);
597 626503 }
598
599 6166 int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src,
600 int field, SwsGraph **out_graph)
601 {
602 int ret;
603 6166 SwsGraph *graph = av_mallocz(sizeof(*graph));
604
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (!graph)
605 return AVERROR(ENOMEM);
606
607 6166 graph->ctx = ctx;
608 6166 graph->src = *src;
609 6166 graph->dst = *dst;
610 6166 graph->field = field;
611 6166 graph->opts_copy = *ctx;
612
613 6166 graph->exec.input.fmt = src->format;
614 6166 graph->exec.output.fmt = dst->format;
615
616 6166 ret = avpriv_slicethread_create(&graph->slicethread, (void *) graph,
617 sws_graph_worker, NULL, ctx->threads);
618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret == AVERROR(ENOSYS))
619 graph->num_threads = 1;
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 else if (ret < 0)
621 goto error;
622 else
623 6166 graph->num_threads = ret;
624
625 6166 ret = init_passes(graph);
626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6166 times.
6166 if (ret < 0)
627 goto error;
628
629 6166 *out_graph = graph;
630 6166 return 0;
631
632 error:
633 ff_sws_graph_free(&graph);
634 return ret;
635 }
636
637 191267 void ff_sws_graph_free(SwsGraph **pgraph)
638 {
639 191267 SwsGraph *graph = *pgraph;
640
2/2
✓ Branch 0 taken 185101 times.
✓ Branch 1 taken 6166 times.
191267 if (!graph)
641 185101 return;
642
643 6166 avpriv_slicethread_free(&graph->slicethread);
644
645
2/2
✓ Branch 0 taken 6225 times.
✓ Branch 1 taken 6166 times.
12391 for (int i = 0; i < graph->num_passes; i++) {
646 6225 SwsPass *pass = graph->passes[i];
647
2/2
✓ Branch 0 taken 6167 times.
✓ Branch 1 taken 58 times.
6225 if (pass->free)
648 6167 pass->free(pass->priv);
649
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 6166 times.
6225 if (pass->output.fmt != AV_PIX_FMT_NONE)
650 59 av_free(pass->output.data[0]);
651 6225 av_free(pass);
652 }
653 6166 av_free(graph->passes);
654
655 6166 av_free(graph);
656 6166 *pgraph = NULL;
657 }
658
659 /* Tests only options relevant to SwsGraph */
660 93633 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
661 {
662 187266 return c1->flags == c2->flags &&
663
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->threads == c2->threads &&
664
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->dither == c2->dither &&
665
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->alpha_blend == c2->alpha_blend &&
666
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->gamma_flag == c2->gamma_flag &&
667
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->src_h_chr_pos == c2->src_h_chr_pos &&
668
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->src_v_chr_pos == c2->src_v_chr_pos &&
669
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
670
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
671
2/4
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93633 times.
✗ Branch 3 not taken.
280899 c1->intent == c2->intent &&
672
1/2
✓ Branch 0 taken 93633 times.
✗ Branch 1 not taken.
93633 !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
673
674 }
675
676 99799 int ff_sws_graph_reinit(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src,
677 int field, SwsGraph **out_graph)
678 {
679 99799 SwsGraph *graph = *out_graph;
680
4/6
✓ Branch 0 taken 93633 times.
✓ Branch 1 taken 6166 times.
✓ Branch 3 taken 93633 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 93633 times.
✗ Branch 6 not taken.
193432 if (graph && ff_fmt_equal(&graph->src, src) &&
681
1/2
✓ Branch 1 taken 93633 times.
✗ Branch 2 not taken.
187266 ff_fmt_equal(&graph->dst, dst) &&
682 93633 opts_equal(ctx, &graph->opts_copy))
683 {
684 93633 ff_sws_graph_update_metadata(graph, &src->color);
685 93633 return 0;
686 }
687
688 6166 ff_sws_graph_free(out_graph);
689 6166 return ff_sws_graph_create(ctx, dst, src, field, out_graph);
690 }
691
692 93633 void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
693 {
694
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93633 times.
93633 if (!color)
695 return;
696
697 93633 ff_color_update_dynamic(&graph->src.color, color);
698 }
699
700 99799 void ff_sws_graph_run(SwsGraph *graph, uint8_t *const out_data[4],
701 const int out_linesize[4],
702 const uint8_t *const in_data[4],
703 const int in_linesize[4])
704 {
705 99799 SwsImg *out = &graph->exec.output;
706 99799 SwsImg *in = &graph->exec.input;
707 99799 memcpy(out->data, out_data, sizeof(out->data));
708 99799 memcpy(out->linesize, out_linesize, sizeof(out->linesize));
709 99799 memcpy(in->data, in_data, sizeof(in->data));
710 99799 memcpy(in->linesize, in_linesize, sizeof(in->linesize));
711
712
2/2
✓ Branch 0 taken 100425 times.
✓ Branch 1 taken 99799 times.
200224 for (int i = 0; i < graph->num_passes; i++) {
713 100425 const SwsPass *pass = graph->passes[i];
714 100425 graph->exec.pass = pass;
715
2/2
✓ Branch 0 taken 99800 times.
✓ Branch 1 taken 625 times.
100425 if (pass->setup)
716 99800 pass->setup(out, in, pass);
717 100425 avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
718 }
719 99799 }
720