2 * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2013 Clément Bœsch <u pkh me>
5 * This file is part of FFmpeg.
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.
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.
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.
24 * 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)"
30 * Originally written by Michael Niedermayer for the MPlayer project, and
31 * ported by Clément Bœsch for FFmpeg.
34 #include "libavutil/avassert.h"
35 #include "libavutil/imgutils.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/pixdesc.h"
47 static const AVClass
*child_class_next(const AVClass
*prev
)
49 return prev
? NULL
: avcodec_dct_get_class();
52 static void *child_next(void *obj
, void *prev
)
55 return prev
? NULL
: s
->dct
;
58 #define OFFSET(x) offsetof(SPPContext, x)
59 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
60 static const AVOption spp_options
[] = {
61 { "quality", "set quality", OFFSET(log2_count
), AV_OPT_TYPE_INT
, {.i64
= 3}, 0, MAX_LEVEL
, FLAGS
},
62 { "qp", "force a constant quantizer parameter", OFFSET(qp
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 63, FLAGS
},
63 { "mode", "set thresholding mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
= MODE_HARD
}, 0, NB_MODES
- 1, FLAGS
, "mode" },
64 { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST
, {.i64
= MODE_HARD
}, INT_MIN
, INT_MAX
, FLAGS
, "mode" },
65 { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST
, {.i64
= MODE_SOFT
}, INT_MIN
, INT_MAX
, FLAGS
, "mode" },
66 { "use_bframe_qp", "use B-frames' QP", OFFSET(use_bframe_qp
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, FLAGS
},
70 static const AVClass spp_class
= {
72 .item_name
= av_default_item_name
,
73 .option
= spp_options
,
74 .version
= LIBAVUTIL_VERSION_INT
,
75 .category
= AV_CLASS_CATEGORY_FILTER
,
76 .child_class_next
= child_class_next
,
77 .child_next
= child_next
,
80 // XXX: share between filters?
81 DECLARE_ALIGNED(8, static const uint8_t, ldither
)[8][8] = {
82 { 0, 48, 12, 60, 3, 51, 15, 63 },
83 { 32, 16, 44, 28, 35, 19, 47, 31 },
84 { 8, 56, 4, 52, 11, 59, 7, 55 },
85 { 40, 24, 36, 20, 43, 27, 39, 23 },
86 { 2, 50, 14, 62, 1, 49, 13, 61 },
87 { 34, 18, 46, 30, 33, 17, 45, 29 },
88 { 10, 58, 6, 54, 9, 57, 5, 53 },
89 { 42, 26, 38, 22, 41, 25, 37, 21 },
92 static const uint8_t offset
[127][2] = {
94 {0,0}, {4,4}, // quality = 1
95 {0,0}, {2,2}, {6,4}, {4,6}, // quality = 2
96 {0,0}, {5,1}, {2,2}, {7,3}, {4,4}, {1,5}, {6,6}, {3,7}, // quality = 3
98 {0,0}, {4,0}, {1,1}, {5,1}, {3,2}, {7,2}, {2,3}, {6,3}, // quality = 4
99 {0,4}, {4,4}, {1,5}, {5,5}, {3,6}, {7,6}, {2,7}, {6,7},
101 {0,0}, {0,2}, {0,4}, {0,6}, {1,1}, {1,3}, {1,5}, {1,7}, // quality = 5
102 {2,0}, {2,2}, {2,4}, {2,6}, {3,1}, {3,3}, {3,5}, {3,7},
103 {4,0}, {4,2}, {4,4}, {4,6}, {5,1}, {5,3}, {5,5}, {5,7},
104 {6,0}, {6,2}, {6,4}, {6,6}, {7,1}, {7,3}, {7,5}, {7,7},
106 {0,0}, {4,4}, {0,4}, {4,0}, {2,2}, {6,6}, {2,6}, {6,2}, // quality = 6
107 {0,2}, {4,6}, {0,6}, {4,2}, {2,0}, {6,4}, {2,4}, {6,0},
108 {1,1}, {5,5}, {1,5}, {5,1}, {3,3}, {7,7}, {3,7}, {7,3},
109 {1,3}, {5,7}, {1,7}, {5,3}, {3,1}, {7,5}, {3,5}, {7,1},
110 {0,1}, {4,5}, {0,5}, {4,1}, {2,3}, {6,7}, {2,7}, {6,3},
111 {0,3}, {4,7}, {0,7}, {4,3}, {2,1}, {6,5}, {2,5}, {6,1},
112 {1,0}, {5,4}, {1,4}, {5,0}, {3,2}, {7,6}, {3,6}, {7,2},
113 {1,2}, {5,6}, {1,6}, {5,2}, {3,0}, {7,4}, {3,4}, {7,0},
116 static void hardthresh_c(int16_t dst
[64], const int16_t src
[64],
117 int qp
, const uint8_t *permutation
)
120 int bias
= 0; // FIXME
122 unsigned threshold1
= qp
* ((1<<4) - bias
) - 1;
123 unsigned threshold2
= threshold1
<< 1;
125 memset(dst
, 0, 64 * sizeof(dst
[0]));
126 dst
[0] = (src
[0] + 4) >> 3;
128 for (i
= 1; i
< 64; i
++) {
130 if (((unsigned)(level
+ threshold1
)) > threshold2
) {
131 const int j
= permutation
[i
];
132 dst
[j
] = (level
+ 4) >> 3;
137 static void softthresh_c(int16_t dst
[64], const int16_t src
[64],
138 int qp
, const uint8_t *permutation
)
141 int bias
= 0; //FIXME
143 unsigned threshold1
= qp
* ((1<<4) - bias
) - 1;
144 unsigned threshold2
= threshold1
<< 1;
146 memset(dst
, 0, 64 * sizeof(dst
[0]));
147 dst
[0] = (src
[0] + 4) >> 3;
149 for (i
= 1; i
< 64; i
++) {
151 if (((unsigned)(level
+ threshold1
)) > threshold2
) {
152 const int j
= permutation
[i
];
153 if (level
> 0) dst
[j
] = (level
- threshold1
+ 4) >> 3;
154 else dst
[j
] = (level
+ threshold1
+ 4) >> 3;
159 static void store_slice_c(uint8_t *dst
, const int16_t *src
,
160 int dst_linesize
, int src_linesize
,
161 int width
, int height
, int log2_scale
,
162 const uint8_t dither
[8][8])
166 #define STORE(pos) do { \
167 temp = ((src[x + y*src_linesize + pos] << log2_scale) + d[pos]) >> 6; \
169 temp = ~(temp >> 31); \
170 dst[x + y*dst_linesize + pos] = temp; \
173 for (y
= 0; y
< height
; y
++) {
174 const uint8_t *d
= dither
[y
];
175 for (x
= 0; x
< width
; x
+= 8) {
189 static inline void add_block(int16_t *dst
, int linesize
, const int16_t block
[64])
193 for (y
= 0; y
< 8; y
++) {
194 *(uint32_t *)&dst
[0 + y
*linesize
] += *(uint32_t *)&block
[0 + y
*8];
195 *(uint32_t *)&dst
[2 + y
*linesize
] += *(uint32_t *)&block
[2 + y
*8];
196 *(uint32_t *)&dst
[4 + y
*linesize
] += *(uint32_t *)&block
[4 + y
*8];
197 *(uint32_t *)&dst
[6 + y
*linesize
] += *(uint32_t *)&block
[6 + y
*8];
201 // XXX: export the function?
202 static inline int norm_qscale(int qscale
, int type
)
205 case FF_QSCALE_TYPE_MPEG1
: return qscale
;
206 case FF_QSCALE_TYPE_MPEG2
: return qscale
>> 1;
207 case FF_QSCALE_TYPE_H264
: return qscale
>> 2;
208 case FF_QSCALE_TYPE_VP56
: return (63 - qscale
+ 2) >> 2;
213 static void filter(SPPContext
*p
, uint8_t *dst
, uint8_t *src
,
214 int dst_linesize
, int src_linesize
, int width
, int height
,
215 const uint8_t *qp_table
, int qp_stride
, int is_luma
)
218 const int count
= 1 << p
->log2_count
;
219 const int linesize
= is_luma
? p
->temp_linesize
: FFALIGN(width
+16, 16);
220 DECLARE_ALIGNED(16, uint64_t, block_align
)[32];
221 int16_t *block
= (int16_t *)block_align
;
222 int16_t *block2
= (int16_t *)(block_align
+ 16);
224 for (y
= 0; y
< height
; y
++) {
225 int index
= 8 + 8*linesize
+ y
*linesize
;
226 memcpy(p
->src
+ index
, src
+ y
*src_linesize
, width
);
227 for (x
= 0; x
< 8; x
++) {
228 p
->src
[index
- x
- 1] = p
->src
[index
+ x
];
229 p
->src
[index
+ width
+ x
] = p
->src
[index
+ width
- x
- 1];
232 for (y
= 0; y
< 8; y
++) {
233 memcpy(p
->src
+ ( 7-y
)*linesize
, p
->src
+ ( y
+8)*linesize
, linesize
);
234 memcpy(p
->src
+ (height
+8+y
)*linesize
, p
->src
+ (height
-y
+7)*linesize
, linesize
);
237 for (y
= 0; y
< height
+ 8; y
+= 8) {
238 memset(p
->temp
+ (8 + y
) * linesize
, 0, 8 * linesize
* sizeof(*p
->temp
));
239 for (x
= 0; x
< width
+ 8; x
+= 8) {
245 const int qps
= 3 + is_luma
;
246 qp
= qp_table
[(FFMIN(x
, width
- 1) >> qps
) + (FFMIN(y
, height
- 1) >> qps
) * qp_stride
];
247 qp
= FFMAX(1, norm_qscale(qp
, p
->qscale_type
));
249 for (i
= 0; i
< count
; i
++) {
250 const int x1
= x
+ offset
[i
+ count
- 1][0];
251 const int y1
= y
+ offset
[i
+ count
- 1][1];
252 const int index
= x1
+ y1
*linesize
;
253 p
->dct
->get_pixels(block
, p
->src
+ index
, linesize
);
255 p
->requantize(block2
, block
, qp
, p
->dct
->idct_permutation
);
256 p
->dct
->idct(block2
);
257 add_block(p
->temp
+ index
, linesize
, block2
);
261 p
->store_slice(dst
+ (y
- 8) * dst_linesize
, p
->temp
+ 8 + y
*linesize
,
262 dst_linesize
, linesize
, width
,
263 FFMIN(8, height
+ 8 - y
), MAX_LEVEL
- p
->log2_count
,
268 static int query_formats(AVFilterContext
*ctx
)
270 static const enum PixelFormat pix_fmts
[] = {
271 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
,
272 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV411P
,
273 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
274 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ422P
,
275 AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ440P
,
278 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
282 static int config_input(AVFilterLink
*inlink
)
284 SPPContext
*spp
= inlink
->dst
->priv
;
285 const int h
= FFALIGN(inlink
->h
+ 16, 16);
286 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
288 spp
->hsub
= desc
->log2_chroma_w
;
289 spp
->vsub
= desc
->log2_chroma_h
;
290 spp
->temp_linesize
= FFALIGN(inlink
->w
+ 16, 16);
291 spp
->temp
= av_malloc_array(spp
->temp_linesize
, h
* sizeof(*spp
->temp
));
292 spp
->src
= av_malloc_array(spp
->temp_linesize
, h
* sizeof(*spp
->src
));
293 if (!spp
->use_bframe_qp
) {
294 /* we are assuming here the qp blocks will not be smaller that 16x16 */
295 spp
->non_b_qp_alloc_size
= FF_CEIL_RSHIFT(inlink
->w
, 4) * FF_CEIL_RSHIFT(inlink
->h
, 4);
296 spp
->non_b_qp_table
= av_calloc(spp
->non_b_qp_alloc_size
, sizeof(*spp
->non_b_qp_table
));
297 if (!spp
->non_b_qp_table
)
298 return AVERROR(ENOMEM
);
300 if (!spp
->temp
|| !spp
->src
)
301 return AVERROR(ENOMEM
);
305 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
307 AVFilterContext
*ctx
= inlink
->dst
;
308 SPPContext
*spp
= ctx
->priv
;
309 AVFilterLink
*outlink
= ctx
->outputs
[0];
312 const int8_t *qp_table
= NULL
;
314 /* if we are not in a constant user quantizer mode and we don't want to use
315 * the quantizers from the B-frames (B-frames often have a higher QP), we
316 * need to save the qp table from the last non B-frame; this is what the
317 * following code block does */
319 qp_table
= av_frame_get_qp_table(in
, &qp_stride
, &spp
->qscale_type
);
321 if (qp_table
&& !spp
->use_bframe_qp
&& in
->pict_type
!= AV_PICTURE_TYPE_B
) {
324 /* if the qp stride is not set, it means the QP are only defined on
327 w
= FF_CEIL_RSHIFT(inlink
->w
, 4);
330 w
= FF_CEIL_RSHIFT(qp_stride
, 4);
331 h
= FF_CEIL_RSHIFT(inlink
->h
, 4);
333 av_assert0(w
* h
<= spp
->non_b_qp_alloc_size
);
334 memcpy(spp
->non_b_qp_table
, qp_table
, w
* h
);
338 if (spp
->log2_count
&& !ctx
->is_disabled
) {
339 if (!spp
->use_bframe_qp
&& spp
->non_b_qp_table
)
340 qp_table
= spp
->non_b_qp_table
;
342 if (qp_table
|| spp
->qp
) {
343 const int cw
= FF_CEIL_RSHIFT(inlink
->w
, spp
->hsub
);
344 const int ch
= FF_CEIL_RSHIFT(inlink
->h
, spp
->vsub
);
346 /* get a new frame if in-place is not possible or if the dimensions
347 * are not multiple of 8 */
348 if (!av_frame_is_writable(in
) || (inlink
->w
& 7) || (inlink
->h
& 7)) {
349 const int aligned_w
= FFALIGN(inlink
->w
, 8);
350 const int aligned_h
= FFALIGN(inlink
->h
, 8);
352 out
= ff_get_video_buffer(outlink
, aligned_w
, aligned_h
);
355 return AVERROR(ENOMEM
);
357 av_frame_copy_props(out
, in
);
358 out
->width
= in
->width
;
359 out
->height
= in
->height
;
362 filter(spp
, out
->data
[0], in
->data
[0], out
->linesize
[0], in
->linesize
[0], inlink
->w
, inlink
->h
, qp_table
, qp_stride
, 1);
363 filter(spp
, out
->data
[1], in
->data
[1], out
->linesize
[1], in
->linesize
[1], cw
, ch
, qp_table
, qp_stride
, 0);
364 filter(spp
, out
->data
[2], in
->data
[2], out
->linesize
[2], in
->linesize
[2], cw
, ch
, qp_table
, qp_stride
, 0);
371 av_image_copy_plane(out
->data
[3], out
->linesize
[3],
372 in
->data
[3], in
->linesize
[3],
373 inlink
->w
, inlink
->h
);
376 return ff_filter_frame(outlink
, out
);
379 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
380 char *res
, int res_len
, int flags
)
382 SPPContext
*spp
= ctx
->priv
;
384 if (!strcmp(cmd
, "level")) {
385 if (!strcmp(args
, "max"))
386 spp
->log2_count
= MAX_LEVEL
;
388 spp
->log2_count
= av_clip(strtol(args
, NULL
, 10), 0, MAX_LEVEL
);
391 return AVERROR(ENOSYS
);
394 static av_cold
int init_dict(AVFilterContext
*ctx
, AVDictionary
**opts
)
396 SPPContext
*spp
= ctx
->priv
;
399 spp
->avctx
= avcodec_alloc_context3(NULL
);
400 spp
->dct
= avcodec_dct_alloc();
401 if (!spp
->avctx
|| !spp
->dct
)
402 return AVERROR(ENOMEM
);
405 AVDictionaryEntry
*e
= NULL
;
407 while ((e
= av_dict_get(*opts
, "", e
, AV_DICT_IGNORE_SUFFIX
))) {
408 if ((ret
= av_opt_set(spp
->dct
, e
->key
, e
->value
, 0)) < 0)
414 avcodec_dct_init(spp
->dct
);
415 spp
->store_slice
= store_slice_c
;
417 case MODE_HARD
: spp
->requantize
= hardthresh_c
; break;
418 case MODE_SOFT
: spp
->requantize
= softthresh_c
; break;
421 ff_spp_init_x86(spp
);
425 static av_cold
void uninit(AVFilterContext
*ctx
)
427 SPPContext
*spp
= ctx
->priv
;
429 av_freep(&spp
->temp
);
432 avcodec_close(spp
->avctx
);
433 av_freep(&spp
->avctx
);
436 av_freep(&spp
->non_b_qp_table
);
439 static const AVFilterPad spp_inputs
[] = {
442 .type
= AVMEDIA_TYPE_VIDEO
,
443 .config_props
= config_input
,
444 .filter_frame
= filter_frame
,
449 static const AVFilterPad spp_outputs
[] = {
452 .type
= AVMEDIA_TYPE_VIDEO
,
457 AVFilter ff_vf_spp
= {
459 .description
= NULL_IF_CONFIG_SMALL("Apply a simple post processing filter."),
460 .priv_size
= sizeof(SPPContext
),
461 .init_dict
= init_dict
,
463 .query_formats
= query_formats
,
464 .inputs
= spp_inputs
,
465 .outputs
= spp_outputs
,
466 .process_command
= process_command
,
467 .priv_class
= &spp_class
,
468 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
,