FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_fspp.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
3  * Copyright (C) 2005 Nikolaj Poroshin <porosh3@psu.ru>
4  * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
5  *
6  * This file is part of FFmpeg.
7  *
8  * FFmpeg is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * FFmpeg is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 /**
24  * @file
25  * Fast Simple Post-processing filter
26  * This implementation is based on an algorithm described in
27  * "Aria Nosratinia Embedded Post-Processing for
28  * Enhancement of Compressed Images (1999)"
29  * (http://www.utdallas.edu/~aria/papers/vlsisp99.pdf)
30  * Further, with splitting (I)DCT into horizontal/vertical passes, one of
31  * them can be performed once per block, not per pixel. This allows for much
32  * higher speed.
33  *
34  * Originally written by Michael Niedermayer and Nikolaj for the MPlayer
35  * project, and ported by Arwa Arif for FFmpeg.
36  */
37 
38 #include "libavutil/avassert.h"
39 #include "libavutil/imgutils.h"
40 #include "libavutil/opt.h"
41 #include "libavutil/pixdesc.h"
42 #include "internal.h"
43 #include "vf_fspp.h"
44 
45 #define OFFSET(x) offsetof(FSPPContext, x)
46 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
47 static const AVOption fspp_options[] = {
48  { "quality", "set quality", OFFSET(log2_count), AV_OPT_TYPE_INT, {.i64 = 4}, 4, MAX_LEVEL, FLAGS },
49  { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 64, FLAGS },
50  { "strength", "set filter strength", OFFSET(strength), AV_OPT_TYPE_INT, {.i64 = 0}, -15, 32, FLAGS },
51  { "use_bframe_qp", "use B-frames' QP", OFFSET(use_bframe_qp), AV_OPT_TYPE_BOOL,{.i64 = 0}, 0, 1, FLAGS },
52  { NULL }
53 };
54 
56 
57 DECLARE_ALIGNED(32, static const uint8_t, dither)[8][8] = {
58  { 0, 48, 12, 60, 3, 51, 15, 63, },
59  { 32, 16, 44, 28, 35, 19, 47, 31, },
60  { 8, 56, 4, 52, 11, 59, 7, 55, },
61  { 40, 24, 36, 20, 43, 27, 39, 23, },
62  { 2, 50, 14, 62, 1, 49, 13, 61, },
63  { 34, 18, 46, 30, 33, 17, 45, 29, },
64  { 10, 58, 6, 54, 9, 57, 5, 53, },
65  { 42, 26, 38, 22, 41, 25, 37, 21, },
66 };
67 
68 static const short custom_threshold[64] = {
69 // values (296) can't be too high
70 // -it causes too big quant dependence
71 // or maybe overflow(check), which results in some flashing
72  71, 296, 295, 237, 71, 40, 38, 19,
73  245, 193, 185, 121, 102, 73, 53, 27,
74  158, 129, 141, 107, 97, 73, 50, 26,
75  102, 116, 109, 98, 82, 66, 45, 23,
76  71, 94, 95, 81, 70, 56, 38, 20,
77  56, 77, 74, 66, 56, 44, 30, 15,
78  38, 53, 50, 45, 38, 30, 21, 11,
79  20, 27, 26, 23, 20, 15, 11, 5
80 };
81 
82 //This func reads from 1 slice, 1 and clears 0 & 1
83 static void store_slice_c(uint8_t *dst, int16_t *src,
84  ptrdiff_t dst_stride, ptrdiff_t src_stride,
85  ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
86 {
87  int y, x;
88 #define STORE(pos) \
89  temp = (src[x + pos] + (d[pos] >> log2_scale)) >> (6 - log2_scale); \
90  src[x + pos] = src[x + pos - 8 * src_stride] = 0; \
91  if (temp & 0x100) temp = ~(temp >> 31); \
92  dst[x + pos] = temp;
93 
94  for (y = 0; y < height; y++) {
95  const uint8_t *d = dither[y];
96  for (x = 0; x < width; x += 8) {
97  int temp;
98  STORE(0);
99  STORE(1);
100  STORE(2);
101  STORE(3);
102  STORE(4);
103  STORE(5);
104  STORE(6);
105  STORE(7);
106  }
107  src += src_stride;
108  dst += dst_stride;
109  }
110 }
111 
112 //This func reads from 2 slices, 0 & 2 and clears 2-nd
113 static void store_slice2_c(uint8_t *dst, int16_t *src,
114  ptrdiff_t dst_stride, ptrdiff_t src_stride,
115  ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
116 {
117  int y, x;
118 #define STORE2(pos) \
119  temp = (src[x + pos] + src[x + pos + 16 * src_stride] + (d[pos] >> log2_scale)) >> (6 - log2_scale); \
120  src[x + pos + 16 * src_stride] = 0; \
121  if (temp & 0x100) temp = ~(temp >> 31); \
122  dst[x + pos] = temp;
123 
124  for (y = 0; y < height; y++) {
125  const uint8_t *d = dither[y];
126  for (x = 0; x < width; x += 8) {
127  int temp;
128  STORE2(0);
129  STORE2(1);
130  STORE2(2);
131  STORE2(3);
132  STORE2(4);
133  STORE2(5);
134  STORE2(6);
135  STORE2(7);
136  }
137  src += src_stride;
138  dst += dst_stride;
139  }
140 }
141 
142 static void mul_thrmat_c(int16_t *thr_adr_noq, int16_t *thr_adr, int q)
143 {
144  int a;
145  for (a = 0; a < 64; a++)
146  thr_adr[a] = q * thr_adr_noq[a];
147 }
148 
149 static void filter(FSPPContext *p, uint8_t *dst, uint8_t *src,
150  int dst_stride, int src_stride,
151  int width, int height,
152  uint8_t *qp_store, int qp_stride, int is_luma)
153 {
154  int x, x0, y, es, qy, t;
155 
156  const int stride = is_luma ? p->temp_stride : (width + 16);
157  const int step = 6 - p->log2_count;
158  const int qpsh = 4 - p->hsub * !is_luma;
159  const int qpsv = 4 - p->vsub * !is_luma;
160 
161  DECLARE_ALIGNED(32, int32_t, block_align)[4 * 8 * BLOCKSZ + 4 * 8 * BLOCKSZ];
162  int16_t *block = (int16_t *)block_align;
163  int16_t *block3 = (int16_t *)(block_align + 4 * 8 * BLOCKSZ);
164 
165  memset(block3, 0, 4 * 8 * BLOCKSZ);
166 
167  if (!src || !dst) return;
168 
169  for (y = 0; y < height; y++) {
170  int index = 8 + 8 * stride + y * stride;
171  memcpy(p->src + index, src + y * src_stride, width);
172  for (x = 0; x < 8; x++) {
173  p->src[index - x - 1] = p->src[index + x ];
174  p->src[index + width + x ] = p->src[index + width - x - 1];
175  }
176  }
177 
178  for (y = 0; y < 8; y++) {
179  memcpy(p->src + ( 7 - y ) * stride, p->src + ( y + 8 ) * stride, stride);
180  memcpy(p->src + (height + 8 + y) * stride, p->src + (height - y + 7) * stride, stride);
181  }
182  //FIXME (try edge emu)
183 
184  for (y = 8; y < 24; y++)
185  memset(p->temp + 8 + y * stride, 0, width * sizeof(int16_t));
186 
187  for (y = step; y < height + 8; y += step) { //step= 1,2
188  const int y1 = y - 8 + step; //l5-7 l4-6;
189  qy = y - 4;
190 
191  if (qy > height - 1) qy = height - 1;
192  if (qy < 0) qy = 0;
193 
194  qy = (qy >> qpsv) * qp_stride;
195  p->row_fdct(block, p->src + y * stride + 2 - (y&1), stride, 2);
196 
197  for (x0 = 0; x0 < width + 8 - 8 * (BLOCKSZ - 1); x0 += 8 * (BLOCKSZ - 1)) {
198  p->row_fdct(block + 8 * 8, p->src + y * stride + 8 + x0 + 2 - (y&1), stride, 2 * (BLOCKSZ - 1));
199 
200  if (p->qp)
201  p->column_fidct((int16_t *)(&p->threshold_mtx[0]), block + 0 * 8, block3 + 0 * 8, 8 * (BLOCKSZ - 1)); //yes, this is a HOTSPOT
202  else
203  for (x = 0; x < 8 * (BLOCKSZ - 1); x += 8) {
204  t = x + x0 - 2; //correct t=x+x0-2-(y&1), but its the same
205 
206  if (t < 0) t = 0; //t always < width-2
207 
208  t = qp_store[qy + (t >> qpsh)];
209  t = ff_norm_qscale(t, p->qscale_type);
210 
211  if (t != p->prev_q) p->prev_q = t, p->mul_thrmat((int16_t *)(&p->threshold_mtx_noq[0]), (int16_t *)(&p->threshold_mtx[0]), t);
212  p->column_fidct((int16_t *)(&p->threshold_mtx[0]), block + x * 8, block3 + x * 8, 8); //yes, this is a HOTSPOT
213  }
214  p->row_idct(block3 + 0 * 8, p->temp + (y & 15) * stride + x0 + 2 - (y & 1), stride, 2 * (BLOCKSZ - 1));
215  memmove(block, block + (BLOCKSZ - 1) * 64, 8 * 8 * sizeof(int16_t)); //cycling
216  memmove(block3, block3 + (BLOCKSZ - 1) * 64, 6 * 8 * sizeof(int16_t));
217  }
218 
219  es = width + 8 - x0; // 8, ...
220  if (es > 8)
221  p->row_fdct(block + 8 * 8, p->src + y * stride + 8 + x0 + 2 - (y & 1), stride, (es - 4) >> 2);
222 
223  p->column_fidct((int16_t *)(&p->threshold_mtx[0]), block, block3, es&(~1));
224  if (es > 3)
225  p->row_idct(block3 + 0 * 8, p->temp + (y & 15) * stride + x0 + 2 - (y & 1), stride, es >> 2);
226 
227  if (!(y1 & 7) && y1) {
228  if (y1 & 8)
229  p->store_slice(dst + (y1 - 8) * dst_stride, p->temp + 8 + 8 * stride,
230  dst_stride, stride, width, 8, 5 - p->log2_count);
231  else
232  p->store_slice2(dst + (y1 - 8) * dst_stride, p->temp + 8 + 0 * stride,
233  dst_stride, stride, width, 8, 5 - p->log2_count);
234  }
235  }
236 
237  if (y & 7) { // height % 8 != 0
238  if (y & 8)
239  p->store_slice(dst + ((y - 8) & ~7) * dst_stride, p->temp + 8 + 8 * stride,
240  dst_stride, stride, width, y&7, 5 - p->log2_count);
241  else
242  p->store_slice2(dst + ((y - 8) & ~7) * dst_stride, p->temp + 8 + 0 * stride,
243  dst_stride, stride, width, y&7, 5 - p->log2_count);
244  }
245 }
246 
247 static void column_fidct_c(int16_t *thr_adr, int16_t *data, int16_t *output, int cnt)
248 {
249  int_simd16_t tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
250  int_simd16_t tmp10, tmp11, tmp12, tmp13;
251  int_simd16_t z1,z2,z3,z4,z5, z10, z11, z12, z13;
252  int_simd16_t d0, d1, d2, d3, d4, d5, d6, d7;
253 
254  int16_t *dataptr;
255  int16_t *wsptr;
256  int16_t *threshold;
257  int ctr;
258 
259  dataptr = data;
260  wsptr = output;
261 
262  for (; cnt > 0; cnt -= 2) { //start positions
263  threshold = (int16_t *)thr_adr;//threshold_mtx
264  for (ctr = DCTSIZE; ctr > 0; ctr--) {
265  // Process columns from input, add to output.
266  tmp0 = dataptr[DCTSIZE * 0] + dataptr[DCTSIZE * 7];
267  tmp7 = dataptr[DCTSIZE * 0] - dataptr[DCTSIZE * 7];
268 
269  tmp1 = dataptr[DCTSIZE * 1] + dataptr[DCTSIZE * 6];
270  tmp6 = dataptr[DCTSIZE * 1] - dataptr[DCTSIZE * 6];
271 
272  tmp2 = dataptr[DCTSIZE * 2] + dataptr[DCTSIZE * 5];
273  tmp5 = dataptr[DCTSIZE * 2] - dataptr[DCTSIZE * 5];
274 
275  tmp3 = dataptr[DCTSIZE * 3] + dataptr[DCTSIZE * 4];
276  tmp4 = dataptr[DCTSIZE * 3] - dataptr[DCTSIZE * 4];
277 
278  // Even part of FDCT
279 
280  tmp10 = tmp0 + tmp3;
281  tmp13 = tmp0 - tmp3;
282  tmp11 = tmp1 + tmp2;
283  tmp12 = tmp1 - tmp2;
284 
285  d0 = tmp10 + tmp11;
286  d4 = tmp10 - tmp11;
287 
288  z1 = MULTIPLY16H((tmp12 + tmp13) << 2, FIX_0_707106781);
289  d2 = tmp13 + z1;
290  d6 = tmp13 - z1;
291 
292  // Even part of IDCT
293 
294  THRESHOLD(tmp0, d0, threshold[0 * 8]);
295  THRESHOLD(tmp1, d2, threshold[2 * 8]);
296  THRESHOLD(tmp2, d4, threshold[4 * 8]);
297  THRESHOLD(tmp3, d6, threshold[6 * 8]);
298  tmp0 += 2;
299  tmp10 = (tmp0 + tmp2) >> 2;
300  tmp11 = (tmp0 - tmp2) >> 2;
301 
302  tmp13 = (tmp1 + tmp3) >>2; //+2 ! (psnr decides)
303  tmp12 = MULTIPLY16H((tmp1 - tmp3), FIX_1_414213562_A) - tmp13; //<<2
304 
305  tmp0 = tmp10 + tmp13; //->temps
306  tmp3 = tmp10 - tmp13; //->temps
307  tmp1 = tmp11 + tmp12; //->temps
308  tmp2 = tmp11 - tmp12; //->temps
309 
310  // Odd part of FDCT
311 
312  tmp10 = tmp4 + tmp5;
313  tmp11 = tmp5 + tmp6;
314  tmp12 = tmp6 + tmp7;
315 
316  z5 = MULTIPLY16H((tmp10 - tmp12) << 2, FIX_0_382683433);
317  z2 = MULTIPLY16H(tmp10 << 2, FIX_0_541196100) + z5;
318  z4 = MULTIPLY16H(tmp12 << 2, FIX_1_306562965) + z5;
319  z3 = MULTIPLY16H(tmp11 << 2, FIX_0_707106781);
320 
321  z11 = tmp7 + z3;
322  z13 = tmp7 - z3;
323 
324  d5 = z13 + z2;
325  d3 = z13 - z2;
326  d1 = z11 + z4;
327  d7 = z11 - z4;
328 
329  // Odd part of IDCT
330 
331  THRESHOLD(tmp4, d1, threshold[1 * 8]);
332  THRESHOLD(tmp5, d3, threshold[3 * 8]);
333  THRESHOLD(tmp6, d5, threshold[5 * 8]);
334  THRESHOLD(tmp7, d7, threshold[7 * 8]);
335 
336  //Simd version uses here a shortcut for the tmp5,tmp6,tmp7 == 0
337  z13 = tmp6 + tmp5;
338  z10 = (tmp6 - tmp5) << 1;
339  z11 = tmp4 + tmp7;
340  z12 = (tmp4 - tmp7) << 1;
341 
342  tmp7 = (z11 + z13) >> 2; //+2 !
343  tmp11 = MULTIPLY16H((z11 - z13) << 1, FIX_1_414213562);
344  z5 = MULTIPLY16H(z10 + z12, FIX_1_847759065);
345  tmp10 = MULTIPLY16H(z12, FIX_1_082392200) - z5;
346  tmp12 = MULTIPLY16H(z10, FIX_2_613125930) + z5; // - !!
347 
348  tmp6 = tmp12 - tmp7;
349  tmp5 = tmp11 - tmp6;
350  tmp4 = tmp10 + tmp5;
351 
352  wsptr[DCTSIZE * 0] += (tmp0 + tmp7);
353  wsptr[DCTSIZE * 1] += (tmp1 + tmp6);
354  wsptr[DCTSIZE * 2] += (tmp2 + tmp5);
355  wsptr[DCTSIZE * 3] += (tmp3 - tmp4);
356  wsptr[DCTSIZE * 4] += (tmp3 + tmp4);
357  wsptr[DCTSIZE * 5] += (tmp2 - tmp5);
358  wsptr[DCTSIZE * 6] = (tmp1 - tmp6);
359  wsptr[DCTSIZE * 7] = (tmp0 - tmp7);
360  //
361  dataptr++; //next column
362  wsptr++;
363  threshold++;
364  }
365  dataptr += 8; //skip each second start pos
366  wsptr += 8;
367  }
368 }
369 
370 static void row_idct_c(int16_t *workspace, int16_t *output_adr, ptrdiff_t output_stride, int cnt)
371 {
372  int_simd16_t tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
373  int_simd16_t tmp10, tmp11, tmp12, tmp13;
374  int_simd16_t z5, z10, z11, z12, z13;
375  int16_t *outptr;
376  int16_t *wsptr;
377 
378  cnt *= 4;
379  wsptr = workspace;
380  outptr = output_adr;
381  for (; cnt > 0; cnt--) {
382  // Even part
383  //Simd version reads 4x4 block and transposes it
384  tmp10 = wsptr[2] + wsptr[3];
385  tmp11 = wsptr[2] - wsptr[3];
386 
387  tmp13 = wsptr[0] + wsptr[1];
388  tmp12 = (MULTIPLY16H(wsptr[0] - wsptr[1], FIX_1_414213562_A) << 2) - tmp13;//this shift order to avoid overflow
389 
390  tmp0 = tmp10 + tmp13; //->temps
391  tmp3 = tmp10 - tmp13; //->temps
392  tmp1 = tmp11 + tmp12;
393  tmp2 = tmp11 - tmp12;
394 
395  // Odd part
396  //Also transpose, with previous:
397  // ---- ---- ||||
398  // ---- ---- idct ||||
399  // ---- ---- ---> ||||
400  // ---- ---- ||||
401  z13 = wsptr[4] + wsptr[5];
402  z10 = wsptr[4] - wsptr[5];
403  z11 = wsptr[6] + wsptr[7];
404  z12 = wsptr[6] - wsptr[7];
405 
406  tmp7 = z11 + z13;
407  tmp11 = MULTIPLY16H(z11 - z13, FIX_1_414213562);
408 
409  z5 = MULTIPLY16H(z10 + z12, FIX_1_847759065);
410  tmp10 = MULTIPLY16H(z12, FIX_1_082392200) - z5;
411  tmp12 = MULTIPLY16H(z10, FIX_2_613125930) + z5; // - FIX_
412 
413  tmp6 = (tmp12 << 3) - tmp7;
414  tmp5 = (tmp11 << 3) - tmp6;
415  tmp4 = (tmp10 << 3) + tmp5;
416 
417  // Final output stage: descale and write column
418  outptr[0 * output_stride] += DESCALE(tmp0 + tmp7, 3);
419  outptr[1 * output_stride] += DESCALE(tmp1 + tmp6, 3);
420  outptr[2 * output_stride] += DESCALE(tmp2 + tmp5, 3);
421  outptr[3 * output_stride] += DESCALE(tmp3 - tmp4, 3);
422  outptr[4 * output_stride] += DESCALE(tmp3 + tmp4, 3);
423  outptr[5 * output_stride] += DESCALE(tmp2 - tmp5, 3);
424  outptr[6 * output_stride] += DESCALE(tmp1 - tmp6, 3); //no += ?
425  outptr[7 * output_stride] += DESCALE(tmp0 - tmp7, 3); //no += ?
426  outptr++;
427 
428  wsptr += DCTSIZE; // advance pointer to next row
429  }
430 }
431 
432 static void row_fdct_c(int16_t *data, const uint8_t *pixels, ptrdiff_t line_size, int cnt)
433 {
434  int_simd16_t tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
435  int_simd16_t tmp10, tmp11, tmp12, tmp13;
436  int_simd16_t z1, z2, z3, z4, z5, z11, z13;
437  int16_t *dataptr;
438 
439  cnt *= 4;
440  // Pass 1: process rows.
441 
442  dataptr = data;
443  for (; cnt > 0; cnt--) {
444  tmp0 = pixels[line_size * 0] + pixels[line_size * 7];
445  tmp7 = pixels[line_size * 0] - pixels[line_size * 7];
446  tmp1 = pixels[line_size * 1] + pixels[line_size * 6];
447  tmp6 = pixels[line_size * 1] - pixels[line_size * 6];
448  tmp2 = pixels[line_size * 2] + pixels[line_size * 5];
449  tmp5 = pixels[line_size * 2] - pixels[line_size * 5];
450  tmp3 = pixels[line_size * 3] + pixels[line_size * 4];
451  tmp4 = pixels[line_size * 3] - pixels[line_size * 4];
452 
453  // Even part
454 
455  tmp10 = tmp0 + tmp3;
456  tmp13 = tmp0 - tmp3;
457  tmp11 = tmp1 + tmp2;
458  tmp12 = tmp1 - tmp2;
459  //Even columns are written first, this leads to different order of columns
460  //in column_fidct(), but they are processed independently, so all ok.
461  //Later in the row_idct() columns readed at the same order.
462  dataptr[2] = tmp10 + tmp11;
463  dataptr[3] = tmp10 - tmp11;
464 
465  z1 = MULTIPLY16H((tmp12 + tmp13) << 2, FIX_0_707106781);
466  dataptr[0] = tmp13 + z1;
467  dataptr[1] = tmp13 - z1;
468 
469  // Odd part
470 
471  tmp10 = (tmp4 + tmp5) << 2;
472  tmp11 = (tmp5 + tmp6) << 2;
473  tmp12 = (tmp6 + tmp7) << 2;
474 
475  z5 = MULTIPLY16H(tmp10 - tmp12, FIX_0_382683433);
476  z2 = MULTIPLY16H(tmp10, FIX_0_541196100) + z5;
477  z4 = MULTIPLY16H(tmp12, FIX_1_306562965) + z5;
478  z3 = MULTIPLY16H(tmp11, FIX_0_707106781);
479 
480  z11 = tmp7 + z3;
481  z13 = tmp7 - z3;
482 
483  dataptr[4] = z13 + z2;
484  dataptr[5] = z13 - z2;
485  dataptr[6] = z11 + z4;
486  dataptr[7] = z11 - z4;
487 
488  pixels++; // advance pointer to next column
489  dataptr += DCTSIZE;
490  }
491 }
492 
494 {
495  static const enum AVPixelFormat pix_fmts[] = {
503  };
504 
505  AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
506  if (!fmts_list)
507  return AVERROR(ENOMEM);
508  return ff_set_common_formats(ctx, fmts_list);
509 }
510 
511 static int config_input(AVFilterLink *inlink)
512 {
513  AVFilterContext *ctx = inlink->dst;
514  FSPPContext *fspp = ctx->priv;
515  const int h = FFALIGN(inlink->h + 16, 16);
517 
518  fspp->hsub = desc->log2_chroma_w;
519  fspp->vsub = desc->log2_chroma_h;
520 
521  fspp->temp_stride = FFALIGN(inlink->w + 16, 16);
522  fspp->temp = av_malloc_array(fspp->temp_stride, h * sizeof(*fspp->temp));
523  fspp->src = av_malloc_array(fspp->temp_stride, h * sizeof(*fspp->src));
524 
525  if (!fspp->temp || !fspp->src)
526  return AVERROR(ENOMEM);
527 
528  if (!fspp->use_bframe_qp && !fspp->qp) {
529  fspp->non_b_qp_alloc_size = AV_CEIL_RSHIFT(inlink->w, 4) * AV_CEIL_RSHIFT(inlink->h, 4);
530  fspp->non_b_qp_table = av_calloc(fspp->non_b_qp_alloc_size, sizeof(*fspp->non_b_qp_table));
531  if (!fspp->non_b_qp_table)
532  return AVERROR(ENOMEM);
533  }
534 
535  fspp->store_slice = store_slice_c;
537  fspp->mul_thrmat = mul_thrmat_c;
539  fspp->row_idct = row_idct_c;
540  fspp->row_fdct = row_fdct_c;
541 
542  if (ARCH_X86)
543  ff_fspp_init_x86(fspp);
544 
545  return 0;
546 }
547 
548 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
549 {
550  AVFilterContext *ctx = inlink->dst;
551  FSPPContext *fspp = ctx->priv;
552  AVFilterLink *outlink = ctx->outputs[0];
553  AVFrame *out = in;
554 
555  int qp_stride = 0;
556  uint8_t *qp_table = NULL;
557  int i, bias;
558  int custom_threshold_m[64];
559 
560  bias = (1 << 4) + fspp->strength;
561 
562  for (i = 0; i < 64; i++) //FIXME: tune custom_threshold[] and remove this !
563  custom_threshold_m[i] = (int)(custom_threshold[i] * (bias / 71.0) + 0.5);
564 
565  for (i = 0; i < 8; i++) {
566  fspp->threshold_mtx_noq[2 * i] = (uint64_t)custom_threshold_m[i * 8 + 2]
567  |(((uint64_t)custom_threshold_m[i * 8 + 6]) << 16)
568  |(((uint64_t)custom_threshold_m[i * 8 + 0]) << 32)
569  |(((uint64_t)custom_threshold_m[i * 8 + 4]) << 48);
570 
571  fspp->threshold_mtx_noq[2 * i + 1] = (uint64_t)custom_threshold_m[i * 8 + 5]
572  |(((uint64_t)custom_threshold_m[i * 8 + 3]) << 16)
573  |(((uint64_t)custom_threshold_m[i * 8 + 1]) << 32)
574  |(((uint64_t)custom_threshold_m[i * 8 + 7]) << 48);
575  }
576 
577  if (fspp->qp)
578  fspp->prev_q = fspp->qp, fspp->mul_thrmat((int16_t *)(&fspp->threshold_mtx_noq[0]), (int16_t *)(&fspp->threshold_mtx[0]), fspp->qp);
579 
580  /* if we are not in a constant user quantizer mode and we don't want to use
581  * the quantizers from the B-frames (B-frames often have a higher QP), we
582  * need to save the qp table from the last non B-frame; this is what the
583  * following code block does */
584  if (!fspp->qp) {
585  qp_table = av_frame_get_qp_table(in, &qp_stride, &fspp->qscale_type);
586 
587  if (qp_table && !fspp->use_bframe_qp && in->pict_type != AV_PICTURE_TYPE_B) {
588  int w, h;
589 
590  /* if the qp stride is not set, it means the QP are only defined on
591  * a line basis */
592  if (!qp_stride) {
593  w = AV_CEIL_RSHIFT(inlink->w, 4);
594  h = 1;
595  } else {
596  w = qp_stride;
597  h = AV_CEIL_RSHIFT(inlink->h, 4);
598  }
599  if (w * h > fspp->non_b_qp_alloc_size) {
600  int ret = av_reallocp_array(&fspp->non_b_qp_table, w, h);
601  if (ret < 0) {
602  fspp->non_b_qp_alloc_size = 0;
603  return ret;
604  }
605  fspp->non_b_qp_alloc_size = w * h;
606  }
607 
608  av_assert0(w * h <= fspp->non_b_qp_alloc_size);
609  memcpy(fspp->non_b_qp_table, qp_table, w * h);
610  }
611  }
612 
613  if (fspp->log2_count && !ctx->is_disabled) {
614  if (!fspp->use_bframe_qp && fspp->non_b_qp_table)
615  qp_table = fspp->non_b_qp_table;
616 
617  if (qp_table || fspp->qp) {
618  const int cw = AV_CEIL_RSHIFT(inlink->w, fspp->hsub);
619  const int ch = AV_CEIL_RSHIFT(inlink->h, fspp->vsub);
620 
621  /* get a new frame if in-place is not possible or if the dimensions
622  * are not multiple of 8 */
623  if (!av_frame_is_writable(in) || (inlink->w & 7) || (inlink->h & 7)) {
624  const int aligned_w = FFALIGN(inlink->w, 8);
625  const int aligned_h = FFALIGN(inlink->h, 8);
626 
627  out = ff_get_video_buffer(outlink, aligned_w, aligned_h);
628  if (!out) {
629  av_frame_free(&in);
630  return AVERROR(ENOMEM);
631  }
632  av_frame_copy_props(out, in);
633  out->width = in->width;
634  out->height = in->height;
635  }
636 
637  filter(fspp, out->data[0], in->data[0], out->linesize[0], in->linesize[0],
638  inlink->w, inlink->h, qp_table, qp_stride, 1);
639  filter(fspp, out->data[1], in->data[1], out->linesize[1], in->linesize[1],
640  cw, ch, qp_table, qp_stride, 0);
641  filter(fspp, out->data[2], in->data[2], out->linesize[2], in->linesize[2],
642  cw, ch, qp_table, qp_stride, 0);
643  emms_c();
644  }
645  }
646 
647  if (in != out) {
648  if (in->data[3])
649  av_image_copy_plane(out->data[3], out->linesize[3],
650  in ->data[3], in ->linesize[3],
651  inlink->w, inlink->h);
652  av_frame_free(&in);
653  }
654  return ff_filter_frame(outlink, out);
655 }
656 
658 {
659  FSPPContext *fspp = ctx->priv;
660  av_freep(&fspp->temp);
661  av_freep(&fspp->src);
662  av_freep(&fspp->non_b_qp_table);
663 }
664 
665 static const AVFilterPad fspp_inputs[] = {
666  {
667  .name = "default",
668  .type = AVMEDIA_TYPE_VIDEO,
669  .config_props = config_input,
670  .filter_frame = filter_frame,
671  },
672  { NULL }
673 };
674 
675 static const AVFilterPad fspp_outputs[] = {
676  {
677  .name = "default",
678  .type = AVMEDIA_TYPE_VIDEO,
679  },
680  { NULL }
681 };
682 
684  .name = "fspp",
685  .description = NULL_IF_CONFIG_SMALL("Apply Fast Simple Post-processing filter."),
686  .priv_size = sizeof(FSPPContext),
687  .uninit = uninit,
689  .inputs = fspp_inputs,
690  .outputs = fspp_outputs,
691  .priv_class = &fspp_class,
693 };
static const int16_t FIX_1_414213562_A
Definition: vf_fspp.h:49
#define NULL
Definition: coverity.c:32
static void column_fidct_c(int16_t *thr_adr, int16_t *data, int16_t *output, int cnt)
Definition: vf_fspp.c:247
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2222
uint64_t threshold_mtx_noq[8 *2]
Definition: vf_fspp.h:57
This structure describes decoded (raw) audio or video data.
Definition: frame.h:184
AVOption.
Definition: opt.h:245
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
uint8_t * non_b_qp_table
Definition: vf_fspp.h:70
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:67
misc image utilities
static void store_slice2_c(uint8_t *dst, int16_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
Definition: vf_fspp.c:113
else temp
Definition: vf_mcdeint.c:259
const char * desc
Definition: nvenc.c:89
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:180
#define DECLARE_ALIGNED(n, t, v)
Definition: mem.h:53
#define BLOCKSZ
Definition: vf_fspp.h:28
#define FIX_1_414213562
Definition: 4xm.c:157
#define FIX_1_082392200
Definition: 4xm.c:156
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:76
uint64_t threshold_mtx[8 *2]
Definition: vf_fspp.h:58
static const short custom_threshold[64]
Definition: vf_fspp.c:68
int8_t * av_frame_get_qp_table(AVFrame *f, int *stride, int *type)
Definition: frame.c:64
int is_disabled
the enabled state from the last expression evaluation
Definition: avfilter.h:361
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:92
static void filter(FSPPContext *p, uint8_t *dst, uint8_t *src, int dst_stride, int src_stride, int width, int height, uint8_t *qp_store, int qp_stride, int is_luma)
Definition: vf_fspp.c:149
#define THRESHOLD(r, x, t)
Definition: vf_fspp.h:39
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:283
void(* row_fdct)(int16_t *data, const uint8_t *pixels, ptrdiff_t line_size, int cnt)
Definition: vf_fspp.h:90
static int16_t block[64]
Definition: dct.c:113
#define DESCALE(x, n)
Definition: jfdctfst.c:135
const char * name
Pad name.
Definition: internal.h:59
#define MULTIPLY16H(x, k)
Definition: vf_fspp.h:38
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
void(* mul_thrmat)(int16_t *thr_adr_noq, int16_t *thr_adr, int q)
Definition: vf_fspp.h:82
int use_bframe_qp
Definition: vf_fspp.h:72
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1180
uint8_t
#define av_cold
Definition: attributes.h:82
AVOptions.
static void row_fdct_c(int16_t *data, const uint8_t *pixels, ptrdiff_t line_size, int cnt)
Definition: vf_fspp.c:432
int non_b_qp_alloc_size
Definition: vf_fspp.h:71
#define height
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range...
Definition: pixfmt.h:101
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition: pixfmt.h:75
#define FIX_0_382683433
Definition: jfdctfst.c:116
#define FFALIGN(x, a)
Definition: macros.h:48
#define MAX_LEVEL
Definition: rl.h:36
A filter pad used for either input or output.
Definition: internal.h:53
int av_reallocp_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:215
static const AVFilterPad fspp_inputs[]
Definition: vf_fspp.c:665
int width
width and height of the video frame
Definition: frame.h:236
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:568
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition: pixdesc.h:101
static const AVOption fspp_options[]
Definition: vf_fspp.c:47
void ff_fspp_init_x86(FSPPContext *fspp)
Definition: vf_fspp_init.c:37
#define AVERROR(e)
Definition: error.h:43
uint8_t * src
Definition: vf_fspp.h:68
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:153
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:176
void(* row_idct)(int16_t *workspace, int16_t *output_adr, ptrdiff_t output_stride, int cnt)
Definition: vf_fspp.h:87
static const uint8_t dither[8][8]
Definition: vf_fspp.c:57
void * priv
private data for use by the filter
Definition: avfilter.h:320
#define FIX_1_847759065
Definition: 4xm.c:158
AVFILTER_DEFINE_CLASS(fspp)
simple assert() macros that are a bit more flexible than ISO C assert().
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:66
#define DCTSIZE
Definition: jfdctfst.c:74
enum AVPictureType pict_type
Picture type of the frame.
Definition: frame.h:258
AVFilter ff_vf_fspp
Definition: vf_fspp.c:683
#define FLAGS
Definition: vf_fspp.c:46
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:74
#define width
int vsub
Definition: vf_fspp.h:63
int32_t
AVFormatContext * ctx
Definition: movenc.c:48
static int config_input(AVFilterLink *inlink)
Definition: vf_fspp.c:511
#define FIX_1_306562965
Definition: jfdctfst.c:119
static const AVFilterPad outputs[]
Definition: af_afftfilt.c:386
#define src
Definition: vp9dsp.c:530
#define STORE2(pos)
#define STORE(pos)
int hsub
Definition: vf_fspp.h:62
static const AVFilterPad inputs[]
Definition: af_afftfilt.c:376
int av_frame_is_writable(AVFrame *frame)
Check if the frame data is writable.
Definition: frame.c:520
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:215
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:81
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(constuint8_t *) pi-0x80)*(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(constuint8_t *) pi-0x80)*(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(constint16_t *) pi >>8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t,*(constint16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t,*(constint16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(constint32_t *) pi >>24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t,*(constint32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t,*(constint32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(constfloat *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(constfloat *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(constfloat *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(constdouble *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(constdouble *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(constdouble *) pi *(1U<< 31))))#defineSET_CONV_FUNC_GROUP(ofmt, ifmt) staticvoidset_generic_function(AudioConvert *ac){}voidff_audio_convert_free(AudioConvert **ac){if(!*ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);}AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enumAVSampleFormatout_fmt, enumAVSampleFormatin_fmt, intchannels, intsample_rate, intapply_map){AudioConvert *ac;intin_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) returnNULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method!=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt)>2){ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc){av_free(ac);returnNULL;}returnac;}in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar){ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar?ac->channels:1;}elseif(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;elseac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);returnac;}intff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in){intuse_generic=1;intlen=in->nb_samples;intp;if(ac->dc){av_log(ac->avr, AV_LOG_TRACE,"%dsamples-audio_convert:%sto%s(dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));returnff_convert_dither(ac-> in
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:68
void(* store_slice2)(uint8_t *dst, int16_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
Definition: vf_fspp.h:78
Filter definition.
Definition: avfilter.h:142
int index
Definition: gxfenc.c:89
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_fspp.c:657
static void store_slice_c(uint8_t *dst, int16_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
Definition: vf_fspp.c:83
#define FIX_0_707106781
Definition: jfdctfst.c:118
#define OFFSET(x)
Definition: vf_fspp.c:45
int strength
Definition: vf_fspp.h:61
const char * name
Filter name.
Definition: avfilter.h:146
static int query_formats(AVFilterContext *ctx)
Definition: vf_fspp.c:493
#define AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
Same as AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, except that the filter will have its filter_frame() c...
Definition: avfilter.h:131
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:317
int qscale_type
Definition: vf_fspp.h:66
static int ff_norm_qscale(int qscale, int type)
Normalize the qscale factor FIXME the H264 qscale is a log based scale, mpeg1/2 is not...
Definition: internal.h:394
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:262
static void row_idct_c(int16_t *workspace, int16_t *output_adr, ptrdiff_t output_stride, int cnt)
Definition: vf_fspp.c:370
void * av_calloc(size_t nmemb, size_t size)
Allocate a block of nmemb * size bytes with alignment suitable for all memory accesses (including vec...
Definition: mem.c:260
static int flags
Definition: cpu.c:47
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:198
void(* store_slice)(uint8_t *dst, int16_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, ptrdiff_t width, ptrdiff_t height, ptrdiff_t log2_scale)
Definition: vf_fspp.h:74
#define FIX_2_613125930
Definition: 4xm.c:159
GLint GLenum GLboolean GLsizei stride
Definition: opengl_enc.c:105
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:62
Y , 8bpp.
Definition: pixfmt.h:70
static void mul_thrmat_c(int16_t *thr_adr_noq, int16_t *thr_adr, int q)
Definition: vf_fspp.c:142
Bi-dir predicted.
Definition: avutil.h:268
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition: pixfmt.h:76
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition: pixfmt.h:69
void(* column_fidct)(int16_t *thr_adr, int16_t *data, int16_t *output, int cnt)
Definition: vf_fspp.h:84
#define FIX_0_541196100
Definition: jfdctfst.c:117
int pixels
Definition: avisynth_c.h:298
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_fspp.c:548
int32_t int_simd16_t
Definition: vf_fspp.h:44
A list of supported formats for one end of a filter link.
Definition: formats.h:64
An instance of a filter.
Definition: avfilter.h:305
int height
Definition: frame.h:236
int qp
Definition: vf_fspp.h:65
FILE * out
Definition: movenc.c:54
int log2_count
Definition: vf_fspp.h:60
#define av_freep(p)
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition: pixfmt.h:100
static const AVFilterPad fspp_outputs[]
Definition: vf_fspp.c:675
#define av_malloc_array(a, b)
int prev_q
Definition: vf_fspp.h:67
void av_image_copy_plane(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, int bytewidth, int height)
Copy image plane from src to dst.
Definition: imgutils.c:287
int temp_stride
Definition: vf_fspp.h:64
#define stride
internal API functions
int16_t * temp
Definition: vf_fspp.h:69
AVPixelFormat
Pixel format.
Definition: pixfmt.h:60
for(j=16;j >0;--j)
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:580
#define AV_CEIL_RSHIFT(a, b)
Definition: common.h:58