blob: e1b79ab779e7cac2cb254f9f16dde9ad5fb1d375
1 | /* |
2 | * Copyright (c) 2016 Paul B Mahol |
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/imgutils.h" |
22 | #include "libavutil/pixdesc.h" |
23 | #include "libavutil/opt.h" |
24 | #include "avfilter.h" |
25 | #include "formats.h" |
26 | #include "framesync.h" |
27 | #include "internal.h" |
28 | #include "video.h" |
29 | |
30 | typedef struct PreMultiplyContext { |
31 | const AVClass *class; |
32 | int width[4], height[4]; |
33 | int nb_planes; |
34 | int planes; |
35 | int half, depth, offset; |
36 | FFFrameSync fs; |
37 | |
38 | void (*premultiply[4])(const uint8_t *msrc, const uint8_t *asrc, |
39 | uint8_t *dst, |
40 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
41 | ptrdiff_t dlinesize, |
42 | int w, int h, |
43 | int half, int shift, int offset); |
44 | } PreMultiplyContext; |
45 | |
46 | #define OFFSET(x) offsetof(PreMultiplyContext, x) |
47 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
48 | |
49 | static const AVOption premultiply_options[] = { |
50 | { NULL } |
51 | }; |
52 | |
53 | AVFILTER_DEFINE_CLASS(premultiply); |
54 | |
55 | static int query_formats(AVFilterContext *ctx) |
56 | { |
57 | static const enum AVPixelFormat pix_fmts[] = { |
58 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, |
59 | AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, |
60 | AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, |
61 | AV_PIX_FMT_YUV444P16, |
62 | AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, |
63 | AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, |
64 | AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY16, |
65 | AV_PIX_FMT_NONE |
66 | }; |
67 | |
68 | return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
69 | } |
70 | |
71 | static void premultiply8(const uint8_t *msrc, const uint8_t *asrc, |
72 | uint8_t *dst, |
73 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
74 | ptrdiff_t dlinesize, |
75 | int w, int h, |
76 | int half, int shift, int offset) |
77 | { |
78 | int x, y; |
79 | |
80 | for (y = 0; y < h; y++) { |
81 | for (x = 0; x < w; x++) { |
82 | dst[x] = ((msrc[x] * (((asrc[x] >> 1) & 1) + asrc[x])) + 128) >> 8; |
83 | } |
84 | |
85 | dst += dlinesize; |
86 | msrc += mlinesize; |
87 | asrc += alinesize; |
88 | } |
89 | } |
90 | |
91 | static void premultiply8yuv(const uint8_t *msrc, const uint8_t *asrc, |
92 | uint8_t *dst, |
93 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
94 | ptrdiff_t dlinesize, |
95 | int w, int h, |
96 | int half, int shift, int offset) |
97 | { |
98 | int x, y; |
99 | |
100 | for (y = 0; y < h; y++) { |
101 | for (x = 0; x < w; x++) { |
102 | dst[x] = ((((msrc[x] - 128) * (((asrc[x] >> 1) & 1) + asrc[x]))) >> 8) + 128; |
103 | } |
104 | |
105 | dst += dlinesize; |
106 | msrc += mlinesize; |
107 | asrc += alinesize; |
108 | } |
109 | } |
110 | |
111 | static void premultiply8offset(const uint8_t *msrc, const uint8_t *asrc, |
112 | uint8_t *dst, |
113 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
114 | ptrdiff_t dlinesize, |
115 | int w, int h, |
116 | int half, int shift, int offset) |
117 | { |
118 | int x, y; |
119 | |
120 | for (y = 0; y < h; y++) { |
121 | for (x = 0; x < w; x++) { |
122 | dst[x] = ((((msrc[x] - offset) * (((asrc[x] >> 1) & 1) + asrc[x])) + 128) >> 8) + offset; |
123 | } |
124 | |
125 | dst += dlinesize; |
126 | msrc += mlinesize; |
127 | asrc += alinesize; |
128 | } |
129 | } |
130 | |
131 | static void premultiply16(const uint8_t *mmsrc, const uint8_t *aasrc, |
132 | uint8_t *ddst, |
133 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
134 | ptrdiff_t dlinesize, |
135 | int w, int h, |
136 | int half, int shift, int offset) |
137 | { |
138 | const uint16_t *msrc = (const uint16_t *)mmsrc; |
139 | const uint16_t *asrc = (const uint16_t *)aasrc; |
140 | uint16_t *dst = (uint16_t *)ddst; |
141 | int x, y; |
142 | |
143 | for (y = 0; y < h; y++) { |
144 | for (x = 0; x < w; x++) { |
145 | dst[x] = ((msrc[x] * (((asrc[x] >> 1) & 1) + asrc[x])) + half) >> shift; |
146 | } |
147 | |
148 | dst += dlinesize / 2; |
149 | msrc += mlinesize / 2; |
150 | asrc += alinesize / 2; |
151 | } |
152 | } |
153 | |
154 | static void premultiply16yuv(const uint8_t *mmsrc, const uint8_t *aasrc, |
155 | uint8_t *ddst, |
156 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
157 | ptrdiff_t dlinesize, |
158 | int w, int h, |
159 | int half, int shift, int offset) |
160 | { |
161 | const uint16_t *msrc = (const uint16_t *)mmsrc; |
162 | const uint16_t *asrc = (const uint16_t *)aasrc; |
163 | uint16_t *dst = (uint16_t *)ddst; |
164 | int x, y; |
165 | |
166 | for (y = 0; y < h; y++) { |
167 | for (x = 0; x < w; x++) { |
168 | dst[x] = ((((msrc[x] - half) * (((asrc[x] >> 1) & 1) + asrc[x]))) >> shift) + half; |
169 | } |
170 | |
171 | dst += dlinesize / 2; |
172 | msrc += mlinesize / 2; |
173 | asrc += alinesize / 2; |
174 | } |
175 | } |
176 | |
177 | static void premultiply16offset(const uint8_t *mmsrc, const uint8_t *aasrc, |
178 | uint8_t *ddst, |
179 | ptrdiff_t mlinesize, ptrdiff_t alinesize, |
180 | ptrdiff_t dlinesize, |
181 | int w, int h, |
182 | int half, int shift, int offset) |
183 | { |
184 | const uint16_t *msrc = (const uint16_t *)mmsrc; |
185 | const uint16_t *asrc = (const uint16_t *)aasrc; |
186 | uint16_t *dst = (uint16_t *)ddst; |
187 | int x, y; |
188 | |
189 | for (y = 0; y < h; y++) { |
190 | for (x = 0; x < w; x++) { |
191 | dst[x] = ((((msrc[x] - offset) * (((asrc[x] >> 1) & 1) + asrc[x])) + half) >> shift) + offset; |
192 | } |
193 | |
194 | dst += dlinesize / 2; |
195 | msrc += mlinesize / 2; |
196 | asrc += alinesize / 2; |
197 | } |
198 | } |
199 | |
200 | static int process_frame(FFFrameSync *fs) |
201 | { |
202 | AVFilterContext *ctx = fs->parent; |
203 | PreMultiplyContext *s = fs->opaque; |
204 | AVFilterLink *outlink = ctx->outputs[0]; |
205 | AVFrame *out, *base, *alpha; |
206 | int ret; |
207 | |
208 | if ((ret = ff_framesync_get_frame(&s->fs, 0, &base, 0)) < 0 || |
209 | (ret = ff_framesync_get_frame(&s->fs, 1, &alpha, 0)) < 0) |
210 | return ret; |
211 | |
212 | if (ctx->is_disabled) { |
213 | out = av_frame_clone(base); |
214 | if (!out) |
215 | return AVERROR(ENOMEM); |
216 | } else { |
217 | int p, full, limited; |
218 | |
219 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
220 | if (!out) |
221 | return AVERROR(ENOMEM); |
222 | av_frame_copy_props(out, base); |
223 | |
224 | full = base->color_range == AVCOL_RANGE_JPEG; |
225 | limited = base->color_range == AVCOL_RANGE_MPEG; |
226 | |
227 | switch (outlink->format) { |
228 | case AV_PIX_FMT_YUV444P: |
229 | s->premultiply[0] = full ? premultiply8 : premultiply8offset; |
230 | s->premultiply[1] = premultiply8yuv; |
231 | s->premultiply[2] = premultiply8yuv; |
232 | break; |
233 | case AV_PIX_FMT_YUVJ444P: |
234 | s->premultiply[0] = premultiply8; |
235 | s->premultiply[1] = premultiply8yuv; |
236 | s->premultiply[2] = premultiply8yuv; |
237 | break; |
238 | case AV_PIX_FMT_GBRP: |
239 | s->premultiply[0] = limited ? premultiply8offset : premultiply8; |
240 | s->premultiply[1] = limited ? premultiply8offset : premultiply8; |
241 | s->premultiply[2] = limited ? premultiply8offset : premultiply8; |
242 | break; |
243 | case AV_PIX_FMT_YUV444P9: |
244 | case AV_PIX_FMT_YUV444P10: |
245 | case AV_PIX_FMT_YUV444P12: |
246 | case AV_PIX_FMT_YUV444P14: |
247 | case AV_PIX_FMT_YUV444P16: |
248 | s->premultiply[0] = full ? premultiply16 : premultiply16offset; |
249 | s->premultiply[1] = premultiply16yuv; |
250 | s->premultiply[2] = premultiply16yuv; |
251 | break; |
252 | case AV_PIX_FMT_GBRP9: |
253 | case AV_PIX_FMT_GBRP10: |
254 | case AV_PIX_FMT_GBRP12: |
255 | case AV_PIX_FMT_GBRP14: |
256 | case AV_PIX_FMT_GBRP16: |
257 | s->premultiply[0] = limited ? premultiply16offset : premultiply16; |
258 | s->premultiply[1] = limited ? premultiply16offset : premultiply16; |
259 | s->premultiply[2] = limited ? premultiply16offset : premultiply16; |
260 | break; |
261 | case AV_PIX_FMT_GRAY8: |
262 | s->premultiply[0] = limited ? premultiply8offset : premultiply8; |
263 | break; |
264 | case AV_PIX_FMT_GRAY10: |
265 | case AV_PIX_FMT_GRAY12: |
266 | case AV_PIX_FMT_GRAY16: |
267 | s->premultiply[0] = limited ? premultiply16offset : premultiply16; |
268 | break; |
269 | } |
270 | |
271 | for (p = 0; p < s->nb_planes; p++) { |
272 | s->premultiply[p](base->data[p], alpha->data[0], |
273 | out->data[p], |
274 | base->linesize[p], alpha->linesize[0], |
275 | out->linesize[p], |
276 | s->width[p], s->height[p], |
277 | s->half, s->depth, s->offset); |
278 | } |
279 | } |
280 | out->pts = av_rescale_q(base->pts, s->fs.time_base, outlink->time_base); |
281 | |
282 | return ff_filter_frame(outlink, out); |
283 | } |
284 | |
285 | static int config_input(AVFilterLink *inlink) |
286 | { |
287 | AVFilterContext *ctx = inlink->dst; |
288 | PreMultiplyContext *s = ctx->priv; |
289 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
290 | int vsub, hsub; |
291 | |
292 | s->nb_planes = av_pix_fmt_count_planes(inlink->format); |
293 | |
294 | hsub = desc->log2_chroma_w; |
295 | vsub = desc->log2_chroma_h; |
296 | s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, vsub); |
297 | s->height[0] = s->height[3] = inlink->h; |
298 | s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, hsub); |
299 | s->width[0] = s->width[3] = inlink->w; |
300 | |
301 | s->depth = desc->comp[0].depth; |
302 | s->half = (1 << s->depth) / 2; |
303 | s->offset = 16 << (s->depth - 8); |
304 | |
305 | return 0; |
306 | } |
307 | |
308 | static int config_output(AVFilterLink *outlink) |
309 | { |
310 | AVFilterContext *ctx = outlink->src; |
311 | PreMultiplyContext *s = ctx->priv; |
312 | AVFilterLink *base = ctx->inputs[0]; |
313 | AVFilterLink *alpha = ctx->inputs[1]; |
314 | FFFrameSyncIn *in; |
315 | int ret; |
316 | |
317 | if (base->format != alpha->format) { |
318 | av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n"); |
319 | return AVERROR(EINVAL); |
320 | } |
321 | if (base->w != alpha->w || |
322 | base->h != alpha->h) { |
323 | av_log(ctx, AV_LOG_ERROR, "First input link %s parameters " |
324 | "(size %dx%d) do not match the corresponding " |
325 | "second input link %s parameters (%dx%d) ", |
326 | ctx->input_pads[0].name, base->w, base->h, |
327 | ctx->input_pads[1].name, alpha->w, alpha->h); |
328 | return AVERROR(EINVAL); |
329 | } |
330 | |
331 | outlink->w = base->w; |
332 | outlink->h = base->h; |
333 | outlink->time_base = base->time_base; |
334 | outlink->sample_aspect_ratio = base->sample_aspect_ratio; |
335 | outlink->frame_rate = base->frame_rate; |
336 | |
337 | if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0) |
338 | return ret; |
339 | |
340 | in = s->fs.in; |
341 | in[0].time_base = base->time_base; |
342 | in[1].time_base = alpha->time_base; |
343 | in[0].sync = 1; |
344 | in[0].before = EXT_STOP; |
345 | in[0].after = EXT_INFINITY; |
346 | in[1].sync = 1; |
347 | in[1].before = EXT_STOP; |
348 | in[1].after = EXT_INFINITY; |
349 | s->fs.opaque = s; |
350 | s->fs.on_event = process_frame; |
351 | |
352 | return ff_framesync_configure(&s->fs); |
353 | } |
354 | |
355 | static int filter_frame(AVFilterLink *inlink, AVFrame *buf) |
356 | { |
357 | PreMultiplyContext *s = inlink->dst->priv; |
358 | return ff_framesync_filter_frame(&s->fs, inlink, buf); |
359 | } |
360 | |
361 | static int request_frame(AVFilterLink *outlink) |
362 | { |
363 | PreMultiplyContext *s = outlink->src->priv; |
364 | return ff_framesync_request_frame(&s->fs, outlink); |
365 | } |
366 | |
367 | static av_cold void uninit(AVFilterContext *ctx) |
368 | { |
369 | PreMultiplyContext *s = ctx->priv; |
370 | |
371 | ff_framesync_uninit(&s->fs); |
372 | } |
373 | |
374 | static const AVFilterPad premultiply_inputs[] = { |
375 | { |
376 | .name = "main", |
377 | .type = AVMEDIA_TYPE_VIDEO, |
378 | .filter_frame = filter_frame, |
379 | .config_props = config_input, |
380 | }, |
381 | { |
382 | .name = "alpha", |
383 | .type = AVMEDIA_TYPE_VIDEO, |
384 | .filter_frame = filter_frame, |
385 | }, |
386 | { NULL } |
387 | }; |
388 | |
389 | static const AVFilterPad premultiply_outputs[] = { |
390 | { |
391 | .name = "default", |
392 | .type = AVMEDIA_TYPE_VIDEO, |
393 | .config_props = config_output, |
394 | .request_frame = request_frame, |
395 | }, |
396 | { NULL } |
397 | }; |
398 | |
399 | AVFilter ff_vf_premultiply = { |
400 | .name = "premultiply", |
401 | .description = NULL_IF_CONFIG_SMALL("PreMultiply first stream with first plane of second stream."), |
402 | .priv_size = sizeof(PreMultiplyContext), |
403 | .uninit = uninit, |
404 | .query_formats = query_formats, |
405 | .inputs = premultiply_inputs, |
406 | .outputs = premultiply_outputs, |
407 | .priv_class = &premultiply_class, |
408 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, |
409 | }; |
410 |