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