2 * Copyright (c) 2013 Vittorio Giovara
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
23 * Generate a frame packed video, by combining two views in a single surface.
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"
42 typedef struct FramepackContext
{
45 const AVPixFmtDescriptor
*pix_desc
; ///< agreed pixel format
47 enum AVStereo3DType format
; ///< frame pack type output
49 AVFrame
*input_views
[2]; ///< input frames
51 int64_t double_pts
; ///< new pts for frameseq mode
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
,
61 static int query_formats(AVFilterContext
*ctx
)
63 // this will ensure that formats are the same on all pads
64 ff_set_common_formats(ctx
, ff_make_format_list(formats_supported
));
68 static av_cold
void framepack_uninit(AVFilterContext
*ctx
)
70 FramepackContext
*s
= ctx
->priv
;
72 // clean any leftover frame
73 av_frame_free(&s
->input_views
[LEFT
]);
74 av_frame_free(&s
->input_views
[RIGHT
]);
77 static int config_output(AVFilterLink
*outlink
)
79 AVFilterContext
*ctx
= outlink
->src
;
80 FramepackContext
*s
= outlink
->src
->priv
;
82 int width
= ctx
->inputs
[LEFT
]->w
;
83 int height
= ctx
->inputs
[LEFT
]->h
;
84 AVRational time_base
= ctx
->inputs
[LEFT
]->time_base
;
86 // check size and fps match on the other input
87 if (width
!= ctx
->inputs
[RIGHT
]->w
||
88 height
!= ctx
->inputs
[RIGHT
]->h
) {
89 av_log(ctx
, AV_LOG_ERROR
,
90 "Left and right sizes differ (%dx%d vs %dx%d).\n",
92 ctx
->inputs
[RIGHT
]->w
, ctx
->inputs
[RIGHT
]->h
);
93 return AVERROR_INVALIDDATA
;
94 } else if (av_cmp_q(time_base
, ctx
->inputs
[RIGHT
]->time_base
) != 0) {
95 av_log(ctx
, AV_LOG_ERROR
,
96 "Left and right framerates differ (%d/%d vs %d/%d).\n",
97 time_base
.num
, time_base
.den
,
98 ctx
->inputs
[RIGHT
]->time_base
.num
,
99 ctx
->inputs
[RIGHT
]->time_base
.den
);
100 return AVERROR_INVALIDDATA
;
103 s
->pix_desc
= av_pix_fmt_desc_get(outlink
->format
);
107 // modify output properties as needed
109 case AV_STEREO3D_FRAMESEQUENCE
:
111 s
->double_pts
= AV_NOPTS_VALUE
;
113 case AV_STEREO3D_COLUMNS
:
114 case AV_STEREO3D_SIDEBYSIDE
:
117 case AV_STEREO3D_LINES
:
118 case AV_STEREO3D_TOPBOTTOM
:
122 av_log(ctx
, AV_LOG_ERROR
, "Unknown packing mode.");
123 return AVERROR_INVALIDDATA
;
128 outlink
->time_base
= time_base
;
133 static void horizontal_frame_pack(FramepackContext
*s
,
138 int length
= dst
->width
/ 2;
139 int lines
= dst
->height
;
141 for (plane
= 0; plane
< s
->pix_desc
->nb_components
; plane
++) {
142 const uint8_t *leftp
= s
->input_views
[LEFT
]->data
[plane
];
143 const uint8_t *rightp
= s
->input_views
[RIGHT
]->data
[plane
];
144 uint8_t *dstp
= dst
->data
[plane
];
146 if (plane
== 1 || plane
== 2) {
147 length
= -(-(dst
->width
/ 2) >> s
->pix_desc
->log2_chroma_w
);
148 lines
= -(-(dst
->height
) >> s
->pix_desc
->log2_chroma_h
);
152 for (i
= 0; i
< lines
; i
++) {
156 for (j
= 0; j
< length
; j
++) {
157 dstp
[k
++] = leftp
[j
];
158 dstp
[k
++] = rightp
[j
];
161 dstp
+= dst
->linesize
[plane
];
162 leftp
+= s
->input_views
[LEFT
]->linesize
[plane
];
163 rightp
+= s
->input_views
[RIGHT
]->linesize
[plane
];
166 av_image_copy_plane(dst
->data
[plane
], dst
->linesize
[plane
],
167 leftp
, s
->input_views
[LEFT
]->linesize
[plane
],
169 av_image_copy_plane(dst
->data
[plane
] + length
, dst
->linesize
[plane
],
170 rightp
, s
->input_views
[RIGHT
]->linesize
[plane
],
176 static void vertical_frame_pack(FramepackContext
*s
,
181 int length
= dst
->width
;
182 int lines
= dst
->height
/ 2;
184 for (plane
= 0; plane
< s
->pix_desc
->nb_components
; plane
++) {
185 if (plane
== 1 || plane
== 2) {
186 length
= -(-(dst
->width
) >> s
->pix_desc
->log2_chroma_w
);
187 lines
= -(-(dst
->height
/ 2) >> s
->pix_desc
->log2_chroma_h
);
190 offset
= interleaved
? dst
->linesize
[plane
] : dst
->linesize
[plane
] * lines
;
192 av_image_copy_plane(dst
->data
[plane
],
193 dst
->linesize
[plane
] << interleaved
,
194 s
->input_views
[LEFT
]->data
[plane
],
195 s
->input_views
[LEFT
]->linesize
[plane
],
197 av_image_copy_plane(dst
->data
[plane
] + offset
,
198 dst
->linesize
[plane
] << interleaved
,
199 s
->input_views
[RIGHT
]->data
[plane
],
200 s
->input_views
[RIGHT
]->linesize
[plane
],
205 static av_always_inline
void spatial_frame_pack(FramepackContext
*s
, AVFrame
*dst
)
208 case AV_STEREO3D_SIDEBYSIDE
:
209 horizontal_frame_pack(s
, dst
, 0);
211 case AV_STEREO3D_COLUMNS
:
212 horizontal_frame_pack(s
, dst
, 1);
214 case AV_STEREO3D_TOPBOTTOM
:
215 vertical_frame_pack(s
, dst
, 0);
217 case AV_STEREO3D_LINES
:
218 vertical_frame_pack(s
, dst
, 1);
223 static int filter_frame_left(AVFilterLink
*inlink
, AVFrame
*frame
)
225 FramepackContext
*s
= inlink
->dst
->priv
;
226 s
->input_views
[LEFT
] = frame
;
230 static int filter_frame_right(AVFilterLink
*inlink
, AVFrame
*frame
)
232 FramepackContext
*s
= inlink
->dst
->priv
;
233 s
->input_views
[RIGHT
] = frame
;
237 static int request_frame(AVFilterLink
*outlink
)
239 AVFilterContext
*ctx
= outlink
->src
;
240 FramepackContext
*s
= ctx
->priv
;
244 /* get a frame on the either input, stop as soon as a video ends */
245 for (i
= 0; i
< 2; i
++) {
246 if (!s
->input_views
[i
]) {
247 ret
= ff_request_frame(ctx
->inputs
[i
]);
253 if (s
->format
== AV_STEREO3D_FRAMESEQUENCE
) {
254 if (s
->double_pts
== AV_NOPTS_VALUE
)
255 s
->double_pts
= s
->input_views
[LEFT
]->pts
;
257 for (i
= 0; i
< 2; i
++) {
258 // set correct timestamps
259 s
->input_views
[i
]->pts
= s
->double_pts
++;
261 // set stereo3d side data
262 stereo
= av_stereo3d_create_side_data(s
->input_views
[i
]);
264 return AVERROR(ENOMEM
);
265 stereo
->type
= s
->format
;
267 // filter the frame and immediately relinquish its pointer
268 ret
= ff_filter_frame(outlink
, s
->input_views
[i
]);
269 s
->input_views
[i
] = NULL
;
275 AVFrame
*dst
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
277 return AVERROR(ENOMEM
);
279 spatial_frame_pack(s
, dst
);
281 // get any property from the original frame
282 ret
= av_frame_copy_props(dst
, s
->input_views
[LEFT
]);
288 for (i
= 0; i
< 2; i
++)
289 av_frame_free(&s
->input_views
[i
]);
291 // set stereo3d side data
292 stereo
= av_stereo3d_create_side_data(dst
);
295 return AVERROR(ENOMEM
);
297 stereo
->type
= s
->format
;
299 return ff_filter_frame(outlink
, dst
);
303 #define OFFSET(x) offsetof(FramepackContext, x)
304 #define V AV_OPT_FLAG_VIDEO_PARAM
305 static const AVOption options
[] = {
306 { "format", "Frame pack output format", OFFSET(format
), AV_OPT_TYPE_INT
,
307 { .i64
= AV_STEREO3D_SIDEBYSIDE
}, 0, INT_MAX
, .flags
= V
, .unit
= "format" },
308 { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST
,
309 { .i64
= AV_STEREO3D_SIDEBYSIDE
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
310 { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST
,
311 { .i64
= AV_STEREO3D_TOPBOTTOM
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
312 { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST
,
313 { .i64
= AV_STEREO3D_FRAMESEQUENCE
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
314 { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST
,
315 { .i64
= AV_STEREO3D_LINES
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
316 { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST
,
317 { .i64
= AV_STEREO3D_COLUMNS
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
321 static const AVClass framepack_class
= {
322 .class_name
= "framepack",
323 .item_name
= av_default_item_name
,
325 .version
= LIBAVUTIL_VERSION_INT
,
328 static const AVFilterPad framepack_inputs
[] = {
331 .type
= AVMEDIA_TYPE_VIDEO
,
332 .filter_frame
= filter_frame_left
,
337 .type
= AVMEDIA_TYPE_VIDEO
,
338 .filter_frame
= filter_frame_right
,
344 static const AVFilterPad framepack_outputs
[] = {
347 .type
= AVMEDIA_TYPE_VIDEO
,
348 .config_props
= config_output
,
349 .request_frame
= request_frame
,
354 AVFilter ff_vf_framepack
= {
356 .description
= NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
357 .priv_size
= sizeof(FramepackContext
),
358 .priv_class
= &framepack_class
,
359 .query_formats
= query_formats
,
360 .inputs
= framepack_inputs
,
361 .outputs
= framepack_outputs
,
362 .uninit
= framepack_uninit
,