FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_pp7.c
Date: 2026-07-19 01:11:25
Exec Total Coverage
Lines: 124 155 80.0%
Functions: 7 9 77.8%
Branches: 60 89 67.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 /**
23 * @file
24 * Postprocessing filter - 7
25 *
26 * Originally written by Michael Niedermayer for the MPlayer
27 * project, and ported by Arwa Arif for FFmpeg.
28 */
29
30 #include "libavutil/imgutils.h"
31 #include "libavutil/mem.h"
32 #include "libavutil/mem_internal.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/pixdesc.h"
35 #include "libavutil/video_enc_params.h"
36
37 #include "avfilter.h"
38 #include "filters.h"
39 #include "qp_table.h"
40 #include "vf_pp7dsp.h"
41 #include "video.h"
42
43 enum mode {
44 MODE_HARD,
45 MODE_SOFT,
46 MODE_MEDIUM
47 };
48
49 typedef struct PP7Context {
50 const AVClass *class;
51 int thres2[99][16];
52
53 int qp;
54 int mode;
55 enum AVVideoEncParamsType qscale_type;
56 int hsub;
57 int vsub;
58 int temp_stride;
59 uint8_t *src;
60
61 int (*requantize)(const struct PP7Context *p, const int16_t *src, int qp);
62
63 PP7DSPContext pp7dsp;
64 } PP7Context;
65
66 #define OFFSET(x) offsetof(PP7Context, x)
67 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
68 static const AVOption pp7_options[] = {
69 { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 64, FLAGS },
70 { "mode", "set thresholding mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_MEDIUM}, 0, 2, FLAGS, .unit = "mode" },
71 { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_HARD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
72 { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_SOFT}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
73 { "medium", "medium thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_MEDIUM}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
74 { NULL }
75 };
76
77 AVFILTER_DEFINE_CLASS(pp7);
78
79 DECLARE_ALIGNED(8, static const uint8_t, dither)[8][8] = {
80 { 0, 48, 12, 60, 3, 51, 15, 63, },
81 { 32, 16, 44, 28, 35, 19, 47, 31, },
82 { 8, 56, 4, 52, 11, 59, 7, 55, },
83 { 40, 24, 36, 20, 43, 27, 39, 23, },
84 { 2, 50, 14, 62, 1, 49, 13, 61, },
85 { 34, 18, 46, 30, 33, 17, 45, 29, },
86 { 10, 58, 6, 54, 9, 57, 5, 53, },
87 { 42, 26, 38, 22, 41, 25, 37, 21, },
88 };
89
90 #define N0 4
91 #define N1 5
92 #define N2 10
93 #define SN0 2
94 #define SN1 2.2360679775
95 #define SN2 3.16227766017
96 #define N (1 << 16)
97
98 static const int factor[16] = {
99 N / (N0 * N0), N / (N0 * N1), N / (N0 * N0), N / (N0 * N2),
100 N / (N1 * N0), N / (N1 * N1), N / (N1 * N0), N / (N1 * N2),
101 N / (N0 * N0), N / (N0 * N1), N / (N0 * N0), N / (N0 * N2),
102 N / (N2 * N0), N / (N2 * N1), N / (N2 * N0), N / (N2 * N2),
103 };
104
105 1 static void init_thres2(PP7Context *p)
106 {
107 int qp, i;
108 1 int bias = 0; //FIXME
109
110
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 1 times.
100 for (qp = 0; qp < 99; qp++) {
111
2/2
✓ Branch 0 taken 1584 times.
✓ Branch 1 taken 99 times.
1683 for (i = 0; i < 16; i++) {
112
6/6
✓ Branch 0 taken 792 times.
✓ Branch 1 taken 792 times.
✓ Branch 2 taken 792 times.
✓ Branch 3 taken 792 times.
✓ Branch 4 taken 1568 times.
✓ Branch 5 taken 16 times.
1584 p->thres2[qp][i] = ((i&1) ? SN2 : SN0) * ((i&4) ? SN2 : SN0) * FFMAX(1, qp) * (1<<2) - 1 - bias;
113 }
114 }
115 1 }
116
117 235008 static inline void dctA_c(int16_t *dst, const uint8_t *src, int stride)
118 {
119 int i;
120
121
2/2
✓ Branch 0 taken 940032 times.
✓ Branch 1 taken 235008 times.
1175040 for (i = 0; i < 4; i++) {
122 940032 int s0 = src[0 * stride] + src[6 * stride];
123 940032 int s1 = src[1 * stride] + src[5 * stride];
124 940032 int s2 = src[2 * stride] + src[4 * stride];
125 940032 int s3 = src[3 * stride];
126 940032 int s = s3 + s3;
127 940032 s3 = s - s0;
128 940032 s0 = s + s0;
129 940032 s = s2 + s1;
130 940032 s2 = s2 - s1;
131 940032 dst[0] = s0 + s;
132 940032 dst[2] = s0 - s;
133 940032 dst[1] = 2 * s3 + s2;
134 940032 dst[3] = s3 - 2 * s2;
135 940032 src++;
136 940032 dst += 4;
137 }
138 235008 }
139
140 static int hardthresh_c(const PP7Context *p, const int16_t *src, int qp)
141 {
142 int i;
143 int a;
144
145 a = src[0] * factor[0];
146 for (i = 1; i < 16; i++) {
147 unsigned int threshold1 = p->thres2[qp][i];
148 unsigned int threshold2 = threshold1 << 1;
149 int level = src[i];
150 if (((unsigned)(level + threshold1)) > threshold2)
151 a += level * factor[i];
152 }
153 return (a + (1 << 11)) >> 12;
154 }
155
156 912384 static int mediumthresh_c(const PP7Context *p, const int16_t *src, int qp)
157 {
158 int i;
159 int a;
160
161 912384 a = src[0] * factor[0];
162
2/2
✓ Branch 0 taken 13685760 times.
✓ Branch 1 taken 912384 times.
14598144 for (i = 1; i < 16; i++) {
163 13685760 unsigned int threshold1 = p->thres2[qp][i];
164 13685760 unsigned int threshold2 = threshold1 << 1;
165 13685760 int level = src[i];
166
2/2
✓ Branch 0 taken 6743694 times.
✓ Branch 1 taken 6942066 times.
13685760 if (((unsigned)(level + threshold1)) > threshold2) {
167
2/2
✓ Branch 0 taken 4707989 times.
✓ Branch 1 taken 2035705 times.
6743694 if (((unsigned)(level + 2 * threshold1)) > 2 * threshold2)
168 4707989 a += level * factor[i];
169 else {
170
2/2
✓ Branch 0 taken 1016686 times.
✓ Branch 1 taken 1019019 times.
2035705 if (level > 0)
171 1016686 a += 2 * (level - (int)threshold1) * factor[i];
172 else
173 1019019 a += 2 * (level + (int)threshold1) * factor[i];
174 }
175 }
176 }
177 912384 return (a + (1 << 11)) >> 12;
178 }
179
180 static int softthresh_c(const PP7Context *p, const int16_t *src, int qp)
181 {
182 int i;
183 int a;
184
185 a = src[0] * factor[0];
186 for (i = 1; i < 16; i++) {
187 unsigned int threshold1 = p->thres2[qp][i];
188 unsigned int threshold2 = threshold1 << 1;
189 int level = src[i];
190 if (((unsigned)(level + threshold1)) > threshold2) {
191 if (level > 0)
192 a += (level - (int)threshold1) * factor[i];
193 else
194 a += (level + (int)threshold1) * factor[i];
195 }
196 }
197 return (a + (1 << 11)) >> 12;
198 }
199
200 18 static void filter(PP7Context *p, uint8_t *dst, const uint8_t *src,
201 int dst_stride, int src_stride,
202 int width, int height,
203 const uint8_t *qp_store, int qp_stride, int is_luma)
204 {
205 int x, y;
206
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 12 times.
18 const int stride = is_luma ? p->temp_stride : ((width + 16 + 15) & (~15));
207 18 uint8_t *p_src = p->src + 8 * stride;
208 18 int16_t *block = (int16_t *)p->src;
209 18 int16_t *temp = (int16_t *)(p->src + 32);
210
211
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (!src || !dst) return;
212
2/2
✓ Branch 0 taken 3456 times.
✓ Branch 1 taken 18 times.
3474 for (y = 0; y < height; y++) {
213 3456 int index = 8 + 8 * stride + y * stride;
214 3456 memcpy(p_src + index, src + y * src_stride, width);
215
2/2
✓ Branch 0 taken 27648 times.
✓ Branch 1 taken 3456 times.
31104 for (x = 0; x < 8; x++) {
216 27648 p_src[index - x - 1]= p_src[index + x ];
217 27648 p_src[index + width + x ]= p_src[index + width - x - 1];
218 }
219 }
220
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 18 times.
162 for (y = 0; y < 8; y++) {
221 144 memcpy(p_src + ( 7 - y ) * stride, p_src + ( y + 8 ) * stride, stride);
222 144 memcpy(p_src + (height + 8 + y) * stride, p_src + (height - y + 7) * stride, stride);
223 }
224 //FIXME (try edge emu)
225
226
2/2
✓ Branch 0 taken 3456 times.
✓ Branch 1 taken 18 times.
3474 for (y = 0; y < height; y++) {
227
2/2
✓ Branch 0 taken 6912 times.
✓ Branch 1 taken 3456 times.
10368 for (x = -8; x < 0; x += 4) {
228 6912 const int index = x + y * stride + (8 - 3) * (1 + stride) + 8; //FIXME silly offset
229 6912 int16_t *tp = temp + 4 * x;
230
231 6912 dctA_c(tp + 4 * 8, p_src + index, stride);
232 }
233
2/2
✓ Branch 0 taken 114048 times.
✓ Branch 1 taken 3456 times.
117504 for (x = 0; x < width; ) {
234 114048 const int qps = 3 + is_luma;
235 int qp;
236
1/2
✓ Branch 0 taken 114048 times.
✗ Branch 1 not taken.
114048 int end = FFMIN(x + 8, width);
237
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114048 times.
114048 if (p->qp)
239 qp = p->qp;
240 else {
241
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 114048 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 114048 times.
114048 qp = qp_store[ (FFMIN(x, width - 1) >> qps) + (FFMIN(y, height - 1) >> qps) * qp_stride];
242 114048 qp = ff_norm_qscale(qp, p->qscale_type);
243 }
244
2/2
✓ Branch 0 taken 912384 times.
✓ Branch 1 taken 114048 times.
1026432 for (; x < end; x++) {
245 912384 const int index = x + y * stride + (8 - 3) * (1 + stride) + 8; //FIXME silly offset
246 912384 int16_t *tp = temp + 4 * x;
247 int v;
248
249
2/2
✓ Branch 0 taken 228096 times.
✓ Branch 1 taken 684288 times.
912384 if ((x & 3) == 0)
250 228096 dctA_c(tp + 4 * 8, p_src + index, stride);
251
252 912384 p->pp7dsp.dctB(block, tp);
253
254 912384 v = p->requantize(p, block, qp);
255 912384 v = (v + dither[y & 7][x & 7]) >> 6;
256
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 912375 times.
912384 if ((unsigned)v > 255)
257 9 v = (-v) >> 31;
258 912384 dst[x + y * dst_stride] = v;
259 }
260 }
261 }
262 }
263
264 static const enum AVPixelFormat pix_fmts[] = {
265 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P,
266 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P,
267 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV440P,
268 AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,
269 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ440P,
270 AV_PIX_FMT_GBRP,
271 AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE
272 };
273
274 1 static int config_input(AVFilterLink *inlink)
275 {
276 1 AVFilterContext *ctx = inlink->dst;
277 1 PP7Context *pp7 = ctx->priv;
278 1 const int h = FFALIGN(inlink->h + 16, 16);
279 1 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
280
281 1 pp7->hsub = desc->log2_chroma_w;
282 1 pp7->vsub = desc->log2_chroma_h;
283
284 1 pp7->temp_stride = FFALIGN(inlink->w + 16, 16);
285 1 pp7->src = av_malloc_array(pp7->temp_stride, (h + 8) * sizeof(uint8_t));
286
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!pp7->src)
288 return AVERROR(ENOMEM);
289
290 1 init_thres2(pp7);
291
292
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 switch (pp7->mode) {
293 case 0: pp7->requantize = hardthresh_c; break;
294 case 1: pp7->requantize = softthresh_c; break;
295 1 default:
296 1 case 2: pp7->requantize = mediumthresh_c; break;
297 }
298
299 1 ff_pp7dsp_init(&pp7->pp7dsp);
300
301 1 return 0;
302 }
303
304 6 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
305 {
306 6 AVFilterContext *ctx = inlink->dst;
307 6 PP7Context *pp7 = ctx->priv;
308 6 AVFilterLink *outlink = ctx->outputs[0];
309 6 AVFrame *out = in;
310
311 6 int qp_stride = 0;
312 6 int8_t *qp_table = NULL;
313
314
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (!pp7->qp) {
315 6 int ret = ff_qp_table_extract(in, &qp_table, &qp_stride, NULL, &pp7->qscale_type);
316
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (ret < 0) {
317 av_frame_free(&in);
318 return ret;
319 }
320 }
321
322
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (!ctx->is_disabled) {
323 6 const int cw = AV_CEIL_RSHIFT(inlink->w, pp7->hsub);
324 6 const int ch = AV_CEIL_RSHIFT(inlink->h, pp7->vsub);
325
326 /* get a new frame if in-place is not possible or if the dimensions
327 * are not multiple of 8 */
328
4/6
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
6 if (!av_frame_is_writable(in) || (inlink->w & 7) || (inlink->h & 7)) {
329 3 const int aligned_w = FFALIGN(inlink->w, 8);
330 3 const int aligned_h = FFALIGN(inlink->h, 8);
331
332 3 out = ff_get_video_buffer(outlink, aligned_w, aligned_h);
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!out) {
334 av_frame_free(&in);
335 av_freep(&qp_table);
336 return AVERROR(ENOMEM);
337 }
338 3 av_frame_copy_props(out, in);
339 3 out->width = in->width;
340 3 out->height = in->height;
341 }
342
343
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 if (qp_table || pp7->qp) {
344
345 6 filter(pp7, out->data[0], in->data[0], out->linesize[0], in->linesize[0],
346 inlink->w, inlink->h, qp_table, qp_stride, 1);
347 6 filter(pp7, out->data[1], in->data[1], out->linesize[1], in->linesize[1],
348 cw, ch, qp_table, qp_stride, 0);
349 6 filter(pp7, out->data[2], in->data[2], out->linesize[2], in->linesize[2],
350 cw, ch, qp_table, qp_stride, 0);
351 }
352 }
353
354
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if (in != out) {
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (in->data[3])
356 av_image_copy_plane(out->data[3], out->linesize[3],
357 in ->data[3], in ->linesize[3],
358 inlink->w, inlink->h);
359 3 av_frame_free(&in);
360 }
361 6 av_freep(&qp_table);
362 6 return ff_filter_frame(outlink, out);
363 }
364
365 2 static av_cold void uninit(AVFilterContext *ctx)
366 {
367 2 PP7Context *pp7 = ctx->priv;
368 2 av_freep(&pp7->src);
369 2 }
370
371 static const AVFilterPad pp7_inputs[] = {
372 {
373 .name = "default",
374 .type = AVMEDIA_TYPE_VIDEO,
375 .config_props = config_input,
376 .filter_frame = filter_frame,
377 },
378 };
379
380 const FFFilter ff_vf_pp7 = {
381 .p.name = "pp7",
382 .p.description = NULL_IF_CONFIG_SMALL("Apply Postprocessing 7 filter."),
383 .p.priv_class = &pp7_class,
384 .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
385 .priv_size = sizeof(PP7Context),
386 .uninit = uninit,
387 FILTER_INPUTS(pp7_inputs),
388 FILTER_OUTPUTS(ff_video_default_filterpad),
389 FILTER_PIXFMTS_ARRAY(pix_fmts),
390 };
391