Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * Copyright (c) 2013 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/avassert.h"
22
#include "libavutil/avstring.h"
23
#include "libavutil/imgutils.h"
24
#include "libavutil/opt.h"
25
#include "libavutil/pixdesc.h"
26
#include "avfilter.h"
27
#include "internal.h"
28
#include "framesync.h"
29
 
30
typedef struct InputParam {
31
    int depth[4];
32
    int nb_planes;
33
    int planewidth[4];
34
    int planeheight[4];
35
} InputParam;
36
 
37
typedef struct MergePlanesContext {
38
    const AVClass *class;
39
    int64_t mapping;
40
    const enum AVPixelFormat out_fmt;
41
    int nb_inputs;
42
    int nb_planes;
43
    int planewidth[4];
44
    int planeheight[4];
45
    int map[4][2];
46
    const AVPixFmtDescriptor *outdesc;
47
 
48
    FFFrameSync fs;
49
    FFFrameSyncIn fsin[3]; /* must be immediately after fs */
50
} MergePlanesContext;
51
 
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}, .flags=FLAGS },
57
    { NULL }
58
};
59
 
60
AVFILTER_DEFINE_CLASS(mergeplanes);
61
 
62
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
63
{
64
    MergePlanesContext *s = inlink->dst->priv;
65
    return ff_framesync_filter_frame(&s->fs, inlink, in);
66
}
67
 
68
static av_cold int init(AVFilterContext *ctx)
69
{
70
    MergePlanesContext *s = ctx->priv;
71
    int64_t m = s->mapping;
72
    int i, ret;
73
 
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);
79
    }
80
    s->nb_planes = av_pix_fmt_count_planes(s->out_fmt);
81
 
82
    for (i = s->nb_planes - 1; i >= 0; i--) {
83
        s->map[i][0] = m & 0xf;
84
        m >>= 4;
85
        s->map[i][1] = m & 0xf;
86
        m >>= 4;
87
 
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);
91
        }
92
 
93
        s->nb_inputs = FFMAX(s->nb_inputs, s->map[i][1] + 1);
94
    }
95
 
96
    av_assert0(s->nb_inputs && s->nb_inputs <= 4);
97
 
98
    for (i = 0; i < s->nb_inputs; i++) {
99
        AVFilterPad pad = { 0 };
100
 
101
        pad.type = AVMEDIA_TYPE_VIDEO;
102
        pad.name = av_asprintf("in%d", i);
103
        if (!pad.name)
104
            return AVERROR(ENOMEM);
105
        pad.filter_frame = filter_frame;
106
 
107
        if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0){
108
            av_freep(&pad.name);
109
            return ret;
110
        }
111
    }
112
 
113
    return 0;
114
}
115
 
116
static int query_formats(AVFilterContext *ctx)
117
{
118
    MergePlanesContext *s = ctx->priv;
119
    AVFilterFormats *formats = NULL;
120
    int i;
121
 
122
    s->outdesc = av_pix_fmt_desc_get(s->out_fmt);
123
    for (i = 0; i < AV_PIX_FMT_NB; 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);
128
    }
129
 
130
    for (i = 0; i < s->nb_inputs; i++)
131
        ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
132
 
133
    formats = NULL;
134
    ff_add_format(&formats, s->out_fmt);
135
    ff_formats_ref(formats, &ctx->outputs[0]->in_formats);
136
 
137
    return 0;
138
}
139
 
140
static int process_frame(FFFrameSync *fs)
141
{
142
    AVFilterContext *ctx = fs->parent;
143
    AVFilterLink *outlink = ctx->outputs[0];
144
    MergePlanesContext *s = fs->opaque;
145
    AVFrame *in[4] = { NULL };
146
    AVFrame *out;
147
    int i, ret;
148
 
149
    for (i = 0; i < s->nb_inputs; i++) {
150
        if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
151
            return ret;
152
    }
153
 
154
    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
155
    if (!out)
156
        return AVERROR(ENOMEM);
157
    out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
158
 
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];
162
 
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]);
166
    }
167
 
168
    return ff_filter_frame(outlink, out);
169
}
170
 
171
static int config_output(AVFilterLink *outlink)
172
{
173
    AVFilterContext *ctx = outlink->src;
174
    MergePlanesContext *s = ctx->priv;
175
    InputParam inputsp[4];
176
    FFFrameSyncIn *in;
177
    int i;
178
 
179
    ff_framesync_init(&s->fs, ctx, s->nb_inputs);
180
    in = s->fs.in;
181
    s->fs.opaque = s;
182
    s->fs.on_event = process_frame;
183
 
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;
189
 
190
    s->planewidth[1]  =
191
    s->planewidth[2]  = FF_CEIL_RSHIFT(outlink->w, s->outdesc->log2_chroma_w);
192
    s->planewidth[0]  =
193
    s->planewidth[3]  = outlink->w;
194
    s->planeheight[1] =
195
    s->planeheight[2] = FF_CEIL_RSHIFT(outlink->h, s->outdesc->log2_chroma_h);
196
    s->planeheight[0] =
197
    s->planeheight[3] = outlink->h;
198
 
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);
203
        int j;
204
 
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);
216
        }
217
 
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);
227
 
228
        for (j = 0; j < inputp->nb_planes; j++)
229
            inputp->depth[j] = indesc->comp[j].depth_minus1 + 1;
230
 
231
        in[i].time_base = inlink->time_base;
232
        in[i].sync   = 1;
233
        in[i].before = EXT_STOP;
234
        in[i].after  = EXT_STOP;
235
    }
236
 
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];
241
 
242
        if (plane + 1 > inputp->nb_planes) {
243
            av_log(ctx, AV_LOG_ERROR, "input %d does not have %d plane\n",
244
                                      input, plane);
245
            goto fail;
246
        }
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]);
252
            goto fail;
253
        }
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",
257
                                      i, s->planewidth[i],
258
                                      input, plane, inputp->planewidth[plane]);
259
            goto fail;
260
        }
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]);
266
            goto fail;
267
        }
268
    }
269
 
270
    return ff_framesync_configure(&s->fs);
271
fail:
272
    return AVERROR(EINVAL);
273
}
274
 
275
static int request_frame(AVFilterLink *outlink)
276
{
277
    MergePlanesContext *s = outlink->src->priv;
278
    return ff_framesync_request_frame(&s->fs, outlink);
279
}
280
 
281
static av_cold void uninit(AVFilterContext *ctx)
282
{
283
    MergePlanesContext *s = ctx->priv;
284
    int i;
285
 
286
    ff_framesync_uninit(&s->fs);
287
 
288
    for (i = 0; i < ctx->nb_inputs; i++)
289
        av_freep(&ctx->input_pads[i].name);
290
}
291
 
292
static const AVFilterPad mergeplanes_outputs[] = {
293
    {
294
        .name          = "default",
295
        .type          = AVMEDIA_TYPE_VIDEO,
296
        .config_props  = config_output,
297
        .request_frame = request_frame,
298
    },
299
    { NULL }
300
};
301
 
302
AVFilter avfilter_vf_mergeplanes = {
303
    .name          = "mergeplanes",
304
    .description   = NULL_IF_CONFIG_SMALL("Merge planes."),
305
    .priv_size     = sizeof(MergePlanesContext),
306
    .priv_class    = &mergeplanes_class,
307
    .init          = init,
308
    .uninit        = uninit,
309
    .query_formats = query_formats,
310
    .inputs        = NULL,
311
    .outputs       = mergeplanes_outputs,
312
    .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
313
};