FFmpeg
ops.c
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2025 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/avstring.h"
23 #include "libavutil/bswap.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/rational.h"
26 #include "libavutil/refstruct.h"
27 
28 #include "ops.h"
29 #include "ops_internal.h"
30 
31 extern const SwsOpBackend backend_c;
32 extern const SwsOpBackend backend_murder;
33 extern const SwsOpBackend backend_x86;
34 extern const SwsOpBackend backend_vulkan;
35 
36 const SwsOpBackend * const ff_sws_op_backends[] = {
38 #if ARCH_X86_64 && HAVE_X86ASM
39  &backend_x86,
40 #endif
41  &backend_c,
42 #if CONFIG_VULKAN
44 #endif
45  NULL
46 };
47 
48 #define RET(x) \
49  do { \
50  if ((ret = (x)) < 0) \
51  return ret; \
52  } while (0)
53 
55 {
56  switch (type) {
57  case SWS_PIXEL_U8: return "u8";
58  case SWS_PIXEL_U16: return "u16";
59  case SWS_PIXEL_U32: return "u32";
60  case SWS_PIXEL_F32: return "f32";
61  case SWS_PIXEL_NONE: return "none";
62  case SWS_PIXEL_TYPE_NB: break;
63  }
64 
65  av_unreachable("Invalid pixel type!");
66  return "ERR";
67 }
68 
70 {
71  switch (type) {
72  case SWS_PIXEL_U8: return sizeof(uint8_t);
73  case SWS_PIXEL_U16: return sizeof(uint16_t);
74  case SWS_PIXEL_U32: return sizeof(uint32_t);
75  case SWS_PIXEL_F32: return sizeof(float);
76  case SWS_PIXEL_NONE: break;
77  case SWS_PIXEL_TYPE_NB: break;
78  }
79 
80  av_unreachable("Invalid pixel type!");
81  return 0;
82 }
83 
85 {
86  switch (type) {
87  case SWS_PIXEL_U8:
88  case SWS_PIXEL_U16:
89  case SWS_PIXEL_U32:
90  return true;
91  case SWS_PIXEL_F32:
92  return false;
93  case SWS_PIXEL_NONE:
94  case SWS_PIXEL_TYPE_NB: break;
95  }
96 
97  av_unreachable("Invalid pixel type!");
98  return false;
99 }
100 
101 /* biased towards `a` */
103 {
104  return av_cmp_q(a, b) == 1 ? b : a;
105 }
106 
108 {
109  return av_cmp_q(a, b) == -1 ? b : a;
110 }
111 
113 {
114  uint64_t mask[4];
115  int shift[4];
116 
117  switch (op->op) {
118  case SWS_OP_READ:
119  case SWS_OP_WRITE:
120  return;
121  case SWS_OP_UNPACK: {
124  unsigned val = x[0].num;
125  for (int i = 0; i < 4; i++)
126  x[i] = Q((val >> shift[i]) & mask[i]);
127  return;
128  }
129  case SWS_OP_PACK: {
132  unsigned val = 0;
133  for (int i = 0; i < 4; i++)
134  val |= (x[i].num & mask[i]) << shift[i];
135  x[0] = Q(val);
136  return;
137  }
138  case SWS_OP_SWAP_BYTES:
140  switch (ff_sws_pixel_type_size(op->type)) {
141  case 2:
142  for (int i = 0; i < 4; i++)
143  x[i].num = av_bswap16(x[i].num);
144  break;
145  case 4:
146  for (int i = 0; i < 4; i++)
147  x[i].num = av_bswap32(x[i].num);
148  break;
149  }
150  return;
151  case SWS_OP_CLEAR:
152  for (int i = 0; i < 4; i++) {
153  if (op->c.q4[i].den)
154  x[i] = op->c.q4[i];
155  }
156  return;
157  case SWS_OP_LSHIFT: {
159  AVRational mult = Q(1 << op->c.u);
160  for (int i = 0; i < 4; i++)
161  x[i] = x[i].den ? av_mul_q(x[i], mult) : x[i];
162  return;
163  }
164  case SWS_OP_RSHIFT: {
166  for (int i = 0; i < 4; i++)
167  x[i] = x[i].den ? Q((x[i].num / x[i].den) >> op->c.u) : x[i];
168  return;
169  }
170  case SWS_OP_SWIZZLE: {
171  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
172  for (int i = 0; i < 4; i++)
173  x[i] = orig[op->swizzle.in[i]];
174  return;
175  }
176  case SWS_OP_CONVERT:
177  if (ff_sws_pixel_type_is_int(op->convert.to)) {
178  const AVRational scale = ff_sws_pixel_expand(op->type, op->convert.to);
179  for (int i = 0; i < 4; i++) {
180  x[i] = x[i].den ? Q(x[i].num / x[i].den) : x[i];
181  if (op->convert.expand)
182  x[i] = av_mul_q(x[i], scale);
183  }
184  }
185  return;
186  case SWS_OP_DITHER:
188  for (int i = 0; i < 4; i++) {
189  if (op->dither.y_offset[i] >= 0 && x[i].den)
190  x[i] = av_add_q(x[i], av_make_q(1, 2));
191  }
192  return;
193  case SWS_OP_MIN:
194  for (int i = 0; i < 4; i++)
195  x[i] = av_min_q(x[i], op->c.q4[i]);
196  return;
197  case SWS_OP_MAX:
198  for (int i = 0; i < 4; i++)
199  x[i] = av_max_q(x[i], op->c.q4[i]);
200  return;
201  case SWS_OP_LINEAR: {
203  const AVRational orig[4] = { x[0], x[1], x[2], x[3] };
204  for (int i = 0; i < 4; i++) {
205  AVRational sum = op->lin.m[i][4];
206  for (int j = 0; j < 4; j++)
207  sum = av_add_q(sum, av_mul_q(orig[j], op->lin.m[i][j]));
208  x[i] = sum;
209  }
210  return;
211  }
212  case SWS_OP_SCALE:
213  for (int i = 0; i < 4; i++)
214  x[i] = x[i].den ? av_mul_q(x[i], op->c.q) : x[i];
215  return;
216  }
217 
218  av_unreachable("Invalid operation type!");
219 }
220 
221 /* merge_comp_flags() forms a monoid with flags_identity as the null element */
222 static const unsigned flags_identity = SWS_COMP_ZERO | SWS_COMP_EXACT;
223 static unsigned merge_comp_flags(unsigned a, unsigned b)
224 {
225  const unsigned flags_or = SWS_COMP_GARBAGE;
226  const unsigned flags_and = SWS_COMP_ZERO | SWS_COMP_EXACT;
227  return ((a & b) & flags_and) | ((a | b) & flags_or);
228 }
229 
230 /* Linearly propagate flags per component */
231 static void propagate_flags(SwsOp *op, const SwsComps *prev)
232 {
233  for (int i = 0; i < 4; i++)
234  op->comps.flags[i] = prev->flags[i];
235 }
236 
237 /* Clear undefined values in dst with src */
239 {
240  for (int i = 0; i < 4; i++) {
241  if (dst[i].den == 0)
242  dst[i] = src[i];
243  }
244 }
245 
246 /* Infer + propagate known information about components */
248 {
249  SwsComps next = { .unused = {true, true, true, true} };
250  SwsComps prev = { .flags = {
252  }};
253 
254  /* Forwards pass, propagates knowledge about the incoming pixel values */
255  for (int n = 0; n < ops->num_ops; n++) {
256  SwsOp *op = &ops->ops[n];
257 
258  switch (op->op) {
259  case SWS_OP_READ:
260  case SWS_OP_LINEAR:
261  case SWS_OP_SWAP_BYTES:
262  case SWS_OP_UNPACK:
263  break; /* special cases, handled below */
264  default:
265  memcpy(op->comps.min, prev.min, sizeof(prev.min));
266  memcpy(op->comps.max, prev.max, sizeof(prev.max));
267  ff_sws_apply_op_q(op, op->comps.min);
268  ff_sws_apply_op_q(op, op->comps.max);
269  break;
270  }
271 
272  switch (op->op) {
273  case SWS_OP_READ:
274  /* Active components are taken from the user-provided values,
275  * other components are explicitly stripped */
276  for (int i = 0; i < op->rw.elems; i++) {
277  const int idx = op->rw.packed ? i : ops->order_src.in[i];
278  op->comps.flags[i] = ops->comps_src.flags[idx];
279  op->comps.min[i] = ops->comps_src.min[idx];
280  op->comps.max[i] = ops->comps_src.max[idx];
281  }
282  for (int i = op->rw.elems; i < 4; i++) {
283  op->comps.flags[i] = prev.flags[i];
284  op->comps.min[i] = prev.min[i];
285  op->comps.max[i] = prev.max[i];
286  }
287  break;
288  case SWS_OP_SWAP_BYTES:
289  for (int i = 0; i < 4; i++) {
290  op->comps.flags[i] = prev.flags[i] ^ SWS_COMP_SWAPPED;
291  op->comps.min[i] = prev.min[i];
292  op->comps.max[i] = prev.max[i];
293  }
294  break;
295  case SWS_OP_WRITE:
296  for (int i = 0; i < op->rw.elems; i++)
297  av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
298  /* fall through */
299  case SWS_OP_LSHIFT:
300  case SWS_OP_RSHIFT:
301  propagate_flags(op, &prev);
302  break;
303  case SWS_OP_MIN:
304  propagate_flags(op, &prev);
305  clear_undefined_values(op->comps.max, op->c.q4);
306  break;
307  case SWS_OP_MAX:
308  propagate_flags(op, &prev);
309  clear_undefined_values(op->comps.min, op->c.q4);
310  break;
311  case SWS_OP_DITHER:
312  /* Strip zero flag because of the nonzero dithering offset */
313  for (int i = 0; i < 4; i++)
314  op->comps.flags[i] = prev.flags[i] & ~SWS_COMP_ZERO;
315  break;
316  case SWS_OP_UNPACK:
317  for (int i = 0; i < 4; i++) {
318  const int pattern = op->pack.pattern[i];
319  if (pattern) {
320  av_assert1(pattern < 32);
321  op->comps.flags[i] = prev.flags[0];
322  op->comps.min[i] = Q(0);
323  op->comps.max[i] = Q((1ULL << pattern) - 1);
324  } else
325  op->comps.flags[i] = SWS_COMP_GARBAGE;
326  }
327  break;
328  case SWS_OP_PACK: {
329  unsigned flags = flags_identity;
330  for (int i = 0; i < 4; i++) {
331  if (op->pack.pattern[i])
332  flags = merge_comp_flags(flags, prev.flags[i]);
333  if (i > 0) /* clear remaining comps for sanity */
334  op->comps.flags[i] = SWS_COMP_GARBAGE;
335  }
336  op->comps.flags[0] = flags;
337  break;
338  }
339  case SWS_OP_CLEAR:
340  for (int i = 0; i < 4; i++) {
341  if (op->c.q4[i].den) {
342  op->comps.flags[i] = 0;
343  if (op->c.q4[i].num == 0)
344  op->comps.flags[i] |= SWS_COMP_ZERO;
345  if (op->c.q4[i].den == 1)
346  op->comps.flags[i] |= SWS_COMP_EXACT;
347  } else {
348  op->comps.flags[i] = prev.flags[i];
349  }
350  }
351  break;
352  case SWS_OP_SWIZZLE:
353  for (int i = 0; i < 4; i++)
354  op->comps.flags[i] = prev.flags[op->swizzle.in[i]];
355  break;
356  case SWS_OP_CONVERT:
357  for (int i = 0; i < 4; i++) {
358  op->comps.flags[i] = prev.flags[i];
359  if (ff_sws_pixel_type_is_int(op->convert.to))
360  op->comps.flags[i] |= SWS_COMP_EXACT;
361  }
362  break;
363  case SWS_OP_LINEAR:
364  for (int i = 0; i < 4; i++) {
365  unsigned flags = flags_identity;
366  AVRational min = Q(0), max = Q(0);
367  for (int j = 0; j < 4; j++) {
368  const AVRational k = op->lin.m[i][j];
369  AVRational mink = av_mul_q(prev.min[j], k);
370  AVRational maxk = av_mul_q(prev.max[j], k);
371  if (k.num) {
372  flags = merge_comp_flags(flags, prev.flags[j]);
373  if (k.den != 1) /* fractional coefficient */
374  flags &= ~SWS_COMP_EXACT;
375  if (k.num < 0)
376  FFSWAP(AVRational, mink, maxk);
377  min = av_add_q(min, mink);
378  max = av_add_q(max, maxk);
379  }
380  }
381  if (op->lin.m[i][4].num) { /* nonzero offset */
382  flags &= ~SWS_COMP_ZERO;
383  if (op->lin.m[i][4].den != 1) /* fractional offset */
384  flags &= ~SWS_COMP_EXACT;
385  min = av_add_q(min, op->lin.m[i][4]);
386  max = av_add_q(max, op->lin.m[i][4]);
387  }
388  op->comps.flags[i] = flags;
389  op->comps.min[i] = min;
390  op->comps.max[i] = max;
391  }
392  break;
393  case SWS_OP_SCALE:
394  for (int i = 0; i < 4; i++) {
395  op->comps.flags[i] = prev.flags[i];
396  if (op->c.q.den != 1) /* fractional scale */
397  op->comps.flags[i] &= ~SWS_COMP_EXACT;
398  if (op->c.q.num < 0)
399  FFSWAP(AVRational, op->comps.min[i], op->comps.max[i]);
400  }
401  break;
402 
403  case SWS_OP_INVALID:
404  case SWS_OP_TYPE_NB:
405  av_unreachable("Invalid operation type!");
406  }
407 
408  prev = op->comps;
409  }
410 
411  /* Backwards pass, solves for component dependencies */
412  for (int n = ops->num_ops - 1; n >= 0; n--) {
413  SwsOp *op = &ops->ops[n];
414 
415  switch (op->op) {
416  case SWS_OP_READ:
417  case SWS_OP_WRITE:
418  for (int i = 0; i < op->rw.elems; i++)
419  op->comps.unused[i] = op->op == SWS_OP_READ;
420  for (int i = op->rw.elems; i < 4; i++)
421  op->comps.unused[i] = next.unused[i];
422  break;
423  case SWS_OP_SWAP_BYTES:
424  case SWS_OP_LSHIFT:
425  case SWS_OP_RSHIFT:
426  case SWS_OP_CONVERT:
427  case SWS_OP_DITHER:
428  case SWS_OP_MIN:
429  case SWS_OP_MAX:
430  case SWS_OP_SCALE:
431  for (int i = 0; i < 4; i++)
432  op->comps.unused[i] = next.unused[i];
433  break;
434  case SWS_OP_UNPACK: {
435  bool unused = true;
436  for (int i = 0; i < 4; i++) {
437  if (op->pack.pattern[i])
438  unused &= next.unused[i];
439  op->comps.unused[i] = i > 0;
440  }
441  op->comps.unused[0] = unused;
442  break;
443  }
444  case SWS_OP_PACK:
445  for (int i = 0; i < 4; i++) {
446  if (op->pack.pattern[i])
447  op->comps.unused[i] = next.unused[0];
448  else
449  op->comps.unused[i] = true;
450  }
451  break;
452  case SWS_OP_CLEAR:
453  for (int i = 0; i < 4; i++) {
454  if (op->c.q4[i].den)
455  op->comps.unused[i] = true;
456  else
457  op->comps.unused[i] = next.unused[i];
458  }
459  break;
460  case SWS_OP_SWIZZLE: {
461  bool unused[4] = { true, true, true, true };
462  for (int i = 0; i < 4; i++)
463  unused[op->swizzle.in[i]] &= next.unused[i];
464  for (int i = 0; i < 4; i++)
465  op->comps.unused[i] = unused[i];
466  break;
467  }
468  case SWS_OP_LINEAR:
469  for (int j = 0; j < 4; j++) {
470  bool unused = true;
471  for (int i = 0; i < 4; i++) {
472  if (op->lin.m[i][j].num)
473  unused &= next.unused[i];
474  }
475  op->comps.unused[j] = unused;
476  }
477  break;
478  }
479 
480  next = op->comps;
481  }
482 }
483 
484 static void op_uninit(SwsOp *op)
485 {
486  switch (op->op) {
487  case SWS_OP_DITHER:
488  av_refstruct_unref(&op->dither.matrix);
489  break;
490  }
491 
492  *op = (SwsOp) {0};
493 }
494 
496 {
497  SwsOpList *ops = av_mallocz(sizeof(SwsOpList));
498  if (!ops)
499  return NULL;
500 
501  ops->order_src = ops->order_dst = SWS_SWIZZLE(0, 1, 2, 3);
502  ff_fmt_clear(&ops->src);
503  ff_fmt_clear(&ops->dst);
504  return ops;
505 }
506 
508 {
509  SwsOpList *ops = *p_ops;
510  if (!ops)
511  return;
512 
513  for (int i = 0; i < ops->num_ops; i++)
514  op_uninit(&ops->ops[i]);
515 
516  av_freep(&ops->ops);
517  av_free(ops);
518  *p_ops = NULL;
519 }
520 
522 {
523  SwsOpList *copy = av_malloc(sizeof(*copy));
524  if (!copy)
525  return NULL;
526 
527  int num = ops->num_ops;
528  if (num)
529  num = 1 << av_ceil_log2(num);
530 
531  *copy = *ops;
532  copy->ops = av_memdup(ops->ops, num * sizeof(ops->ops[0]));
533  if (!copy->ops) {
534  av_free(copy);
535  return NULL;
536  }
537 
538  for (int i = 0; i < ops->num_ops; i++) {
539  const SwsOp *op = &ops->ops[i];
540  switch (op->op) {
541  case SWS_OP_DITHER:
542  av_refstruct_ref(copy->ops[i].dither.matrix);
543  break;
544  }
545  }
546 
547  return copy;
548 }
549 
550 void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
551 {
552  const int end = ops->num_ops - count;
553  av_assert2(index >= 0 && count >= 0 && index + count <= ops->num_ops);
554  op_uninit(&ops->ops[index]);
555  for (int i = index; i < end; i++)
556  ops->ops[i] = ops->ops[i + count];
557  ops->num_ops = end;
558 }
559 
561 {
562  void *ret = av_dynarray2_add((void **) &ops->ops, &ops->num_ops, sizeof(*op), NULL);
563  if (!ret) {
564  op_uninit(op);
565  return AVERROR(ENOMEM);
566  }
567 
568  for (int i = ops->num_ops - 1; i > index; i--)
569  ops->ops[i] = ops->ops[i - 1];
570  ops->ops[index] = *op;
571  return 0;
572 }
573 
575 {
576  return ff_sws_op_list_insert_at(ops, ops->num_ops, op);
577 }
578 
580 {
581  if (!ops->num_ops)
582  return true;
583 
584  const SwsOp *read = &ops->ops[0];
585  const SwsOp *write = &ops->ops[1];
586  if (ops->num_ops != 2 ||
587  read->op != SWS_OP_READ ||
588  write->op != SWS_OP_WRITE ||
589  read->type != write->type ||
590  read->rw.packed != write->rw.packed ||
591  read->rw.elems != write->rw.elems ||
592  read->rw.frac != write->rw.frac)
593  return false;
594 
595  /**
596  * Note that this check is unlikely to ever be hit in practice, since it
597  * would imply the existence of planar formats with different plane orders
598  * between them, e.g. rgbap <-> gbrap, which doesn't currently exist.
599  * However, the check is cheap and lets me sleep at night.
600  */
601  const int num_planes = read->rw.packed ? 1 : read->rw.elems;
602  for (int i = 0; i < num_planes; i++) {
603  if (ops->order_src.in[i] != ops->order_dst.in[i])
604  return false;
605  }
606 
607  return true;
608 }
609 
611 {
612  int max_size = 0;
613  for (int i = 0; i < ops->num_ops; i++) {
614  const int size = ff_sws_pixel_type_size(ops->ops[i].type);
615  max_size = FFMAX(max_size, size);
616  }
617 
618  return max_size;
619 }
620 
622 {
623  uint32_t mask = 0;
624  for (int i = 0; i < 4; i++) {
625  for (int j = 0; j < 5; j++) {
626  if (av_cmp_q(c.m[i][j], Q(i == j)))
627  mask |= SWS_MASK(i, j);
628  }
629  }
630  return mask;
631 }
632 
633 static const char *describe_lin_mask(uint32_t mask)
634 {
635  /* Try to be fairly descriptive without assuming too much */
636  static const struct {
637  char name[24];
638  uint32_t mask;
639  } patterns[] = {
640  { "noop", 0 },
641  { "luma", SWS_MASK_LUMA },
642  { "alpha", SWS_MASK_ALPHA },
643  { "luma+alpha", SWS_MASK_LUMA | SWS_MASK_ALPHA },
644  { "dot3", 0x7 },
645  { "dot4", 0xF },
646  { "row0", SWS_MASK_ROW(0) },
647  { "row0+alpha", SWS_MASK_ROW(0) | SWS_MASK_ALPHA },
648  { "col0", SWS_MASK_COL(0) },
649  { "col0+off3", SWS_MASK_COL(0) | SWS_MASK_OFF3 },
650  { "off3", SWS_MASK_OFF3 },
651  { "off3+alpha", SWS_MASK_OFF3 | SWS_MASK_ALPHA },
652  { "diag3", SWS_MASK_DIAG3 },
653  { "diag4", SWS_MASK_DIAG4 },
654  { "diag3+alpha", SWS_MASK_DIAG3 | SWS_MASK_ALPHA },
655  { "diag3+off3", SWS_MASK_DIAG3 | SWS_MASK_OFF3 },
656  { "diag3+off3+alpha", SWS_MASK_DIAG3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
657  { "diag4+off4", SWS_MASK_DIAG4 | SWS_MASK_OFF4 },
658  { "matrix3", SWS_MASK_MAT3 },
659  { "matrix3+off3", SWS_MASK_MAT3 | SWS_MASK_OFF3 },
660  { "matrix3+off3+alpha", SWS_MASK_MAT3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
661  { "matrix4", SWS_MASK_MAT4 },
662  { "matrix4+off4", SWS_MASK_MAT4 | SWS_MASK_OFF4 },
663  };
664 
665  for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
666  if (!(mask & ~patterns[i].mask))
667  return patterns[i].name;
668  }
669 
670  av_unreachable("Invalid linear mask!");
671  return "ERR";
672 }
673 
674 static char describe_comp_flags(unsigned flags)
675 {
676  if (flags & SWS_COMP_GARBAGE)
677  return 'X';
678  else if (flags & SWS_COMP_ZERO)
679  return '0';
680  else if (flags & SWS_COMP_SWAPPED)
681  return 'z';
682  else if (flags & SWS_COMP_EXACT)
683  return '+';
684  else
685  return '.';
686 }
687 
688 static const char *describe_order(SwsSwizzleOp order, int planes, char buf[32])
689 {
690  if (order.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
691  return "";
692 
693  av_strlcpy(buf, ", via {", 32);
694  for (int i = 0; i < planes; i++)
695  av_strlcatf(buf, 32, "%s%d", i ? ", " : "", order.in[i]);
696  av_strlcat(buf, "}", 32);
697  return buf;
698 }
699 
700 static const char *print_q(const AVRational q, char buf[], int buf_len)
701 {
702  if (!q.den) {
703  return q.num > 0 ? "inf" : q.num < 0 ? "-inf" : "nan";
704  } else if (q.den == 1) {
705  snprintf(buf, buf_len, "%d", q.num);
706  return buf;
707  } else if (abs(q.num) > 1000 || abs(q.den) > 1000) {
708  snprintf(buf, buf_len, "%f", av_q2d(q));
709  return buf;
710  } else {
711  snprintf(buf, buf_len, "%d/%d", q.num, q.den);
712  return buf;
713  }
714 }
715 
716 #define PRINTQ(q) print_q(q, (char[32]){0}, sizeof(char[32]))
717 
718 void ff_sws_op_list_print(void *log, int lev, int lev_extra,
719  const SwsOpList *ops)
720 {
721  if (!ops->num_ops) {
722  av_log(log, lev, " (empty)\n");
723  return;
724  }
725 
726  for (int i = 0; i < ops->num_ops; i++) {
727  const SwsOp *op = &ops->ops[i];
728  const SwsOp *next = i + 1 < ops->num_ops ? &ops->ops[i + 1] : op;
729  char buf[32];
730 
731  av_log(log, lev, " [%3s %c%c%c%c -> %c%c%c%c] ",
732  ff_sws_pixel_type_name(op->type),
733  op->comps.unused[0] ? 'X' : '.',
734  op->comps.unused[1] ? 'X' : '.',
735  op->comps.unused[2] ? 'X' : '.',
736  op->comps.unused[3] ? 'X' : '.',
737  next->comps.unused[0] ? 'X' : describe_comp_flags(op->comps.flags[0]),
738  next->comps.unused[1] ? 'X' : describe_comp_flags(op->comps.flags[1]),
739  next->comps.unused[2] ? 'X' : describe_comp_flags(op->comps.flags[2]),
740  next->comps.unused[3] ? 'X' : describe_comp_flags(op->comps.flags[3]));
741 
742  switch (op->op) {
743  case SWS_OP_INVALID:
744  av_log(log, lev, "SWS_OP_INVALID\n");
745  break;
746  case SWS_OP_READ:
747  case SWS_OP_WRITE:
748  av_log(log, lev, "%-20s: %d elem(s) %s >> %d%s\n",
749  op->op == SWS_OP_READ ? "SWS_OP_READ"
750  : "SWS_OP_WRITE",
751  op->rw.elems, op->rw.packed ? "packed" : "planar",
752  op->rw.frac,
753  describe_order(op->op == SWS_OP_READ ? ops->order_src
754  : ops->order_dst,
755  op->rw.packed ? 1 : op->rw.elems, buf));
756  break;
757  case SWS_OP_SWAP_BYTES:
758  av_log(log, lev, "SWS_OP_SWAP_BYTES\n");
759  break;
760  case SWS_OP_LSHIFT:
761  av_log(log, lev, "%-20s: << %u\n", "SWS_OP_LSHIFT", op->c.u);
762  break;
763  case SWS_OP_RSHIFT:
764  av_log(log, lev, "%-20s: >> %u\n", "SWS_OP_RSHIFT", op->c.u);
765  break;
766  case SWS_OP_PACK:
767  case SWS_OP_UNPACK:
768  av_log(log, lev, "%-20s: {%d %d %d %d}\n",
769  op->op == SWS_OP_PACK ? "SWS_OP_PACK"
770  : "SWS_OP_UNPACK",
771  op->pack.pattern[0], op->pack.pattern[1],
772  op->pack.pattern[2], op->pack.pattern[3]);
773  break;
774  case SWS_OP_CLEAR:
775  av_log(log, lev, "%-20s: {%s %s %s %s}\n", "SWS_OP_CLEAR",
776  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
777  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
778  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
779  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
780  break;
781  case SWS_OP_SWIZZLE:
782  av_log(log, lev, "%-20s: %d%d%d%d\n", "SWS_OP_SWIZZLE",
783  op->swizzle.x, op->swizzle.y, op->swizzle.z, op->swizzle.w);
784  break;
785  case SWS_OP_CONVERT:
786  av_log(log, lev, "%-20s: %s -> %s%s\n", "SWS_OP_CONVERT",
787  ff_sws_pixel_type_name(op->type),
788  ff_sws_pixel_type_name(op->convert.to),
789  op->convert.expand ? " (expand)" : "");
790  break;
791  case SWS_OP_DITHER:
792  av_log(log, lev, "%-20s: %dx%d matrix + {%d %d %d %d}\n", "SWS_OP_DITHER",
793  1 << op->dither.size_log2, 1 << op->dither.size_log2,
794  op->dither.y_offset[0], op->dither.y_offset[1],
795  op->dither.y_offset[2], op->dither.y_offset[3]);
796  break;
797  case SWS_OP_MIN:
798  av_log(log, lev, "%-20s: x <= {%s %s %s %s}\n", "SWS_OP_MIN",
799  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
800  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
801  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
802  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
803  break;
804  case SWS_OP_MAX:
805  av_log(log, lev, "%-20s: {%s %s %s %s} <= x\n", "SWS_OP_MAX",
806  op->c.q4[0].den ? PRINTQ(op->c.q4[0]) : "_",
807  op->c.q4[1].den ? PRINTQ(op->c.q4[1]) : "_",
808  op->c.q4[2].den ? PRINTQ(op->c.q4[2]) : "_",
809  op->c.q4[3].den ? PRINTQ(op->c.q4[3]) : "_");
810  break;
811  case SWS_OP_LINEAR:
812  av_log(log, lev, "%-20s: %s [[%s %s %s %s %s] "
813  "[%s %s %s %s %s] "
814  "[%s %s %s %s %s] "
815  "[%s %s %s %s %s]]\n",
816  "SWS_OP_LINEAR", describe_lin_mask(op->lin.mask),
817  PRINTQ(op->lin.m[0][0]), PRINTQ(op->lin.m[0][1]), PRINTQ(op->lin.m[0][2]), PRINTQ(op->lin.m[0][3]), PRINTQ(op->lin.m[0][4]),
818  PRINTQ(op->lin.m[1][0]), PRINTQ(op->lin.m[1][1]), PRINTQ(op->lin.m[1][2]), PRINTQ(op->lin.m[1][3]), PRINTQ(op->lin.m[1][4]),
819  PRINTQ(op->lin.m[2][0]), PRINTQ(op->lin.m[2][1]), PRINTQ(op->lin.m[2][2]), PRINTQ(op->lin.m[2][3]), PRINTQ(op->lin.m[2][4]),
820  PRINTQ(op->lin.m[3][0]), PRINTQ(op->lin.m[3][1]), PRINTQ(op->lin.m[3][2]), PRINTQ(op->lin.m[3][3]), PRINTQ(op->lin.m[3][4]));
821  break;
822  case SWS_OP_SCALE:
823  av_log(log, lev, "%-20s: * %s\n", "SWS_OP_SCALE",
824  PRINTQ(op->c.q));
825  break;
826  case SWS_OP_TYPE_NB:
827  break;
828  }
829 
830  if (op->comps.min[0].den || op->comps.min[1].den ||
831  op->comps.min[2].den || op->comps.min[3].den ||
832  op->comps.max[0].den || op->comps.max[1].den ||
833  op->comps.max[2].den || op->comps.max[3].den)
834  {
835  av_log(log, lev_extra, " min: {%s, %s, %s, %s}, max: {%s, %s, %s, %s}\n",
836  next->comps.unused[0] ? "_" : PRINTQ(op->comps.min[0]),
837  next->comps.unused[1] ? "_" : PRINTQ(op->comps.min[1]),
838  next->comps.unused[2] ? "_" : PRINTQ(op->comps.min[2]),
839  next->comps.unused[3] ? "_" : PRINTQ(op->comps.min[3]),
840  next->comps.unused[0] ? "_" : PRINTQ(op->comps.max[0]),
841  next->comps.unused[1] ? "_" : PRINTQ(op->comps.max[1]),
842  next->comps.unused[2] ? "_" : PRINTQ(op->comps.max[2]),
843  next->comps.unused[3] ? "_" : PRINTQ(op->comps.max[3]));
844  }
845 
846  }
847 
848  av_log(log, lev, " (X = unused, z = byteswapped, + = exact, 0 = zero)\n");
849 }
850 
852  const SwsOpList *ops, SwsCompiledOp *out)
853 {
854  SwsOpList *copy, rest;
855  SwsCompiledOp compiled = {0};
856  int ret = 0;
857 
859  if (!copy)
860  return AVERROR(ENOMEM);
861 
862  /* Ensure these are always set during compilation */
864 
865  /* Make an on-stack copy of `ops` to ensure we can still properly clean up
866  * the copy afterwards */
867  rest = *copy;
868 
869  ret = backend->compile(ctx, &rest, &compiled);
870  if (ret < 0) {
871  int msg_lev = ret == AVERROR(ENOTSUP) ? AV_LOG_TRACE : AV_LOG_ERROR;
872  av_log(ctx, msg_lev, "Backend '%s' failed to compile operations: %s\n",
873  backend->name, av_err2str(ret));
874  if (rest.num_ops != ops->num_ops) {
875  av_log(ctx, msg_lev, "Uncompiled remainder:\n");
876  ff_sws_op_list_print(ctx, msg_lev, AV_LOG_TRACE, &rest);
877  }
878  } else {
879  *out = compiled;
880  }
881 
883  return ret;
884 }
885 
887 {
888  for (int n = 0; ff_sws_op_backends[n]; n++) {
889  const SwsOpBackend *backend = ff_sws_op_backends[n];
890  if (ops->src.hw_format != backend->hw_format ||
891  ops->dst.hw_format != backend->hw_format)
892  continue;
893  if (ff_sws_ops_compile_backend(ctx, backend, ops, out) < 0)
894  continue;
895 
896  av_log(ctx, AV_LOG_VERBOSE, "Compiled using backend '%s': "
897  "block size = %d, over-read = %d, over-write = %d, cpu flags = 0x%x\n",
898  backend->name, out->block_size, out->over_read, out->over_write,
899  out->cpu_flags);
900  return 0;
901  }
902 
903  av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
905  return AVERROR(ENOTSUP);
906 }
907 
908 typedef struct SwsOpPass {
910  SwsOpExec exec_base;
911  int num_blocks;
912  int tail_off_in;
913  int tail_off_out;
914  int tail_size_in;
915  int tail_size_out;
916  int planes_in;
917  int planes_out;
918  int pixel_bits_in;
919  int pixel_bits_out;
920  int idx_in[4];
921  int idx_out[4];
922  bool memcpy_in;
923  bool memcpy_out;
924 } SwsOpPass;
925 
926 static void op_pass_free(void *ptr)
927 {
928  SwsOpPass *p = ptr;
929  if (!p)
930  return;
931 
932  if (p->comp.free)
933  p->comp.free(p->comp.priv);
934 
935  av_free(p);
936 }
937 
938 static inline SwsImg img_shift_idx(const SwsImg *base, const int y,
939  const int plane_idx[4])
940 {
941  SwsImg img = *base;
942  for (int i = 0; i < 4; i++) {
943  const int idx = plane_idx[i];
944  if (idx >= 0) {
945  const int yshift = y >> ff_fmt_vshift(base->fmt, idx);
946  img.data[i] = base->data[idx] + yshift * base->linesize[idx];
947  } else {
948  img.data[i] = NULL;
949  }
950  }
951  return img;
952 }
953 
954 static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
955  const SwsPass *pass)
956 {
957  const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(in_base->fmt);
958  const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out_base->fmt);
959 
960  SwsOpPass *p = pass->priv;
961  SwsOpExec *exec = &p->exec_base;
962  const SwsCompiledOp *comp = &p->comp;
963  const int block_size = comp->block_size;
964  p->num_blocks = (pass->width + block_size - 1) / block_size;
965 
966  /* Set up main loop parameters */
967  const int aligned_w = p->num_blocks * block_size;
968  const int safe_width = (p->num_blocks - 1) * block_size;
969  const int tail_size = pass->width - safe_width;
970  p->tail_off_in = safe_width * p->pixel_bits_in >> 3;
971  p->tail_off_out = safe_width * p->pixel_bits_out >> 3;
972  p->tail_size_in = tail_size * p->pixel_bits_in >> 3;
973  p->tail_size_out = tail_size * p->pixel_bits_out >> 3;
974  p->memcpy_in = false;
975  p->memcpy_out = false;
976 
977  const SwsImg in = img_shift_idx(in_base, 0, p->idx_in);
978  const SwsImg out = img_shift_idx(out_base, 0, p->idx_out);
979 
980  exec->src_frame_ptr = in.frame_ptr;
981  exec->dst_frame_ptr = out.frame_ptr;
982 
983  for (int i = 0; i < p->planes_in; i++) {
984  const int idx = p->idx_in[i];
985  const int sub_x = (idx == 1 || idx == 2) ? indesc->log2_chroma_w : 0;
986  const int plane_w = (aligned_w + sub_x) >> sub_x;
987  const int plane_pad = (comp->over_read + sub_x) >> sub_x;
988  const int plane_size = plane_w * p->pixel_bits_in >> 3;
989  if (comp->slice_align)
990  p->memcpy_in |= plane_size + plane_pad > in.linesize[i];
991  exec->in_stride[i] = in.linesize[i];
992  }
993 
994  for (int i = 0; i < p->planes_out; i++) {
995  const int idx = p->idx_out[i];
996  const int sub_x = (idx == 1 || idx == 2) ? outdesc->log2_chroma_w : 0;
997  const int plane_w = (aligned_w + sub_x) >> sub_x;
998  const int plane_pad = (comp->over_write + sub_x) >> sub_x;
999  const int plane_size = plane_w * p->pixel_bits_out >> 3;
1000  if (comp->slice_align)
1001  p->memcpy_out |= plane_size + plane_pad > out.linesize[i];
1002  exec->out_stride[i] = out.linesize[i];
1003  }
1004 
1005  /* Pre-fill pointer bump for the main section only; this value does not
1006  * matter at all for the tail / last row handlers because they only ever
1007  * process a single line */
1008  const int blocks_main = p->num_blocks - p->memcpy_out;
1009  for (int i = 0; i < 4; i++) {
1010  exec->in_bump[i] = in.linesize[i] - blocks_main * exec->block_size_in;
1011  exec->out_bump[i] = out.linesize[i] - blocks_main * exec->block_size_out;
1012  }
1013 }
1014 
1015 /* Dispatch kernel over the last column of the image using memcpy */
1016 static av_always_inline void
1017 handle_tail(const SwsOpPass *p, SwsOpExec *exec,
1018  const SwsImg *out_base, const bool copy_out,
1019  const SwsImg *in_base, const bool copy_in,
1020  int y, const int h)
1021 {
1022  DECLARE_ALIGNED_64(uint8_t, tmp)[2][4][sizeof(uint32_t[128])];
1023 
1024  const SwsCompiledOp *comp = &p->comp;
1025  const int tail_size_in = p->tail_size_in;
1026  const int tail_size_out = p->tail_size_out;
1027  const int bx = p->num_blocks - 1;
1028 
1029  SwsImg in = img_shift_idx(in_base, y, p->idx_in);
1030  SwsImg out = img_shift_idx(out_base, y, p->idx_out);
1031  for (int i = 0; i < p->planes_in; i++) {
1032  in.data[i] += p->tail_off_in;
1033  if (copy_in) {
1034  exec->in[i] = (void *) tmp[0][i];
1035  exec->in_stride[i] = sizeof(tmp[0][i]);
1036  } else {
1037  exec->in[i] = in.data[i];
1038  }
1039  }
1040 
1041  for (int i = 0; i < p->planes_out; i++) {
1042  out.data[i] += p->tail_off_out;
1043  if (copy_out) {
1044  exec->out[i] = (void *) tmp[1][i];
1045  exec->out_stride[i] = sizeof(tmp[1][i]);
1046  } else {
1047  exec->out[i] = out.data[i];
1048  }
1049  }
1050 
1051  for (int y_end = y + h; y < y_end; y++) {
1052  if (copy_in) {
1053  for (int i = 0; i < p->planes_in; i++) {
1054  av_assert2(tmp[0][i] + tail_size_in < (uint8_t *) tmp[1]);
1055  memcpy(tmp[0][i], in.data[i], tail_size_in);
1056  in.data[i] += in.linesize[i];
1057  }
1058  }
1059 
1060  comp->func(exec, comp->priv, bx, y, p->num_blocks, y + 1);
1061 
1062  if (copy_out) {
1063  for (int i = 0; i < p->planes_out; i++) {
1064  av_assert2(tmp[1][i] + tail_size_out < (uint8_t *) tmp[2]);
1065  memcpy(out.data[i], tmp[1][i], tail_size_out);
1066  out.data[i] += out.linesize[i];
1067  }
1068  }
1069 
1070  for (int i = 0; i < 4; i++) {
1071  if (!copy_in && exec->in[i])
1072  exec->in[i] += in.linesize[i];
1073  if (!copy_out && exec->out[i])
1074  exec->out[i] += out.linesize[i];
1075  }
1076  }
1077 }
1078 
1079 static void op_pass_run(const SwsImg *out_base, const SwsImg *in_base,
1080  const int y, const int h, const SwsPass *pass)
1081 {
1082  const SwsOpPass *p = pass->priv;
1083  const SwsCompiledOp *comp = &p->comp;
1084  const SwsImg in = img_shift_idx(in_base, y, p->idx_in);
1085  const SwsImg out = img_shift_idx(out_base, y, p->idx_out);
1086 
1087  /* Fill exec metadata for this slice */
1088  DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
1089  exec.slice_y = y;
1090  exec.slice_h = h;
1091  for (int i = 0; i < 4; i++) {
1092  exec.in[i] = in.data[i];
1093  exec.out[i] = out.data[i];
1094  }
1095 
1096  exec.src_frame_ptr = in_base->frame_ptr;
1097  exec.dst_frame_ptr = out_base->frame_ptr;
1098 
1099  /**
1100  * To ensure safety, we need to consider the following:
1101  *
1102  * 1. We can overread the input, unless this is the last line of an
1103  * unpadded buffer. All defined operations can handle arbitrary pixel
1104  * input, so overread of arbitrary data is fine.
1105  *
1106  * 2. We can overwrite the output, as long as we don't write more than the
1107  * amount of pixels that fit into one linesize. So we always need to
1108  * memcpy the last column on the output side if unpadded.
1109  *
1110  * 3. For the last row, we also need to memcpy the remainder of the input,
1111  * to avoid reading past the end of the buffer. Note that since we know
1112  * the run() function is called on stripes of the same buffer, we don't
1113  * need to worry about this for the end of a slice.
1114  */
1115 
1116  const int last_slice = y + h == pass->height;
1117  const bool memcpy_in = last_slice && p->memcpy_in;
1118  const bool memcpy_out = p->memcpy_out;
1119  const int num_blocks = p->num_blocks;
1120  const int blocks_main = num_blocks - memcpy_out;
1121  const int h_main = h - memcpy_in;
1122 
1123  /* Handle main section */
1124  comp->func(&exec, comp->priv, 0, y, blocks_main, y + h_main);
1125 
1126  if (memcpy_in) {
1127  /* Safe part of last row */
1128  for (int i = 0; i < 4; i++) {
1129  exec.in[i] += h_main * in.linesize[i];
1130  exec.out[i] += h_main * out.linesize[i];
1131  }
1132  comp->func(&exec, comp->priv, 0, y + h_main, num_blocks - 1, y + h);
1133  }
1134 
1135  /* Handle last column via memcpy, takes over `exec` so call these last */
1136  if (memcpy_out)
1137  handle_tail(p, &exec, out_base, true, in_base, false, y, h_main);
1138  if (memcpy_in)
1139  handle_tail(p, &exec, out_base, memcpy_out, in_base, true, y + h_main, 1);
1140 }
1141 
1142 static int rw_planes(const SwsOp *op)
1143 {
1144  return op->rw.packed ? 1 : op->rw.elems;
1145 }
1146 
1147 static int rw_pixel_bits(const SwsOp *op)
1148 {
1149  const int elems = op->rw.packed ? op->rw.elems : 1;
1150  const int size = ff_sws_pixel_type_size(op->type);
1151  const int bits = 8 >> op->rw.frac;
1152  av_assert1(bits >= 1);
1153  return elems * size * bits;
1154 }
1155 
1156 int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags, SwsFormat dst,
1158 {
1159  SwsContext *ctx = graph->ctx;
1160  SwsOpPass *p = NULL;
1161  const SwsOp *read = &ops->ops[0];
1162  const SwsOp *write = &ops->ops[ops->num_ops - 1];
1163  SwsPass *pass;
1164  int ret;
1165 
1166  /* Check if the whole operation graph is an end-to-end no-op */
1167  if (ff_sws_op_list_is_noop(ops)) {
1168  *output = input;
1169  return 0;
1170  }
1171 
1172  if (ops->num_ops < 2) {
1173  av_log(ctx, AV_LOG_ERROR, "Need at least two operations.\n");
1174  return AVERROR(EINVAL);
1175  }
1176 
1177  if (read->op != SWS_OP_READ || write->op != SWS_OP_WRITE) {
1178  av_log(ctx, AV_LOG_ERROR, "First and last operations must be a read "
1179  "and write, respectively.\n");
1180  return AVERROR(EINVAL);
1181  }
1182 
1185  else
1187 
1188  p = av_mallocz(sizeof(*p));
1189  if (!p)
1190  return AVERROR(ENOMEM);
1191 
1192  ret = ff_sws_ops_compile(ctx, ops, &p->comp);
1193  if (ret < 0)
1194  goto fail;
1195 
1196  p->planes_in = rw_planes(read);
1197  p->planes_out = rw_planes(write);
1198  p->pixel_bits_in = rw_pixel_bits(read);
1199  p->pixel_bits_out = rw_pixel_bits(write);
1200  p->exec_base = (SwsOpExec) {
1201  .width = dst.width,
1202  .height = dst.height,
1203  .block_size_in = p->comp.block_size * p->pixel_bits_in >> 3,
1204  .block_size_out = p->comp.block_size * p->pixel_bits_out >> 3,
1205  };
1206 
1207  for (int i = 0; i < 4; i++) {
1208  p->idx_in[i] = i < p->planes_in ? ops->order_src.in[i] : -1;
1209  p->idx_out[i] = i < p->planes_out ? ops->order_dst.in[i] : -1;
1210  }
1211 
1212  pass = ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height, input,
1213  p->comp.slice_align, p, op_pass_run);
1214  if (!pass) {
1215  ret = AVERROR(ENOMEM);
1216  goto fail;
1217  }
1218  pass->setup = op_pass_setup;
1219  pass->free = op_pass_free;
1220 
1221  *output = pass;
1222  return 0;
1223 
1224 fail:
1225  op_pass_free(p);
1226  return ret;
1227 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:47
flags
const SwsFlags flags[]
Definition: swscale.c:61
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: ops.h:33
ff_sws_op_list_free
void ff_sws_op_list_free(SwsOpList **p_ops)
Definition: ops.c:507
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
name
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 just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
SwsComps::flags
unsigned flags[4]
Definition: ops.h:90
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:50
SwsGraph::ctx
SwsContext * ctx
Definition: graph.h:121
SwsPass
Represents a single filter pass in the scaling graph.
Definition: graph.h:80
ff_sws_op_list_alloc
SwsOpList * ff_sws_op_list_alloc(void)
Definition: ops.c:495
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
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:55
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:53
ff_sws_op_list_duplicate
SwsOpList * ff_sws_op_list_duplicate(const SwsOpList *ops)
Returns a duplicate of ops, or NULL on OOM.
Definition: ops.c:521
out
static FILE * out
Definition: movenc.c:55
SwsSwizzleOp::mask
uint32_t mask
Definition: ops.h:126
av_min_q
static AVRational av_min_q(AVRational a, AVRational b)
Definition: ops.c:102
comp
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition: eamad.c:79
SwsOpList::comps_src
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition: ops.h:238
SwsOpExec::in_bump
ptrdiff_t in_bump[4]
Definition: ops_internal.h:70
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3456
SWS_COMP_ZERO
@ SWS_COMP_ZERO
Definition: ops.h:75
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:59
ff_sws_linear_mask
uint32_t ff_sws_linear_mask(const SwsLinearOp c)
Definition: ops.c:621
SwsOpExec::in
const uint8_t * in[4]
Definition: ops_internal.h:62
SwsOpExec::out_stride
ptrdiff_t out_stride[4]
Definition: ops_internal.h:67
ff_sws_op_list_max_size
int ff_sws_op_list_max_size(const SwsOpList *ops)
Returns the size of the largest pixel type used in ops.
Definition: ops.c:610
backend_x86
const SwsOpBackend backend_x86
Definition: ops.c:757
SwsComps::unused
bool unused[4]
Definition: ops.h:91
rational.h
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
ff_sws_op_list_append
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
These will take over ownership of op and set it to {0}, even on failure.
Definition: ops.c:574
normalize.log
log
Definition: normalize.py:21
mask
int mask
Definition: mediacodecdec_common.c:154
SwsOp::rw
SwsReadWriteOp rw
Definition: ops.h:191
backend_vulkan
const SwsOpBackend backend_vulkan
Definition: ops.c:338
ops.h
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:67
SwsImg::frame_ptr
const AVFrame * frame_ptr
Definition: graph.h:39
av_dynarray2_add
void * av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, const uint8_t *elem_data)
Add an element of size elem_size to a dynamic array.
Definition: mem.c:343
SwsOpExec::block_size_in
int32_t block_size_in
Definition: ops_internal.h:76
b
#define b
Definition: input.c:42
SWS_MASK_MAT3
@ SWS_MASK_MAT3
Definition: ops.h:174
SWS_MASK_OFF3
@ SWS_MASK_OFF3
Definition: ops.h:173
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:226
base
uint8_t base
Definition: vp3data.h:128
max
#define max(a, b)
Definition: cuda_runtime.h:33
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: ops.h:34
SWS_OP_TYPE_NB
@ SWS_OP_TYPE_NB
Definition: ops.h:69
SwsPass::free
void(* free)(void *priv)
Optional private state and associated free() function.
Definition: graph.h:113
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
SwsOpExec::in_stride
ptrdiff_t in_stride[4]
Definition: ops_internal.h:66
SwsImg
Represents a view into a single field of frame data.
Definition: graph.h:35
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
SwsOpBackend::name
const char * name
Definition: ops_internal.h:114
ff_sws_pixel_type_size
int ff_sws_pixel_type_size(SwsPixelType type)
Definition: ops.c:69
describe_comp_flags
static char describe_comp_flags(unsigned flags)
Definition: ops.c:674
clear_undefined_values
static void clear_undefined_values(AVRational dst[4], const AVRational src[4])
Definition: ops.c:238
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: ops.h:164
av_memdup
void * av_memdup(const void *p, size_t size)
Duplicate a buffer with av_malloc().
Definition: mem.c:304
DECLARE_ALIGNED_64
#define DECLARE_ALIGNED_64(t, v)
Definition: mem_internal.h:114
SwsPixelType
SwsPixelType
Copyright (C) 2025 Niklas Haas.
Definition: ops.h:30
SwsPass::width
int width
Definition: graph.h:90
SwsComps::max
AVRational max[4]
Definition: ops.h:95
ff_sws_op_list_print
void ff_sws_op_list_print(void *log, int lev, int lev_extra, const SwsOpList *ops)
Print out the contents of an operation list.
Definition: ops.c:718
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: ops.h:35
ff_sws_op_backends
const SwsOpBackend *const ff_sws_op_backends[]
Definition: ops.c:36
av_ceil_log2
#define av_ceil_log2
Definition: common.h:97
fail
#define fail()
Definition: checkasm.h:218
SwsOpList::num_ops
int num_ops
Definition: ops.h:224
SWS_MASK_COL
#define SWS_MASK_COL(J)
Definition: ops.h:165
flags_identity
static const unsigned flags_identity
Definition: ops.c:222
SwsOpBackend::compile
int(* compile)(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out)
Compile an operation list to an implementation chain.
Definition: ops_internal.h:122
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: ops.h:32
SwsSwizzleOp
Definition: ops.h:120
ff_sws_pixel_type_is_int
bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: ops.c:84
val
static double val(void *priv, double ch)
Definition: aeval.c:77
type
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 just let it vf type
Definition: writing_filters.txt:86
SwsOpBackend::hw_format
enum AVPixelFormat hw_format
If NONE, backend only supports software frames.
Definition: ops_internal.h:129
AVRational::num
int num
Numerator.
Definition: rational.h:59
ff_sws_ops_compile_backend
int ff_sws_ops_compile_backend(SwsContext *ctx, const SwsOpBackend *backend, const SwsOpList *ops, SwsCompiledOp *out)
Attempt to compile a list of operations using a specific backend.
refstruct.h
RET
#define RET(x)
Definition: ops.c:48
SwsOp::op
SwsOpType op
Definition: ops.h:187
Q
#define Q(q)
mult
static int16_t mult(Float11 *f1, Float11 *f2)
Definition: g726.c:60
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:63
avassert.h
SWS_MASK_ALPHA
@ SWS_MASK_ALPHA
Definition: ops.h:170
SwsOpExec::src_frame_ptr
const AVFrame * src_frame_ptr
Definition: ops_internal.h:79
AV_LOG_TRACE
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:236
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
SwsPass::priv
void * priv
Definition: graph.h:114
float
float
Definition: af_crystalizer.c:122
SWS_SWIZZLE
#define SWS_SWIZZLE(X, Y, Z, W)
Definition: ops.h:132
SwsComps::min
AVRational min[4]
Definition: ops.h:95
op
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition: anm.c:76
av_q2d
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition: rational.h:104
backend_c
const SwsOpBackend backend_c
Copyright (C) 2025 Niklas Haas.
Definition: ops_backend.c:97
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:108
bits
uint8_t bits
Definition: vp3data.h:128
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:61
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
ff_sws_pixel_expand
static AVRational ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
Definition: ops_internal.h:30
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:66
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
PRINTQ
#define PRINTQ(q)
Definition: ops.c:716
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
describe_order
static const char * describe_order(SwsSwizzleOp order, int planes, char buf[32])
Definition: ops.c:688
SwsOpBackend
Definition: ops_internal.h:113
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:54
SwsOpExec
Global execution context for all compiled functions.
Definition: ops_internal.h:60
merge_comp_flags
static unsigned merge_comp_flags(unsigned a, unsigned b)
Definition: ops.c:223
ff_sws_op_list_is_noop
bool ff_sws_op_list_is_noop(const SwsOpList *ops)
Returns whether an op list represents a true no-op operation, i.e.
Definition: ops.c:579
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:99
SWS_MASK_LUMA
@ SWS_MASK_LUMA
Definition: ops.h:169
rw_pixel_bits
static int rw_pixel_bits(const SwsOp *op)
Definition: sw_ops.c:55
NULL
#define NULL
Definition: coverity.c:32
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsReadWriteOp::frac
uint8_t frac
Definition: ops.h:100
SWS_COMP_GARBAGE
@ SWS_COMP_GARBAGE
Definition: ops.h:73
abs
#define abs(x)
Definition: cuda_runtime.h:35
ff_sws_op_list_remove_at
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
Definition: ops.c:550
SwsOpExec::slice_h
int32_t slice_h
Definition: ops_internal.h:75
print_q
static const char * print_q(const AVRational q, char buf[], int buf_len)
Definition: ops.c:700
SWS_MASK
#define SWS_MASK(I, J)
Definition: ops.h:162
SWS_PIXEL_NONE
@ SWS_PIXEL_NONE
Definition: ops.h:31
index
int index
Definition: gxfenc.c:90
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
ff_sws_apply_op_q
void ff_sws_apply_op_q(const SwsOp *op, AVRational x[4])
Apply an operation to an AVRational.
Definition: ops.c:112
SwsOpList::order_dst
SwsSwizzleOp order_dst
Definition: ops.h:227
SwsPass::height
int height
Definition: graph.h:90
SwsImg::linesize
int linesize[4]
Definition: graph.h:38
SwsOpExec::block_size_out
int32_t block_size_out
Definition: ops_internal.h:77
copy
static void copy(const float *p1, float *p2, const int length)
Definition: vf_vaguedenoiser.c:186
shift
static int shift(int a, int b)
Definition: bonk.c:261
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
av_bswap32
#define av_bswap32
Definition: bswap.h:47
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
SwsOp::type
SwsPixelType type
Definition: ops.h:188
ff_sws_op_list_insert_at
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
Definition: ops.c:560
size
int size
Definition: twinvq_data.h:10344
av_make_q
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition: rational.h:71
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:56
SwsOpList::src
SwsFormat src
Definition: ops.h:241
SWS_OP_INVALID
@ SWS_OP_INVALID
Definition: ops.h:44
ff_sws_op_list_update_comps
void ff_sws_op_list_update_comps(SwsOpList *ops)
Infer + propagate known information about components.
Definition: ops.c:247
SwsFormat
Definition: format.h:77
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:48
av_refstruct_ref
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
img
#define img
Definition: vf_colormatrix.c:114
op_uninit
static void op_uninit(SwsOp *op)
Definition: ops.c:484
SwsOp::comps
SwsComps comps
Metadata about the operation's input/output components.
Definition: ops.h:206
SWS_OP_FLAG_OPTIMIZE
@ SWS_OP_FLAG_OPTIMIZE
Definition: ops.h:291
SwsLinearOp
Definition: ops.h:145
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
av_refstruct_unref
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition: refstruct.c:120
SwsOpExec::out
uint8_t * out[4]
Definition: ops_internal.h:63
ff_sws_op_list_optimize
int ff_sws_op_list_optimize(SwsOpList *ops)
Fuse compatible and eliminate redundant operations, as well as replacing some operations with more ef...
Definition: ops_optimizer.c:283
av_assert2
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition: avassert.h:68
ff_fmt_vshift
static av_always_inline av_const int ff_fmt_vshift(enum AVPixelFormat fmt, int plane)
Definition: graph.h:42
av_max_q
static AVRational av_max_q(AVRational a, AVRational b)
Definition: ops.c:107
SwsOpList::ops
SwsOp * ops
Definition: ops.h:223
SwsOpList::order_src
SwsSwizzleOp order_src
Definition: ops.h:227
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:58
av_always_inline
#define av_always_inline
Definition: attributes.h:63
DECLARE_ALIGNED_32
#define DECLARE_ALIGNED_32(t, v)
Definition: mem_internal.h:113
ops_internal.h
ff_sws_ops_compile
int ff_sws_ops_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
Compile a list of operations using the best available backend.
lev
static LevelCodes lev[4+3+3]
Definition: clearvideo.c:80
SwsOp
Definition: ops.h:186
av_cmp_q
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:89
ret
ret
Definition: filter_design.txt:187
bswap.h
backend_murder
const SwsOpBackend backend_murder
Definition: ops_memcpy.c:130
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
av_strlcat
size_t av_strlcat(char *dst, const char *src, size_t size)
Append the string src to the string dst, but to a total length of no more than size - 1 bytes,...
Definition: avstring.c:95
av_malloc
void * av_malloc(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:98
SwsOpList::dst
SwsFormat dst
Definition: ops.h:241
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:62
SwsCompiledOp
Definition: ops_internal.h:99
SWS_PIXEL_TYPE_NB
@ SWS_PIXEL_TYPE_NB
Definition: ops.h:36
SwsComps
Definition: ops.h:89
ff_sws_compile_pass
int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags, SwsFormat dst, SwsPass *input, SwsPass **output)
Resolves an operation list to a graph pass.
AVRational::den
int den
Denominator.
Definition: rational.h:60
SWS_COMP_SWAPPED
@ SWS_COMP_SWAPPED
Definition: ops.h:76
SwsReadWriteOp::packed
bool packed
Definition: ops.h:101
ff_sws_pixel_type_name
const char * ff_sws_pixel_type_name(SwsPixelType type)
Definition: ops.c:54
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:49
SwsFormat::hw_format
enum AVPixelFormat hw_format
Definition: format.h:81
planes
static const struct @554 planes[]
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
av_mul_q
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition: rational.c:80
SWS_MASK_DIAG3
@ SWS_MASK_DIAG3
Definition: ops.h:172
SWS_COMP_EXACT
@ SWS_COMP_EXACT
Definition: ops.h:74
describe_lin_mask
static const char * describe_lin_mask(uint32_t mask)
Definition: ops.c:633
SwsReadWriteOp::elems
uint8_t elems
Definition: ops.h:99
mem.h
SwsGraph
Filter graph, which represents a 'baked' pixel format conversion.
Definition: graph.h:120
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
SwsImg::fmt
enum AVPixelFormat fmt
Definition: graph.h:36
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
av_add_q
AVRational av_add_q(AVRational b, AVRational c)
Add two rationals.
Definition: rational.c:93
SWS_MASK_DIAG4
@ SWS_MASK_DIAG4
Definition: ops.h:178
ff_sws_pack_op_decode
static void ff_sws_pack_op_decode(const SwsOp *op, uint64_t mask[4], int shift[4])
Definition: ops_internal.h:42
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
SWS_MASK_MAT4
@ SWS_MASK_MAT4
Definition: ops.h:180
SwsSwizzleOp::in
uint8_t in[4]
Definition: ops.h:127
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:60
av_strlcpy
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:85
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
propagate_flags
static void propagate_flags(SwsOp *op, const SwsComps *prev)
Definition: ops.c:231
h
h
Definition: vp9dsp_template.c:2070
avstring.h
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:222
av_bswap16
#define av_bswap16
Definition: bswap.h:28
SwsOpExec::slice_y
int32_t slice_y
Definition: ops_internal.h:75
SWS_MASK_OFF4
@ SWS_MASK_OFF4
Definition: ops.h:179
SwsContext
Main external API structure.
Definition: swscale.h:191
snprintf
#define snprintf
Definition: snprintf.h:34
SwsOpExec::dst_frame_ptr
const AVFrame * dst_frame_ptr
Definition: ops_internal.h:80
src
#define src
Definition: vp8dsp.c:248
SwsOpExec::out_bump
ptrdiff_t out_bump[4]
Definition: ops_internal.h:71
read
static uint32_t BS_FUNC() read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
Definition: bitstream_template.h:239
SwsImg::data
uint8_t * data[4]
Definition: graph.h:37
ff_fmt_clear
static void ff_fmt_clear(SwsFormat *fmt)
Definition: format.h:89
min
float min
Definition: vorbis_enc_data.h:429