]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_framepack.c
avfilter/af_sofalizer: Fix occured typo
[ffmpeg] / libavfilter / vf_framepack.c
1 /*
2  * Copyright (c) 2013 Vittorio Giovara
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  * Generate a frame packed video, by combining two views in a single surface.
24  */
25
26 #include <string.h>
27
28 #include "libavutil/imgutils.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/pixdesc.h"
31 #include "libavutil/rational.h"
32 #include "libavutil/stereo3d.h"
33
34 #include "avfilter.h"
35 #include "formats.h"
36 #include "internal.h"
37 #include "video.h"
38
39 #define LEFT  0
40 #define RIGHT 1
41
42 typedef struct FramepackContext {
43     const AVClass *class;
44
45     const AVPixFmtDescriptor *pix_desc; ///< agreed pixel format
46
47     enum AVStereo3DType format;         ///< frame pack type output
48
49     AVFrame *input_views[2];            ///< input frames
50
51     int64_t double_pts;                 ///< new pts for frameseq mode
52 } FramepackContext;
53
54 static const enum AVPixelFormat formats_supported[] = {
55     AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV444P,
56     AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ420P,
57     AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
58     AV_PIX_FMT_NONE
59 };
60
61 static int query_formats(AVFilterContext *ctx)
62 {
63     // this will ensure that formats are the same on all pads
64     AVFilterFormats *fmts_list = ff_make_format_list(formats_supported);
65     if (!fmts_list)
66         return AVERROR(ENOMEM);
67     return ff_set_common_formats(ctx, fmts_list);
68 }
69
70 static av_cold void framepack_uninit(AVFilterContext *ctx)
71 {
72     FramepackContext *s = ctx->priv;
73
74     // clean any leftover frame
75     av_frame_free(&s->input_views[LEFT]);
76     av_frame_free(&s->input_views[RIGHT]);
77 }
78
79 static int config_output(AVFilterLink *outlink)
80 {
81     AVFilterContext *ctx  = outlink->src;
82     FramepackContext *s   = outlink->src->priv;
83
84     int width             = ctx->inputs[LEFT]->w;
85     int height            = ctx->inputs[LEFT]->h;
86     AVRational time_base  = ctx->inputs[LEFT]->time_base;
87     AVRational frame_rate = ctx->inputs[LEFT]->frame_rate;
88
89     // check size and fps match on the other input
90     if (width  != ctx->inputs[RIGHT]->w ||
91         height != ctx->inputs[RIGHT]->h) {
92         av_log(ctx, AV_LOG_ERROR,
93                "Left and right sizes differ (%dx%d vs %dx%d).\n",
94                width, height,
95                ctx->inputs[RIGHT]->w, ctx->inputs[RIGHT]->h);
96         return AVERROR_INVALIDDATA;
97     } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
98         av_log(ctx, AV_LOG_ERROR,
99                "Left and right time bases differ (%d/%d vs %d/%d).\n",
100                time_base.num, time_base.den,
101                ctx->inputs[RIGHT]->time_base.num,
102                ctx->inputs[RIGHT]->time_base.den);
103         return AVERROR_INVALIDDATA;
104     } else if (av_cmp_q(frame_rate, ctx->inputs[RIGHT]->frame_rate) != 0) {
105         av_log(ctx, AV_LOG_ERROR,
106                "Left and right framerates differ (%d/%d vs %d/%d).\n",
107                frame_rate.num, frame_rate.den,
108                ctx->inputs[RIGHT]->frame_rate.num,
109                ctx->inputs[RIGHT]->frame_rate.den);
110         return AVERROR_INVALIDDATA;
111     }
112
113     s->pix_desc = av_pix_fmt_desc_get(outlink->format);
114     if (!s->pix_desc)
115         return AVERROR_BUG;
116
117     // modify output properties as needed
118     switch (s->format) {
119     case AV_STEREO3D_FRAMESEQUENCE:
120         time_base.den  *= 2;
121         frame_rate.num *= 2;
122
123         s->double_pts = AV_NOPTS_VALUE;
124         break;
125     case AV_STEREO3D_COLUMNS:
126     case AV_STEREO3D_SIDEBYSIDE:
127         width *= 2;
128         break;
129     case AV_STEREO3D_LINES:
130     case AV_STEREO3D_TOPBOTTOM:
131         height *= 2;
132         break;
133     default:
134         av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.");
135         return AVERROR_INVALIDDATA;
136     }
137
138     outlink->w          = width;
139     outlink->h          = height;
140     outlink->time_base  = time_base;
141     outlink->frame_rate = frame_rate;
142
143     return 0;
144 }
145
146 static void horizontal_frame_pack(AVFilterLink *outlink,
147                                   AVFrame *out,
148                                   int interleaved)
149 {
150     AVFilterContext *ctx = outlink->src;
151     FramepackContext *s = ctx->priv;
152     int i, plane;
153
154     if (interleaved) {
155         const uint8_t *leftp  = s->input_views[LEFT]->data[0];
156         const uint8_t *rightp = s->input_views[RIGHT]->data[0];
157         uint8_t *dstp         = out->data[0];
158         int length = out->width / 2;
159         int lines  = out->height;
160
161         for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
162             if (plane == 1 || plane == 2) {
163                 length = FF_CEIL_RSHIFT(out->width / 2, s->pix_desc->log2_chroma_w);
164                 lines  = FF_CEIL_RSHIFT(out->height,    s->pix_desc->log2_chroma_h);
165             }
166             for (i = 0; i < lines; i++) {
167                 int j;
168                 leftp  = s->input_views[LEFT]->data[plane] +
169                          s->input_views[LEFT]->linesize[plane] * i;
170                 rightp = s->input_views[RIGHT]->data[plane] +
171                          s->input_views[RIGHT]->linesize[plane] * i;
172                 dstp   = out->data[plane] + out->linesize[plane] * i;
173                 for (j = 0; j < length; j++) {
174                     // interpolate chroma as necessary
175                     if ((s->pix_desc->log2_chroma_w ||
176                          s->pix_desc->log2_chroma_h) &&
177                         (plane == 1 || plane == 2)) {
178                         *dstp++ = (*leftp + *rightp) / 2;
179                         *dstp++ = (*leftp + *rightp) / 2;
180                     } else {
181                         *dstp++ = *leftp;
182                         *dstp++ = *rightp;
183                     }
184                     leftp += 1;
185                     rightp += 1;
186                 }
187             }
188         }
189     } else {
190         for (i = 0; i < 2; i++) {
191             const uint8_t *src[4];
192             uint8_t *dst[4];
193             int sub_w = s->input_views[i]->width >> s->pix_desc->log2_chroma_w;
194
195             src[0] = s->input_views[i]->data[0];
196             src[1] = s->input_views[i]->data[1];
197             src[2] = s->input_views[i]->data[2];
198
199             dst[0] = out->data[0] + i * s->input_views[i]->width;
200             dst[1] = out->data[1] + i * sub_w;
201             dst[2] = out->data[2] + i * sub_w;
202
203             av_image_copy(dst, out->linesize, src, s->input_views[i]->linesize,
204                           s->input_views[i]->format,
205                           s->input_views[i]->width,
206                           s->input_views[i]->height);
207         }
208     }
209 }
210
211 static void vertical_frame_pack(AVFilterLink *outlink,
212                                 AVFrame *out,
213                                 int interleaved)
214 {
215     AVFilterContext *ctx = outlink->src;
216     FramepackContext *s = ctx->priv;
217     int i;
218
219     for (i = 0; i < 2; i++) {
220         const uint8_t *src[4];
221         uint8_t *dst[4];
222         int linesizes[4];
223         int sub_h = s->input_views[i]->height >> s->pix_desc->log2_chroma_h;
224
225         src[0] = s->input_views[i]->data[0];
226         src[1] = s->input_views[i]->data[1];
227         src[2] = s->input_views[i]->data[2];
228
229         dst[0] = out->data[0] + i * out->linesize[0] *
230                  (interleaved + s->input_views[i]->height * (1 - interleaved));
231         dst[1] = out->data[1] + i * out->linesize[1] *
232                  (interleaved + sub_h * (1 - interleaved));
233         dst[2] = out->data[2] + i * out->linesize[2] *
234                  (interleaved + sub_h * (1 - interleaved));
235
236         linesizes[0] = out->linesize[0] +
237                        interleaved * out->linesize[0];
238         linesizes[1] = out->linesize[1] +
239                        interleaved * out->linesize[1];
240         linesizes[2] = out->linesize[2] +
241                        interleaved * out->linesize[2];
242
243         av_image_copy(dst, linesizes, src, s->input_views[i]->linesize,
244                       s->input_views[i]->format,
245                       s->input_views[i]->width,
246                       s->input_views[i]->height);
247     }
248 }
249
250 static av_always_inline void spatial_frame_pack(AVFilterLink *outlink,
251                                                 AVFrame *dst)
252 {
253     AVFilterContext *ctx = outlink->src;
254     FramepackContext *s = ctx->priv;
255     switch (s->format) {
256     case AV_STEREO3D_SIDEBYSIDE:
257         horizontal_frame_pack(outlink, dst, 0);
258         break;
259     case AV_STEREO3D_COLUMNS:
260         horizontal_frame_pack(outlink, dst, 1);
261         break;
262     case AV_STEREO3D_TOPBOTTOM:
263         vertical_frame_pack(outlink, dst, 0);
264         break;
265     case AV_STEREO3D_LINES:
266         vertical_frame_pack(outlink, dst, 1);
267         break;
268     }
269 }
270
271 static int try_push_frame(AVFilterContext *ctx);
272
273 static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
274 {
275     FramepackContext *s = inlink->dst->priv;
276     s->input_views[LEFT] = frame;
277     return try_push_frame(inlink->dst);
278 }
279
280 static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
281 {
282     FramepackContext *s = inlink->dst->priv;
283     s->input_views[RIGHT] = frame;
284     return try_push_frame(inlink->dst);
285 }
286
287 static int request_frame(AVFilterLink *outlink)
288 {
289     AVFilterContext *ctx = outlink->src;
290     FramepackContext *s = ctx->priv;
291     int ret, i;
292
293     /* get a frame on the either input, stop as soon as a video ends */
294     for (i = 0; i < 2; i++) {
295         if (!s->input_views[i]) {
296             ret = ff_request_frame(ctx->inputs[i]);
297             if (ret < 0)
298                 return ret;
299         }
300     }
301     return 0;
302 }
303
304 static int try_push_frame(AVFilterContext *ctx)
305 {
306     FramepackContext *s = ctx->priv;
307     AVFilterLink *outlink = ctx->outputs[0];
308     AVStereo3D *stereo;
309     int ret, i;
310
311     if (!(s->input_views[0] && s->input_views[1]))
312         return 0;
313     if (s->format == AV_STEREO3D_FRAMESEQUENCE) {
314         if (s->double_pts == AV_NOPTS_VALUE)
315             s->double_pts = s->input_views[LEFT]->pts;
316
317         for (i = 0; i < 2; i++) {
318             // set correct timestamps
319             s->input_views[i]->pts = s->double_pts++;
320
321             // set stereo3d side data
322             stereo = av_stereo3d_create_side_data(s->input_views[i]);
323             if (!stereo)
324                 return AVERROR(ENOMEM);
325             stereo->type = s->format;
326
327             // filter the frame and immediately relinquish its pointer
328             ret = ff_filter_frame(outlink, s->input_views[i]);
329             s->input_views[i] = NULL;
330             if (ret < 0)
331                 return ret;
332         }
333         return ret;
334     } else {
335         AVFrame *dst = ff_get_video_buffer(outlink, outlink->w, outlink->h);
336         if (!dst)
337             return AVERROR(ENOMEM);
338
339         spatial_frame_pack(outlink, dst);
340
341         // get any property from the original frame
342         ret = av_frame_copy_props(dst, s->input_views[LEFT]);
343         if (ret < 0) {
344             av_frame_free(&dst);
345             return ret;
346         }
347
348         for (i = 0; i < 2; i++)
349             av_frame_free(&s->input_views[i]);
350
351         // set stereo3d side data
352         stereo = av_stereo3d_create_side_data(dst);
353         if (!stereo) {
354             av_frame_free(&dst);
355             return AVERROR(ENOMEM);
356         }
357         stereo->type = s->format;
358
359         return ff_filter_frame(outlink, dst);
360     }
361 }
362
363 #define OFFSET(x) offsetof(FramepackContext, x)
364 #define V AV_OPT_FLAG_VIDEO_PARAM
365 static const AVOption framepack_options[] = {
366     { "format", "Frame pack output format", OFFSET(format), AV_OPT_TYPE_INT,
367         { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = V, .unit = "format" },
368     { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST,
369         { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
370     { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST,
371         { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
372     { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST,
373         { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
374     { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST,
375         { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
376     { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST,
377         { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
378     { NULL },
379 };
380
381 AVFILTER_DEFINE_CLASS(framepack);
382
383 static const AVFilterPad framepack_inputs[] = {
384     {
385         .name         = "left",
386         .type         = AVMEDIA_TYPE_VIDEO,
387         .filter_frame = filter_frame_left,
388         .needs_fifo   = 1,
389     },
390     {
391         .name         = "right",
392         .type         = AVMEDIA_TYPE_VIDEO,
393         .filter_frame = filter_frame_right,
394         .needs_fifo   = 1,
395     },
396     { NULL }
397 };
398
399 static const AVFilterPad framepack_outputs[] = {
400     {
401         .name          = "packed",
402         .type          = AVMEDIA_TYPE_VIDEO,
403         .config_props  = config_output,
404         .request_frame = request_frame,
405     },
406     { NULL }
407 };
408
409 AVFilter ff_vf_framepack = {
410     .name          = "framepack",
411     .description   = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
412     .priv_size     = sizeof(FramepackContext),
413     .priv_class    = &framepack_class,
414     .query_formats = query_formats,
415     .inputs        = framepack_inputs,
416     .outputs       = framepack_outputs,
417     .uninit        = framepack_uninit,
418 };