2 * Copyright (c) 2016 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/imgutils.h"
22 #include "libavutil/pixdesc.h"
23 #include "libavutil/opt.h"
27 #include "framesync.h"
28 #include "maskedclamp.h"
30 #define OFFSET(x) offsetof(MaskedClampContext, x)
31 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
33 typedef struct ThreadData
{
34 AVFrame
*b
, *o
, *m
, *d
;
37 typedef struct MaskedClampContext
{
45 int width
[4], height
[4];
50 MaskedClampDSPContext dsp
;
53 static const AVOption maskedclamp_options
[] = {
54 { "undershoot", "set undershoot", OFFSET(undershoot
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, UINT16_MAX
, FLAGS
},
55 { "overshoot", "set overshoot", OFFSET(overshoot
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, UINT16_MAX
, FLAGS
},
56 { "planes", "set planes", OFFSET(planes
), AV_OPT_TYPE_INT
, {.i64
=0xF}, 0, 0xF, FLAGS
},
60 AVFILTER_DEFINE_CLASS(maskedclamp
);
62 static const enum AVPixelFormat pix_fmts
[] = {
63 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
64 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
65 AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUV420P
,
66 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
67 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
68 AV_PIX_FMT_YUV420P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV444P9
,
69 AV_PIX_FMT_YUV420P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV444P10
,
70 AV_PIX_FMT_YUV420P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV440P12
,
71 AV_PIX_FMT_YUV420P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV444P14
,
72 AV_PIX_FMT_YUV420P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV444P16
,
73 AV_PIX_FMT_YUVA420P9
, AV_PIX_FMT_YUVA422P9
, AV_PIX_FMT_YUVA444P9
,
74 AV_PIX_FMT_YUVA420P10
, AV_PIX_FMT_YUVA422P10
, AV_PIX_FMT_YUVA444P10
,
75 AV_PIX_FMT_YUVA422P12
, AV_PIX_FMT_YUVA444P12
,
76 AV_PIX_FMT_YUVA420P16
, AV_PIX_FMT_YUVA422P16
, AV_PIX_FMT_YUVA444P16
,
77 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
,
78 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
79 AV_PIX_FMT_GBRAP
, AV_PIX_FMT_GBRAP10
, AV_PIX_FMT_GBRAP12
, AV_PIX_FMT_GBRAP16
,
80 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY9
, AV_PIX_FMT_GRAY10
, AV_PIX_FMT_GRAY12
, AV_PIX_FMT_GRAY14
, AV_PIX_FMT_GRAY16
,
84 static int maskedclamp_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
86 MaskedClampContext
*s
= ctx
->priv
;
90 for (p
= 0; p
< s
->nb_planes
; p
++) {
91 const ptrdiff_t blinesize
= td
->b
->linesize
[p
];
92 const ptrdiff_t brightlinesize
= td
->m
->linesize
[p
];
93 const ptrdiff_t darklinesize
= td
->o
->linesize
[p
];
94 const ptrdiff_t dlinesize
= td
->d
->linesize
[p
];
95 const int w
= s
->width
[p
];
96 const int h
= s
->height
[p
];
97 const int slice_start
= (h
* jobnr
) / nb_jobs
;
98 const int slice_end
= (h
* (jobnr
+1)) / nb_jobs
;
99 const uint8_t *bsrc
= td
->b
->data
[p
] + slice_start
* blinesize
;
100 const uint8_t *darksrc
= td
->o
->data
[p
] + slice_start
* darklinesize
;
101 const uint8_t *brightsrc
= td
->m
->data
[p
] + slice_start
* brightlinesize
;
102 uint8_t *dst
= td
->d
->data
[p
] + slice_start
* dlinesize
;
103 const int undershoot
= s
->undershoot
;
104 const int overshoot
= s
->overshoot
;
107 if (!((1 << p
) & s
->planes
)) {
108 av_image_copy_plane(dst
, dlinesize
, bsrc
, blinesize
,
109 s
->linesize
[p
], slice_end
- slice_start
);
113 for (y
= slice_start
; y
< slice_end
; y
++) {
114 s
->dsp
.maskedclamp(bsrc
, dst
, darksrc
, brightsrc
, w
, undershoot
, overshoot
);
118 darksrc
+= darklinesize
;
119 brightsrc
+= brightlinesize
;
126 static int process_frame(FFFrameSync
*fs
)
128 AVFilterContext
*ctx
= fs
->parent
;
129 MaskedClampContext
*s
= fs
->opaque
;
130 AVFilterLink
*outlink
= ctx
->outputs
[0];
131 AVFrame
*out
, *base
, *dark
, *bright
;
134 if ((ret
= ff_framesync_get_frame(&s
->fs
, 0, &base
, 0)) < 0 ||
135 (ret
= ff_framesync_get_frame(&s
->fs
, 1, &dark
, 0)) < 0 ||
136 (ret
= ff_framesync_get_frame(&s
->fs
, 2, &bright
, 0)) < 0)
139 if (ctx
->is_disabled
) {
140 out
= av_frame_clone(base
);
142 return AVERROR(ENOMEM
);
146 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
148 return AVERROR(ENOMEM
);
149 av_frame_copy_props(out
, base
);
156 ff_filter_execute(ctx
, maskedclamp_slice
, &td
, NULL
,
157 FFMIN(s
->height
[0], ff_filter_get_nb_threads(ctx
)));
159 out
->pts
= av_rescale_q(s
->fs
.pts
, s
->fs
.time_base
, outlink
->time_base
);
161 return ff_filter_frame(outlink
, out
);
164 #define MASKEDCLAMP(type, name) \
165 static void maskedclamp##name(const uint8_t *bbsrc, uint8_t *ddst, \
166 const uint8_t *ddarksrc, const uint8_t *bbrightsrc, \
167 int w, int undershoot, int overshoot) \
169 const type *bsrc = (const type *)bbsrc; \
170 const type *darksrc = (const type *)ddarksrc; \
171 const type *brightsrc = (const type *)bbrightsrc; \
172 type *dst = (type *)ddst; \
174 for (int x = 0; x < w; x++) { \
175 dst[x] = FFMAX(bsrc[x], darksrc[x] - undershoot); \
176 dst[x] = FFMIN(dst[x], brightsrc[x] + overshoot); \
180 MASKEDCLAMP(uint8_t, 8)
181 MASKEDCLAMP(uint16_t, 16)
183 static int config_input(AVFilterLink
*inlink
)
185 AVFilterContext
*ctx
= inlink
->dst
;
186 MaskedClampContext
*s
= ctx
->priv
;
187 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
190 s
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
192 if ((ret
= av_image_fill_linesizes(s
->linesize
, inlink
->format
, inlink
->w
)) < 0)
195 hsub
= desc
->log2_chroma_w
;
196 vsub
= desc
->log2_chroma_h
;
197 s
->height
[1] = s
->height
[2] = AV_CEIL_RSHIFT(inlink
->h
, vsub
);
198 s
->height
[0] = s
->height
[3] = inlink
->h
;
199 s
->width
[1] = s
->width
[2] = AV_CEIL_RSHIFT(inlink
->w
, hsub
);
200 s
->width
[0] = s
->width
[3] = inlink
->w
;
202 s
->depth
= desc
->comp
[0].depth
;
203 s
->undershoot
= FFMIN(s
->undershoot
, (1 << s
->depth
) - 1);
204 s
->overshoot
= FFMIN(s
->overshoot
, (1 << s
->depth
) - 1);
207 s
->dsp
.maskedclamp
= maskedclamp8
;
209 s
->dsp
.maskedclamp
= maskedclamp16
;
212 ff_maskedclamp_init_x86(&s
->dsp
, s
->depth
);
218 static int config_output(AVFilterLink
*outlink
)
220 AVFilterContext
*ctx
= outlink
->src
;
221 MaskedClampContext
*s
= ctx
->priv
;
222 AVFilterLink
*base
= ctx
->inputs
[0];
223 AVFilterLink
*dark
= ctx
->inputs
[1];
224 AVFilterLink
*bright
= ctx
->inputs
[2];
225 FilterLink
*il
= ff_filter_link(base
);
226 FilterLink
*ol
= ff_filter_link(outlink
);
230 if (base
->w
!= dark
->w
|| base
->h
!= dark
->h
||
231 base
->w
!= bright
->w
|| base
->h
!= bright
->h
) {
232 av_log(ctx
, AV_LOG_ERROR
, "First input link %s parameters "
233 "(size %dx%d) do not match the corresponding "
234 "second input link %s parameters (%dx%d) "
235 "and/or third input link %s parameters (size %dx%d)\n",
236 ctx
->input_pads
[0].name
, base
->w
, base
->h
,
237 ctx
->input_pads
[1].name
, dark
->w
, dark
->h
,
238 ctx
->input_pads
[2].name
, bright
->w
, bright
->h
);
239 return AVERROR(EINVAL
);
242 outlink
->w
= base
->w
;
243 outlink
->h
= base
->h
;
244 outlink
->sample_aspect_ratio
= base
->sample_aspect_ratio
;
245 ol
->frame_rate
= il
->frame_rate
;
247 if ((ret
= ff_framesync_init(&s
->fs
, ctx
, 3)) < 0)
251 in
[0].time_base
= base
->time_base
;
252 in
[1].time_base
= dark
->time_base
;
253 in
[2].time_base
= bright
->time_base
;
255 in
[0].before
= EXT_STOP
;
256 in
[0].after
= EXT_INFINITY
;
258 in
[1].before
= EXT_STOP
;
259 in
[1].after
= EXT_INFINITY
;
261 in
[2].before
= EXT_STOP
;
262 in
[2].after
= EXT_INFINITY
;
264 s
->fs
.on_event
= process_frame
;
266 ret
= ff_framesync_configure(&s
->fs
);
267 outlink
->time_base
= s
->fs
.time_base
;
272 static int activate(AVFilterContext
*ctx
)
274 MaskedClampContext
*s
= ctx
->priv
;
275 return ff_framesync_activate(&s
->fs
);
278 static av_cold
void uninit(AVFilterContext
*ctx
)
280 MaskedClampContext
*s
= ctx
->priv
;
282 ff_framesync_uninit(&s
->fs
);
285 static const AVFilterPad maskedclamp_inputs
[] = {
288 .type
= AVMEDIA_TYPE_VIDEO
,
289 .config_props
= config_input
,
293 .type
= AVMEDIA_TYPE_VIDEO
,
297 .type
= AVMEDIA_TYPE_VIDEO
,
301 static const AVFilterPad maskedclamp_outputs
[] = {
304 .type
= AVMEDIA_TYPE_VIDEO
,
305 .config_props
= config_output
,
309 const FFFilter ff_vf_maskedclamp
= {
310 .p
.name
= "maskedclamp",
311 .p
.description
= NULL_IF_CONFIG_SMALL("Clamp first stream with second stream and third stream."),
312 .p
.priv_class
= &maskedclamp_class
,
313 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
|
314 AVFILTER_FLAG_SLICE_THREADS
,
315 .priv_size
= sizeof(MaskedClampContext
),
317 .activate
= activate
,
318 FILTER_INPUTS(maskedclamp_inputs
),
319 FILTER_OUTPUTS(maskedclamp_outputs
),
320 FILTER_PIXFMTS_ARRAY(pix_fmts
),
321 .process_command
= ff_filter_process_command
,