]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_framepack.c
libavformat/avienc: Fix duration of audio segment in OpenDML master index
[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     ff_set_common_formats(ctx, ff_make_format_list(formats_supported));
65     return 0;
66 }
67
68 static av_cold void framepack_uninit(AVFilterContext *ctx)
69 {
70     FramepackContext *s = ctx->priv;
71
72     // clean any leftover frame
73     av_frame_free(&s->input_views[LEFT]);
74     av_frame_free(&s->input_views[RIGHT]);
75 }
76
77 static int config_output(AVFilterLink *outlink)
78 {
79     AVFilterContext *ctx = outlink->src;
80     FramepackContext *s  = outlink->src->priv;
81
82     int width            = ctx->inputs[LEFT]->w;
83     int height           = ctx->inputs[LEFT]->h;
84     AVRational time_base = ctx->inputs[LEFT]->time_base;
85     AVRational frame_rate = ctx->inputs[LEFT]->frame_rate;
86
87     // check size and fps match on the other input
88     if (width  != ctx->inputs[RIGHT]->w ||
89         height != ctx->inputs[RIGHT]->h) {
90         av_log(ctx, AV_LOG_ERROR,
91                "Left and right sizes differ (%dx%d vs %dx%d).\n",
92                width, height,
93                ctx->inputs[RIGHT]->w, ctx->inputs[RIGHT]->h);
94         return AVERROR_INVALIDDATA;
95     } else if (av_cmp_q(time_base, ctx->inputs[RIGHT]->time_base) != 0) {
96         av_log(ctx, AV_LOG_ERROR,
97                "Left and right time bases differ (%d/%d vs %d/%d).\n",
98                time_base.num, time_base.den,
99                ctx->inputs[RIGHT]->time_base.num,
100                ctx->inputs[RIGHT]->time_base.den);
101         return AVERROR_INVALIDDATA;
102     } else if (av_cmp_q(frame_rate, ctx->inputs[RIGHT]->frame_rate) != 0) {
103         av_log(ctx, AV_LOG_ERROR,
104                "Left and right framerates differ (%d/%d vs %d/%d).\n",
105                frame_rate.num, frame_rate.den,
106                ctx->inputs[RIGHT]->frame_rate.num,
107                ctx->inputs[RIGHT]->frame_rate.den);
108         return AVERROR_INVALIDDATA;
109     }
110
111     s->pix_desc = av_pix_fmt_desc_get(outlink->format);
112     if (!s->pix_desc)
113         return AVERROR_BUG;
114
115     // modify output properties as needed
116     switch (s->format) {
117     case AV_STEREO3D_FRAMESEQUENCE:
118         time_base.den *= 2;
119         frame_rate.num *= 2;
120
121         s->double_pts = AV_NOPTS_VALUE;
122         break;
123     case AV_STEREO3D_COLUMNS:
124     case AV_STEREO3D_SIDEBYSIDE:
125         width *= 2;
126         break;
127     case AV_STEREO3D_LINES:
128     case AV_STEREO3D_TOPBOTTOM:
129         height *= 2;
130         break;
131     default:
132         av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.");
133         return AVERROR_INVALIDDATA;
134     }
135
136     outlink->w         = width;
137     outlink->h         = height;
138     outlink->time_base = time_base;
139     outlink->frame_rate= frame_rate;
140
141     return 0;
142 }
143
144 static void horizontal_frame_pack(FramepackContext *s,
145                                   AVFrame *dst,
146                                   int interleaved)
147 {
148     int plane, i;
149     int length = dst->width / 2;
150     int lines  = dst->height;
151
152     for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
153         const uint8_t *leftp  = s->input_views[LEFT]->data[plane];
154         const uint8_t *rightp = s->input_views[RIGHT]->data[plane];
155         uint8_t *dstp         = dst->data[plane];
156
157         if (plane == 1 || plane == 2) {
158             length = FF_CEIL_RSHIFT(dst->width / 2, s->pix_desc->log2_chroma_w);
159             lines  = FF_CEIL_RSHIFT(dst->height,    s->pix_desc->log2_chroma_h);
160         }
161
162         if (interleaved) {
163             for (i = 0; i < lines; i++) {
164                 int j;
165                 int k = 0;
166
167                 for (j = 0; j < length; j++) {
168                     dstp[k++] = leftp[j];
169                     dstp[k++] = rightp[j];
170                 }
171
172                 dstp   += dst->linesize[plane];
173                 leftp  += s->input_views[LEFT]->linesize[plane];
174                 rightp += s->input_views[RIGHT]->linesize[plane];
175             }
176         } else {
177             av_image_copy_plane(dst->data[plane], dst->linesize[plane],
178                                 leftp, s->input_views[LEFT]->linesize[plane],
179                                 length, lines);
180             av_image_copy_plane(dst->data[plane] + length, dst->linesize[plane],
181                                 rightp, s->input_views[RIGHT]->linesize[plane],
182                                 length, lines);
183         }
184     }
185 }
186
187 static void vertical_frame_pack(FramepackContext *s,
188                                 AVFrame *dst,
189                                 int interleaved)
190 {
191     int plane, offset;
192     int length = dst->width;
193     int lines  = dst->height / 2;
194
195     for (plane = 0; plane < s->pix_desc->nb_components; plane++) {
196         if (plane == 1 || plane == 2) {
197             length = -(-(dst->width)      >> s->pix_desc->log2_chroma_w);
198             lines  = -(-(dst->height / 2) >> s->pix_desc->log2_chroma_h);
199         }
200
201         offset = interleaved ? dst->linesize[plane] : dst->linesize[plane] * lines;
202
203         av_image_copy_plane(dst->data[plane],
204                             dst->linesize[plane] << interleaved,
205                             s->input_views[LEFT]->data[plane],
206                             s->input_views[LEFT]->linesize[plane],
207                             length, lines);
208         av_image_copy_plane(dst->data[plane] + offset,
209                             dst->linesize[plane] << interleaved,
210                             s->input_views[RIGHT]->data[plane],
211                             s->input_views[RIGHT]->linesize[plane],
212                             length, lines);
213     }
214 }
215
216 static av_always_inline void spatial_frame_pack(FramepackContext *s, AVFrame *dst)
217 {
218     switch (s->format) {
219     case AV_STEREO3D_SIDEBYSIDE:
220         horizontal_frame_pack(s, dst, 0);
221         break;
222     case AV_STEREO3D_COLUMNS:
223         horizontal_frame_pack(s, dst, 1);
224         break;
225     case AV_STEREO3D_TOPBOTTOM:
226         vertical_frame_pack(s, dst, 0);
227         break;
228     case AV_STEREO3D_LINES:
229         vertical_frame_pack(s, dst, 1);
230         break;
231     }
232 }
233
234 static int filter_frame_left(AVFilterLink *inlink, AVFrame *frame)
235 {
236     FramepackContext *s = inlink->dst->priv;
237     s->input_views[LEFT] = frame;
238     return 0;
239 }
240
241 static int filter_frame_right(AVFilterLink *inlink, AVFrame *frame)
242 {
243     FramepackContext *s = inlink->dst->priv;
244     s->input_views[RIGHT] = frame;
245     return 0;
246 }
247
248 static int request_frame(AVFilterLink *outlink)
249 {
250     AVFilterContext *ctx = outlink->src;
251     FramepackContext *s = ctx->priv;
252     AVStereo3D *stereo;
253     int ret, i;
254
255     /* get a frame on the either input, stop as soon as a video ends */
256     for (i = 0; i < 2; i++) {
257         if (!s->input_views[i]) {
258             ret = ff_request_frame(ctx->inputs[i]);
259             if (ret < 0)
260                 return ret;
261         }
262     }
263
264     if (s->format == AV_STEREO3D_FRAMESEQUENCE) {
265         if (s->double_pts == AV_NOPTS_VALUE)
266             s->double_pts = s->input_views[LEFT]->pts;
267
268         for (i = 0; i < 2; i++) {
269             // set correct timestamps
270             s->input_views[i]->pts = s->double_pts++;
271
272             // set stereo3d side data
273             stereo = av_stereo3d_create_side_data(s->input_views[i]);
274             if (!stereo)
275                 return AVERROR(ENOMEM);
276             stereo->type = s->format;
277
278             // filter the frame and immediately relinquish its pointer
279             ret = ff_filter_frame(outlink, s->input_views[i]);
280             s->input_views[i] = NULL;
281             if (ret < 0)
282                 return ret;
283         }
284         return ret;
285     } else {
286         AVFrame *dst = ff_get_video_buffer(outlink, outlink->w, outlink->h);
287         if (!dst)
288             return AVERROR(ENOMEM);
289
290         spatial_frame_pack(s, dst);
291
292         // get any property from the original frame
293         ret = av_frame_copy_props(dst, s->input_views[LEFT]);
294         if (ret < 0) {
295             av_frame_free(&dst);
296             return ret;
297         }
298
299         for (i = 0; i < 2; i++)
300             av_frame_free(&s->input_views[i]);
301
302         // set stereo3d side data
303         stereo = av_stereo3d_create_side_data(dst);
304         if (!stereo) {
305             av_frame_free(&dst);
306             return AVERROR(ENOMEM);
307         }
308         stereo->type = s->format;
309
310         return ff_filter_frame(outlink, dst);
311     }
312 }
313
314 #define OFFSET(x) offsetof(FramepackContext, x)
315 #define V AV_OPT_FLAG_VIDEO_PARAM
316 static const AVOption options[] = {
317     { "format", "Frame pack output format", OFFSET(format), AV_OPT_TYPE_INT,
318         { .i64 = AV_STEREO3D_SIDEBYSIDE }, 0, INT_MAX, .flags = V, .unit = "format" },
319     { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST,
320         { .i64 = AV_STEREO3D_SIDEBYSIDE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
321     { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST,
322         { .i64 = AV_STEREO3D_TOPBOTTOM }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
323     { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST,
324         { .i64 = AV_STEREO3D_FRAMESEQUENCE }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
325     { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST,
326         { .i64 = AV_STEREO3D_LINES }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
327     { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST,
328         { .i64 = AV_STEREO3D_COLUMNS }, INT_MIN, INT_MAX, .flags = V, .unit = "format" },
329     { NULL },
330 };
331
332 static const AVClass framepack_class = {
333     .class_name = "framepack",
334     .item_name  = av_default_item_name,
335     .option     = options,
336     .version    = LIBAVUTIL_VERSION_INT,
337 };
338
339 static const AVFilterPad framepack_inputs[] = {
340     {
341         .name         = "left",
342         .type         = AVMEDIA_TYPE_VIDEO,
343         .filter_frame = filter_frame_left,
344         .needs_fifo   = 1,
345     },
346     {
347         .name         = "right",
348         .type         = AVMEDIA_TYPE_VIDEO,
349         .filter_frame = filter_frame_right,
350         .needs_fifo   = 1,
351     },
352     { NULL }
353 };
354
355 static const AVFilterPad framepack_outputs[] = {
356     {
357         .name          = "packed",
358         .type          = AVMEDIA_TYPE_VIDEO,
359         .config_props  = config_output,
360         .request_frame = request_frame,
361     },
362     { NULL }
363 };
364
365 AVFilter ff_vf_framepack = {
366     .name          = "framepack",
367     .description   = NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
368     .priv_size     = sizeof(FramepackContext),
369     .priv_class    = &framepack_class,
370     .query_formats = query_formats,
371     .inputs        = framepack_inputs,
372     .outputs       = framepack_outputs,
373     .uninit        = framepack_uninit,
374 };