2 * Copyright (c) 2013 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/avassert.h"
22 #include "libavutil/avstring.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
28 #include "framesync.h"
30 typedef struct InputParam
{
37 typedef struct MergePlanesContext
{
40 const enum AVPixelFormat out_fmt
;
46 const AVPixFmtDescriptor
*outdesc
;
49 FFFrameSyncIn fsin
[3]; /* must be immediately after fs */
52 #define OFFSET(x) offsetof(MergePlanesContext, x)
53 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
54 static const AVOption mergeplanes_options
[] = {
55 { "mapping", "set input to output plane mapping", OFFSET(mapping
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 0x33333333, FLAGS
},
56 { "format", "set output pixel format", OFFSET(out_fmt
), AV_OPT_TYPE_PIXEL_FMT
, {.i64
=AV_PIX_FMT_YUVA444P
}, 0, INT_MAX
, .flags
=FLAGS
},
60 AVFILTER_DEFINE_CLASS(mergeplanes
);
62 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
64 MergePlanesContext
*s
= inlink
->dst
->priv
;
65 return ff_framesync_filter_frame(&s
->fs
, inlink
, in
);
68 static av_cold
int init(AVFilterContext
*ctx
)
70 MergePlanesContext
*s
= ctx
->priv
;
71 int64_t m
= s
->mapping
;
74 s
->outdesc
= av_pix_fmt_desc_get(s
->out_fmt
);
75 if (!(s
->outdesc
->flags
& AV_PIX_FMT_FLAG_PLANAR
) ||
76 s
->outdesc
->nb_components
< 2) {
77 av_log(ctx
, AV_LOG_ERROR
, "Only planar formats with more than one component are supported.\n");
78 return AVERROR(EINVAL
);
80 s
->nb_planes
= av_pix_fmt_count_planes(s
->out_fmt
);
82 for (i
= s
->nb_planes
- 1; i
>= 0; i
--) {
83 s
->map
[i
][0] = m
& 0xf;
85 s
->map
[i
][1] = m
& 0xf;
88 if (s
->map
[i
][0] > 3 || s
->map
[i
][1] > 3) {
89 av_log(ctx
, AV_LOG_ERROR
, "Mapping with out of range input and/or plane number.\n");
90 return AVERROR(EINVAL
);
93 s
->nb_inputs
= FFMAX(s
->nb_inputs
, s
->map
[i
][1] + 1);
96 av_assert0(s
->nb_inputs
&& s
->nb_inputs
<= 4);
98 for (i
= 0; i
< s
->nb_inputs
; i
++) {
99 AVFilterPad pad
= { 0 };
101 pad
.type
= AVMEDIA_TYPE_VIDEO
;
102 pad
.name
= av_asprintf("in%d", i
);
104 return AVERROR(ENOMEM
);
105 pad
.filter_frame
= filter_frame
;
107 if ((ret
= ff_insert_inpad(ctx
, i
, &pad
)) < 0){
116 static int query_formats(AVFilterContext
*ctx
)
118 MergePlanesContext
*s
= ctx
->priv
;
119 AVFilterFormats
*formats
= NULL
;
122 s
->outdesc
= av_pix_fmt_desc_get(s
->out_fmt
);
123 for (i
= 0; av_pix_fmt_desc_get(i
); i
++) {
124 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(i
);
125 if (desc
->comp
[0].depth_minus1
== s
->outdesc
->comp
[0].depth_minus1
&&
126 av_pix_fmt_count_planes(i
) == desc
->nb_components
)
127 ff_add_format(&formats
, i
);
130 for (i
= 0; i
< s
->nb_inputs
; i
++)
131 ff_formats_ref(formats
, &ctx
->inputs
[i
]->out_formats
);
134 ff_add_format(&formats
, s
->out_fmt
);
135 ff_formats_ref(formats
, &ctx
->outputs
[0]->in_formats
);
140 static int process_frame(FFFrameSync
*fs
)
142 AVFilterContext
*ctx
= fs
->parent
;
143 AVFilterLink
*outlink
= ctx
->outputs
[0];
144 MergePlanesContext
*s
= fs
->opaque
;
145 AVFrame
*in
[4] = { NULL
};
149 for (i
= 0; i
< s
->nb_inputs
; i
++) {
150 if ((ret
= ff_framesync_get_frame(&s
->fs
, i
, &in
[i
], 0)) < 0)
154 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
156 return AVERROR(ENOMEM
);
157 out
->pts
= av_rescale_q(s
->fs
.pts
, s
->fs
.time_base
, outlink
->time_base
);
159 for (i
= 0; i
< s
->nb_planes
; i
++) {
160 const int input
= s
->map
[i
][1];
161 const int plane
= s
->map
[i
][0];
163 av_image_copy_plane(out
->data
[i
], out
->linesize
[i
],
164 in
[input
]->data
[plane
], in
[input
]->linesize
[plane
],
165 s
->planewidth
[i
], s
->planeheight
[i
]);
168 return ff_filter_frame(outlink
, out
);
171 static int config_output(AVFilterLink
*outlink
)
173 AVFilterContext
*ctx
= outlink
->src
;
174 MergePlanesContext
*s
= ctx
->priv
;
175 InputParam inputsp
[4];
179 ff_framesync_init(&s
->fs
, ctx
, s
->nb_inputs
);
182 s
->fs
.on_event
= process_frame
;
184 outlink
->w
= ctx
->inputs
[0]->w
;
185 outlink
->h
= ctx
->inputs
[0]->h
;
186 outlink
->time_base
= ctx
->inputs
[0]->time_base
;
187 outlink
->frame_rate
= ctx
->inputs
[0]->frame_rate
;
188 outlink
->sample_aspect_ratio
= ctx
->inputs
[0]->sample_aspect_ratio
;
191 s
->planewidth
[2] = FF_CEIL_RSHIFT(outlink
->w
, s
->outdesc
->log2_chroma_w
);
193 s
->planewidth
[3] = outlink
->w
;
195 s
->planeheight
[2] = FF_CEIL_RSHIFT(outlink
->h
, s
->outdesc
->log2_chroma_h
);
197 s
->planeheight
[3] = outlink
->h
;
199 for (i
= 0; i
< s
->nb_inputs
; i
++) {
200 InputParam
*inputp
= &inputsp
[i
];
201 AVFilterLink
*inlink
= ctx
->inputs
[i
];
202 const AVPixFmtDescriptor
*indesc
= av_pix_fmt_desc_get(inlink
->format
);
205 if (outlink
->sample_aspect_ratio
.num
!= inlink
->sample_aspect_ratio
.num
||
206 outlink
->sample_aspect_ratio
.den
!= inlink
->sample_aspect_ratio
.den
) {
207 av_log(ctx
, AV_LOG_ERROR
, "input #%d link %s SAR %d:%d "
208 "does not match output link %s SAR %d:%d\n",
209 i
, ctx
->input_pads
[i
].name
,
210 inlink
->sample_aspect_ratio
.num
,
211 inlink
->sample_aspect_ratio
.den
,
212 ctx
->output_pads
[0].name
,
213 outlink
->sample_aspect_ratio
.num
,
214 outlink
->sample_aspect_ratio
.den
);
215 return AVERROR(EINVAL
);
218 inputp
->planewidth
[1] =
219 inputp
->planewidth
[2] = FF_CEIL_RSHIFT(inlink
->w
, indesc
->log2_chroma_w
);
220 inputp
->planewidth
[0] =
221 inputp
->planewidth
[3] = inlink
->w
;
222 inputp
->planeheight
[1] =
223 inputp
->planeheight
[2] = FF_CEIL_RSHIFT(inlink
->h
, indesc
->log2_chroma_h
);
224 inputp
->planeheight
[0] =
225 inputp
->planeheight
[3] = inlink
->h
;
226 inputp
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
228 for (j
= 0; j
< inputp
->nb_planes
; j
++)
229 inputp
->depth
[j
] = indesc
->comp
[j
].depth_minus1
+ 1;
231 in
[i
].time_base
= inlink
->time_base
;
233 in
[i
].before
= EXT_STOP
;
234 in
[i
].after
= EXT_STOP
;
237 for (i
= 0; i
< s
->nb_planes
; i
++) {
238 const int input
= s
->map
[i
][1];
239 const int plane
= s
->map
[i
][0];
240 InputParam
*inputp
= &inputsp
[input
];
242 if (plane
+ 1 > inputp
->nb_planes
) {
243 av_log(ctx
, AV_LOG_ERROR
, "input %d does not have %d plane\n",
247 if (s
->outdesc
->comp
[i
].depth_minus1
+ 1 != inputp
->depth
[plane
]) {
248 av_log(ctx
, AV_LOG_ERROR
, "output plane %d depth %d does not "
249 "match input %d plane %d depth %d\n",
250 i
, s
->outdesc
->comp
[i
].depth_minus1
+ 1,
251 input
, plane
, inputp
->depth
[plane
]);
254 if (s
->planewidth
[i
] != inputp
->planewidth
[plane
]) {
255 av_log(ctx
, AV_LOG_ERROR
, "output plane %d width %d does not "
256 "match input %d plane %d width %d\n",
258 input
, plane
, inputp
->planewidth
[plane
]);
261 if (s
->planeheight
[i
] != inputp
->planeheight
[plane
]) {
262 av_log(ctx
, AV_LOG_ERROR
, "output plane %d height %d does not "
263 "match input %d plane %d height %d\n",
264 i
, s
->planeheight
[i
],
265 input
, plane
, inputp
->planeheight
[plane
]);
270 return ff_framesync_configure(&s
->fs
);
272 return AVERROR(EINVAL
);
275 static int request_frame(AVFilterLink
*outlink
)
277 MergePlanesContext
*s
= outlink
->src
->priv
;
278 return ff_framesync_request_frame(&s
->fs
, outlink
);
281 static av_cold
void uninit(AVFilterContext
*ctx
)
283 MergePlanesContext
*s
= ctx
->priv
;
286 ff_framesync_uninit(&s
->fs
);
288 for (i
= 0; i
< ctx
->nb_inputs
; i
++)
289 av_freep(&ctx
->input_pads
[i
].name
);
292 static const AVFilterPad mergeplanes_outputs
[] = {
295 .type
= AVMEDIA_TYPE_VIDEO
,
296 .config_props
= config_output
,
297 .request_frame
= request_frame
,
302 AVFilter ff_vf_mergeplanes
= {
303 .name
= "mergeplanes",
304 .description
= NULL_IF_CONFIG_SMALL("Merge planes."),
305 .priv_size
= sizeof(MergePlanesContext
),
306 .priv_class
= &mergeplanes_class
,
309 .query_formats
= query_formats
,
311 .outputs
= mergeplanes_outputs
,
312 .flags
= AVFILTER_FLAG_DYNAMIC_INPUTS
,