Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (c) 2012 Steven Robertson | |
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 | /** | |
22 | * @file | |
23 | * copy an alpha component from another video's luma | |
24 | */ | |
25 | ||
26 | #include <string.h> | |
27 | ||
28 | #include "libavutil/pixfmt.h" | |
29 | #include "avfilter.h" | |
30 | #include "bufferqueue.h" | |
31 | #include "drawutils.h" | |
32 | #include "formats.h" | |
33 | #include "internal.h" | |
34 | #include "video.h" | |
35 | ||
36 | enum { Y, U, V, A }; | |
37 | ||
38 | typedef struct { | |
39 | int frame_requested; | |
40 | int is_packed_rgb; | |
41 | uint8_t rgba_map[4]; | |
42 | struct FFBufQueue queue_main; | |
43 | struct FFBufQueue queue_alpha; | |
44 | } AlphaMergeContext; | |
45 | ||
46 | static av_cold void uninit(AVFilterContext *ctx) | |
47 | { | |
48 | AlphaMergeContext *merge = ctx->priv; | |
49 | ff_bufqueue_discard_all(&merge->queue_main); | |
50 | ff_bufqueue_discard_all(&merge->queue_alpha); | |
51 | } | |
52 | ||
53 | static int query_formats(AVFilterContext *ctx) | |
54 | { | |
55 | static const enum AVPixelFormat main_fmts[] = { | |
56 | AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, | |
57 | AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, | |
58 | AV_PIX_FMT_NONE | |
59 | }; | |
60 | static const enum AVPixelFormat alpha_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; | |
61 | AVFilterFormats *main_formats = ff_make_format_list(main_fmts); | |
62 | AVFilterFormats *alpha_formats = ff_make_format_list(alpha_fmts); | |
63 | ff_formats_ref(main_formats, &ctx->inputs[0]->out_formats); | |
64 | ff_formats_ref(alpha_formats, &ctx->inputs[1]->out_formats); | |
65 | ff_formats_ref(main_formats, &ctx->outputs[0]->in_formats); | |
66 | return 0; | |
67 | } | |
68 | ||
69 | static int config_input_main(AVFilterLink *inlink) | |
70 | { | |
71 | AlphaMergeContext *merge = inlink->dst->priv; | |
72 | merge->is_packed_rgb = | |
73 | ff_fill_rgba_map(merge->rgba_map, inlink->format) >= 0; | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static int config_output(AVFilterLink *outlink) | |
78 | { | |
79 | AVFilterContext *ctx = outlink->src; | |
80 | AVFilterLink *mainlink = ctx->inputs[0]; | |
81 | AVFilterLink *alphalink = ctx->inputs[1]; | |
82 | if (mainlink->w != alphalink->w || mainlink->h != alphalink->h) { | |
83 | av_log(ctx, AV_LOG_ERROR, | |
84 | "Input frame sizes do not match (%dx%d vs %dx%d).\n", | |
85 | mainlink->w, mainlink->h, | |
86 | alphalink->w, alphalink->h); | |
87 | return AVERROR(EINVAL); | |
88 | } | |
89 | ||
90 | outlink->w = mainlink->w; | |
91 | outlink->h = mainlink->h; | |
92 | outlink->time_base = mainlink->time_base; | |
93 | outlink->sample_aspect_ratio = mainlink->sample_aspect_ratio; | |
94 | outlink->frame_rate = mainlink->frame_rate; | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static void draw_frame(AVFilterContext *ctx, | |
99 | AVFrame *main_buf, | |
100 | AVFrame *alpha_buf) | |
101 | { | |
102 | AlphaMergeContext *merge = ctx->priv; | |
103 | int h = main_buf->height; | |
104 | ||
105 | if (merge->is_packed_rgb) { | |
106 | int x, y; | |
107 | uint8_t *pin, *pout; | |
108 | for (y = 0; y < h; y++) { | |
109 | pin = alpha_buf->data[0] + y * alpha_buf->linesize[0]; | |
110 | pout = main_buf->data[0] + y * main_buf->linesize[0] + merge->rgba_map[A]; | |
111 | for (x = 0; x < main_buf->width; x++) { | |
112 | *pout = *pin; | |
113 | pin += 1; | |
114 | pout += 4; | |
115 | } | |
116 | } | |
117 | } else { | |
118 | int y; | |
119 | const int main_linesize = main_buf->linesize[A]; | |
120 | const int alpha_linesize = alpha_buf->linesize[Y]; | |
121 | for (y = 0; y < h && y < alpha_buf->height; y++) { | |
122 | memcpy(main_buf->data[A] + y * main_linesize, | |
123 | alpha_buf->data[Y] + y * alpha_linesize, | |
124 | FFMIN(main_linesize, alpha_linesize)); | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | static int filter_frame(AVFilterLink *inlink, AVFrame *buf) | |
130 | { | |
131 | AVFilterContext *ctx = inlink->dst; | |
132 | AlphaMergeContext *merge = ctx->priv; | |
133 | ||
134 | int ret = 0; | |
135 | int is_alpha = (inlink == ctx->inputs[1]); | |
136 | struct FFBufQueue *queue = | |
137 | (is_alpha ? &merge->queue_alpha : &merge->queue_main); | |
138 | ff_bufqueue_add(ctx, queue, buf); | |
139 | ||
140 | do { | |
141 | AVFrame *main_buf, *alpha_buf; | |
142 | ||
143 | if (!ff_bufqueue_peek(&merge->queue_main, 0) || | |
144 | !ff_bufqueue_peek(&merge->queue_alpha, 0)) break; | |
145 | ||
146 | main_buf = ff_bufqueue_get(&merge->queue_main); | |
147 | alpha_buf = ff_bufqueue_get(&merge->queue_alpha); | |
148 | ||
149 | merge->frame_requested = 0; | |
150 | draw_frame(ctx, main_buf, alpha_buf); | |
151 | ret = ff_filter_frame(ctx->outputs[0], main_buf); | |
152 | av_frame_free(&alpha_buf); | |
153 | } while (ret >= 0); | |
154 | return ret; | |
155 | } | |
156 | ||
157 | static int request_frame(AVFilterLink *outlink) | |
158 | { | |
159 | AVFilterContext *ctx = outlink->src; | |
160 | AlphaMergeContext *merge = ctx->priv; | |
161 | int in, ret; | |
162 | ||
163 | merge->frame_requested = 1; | |
164 | while (merge->frame_requested) { | |
165 | in = ff_bufqueue_peek(&merge->queue_main, 0) ? 1 : 0; | |
166 | ret = ff_request_frame(ctx->inputs[in]); | |
167 | if (ret < 0) | |
168 | return ret; | |
169 | } | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static const AVFilterPad alphamerge_inputs[] = { | |
174 | { | |
175 | .name = "main", | |
176 | .type = AVMEDIA_TYPE_VIDEO, | |
177 | .config_props = config_input_main, | |
178 | .filter_frame = filter_frame, | |
179 | .needs_writable = 1, | |
180 | },{ | |
181 | .name = "alpha", | |
182 | .type = AVMEDIA_TYPE_VIDEO, | |
183 | .filter_frame = filter_frame, | |
184 | }, | |
185 | { NULL } | |
186 | }; | |
187 | ||
188 | static const AVFilterPad alphamerge_outputs[] = { | |
189 | { | |
190 | .name = "default", | |
191 | .type = AVMEDIA_TYPE_VIDEO, | |
192 | .config_props = config_output, | |
193 | .request_frame = request_frame, | |
194 | }, | |
195 | { NULL } | |
196 | }; | |
197 | ||
198 | AVFilter ff_vf_alphamerge = { | |
199 | .name = "alphamerge", | |
200 | .description = NULL_IF_CONFIG_SMALL("Copy the luma value of the second " | |
201 | "input into the alpha channel of the first input."), | |
202 | .uninit = uninit, | |
203 | .priv_size = sizeof(AlphaMergeContext), | |
204 | .query_formats = query_formats, | |
205 | .inputs = alphamerge_inputs, | |
206 | .outputs = alphamerge_outputs, | |
207 | }; |