FFmpeg
graph.c
Go to the documentation of this file.
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 static int pass_alloc_output(SwsPass *pass)
39 {
40  if (!pass || pass->output.fmt != AV_PIX_FMT_NONE)
41  return 0;
42  pass->output.fmt = pass->format;
43  return av_image_alloc(pass->output.data, pass->output.linesize, pass->width,
44  pass->num_slices * pass->slice_h, pass->format, 64);
45 }
46 
48  int width, int height, SwsPass *input,
49  int align, void *priv, sws_filter_run_t run)
50 {
51  int ret;
52  SwsPass *pass = av_mallocz(sizeof(*pass));
53  if (!pass)
54  return NULL;
55 
56  pass->graph = graph;
57  pass->run = run;
58  pass->priv = priv;
59  pass->format = fmt;
60  pass->width = width;
61  pass->height = height;
62  pass->input = input;
63  pass->output.fmt = AV_PIX_FMT_NONE;
64 
66  if (ret < 0) {
67  av_free(pass);
68  return NULL;
69  }
70 
71  if (!align) {
72  pass->slice_h = pass->height;
73  pass->num_slices = 1;
74  } else {
75  pass->slice_h = (pass->height + graph->num_threads - 1) / graph->num_threads;
76  pass->slice_h = FFALIGN(pass->slice_h, align);
77  pass->num_slices = (pass->height + pass->slice_h - 1) / pass->slice_h;
78  }
79 
80  ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
81  if (ret < 0)
82  av_freep(&pass);
83  return pass;
84 }
85 
86 /* Wrapper around ff_sws_graph_add_pass() that chains a pass "in-place" */
87 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  SwsPass *new = ff_sws_graph_add_pass(graph, fmt, w, h, *pass, align, priv, run);
91  if (!new)
92  return AVERROR(ENOMEM);
93  *pass = new;
94  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 static void run_rgb0(const SwsImg *out, const SwsImg *in, int y, int h,
121  const SwsPass *pass)
122 {
123  SwsInternal *c = pass->priv;
124  const int x0 = c->src0Alpha - 1;
125  const int w4 = 4 * pass->width;
126  const int src_stride = in->linesize[0];
127  const int dst_stride = out->linesize[0];
128  const uint8_t *src = in->data[0] + y * src_stride;
129  uint8_t *dst = out->data[0] + y * dst_stride;
130 
131  for (int y = 0; y < h; y++) {
132  memcpy(dst, src, w4 * sizeof(*dst));
133  for (int x = x0; x < w4; x += 4)
134  dst[x] = 0xFF;
135 
136  src += src_stride;
137  dst += dst_stride;
138  }
139 }
140 
141 static void run_xyz2rgb(const SwsImg *out, const SwsImg *in, int y, int h,
142  const SwsPass *pass)
143 {
144  ff_xyz12Torgb48(pass->priv, out->data[0] + y * out->linesize[0], out->linesize[0],
145  in->data[0] + y * in->linesize[0], in->linesize[0],
146  pass->width, h);
147 }
148 
149 static void run_rgb2xyz(const SwsImg *out, const SwsImg *in, int y, int h,
150  const SwsPass *pass)
151 {
152  ff_rgb48Toxyz12(pass->priv, out->data[0] + y * out->linesize[0], out->linesize[0],
153  in->data[0] + y * in->linesize[0], in->linesize[0],
154  pass->width, h);
155 }
156 
157 /***********************************************************************
158  * Internal ff_swscale() wrapper. This reuses the legacy scaling API. *
159  * This is considered fully deprecated, and will be replaced by a full *
160  * reimplementation ASAP. *
161  ***********************************************************************/
162 
163 static void free_legacy_swscale(void *priv)
164 {
165  SwsContext *sws = priv;
167 }
168 
169 static void setup_legacy_swscale(const SwsImg *out, const SwsImg *in,
170  const SwsPass *pass)
171 {
172  SwsContext *sws = pass->priv;
174  if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) {
175  for (int i = 0; i < 4; i++)
176  memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2));
177  }
178 
179  if (usePal(sws->src_format))
180  ff_update_palette(c, (const uint32_t *) in->data[1]);
181 }
182 
183 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
184 {
185  SwsContext *sws = pass->priv;
186  SwsInternal *parent = sws_internal(sws);
187  if (pass->num_slices == 1)
188  return sws;
189 
190  av_assert1(parent->nb_slice_ctx == pass->num_slices);
191  sws = parent->slice_ctx[y / pass->slice_h];
192 
193  if (usePal(sws->src_format)) {
194  SwsInternal *sub = sws_internal(sws);
195  memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv));
196  memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb));
197  }
198 
199  return sws;
200 }
201 
202 static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in_base,
203  int y, int h, const SwsPass *pass)
204 {
205  SwsContext *sws = slice_ctx(pass, y);
207  const SwsImg in = ff_sws_img_shift(in_base, y);
208 
209  c->convert_unscaled(c, (const uint8_t *const *) in.data, in.linesize, y, h,
210  out->data, out->linesize);
211 }
212 
213 static void run_legacy_swscale(const SwsImg *out_base, const SwsImg *in,
214  int y, int h, const SwsPass *pass)
215 {
216  SwsContext *sws = slice_ctx(pass, y);
218  const SwsImg out = ff_sws_img_shift(out_base, y);
219 
220  ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
221  sws->src_h, out.data, out.linesize, y, h);
222 }
223 
224 static void get_chroma_pos(SwsGraph *graph, int *h_chr_pos, int *v_chr_pos,
225  const SwsFormat *fmt)
226 {
227  enum AVChromaLocation chroma_loc = fmt->loc;
228  const int sub_x = fmt->desc->log2_chroma_w;
229  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  if (chroma_loc == AVCHROMA_LOC_UNSPECIFIED) {
234  chroma_loc = AVCHROMA_LOC_CENTER;
235  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  av_chroma_location_enum_to_pos(&x_pos, &y_pos, chroma_loc);
242  x_pos *= (1 << sub_x) - 1;
243  y_pos *= (1 << sub_y) - 1;
244 
245  /* Fix vertical chroma position for interlaced frames */
246  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  *h_chr_pos = sub_x ? x_pos : -513;
264  *v_chr_pos = sub_y ? y_pos : -513;
265 }
266 
267 static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
268 {
269  if (override == -513 || override == *chr_pos)
270  return;
271 
272  if (!*warned) {
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 /* Takes over ownership of `sws` */
285 {
287  const int src_w = sws->src_w, src_h = sws->src_h;
288  const int dst_w = sws->dst_w, dst_h = sws->dst_h;
289  const int unscaled = src_w == dst_w && src_h == dst_h;
290  int align = c->dst_slice_align;
291  SwsPass *pass = NULL;
292  int ret;
293 
294  if (c->cascaded_context[0]) {
295  const int num_cascaded = c->cascaded_context[2] ? 3 : 2;
296  for (int i = 0; i < num_cascaded; i++) {
297  const int is_last = i + 1 == num_cascaded;
298 
299  /* Steal cascaded context, so we can manage its lifetime independently */
300  SwsContext *sub = c->cascaded_context[i];
301  c->cascaded_context[i] = NULL;
302 
303  ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input);
304  if (ret < 0)
305  break;
306  }
307 
309  return ret;
310  }
311 
312  if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled)
313  align = 0; /* disable slice threading */
314 
315  if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) {
316  ret = pass_append(graph, AV_PIX_FMT_RGBA, src_w, src_h, &input, 1, c, run_rgb0);
317  if (ret < 0) {
319  return ret;
320  }
321  }
322 
323  if (c->srcXYZ && !(c->dstXYZ && unscaled)) {
324  ret = pass_append(graph, AV_PIX_FMT_RGB48, src_w, src_h, &input, 1, c, run_xyz2rgb);
325  if (ret < 0) {
327  return ret;
328  }
329  }
330 
331  pass = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, align, sws,
332  c->convert_unscaled ? run_legacy_unscaled : run_legacy_swscale);
333  if (!pass) {
335  return AVERROR(ENOMEM);
336  }
337  pass->setup = setup_legacy_swscale;
338  pass->free = free_legacy_swscale;
339 
340  /**
341  * For slice threading, we need to create sub contexts, similar to how
342  * swscale normally handles it internally. The most important difference
343  * is that we handle cascaded contexts before threaded contexts; whereas
344  * context_init_threaded() does it the other way around.
345  */
346 
347  if (pass->num_slices > 1) {
348  c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx));
349  if (!c->slice_ctx)
350  return AVERROR(ENOMEM);
351 
352  for (int i = 0; i < pass->num_slices; i++) {
353  SwsContext *slice;
354  SwsInternal *c2;
355  slice = c->slice_ctx[i] = sws_alloc_context();
356  if (!slice)
357  return AVERROR(ENOMEM);
358  c->nb_slice_ctx++;
359 
360  c2 = sws_internal(slice);
361  c2->parent = sws;
362 
363  ret = av_opt_copy(slice, sws);
364  if (ret < 0)
365  return ret;
366 
368  if (ret < 0)
369  return ret;
370 
371  sws_setColorspaceDetails(slice, c->srcColorspaceTable,
372  slice->src_range, c->dstColorspaceTable,
373  slice->dst_range, c->brightness, c->contrast,
374  c->saturation);
375 
376  for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) {
377  c2->srcColorspaceTable[i] = c->srcColorspaceTable[i];
378  c2->dstColorspaceTable[i] = c->dstColorspaceTable[i];
379  }
380  }
381  }
382 
383  if (c->dstXYZ && !(c->srcXYZ && unscaled)) {
384  ret = pass_append(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, &pass, 1, c, run_rgb2xyz);
385  if (ret < 0)
386  return ret;
387  }
388 
389  *output = pass;
390  return 0;
391 }
392 
395 {
396  int ret, warned = 0;
397  SwsContext *const ctx = graph->ctx;
399  if (!sws)
400  return AVERROR(ENOMEM);
401 
402  sws->flags = ctx->flags;
403  sws->dither = ctx->dither;
404  sws->alpha_blend = ctx->alpha_blend;
405  sws->gamma_flag = ctx->gamma_flag;
406 
407  sws->src_w = src.width;
408  sws->src_h = src.height;
409  sws->src_format = src.format;
410  sws->src_range = src.range == AVCOL_RANGE_JPEG;
411 
412  sws->dst_w = dst.width;
413  sws->dst_h = dst.height;
414  sws->dst_format = dst.format;
415  sws->dst_range = dst.range == AVCOL_RANGE_JPEG;
418 
419  graph->incomplete |= src.range == AVCOL_RANGE_UNSPECIFIED;
420  graph->incomplete |= dst.range == AVCOL_RANGE_UNSPECIFIED;
421 
422  /* Allow overriding chroma position with the legacy API */
423  legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned);
424  legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned);
425  legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned);
426  legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned);
427 
428  sws->scaler_params[0] = ctx->scaler_params[0];
429  sws->scaler_params[1] = ctx->scaler_params[1];
430 
432  if (ret < 0) {
434  return ret;
435  }
436 
437  /* Set correct color matrices */
438  {
439  int in_full, out_full, brightness, contrast, saturation;
440  const int *inv_table, *table;
441  sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full,
442  (int **)&table, &out_full,
443  &brightness, &contrast, &saturation);
444 
445  inv_table = sws_getCoefficients(src.csp);
447 
448  graph->incomplete |= src.csp != dst.csp &&
449  (src.csp == AVCOL_SPC_UNSPECIFIED ||
450  dst.csp == AVCOL_SPC_UNSPECIFIED);
451 
452  sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full,
453  brightness, contrast, saturation);
454  }
455 
456  return init_legacy_subpass(graph, sws, input, output);
457 }
458 
459 /**************************
460  * Gamut and tone mapping *
461  **************************/
462 
463 static void free_lut3d(void *priv)
464 {
465  SwsLut3D *lut = priv;
466  ff_sws_lut3d_free(&lut);
467 }
468 
469 static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
470 {
471  SwsLut3D *lut = pass->priv;
472 
473  /* Update dynamic frame metadata from the original source frame */
474  ff_sws_lut3d_update(lut, &pass->graph->src.color);
475 }
476 
477 static void run_lut3d(const SwsImg *out_base, const SwsImg *in_base,
478  int y, int h, const SwsPass *pass)
479 {
480  SwsLut3D *lut = pass->priv;
481  const SwsImg in = ff_sws_img_shift(in_base, y);
482  const SwsImg out = ff_sws_img_shift(out_base, y);
483 
484  ff_sws_lut3d_apply(lut, in.data[0], in.linesize[0], out.data[0],
485  out.linesize[0], pass->width, h);
486 }
487 
490 {
491  enum AVPixelFormat fmt_in, fmt_out;
492  SwsColorMap map = {0};
493  SwsLut3D *lut;
494  SwsPass *pass;
495  int ret;
496 
497  /**
498  * Grayspace does not really have primaries, so just force the use of
499  * the equivalent other primary set to avoid a conversion. Technically,
500  * this does affect the weights used for the Grayscale conversion, but
501  * in practise, that should give the expected results more often than not.
502  */
503  if (isGray(dst.format)) {
504  dst.color = src.color;
505  } else if (isGray(src.format)) {
506  src.color = dst.color;
507  }
508 
509  /* Fully infer color spaces before color mapping logic */
510  graph->incomplete |= ff_infer_colors(&src.color, &dst.color);
511 
512  map.intent = graph->ctx->intent;
513  map.src = src.color;
514  map.dst = dst.color;
515 
517  return 0;
518 
519  lut = ff_sws_lut3d_alloc();
520  if (!lut)
521  return AVERROR(ENOMEM);
522 
523  fmt_in = ff_sws_lut3d_pick_pixfmt(src, 0);
524  fmt_out = ff_sws_lut3d_pick_pixfmt(dst, 1);
525  if (fmt_in != src.format) {
526  SwsFormat tmp = src;
527  tmp.format = fmt_in;
528  ret = add_legacy_sws_pass(graph, src, tmp, input, &input);
529  if (ret < 0)
530  return ret;
531  }
532 
533  ret = ff_sws_lut3d_generate(lut, fmt_in, fmt_out, &map);
534  if (ret < 0) {
535  ff_sws_lut3d_free(&lut);
536  return ret;
537  }
538 
539  pass = ff_sws_graph_add_pass(graph, fmt_out, src.width, src.height,
540  input, 1, lut, run_lut3d);
541  if (!pass) {
542  ff_sws_lut3d_free(&lut);
543  return AVERROR(ENOMEM);
544  }
545  pass->setup = setup_lut3d;
546  pass->free = free_lut3d;
547 
548  *output = pass;
549  return 0;
550 }
551 
552 /***************************************
553  * Main filter graph construction code *
554  ***************************************/
555 
556 static int init_passes(SwsGraph *graph)
557 {
558  SwsFormat src = graph->src;
559  SwsFormat dst = graph->dst;
560  SwsPass *pass = NULL; /* read from main input image */
561  int ret;
562 
563  ret = adapt_colors(graph, src, dst, pass, &pass);
564  if (ret < 0)
565  return ret;
566  src.format = pass ? pass->format : src.format;
567  src.color = dst.color;
568 
569  if (!ff_fmt_equal(&src, &dst)) {
570  ret = add_legacy_sws_pass(graph, src, dst, pass, &pass);
571  if (ret < 0)
572  return ret;
573  }
574 
575  if (!pass) {
576  /* No passes were added, so no operations were necessary */
577  graph->noop = 1;
578 
579  /* Add threaded memcpy pass */
580  pass = ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
581  pass, 1, NULL, run_copy);
582  if (!pass)
583  return AVERROR(ENOMEM);
584  }
585 
586  return 0;
587 }
588 
589 static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
590  int nb_threads)
591 {
592  SwsGraph *graph = priv;
593  const SwsPass *pass = graph->exec.pass;
594  const SwsImg *input = pass->input ? &pass->input->output : &graph->exec.input;
595  const SwsImg *output = pass->output.fmt != AV_PIX_FMT_NONE ? &pass->output : &graph->exec.output;
596  const int slice_y = jobnr * pass->slice_h;
597  const int slice_h = FFMIN(pass->slice_h, pass->height - slice_y);
598 
599  pass->run(output, input, slice_y, slice_h, pass);
600 }
601 
603  int field, SwsGraph **out_graph)
604 {
605  int ret;
606  SwsGraph *graph = av_mallocz(sizeof(*graph));
607  if (!graph)
608  return AVERROR(ENOMEM);
609 
610  graph->ctx = ctx;
611  graph->src = *src;
612  graph->dst = *dst;
613  graph->field = field;
614  graph->opts_copy = *ctx;
615 
616  graph->exec.input.fmt = src->format;
617  graph->exec.output.fmt = dst->format;
618 
619  ret = avpriv_slicethread_create(&graph->slicethread, (void *) graph,
620  sws_graph_worker, NULL, ctx->threads);
621  if (ret == AVERROR(ENOSYS))
622  graph->num_threads = 1;
623  else if (ret < 0)
624  goto error;
625  else
626  graph->num_threads = ret;
627 
628  ret = init_passes(graph);
629  if (ret < 0)
630  goto error;
631 
632  *out_graph = graph;
633  return 0;
634 
635 error:
636  ff_sws_graph_free(&graph);
637  return ret;
638 }
639 
641 {
642  SwsGraph *graph = *pgraph;
643  if (!graph)
644  return;
645 
647 
648  for (int i = 0; i < graph->num_passes; i++) {
649  SwsPass *pass = graph->passes[i];
650  if (pass->free)
651  pass->free(pass->priv);
652  if (pass->output.fmt != AV_PIX_FMT_NONE)
653  av_free(pass->output.data[0]);
654  av_free(pass);
655  }
656  av_free(graph->passes);
657 
658  av_free(graph);
659  *pgraph = NULL;
660 }
661 
662 /* Tests only options relevant to SwsGraph */
663 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
664 {
665  return c1->flags == c2->flags &&
666  c1->threads == c2->threads &&
667  c1->dither == c2->dither &&
668  c1->alpha_blend == c2->alpha_blend &&
669  c1->gamma_flag == c2->gamma_flag &&
670  c1->src_h_chr_pos == c2->src_h_chr_pos &&
671  c1->src_v_chr_pos == c2->src_v_chr_pos &&
672  c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
673  c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
674  c1->intent == c2->intent &&
675  !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
676 
677 }
678 
680  int field, SwsGraph **out_graph)
681 {
682  SwsGraph *graph = *out_graph;
683  if (graph && ff_fmt_equal(&graph->src, src) &&
684  ff_fmt_equal(&graph->dst, dst) &&
685  opts_equal(ctx, &graph->opts_copy))
686  {
687  ff_sws_graph_update_metadata(graph, &src->color);
688  return 0;
689  }
690 
691  ff_sws_graph_free(out_graph);
692  return ff_sws_graph_create(ctx, dst, src, field, out_graph);
693 }
694 
696 {
697  if (!color)
698  return;
699 
701 }
702 
703 void ff_sws_graph_run(SwsGraph *graph, uint8_t *const out_data[4],
704  const int out_linesize[4],
705  const uint8_t *const in_data[4],
706  const int in_linesize[4])
707 {
708  SwsImg *out = &graph->exec.output;
709  SwsImg *in = &graph->exec.input;
710  memcpy(out->data, out_data, sizeof(out->data));
711  memcpy(out->linesize, out_linesize, sizeof(out->linesize));
712  memcpy(in->data, in_data, sizeof(in->data));
713  memcpy(in->linesize, in_linesize, sizeof(in->linesize));
714 
715  for (int i = 0; i < graph->num_passes; i++) {
716  const SwsPass *pass = graph->passes[i];
717  graph->exec.pass = pass;
718  if (pass->setup)
719  pass->setup(out, in, pass);
721  }
722 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:32
sws_setColorspaceDetails
int sws_setColorspaceDetails(SwsContext *c, const int inv_table[4], int srcRange, const int table[4], int dstRange, int brightness, int contrast, int saturation)
Definition: utils.c:835
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
SwsGraph::slicethread
AVSliceThread * slicethread
Definition: graph.h:112
SwsGraph::ctx
SwsContext * ctx
Definition: graph.h:111
SwsPass
Represents a single filter pass in the scaling graph.
Definition: graph.h:68
SwsGraph::pass
const SwsPass * pass
Definition: graph.h:135
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
SwsGraph::passes
SwsPass ** passes
Sorted sequence of filter passes to apply.
Definition: graph.h:118
adapt_colors
static int adapt_colors(SwsGraph *graph, SwsFormat src, SwsFormat dst, SwsPass *input, SwsPass **output)
Definition: graph.c:488
out
FILE * out
Definition: movenc.c:55
color
Definition: vf_paletteuse.c:513
init_passes
static int init_passes(SwsGraph *graph)
Definition: graph.c:556
ff_rgb48Toxyz12
void ff_rgb48Toxyz12(const SwsInternal *c, uint8_t *dst, int dst_stride, const uint8_t *src, int src_stride, int w, int h)
Definition: swscale.c:798
SwsPass::output
SwsImg output
Filter output buffer.
Definition: graph.h:91
SwsFormat::interlaced
int interlaced
Definition: format.h:79
SwsContext::src_w
int src_w
Deprecated frame property overrides, for the legacy API only.
Definition: swscale.h:228
ff_sws_graph_reinit
int ff_sws_graph_reinit(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src, int field, SwsGraph **out_graph)
Wrapper around ff_sws_graph_create() that reuses the existing graph if the format is compatible.
Definition: graph.c:679
SwsPass::format
enum AVPixelFormat format
Definition: graph.h:77
saturation
static IPT saturation(const CmsCtx *ctx, IPT ipt)
Definition: cms.c:559
output
filter_frame For filters that do not use the this method is called when a frame is pushed to the filter s input It can be called at any time except in a reentrant way If the input frame is enough to produce output
Definition: filter_design.txt:226
sws_filter_run_t
void(* sws_filter_run_t)(const SwsImg *out, const SwsImg *in, int y, int h, const SwsPass *pass)
Output h lines of filtered data.
Definition: graph.h:60
SwsGraph::src
SwsFormat src
Currently active format and processing parameters.
Definition: graph.h:130
avpriv_slicethread_execute
void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
Execute slice threading.
Definition: slicethread.c:271
pixdesc.h
w
uint8_t w
Definition: llviddspenc.c:38
AVCOL_RANGE_JPEG
@ AVCOL_RANGE_JPEG
Full range content.
Definition: pixfmt.h:767
isGray
static av_always_inline int isGray(enum AVPixelFormat pix_fmt)
Definition: swscale_internal.h:787
SWS_BITEXACT
@ SWS_BITEXACT
Definition: swscale.h:156
table
static const uint16_t table[]
Definition: prosumer.c:203
SwsContext::flags
unsigned flags
Bitmask of SWS_*.
Definition: swscale.h:195
ff_sws_lut3d_pick_pixfmt
enum AVPixelFormat ff_sws_lut3d_pick_pixfmt(SwsFormat fmt, int output)
Pick the best compatible pixfmt for a given SwsFormat.
Definition: lut3d.c:52
SwsPass::free
void(* free)(void *priv)
Optional private state and associated free() function.
Definition: graph.h:103
c1
static const uint64_t c1
Definition: murmur3.c:52
SwsImg
Represents a view into a single field of frame data.
Definition: graph.h:33
format.h
ff_sws_init_single_context
int ff_sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter, SwsFilter *dstFilter)
Definition: utils.c:1107
SwsColorMap
Definition: cms.h:60
SwsPass::width
int width
Definition: graph.h:78
ff_color_update_dynamic
static void ff_color_update_dynamic(SwsColor *dst, const SwsColor *src)
Definition: format.h:70
init_legacy_subpass
static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws, SwsPass *input, SwsPass **output)
Definition: graph.c:283
avpriv_slicethread_create
int avpriv_slicethread_create(AVSliceThread **pctx, void *priv, void(*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), void(*main_func)(void *priv), int nb_threads)
Create slice threading context.
Definition: slicethread.c:262
macros.h
pass_append
static int pass_append(SwsGraph *graph, enum AVPixelFormat fmt, int w, int h, SwsPass **pass, int align, void *priv, sws_filter_run_t run)
Definition: graph.c:87
SwsContext::src_v_chr_pos
int src_v_chr_pos
Source vertical chroma position in luma grid / 256.
Definition: swscale.h:234
slice_ctx
static SwsContext * slice_ctx(const SwsPass *pass, int y)
Definition: graph.c:183
sws_init_context
av_warn_unused_result int sws_init_context(SwsContext *sws_context, SwsFilter *srcFilter, SwsFilter *dstFilter)
Initialize the swscaler context sws_context.
Definition: utils.c:1879
SwsGraph::opts_copy
SwsContext opts_copy
Cached copy of the public options that were used to construct this SwsGraph.
Definition: graph.h:125
ff_sws_graph_create
int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src, int field, SwsGraph **out_graph)
Allocate and initialize the filter graph.
Definition: graph.c:602
avassert.h
ff_sws_graph_run
void ff_sws_graph_run(SwsGraph *graph, uint8_t *const out_data[4], const int out_linesize[4], const uint8_t *const in_data[4], const int in_linesize[4])
Dispatch the filter graph on a single field.
Definition: graph.c:703
SwsInternal::pal_rgb
uint32_t pal_rgb[256]
Definition: swscale_internal.h:383
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
legacy_chr_pos
static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned)
Definition: graph.c:267
SwsContext::dither
SwsDither dither
Dither mode.
Definition: swscale.h:210
SwsPass::priv
void * priv
Definition: graph.h:104
SwsInternal::nb_slice_ctx
int nb_slice_ctx
Definition: swscale_internal.h:327
SwsInternal::slice_ctx
SwsContext ** slice_ctx
Definition: swscale_internal.h:325
av_chroma_location_enum_to_pos
int av_chroma_location_enum_to_pos(int *xpos, int *ypos, enum AVChromaLocation pos)
Converts AVChromaLocation to swscale x/y chroma position.
Definition: pixdesc.c:3859
ff_update_palette
void ff_update_palette(SwsInternal *c, const uint32_t *pal)
Definition: swscale.c:857
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1415
ff_sws_lut3d_alloc
SwsLut3D * ff_sws_lut3d_alloc(void)
Definition: lut3d.c:32
SwsPass::setup
void(* setup)(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
Called once from the main thread before running the filter.
Definition: graph.h:98
SwsContext::intent
int intent
Desired ICC intent for color space conversions.
Definition: swscale.h:242
SwsGraph::num_passes
int num_passes
Definition: graph.h:119
ctx
AVFormatContext * ctx
Definition: movenc.c:49
ff_sws_lut3d_update
void ff_sws_lut3d_update(SwsLut3D *lut3d, const SwsColor *new_src)
Update the tone mapping state.
Definition: lut3d.c:239
field
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this field
Definition: writing_filters.txt:78
AVPixFmtDescriptor::log2_chroma_w
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:80
ff_xyz12Torgb48
void ff_xyz12Torgb48(const SwsInternal *c, uint8_t *dst, int dst_stride, const uint8_t *src, int src_stride, int w, int h)
Definition: swscale.c:739
AV_PIX_FMT_RGBA
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:100
SwsGraph::field
int field
Definition: graph.h:131
ff_sws_graph_add_pass
SwsPass * ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt, int width, int height, SwsPass *input, int align, void *priv, sws_filter_run_t run)
Allocate and add a new pass to the filter graph.
Definition: graph.c:47
ff_sws_lut3d_free
void ff_sws_lut3d_free(SwsLut3D **plut3d)
Definition: lut3d.c:42
FIELD_BOTTOM
@ FIELD_BOTTOM
Definition: format.h:57
NULL
#define NULL
Definition: coverity.c:32
run
uint8_t run
Definition: svq3.c:207
tmp
static uint8_t tmp[20]
Definition: aes_ctr.c:47
SwsContext::gamma_flag
int gamma_flag
Use gamma correct scaling.
Definition: swscale.h:220
run_xyz2rgb
static void run_xyz2rgb(const SwsImg *out, const SwsImg *in, int y, int h, const SwsPass *pass)
Definition: graph.c:141
ff_infer_colors
bool ff_infer_colors(SwsColor *src, SwsColor *dst)
Definition: format.c:494
SwsGraph::input
SwsImg input
Definition: graph.h:136
ff_sws_lut3d_generate
int ff_sws_lut3d_generate(SwsLut3D *lut3d, enum AVPixelFormat fmt_in, enum AVPixelFormat fmt_out, const SwsColorMap *map)
Recalculate the (static) 3DLUT state with new settings.
Definition: lut3d.c:211
SwsContext::src_range
int src_range
Source is full range.
Definition: swscale.h:232
SwsPass::graph
const SwsGraph * graph
Definition: graph.h:69
AVCOL_RANGE_UNSPECIFIED
@ AVCOL_RANGE_UNSPECIFIED
Definition: pixfmt.h:733
SwsContext::dst_h_chr_pos
int dst_h_chr_pos
Destination horizontal chroma position.
Definition: swscale.h:237
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
run_legacy_swscale
static void run_legacy_swscale(const SwsImg *out_base, const SwsImg *in, int y, int h, const SwsPass *pass)
Definition: graph.c:213
error.h
av_opt_copy
int av_opt_copy(void *dst, const void *src)
Copy options from src object into dest object.
Definition: opt.c:2151
av_image_alloc
int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align)
Allocate an image with size w and h and pixel format pix_fmt, and fill pointers and linesizes accordi...
Definition: imgutils.c:218
ff_sws_graph_free
void ff_sws_graph_free(SwsGraph **pgraph)
Uninitialize any state associate with this filter graph and free it.
Definition: graph.c:640
SwsPass::height
int height
Definition: graph.h:78
SwsImg::linesize
int linesize[4]
Definition: graph.h:36
add_legacy_sws_pass
static int add_legacy_sws_pass(SwsGraph *graph, SwsFormat src, SwsFormat dst, SwsPass *input, SwsPass **output)
Definition: graph.c:393
lut3d.h
free_lut3d
static void free_lut3d(void *priv)
Definition: graph.c:463
height
#define height
Definition: dsp.h:89
sws_alloc_context
SwsContext * sws_alloc_context(void)
Allocate an empty SwsContext and set its fields to default values.
Definition: utils.c:1018
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
usePal
static av_always_inline int usePal(enum AVPixelFormat pix_fmt)
Definition: swscale_internal.h:918
setup_lut3d
static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
Definition: graph.c:469
ff_sws_lut3d_apply
void ff_sws_lut3d_apply(const SwsLut3D *lut3d, const uint8_t *in, int in_stride, uint8_t *out, int out_stride, int w, int h)
Applies a color transformation to a plane.
Definition: lut3d.c:250
AV_PIX_FMT_RGB48
#define AV_PIX_FMT_RGB48
Definition: pixfmt.h:525
SwsContext::alpha_blend
SwsAlphaBlend alpha_blend
Alpha blending mode.
Definition: swscale.h:215
SwsContext::src_h
int src_h
Width and height of the source frame.
Definition: swscale.h:228
SwsGraph::output
SwsImg output
Definition: graph.h:137
AVCHROMA_LOC_UNSPECIFIED
@ AVCHROMA_LOC_UNSPECIFIED
Definition: pixfmt.h:787
SwsFormat
Definition: format.h:77
sws_getColorspaceDetails
int sws_getColorspaceDetails(SwsContext *c, int **inv_table, int *srcRange, int **table, int *dstRange, int *brightness, int *contrast, int *saturation)
Definition: utils.c:993
align
static const uint8_t *BS_FUNC() align(BSCTX *bc)
Skip bits to a byte boundary.
Definition: bitstream_template.h:419
SwsFormat::loc
enum AVChromaLocation loc
Definition: format.h:83
SwsColor
Definition: format.h:60
SwsContext::dst_format
int dst_format
Destination pixel format.
Definition: swscale.h:231
input
and forward the test the status of outputs and forward it to the corresponding return FFERROR_NOT_READY If the filters stores internally one or a few frame for some input
Definition: filter_design.txt:172
slicethread.h
AVChromaLocation
AVChromaLocation
Location of chroma samples.
Definition: pixfmt.h:786
SwsGraph::exec
struct SwsGraph::@506 exec
Temporary execution state inside ff_sws_graph_run.
sws
static SwsContext * sws[3]
Definition: swscale.c:73
free_legacy_swscale
static void free_legacy_swscale(void *priv)
Definition: graph.c:163
SwsLut3D
Definition: lut3d.h:50
SwsGraph::dst
SwsFormat dst
Definition: graph.h:130
ff_fmt_vshift
static av_always_inline av_const int ff_fmt_vshift(enum AVPixelFormat fmt, int plane)
Definition: graph.h:39
SwsPass::slice_h
int slice_h
Definition: graph.h:79
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
SwsGraph::num_threads
int num_threads
Definition: graph.h:113
opts_equal
static int opts_equal(const SwsContext *c1, const SwsContext *c2)
Definition: graph.c:663
run_rgb0
static void run_rgb0(const SwsImg *out, const SwsImg *in, int y, int h, const SwsPass *pass)
Definition: graph.c:120
SwsFormat::desc
const AVPixFmtDescriptor * desc
Definition: format.h:84
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:57
run_rgb2xyz
static void run_rgb2xyz(const SwsImg *out, const SwsImg *in, int y, int h, const SwsPass *pass)
Definition: graph.c:149
swscale_internal.h
graph.h
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:256
SwsContext::dst_h
int dst_h
Width and height of the destination frame.
Definition: swscale.h:229
run_copy
static void run_copy(const SwsImg *out_base, const SwsImg *in_base, int y, int h, const SwsPass *pass)
Definition: graph.c:97
AVCOL_SPC_UNSPECIFIED
@ AVCOL_SPC_UNSPECIFIED
Definition: pixfmt.h:693
ff_sws_img_shift
static av_const SwsImg ff_sws_img_shift(const SwsImg *base, const int y)
Definition: graph.h:45
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
get_chroma_pos
static void get_chroma_pos(SwsGraph *graph, int *h_chr_pos, int *v_chr_pos, const SwsFormat *fmt)
Definition: graph.c:224
SWS_DITHER_ED
@ SWS_DITHER_ED
Definition: swscale.h:83
SwsInternal
Definition: swscale_internal.h:317
ret
ret
Definition: filter_design.txt:187
ff_fmt_equal
static int ff_fmt_equal(const SwsFormat *fmt1, const SwsFormat *fmt2)
Definition: format.h:115
SwsGraph::noop
bool noop
Definition: graph.h:115
av_dynarray_add_nofree
int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem)
Add an element to a dynamic array.
Definition: mem.c:315
c2
static const uint64_t c2
Definition: murmur3.c:53
SwsContext::scaler_params
double scaler_params[2]
Extra parameters for fine-tuning certain scalers.
Definition: swscale.h:200
AVCHROMA_LOC_CENTER
@ AVCHROMA_LOC_CENTER
MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0.
Definition: pixfmt.h:789
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
SwsFormat::color
SwsColor color
Definition: format.h:85
ff_sws_color_map_noop
bool ff_sws_color_map_noop(const SwsColorMap *map)
Returns true if the given color map is a semantic no-op - that is, the overall RGB end to end transfo...
Definition: cms.c:34
setup_legacy_swscale
static void setup_legacy_swscale(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
Definition: graph.c:169
ff_swscale
int ff_swscale(SwsInternal *c, const uint8_t *const src[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[], int dstSliceY, int dstSliceH)
Definition: swscale.c:259
cms.h
SwsInternal::pal_yuv
uint32_t pal_yuv[256]
Definition: swscale_internal.h:382
SwsGraph::incomplete
bool incomplete
Definition: graph.h:114
mem.h
sws_getCoefficients
const int * sws_getCoefficients(int colorspace)
Return a pointer to yuv<->rgb coefficients for the given colorspace suitable for sws_setColorspaceDet...
Definition: yuv2rgb.c:61
SwsContext::dst_w
int dst_w
Definition: swscale.h:229
SwsGraph
Filter graph, which represents a 'baked' pixel format conversion.
Definition: graph.h:110
SwsContext::src_format
int src_format
Source pixel format.
Definition: swscale.h:230
map
const VDPAUPixFmtMap * map
Definition: hwcontext_vdpau.c:71
SwsImg::fmt
enum AVPixelFormat fmt
Definition: graph.h:34
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
FFALIGN
#define FFALIGN(x, a)
Definition: macros.h:78
SwsContext::dst_range
int dst_range
Destination is full range.
Definition: swscale.h:233
pass_alloc_output
static int pass_alloc_output(SwsPass *pass)
Definition: graph.c:38
run_legacy_unscaled
static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in_base, int y, int h, const SwsPass *pass)
Definition: graph.c:202
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
SwsPass::run
sws_filter_run_t run
Filter main execution function.
Definition: graph.h:76
sws_free_context
void sws_free_context(SwsContext **ctx)
Free the context and everything associated with it, and write NULL to the provided pointer.
Definition: utils.c:2326
imgutils.h
avpriv_slicethread_free
void avpriv_slicethread_free(AVSliceThread **pctx)
Destroy slice threading context.
Definition: slicethread.c:276
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
SwsContext::src_h_chr_pos
int src_h_chr_pos
Source horizontal chroma position.
Definition: swscale.h:235
sws_internal
static SwsInternal * sws_internal(const SwsContext *sws)
Definition: swscale_internal.h:74
SwsPass::input
const SwsPass * input
Filter input.
Definition: graph.h:86
h
h
Definition: vp9dsp_template.c:2070
SwsPass::num_slices
int num_slices
Definition: graph.h:80
width
#define width
Definition: dsp.h:89
run_lut3d
static void run_lut3d(const SwsImg *out_base, const SwsImg *in_base, int y, int h, const SwsPass *pass)
Definition: graph.c:477
SwsContext::dst_v_chr_pos
int dst_v_chr_pos
Destination vertical chroma position.
Definition: swscale.h:236
SwsContext
Main external API structure.
Definition: swscale.h:182
sws_graph_worker
static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads)
Definition: graph.c:589
ff_sws_graph_update_metadata
void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
Update dynamic per-frame HDR metadata without requiring a full reinit.
Definition: graph.c:695
AVPixFmtDescriptor::log2_chroma_h
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition: pixdesc.h:89
src
#define src
Definition: vp8dsp.c:248
swscale.h
SwsImg::data
uint8_t * data[4]
Definition: graph.h:35
isALPHA
static av_always_inline int isALPHA(enum AVPixelFormat pix_fmt)
Definition: swscale_internal.h:878