2 * Copyright (c) 2013 Vittorio Giovara
4 * This file is part of Libav.
6 * Libav 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 * Libav 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 Libav; 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
;
85 AVRational frame_rate
= ctx
->inputs
[LEFT
]->frame_rate
;
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",
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
;
111 s
->pix_desc
= av_pix_fmt_desc_get(outlink
->format
);
115 // modify output properties as needed
117 case AV_STEREO3D_FRAMESEQUENCE
:
121 s
->double_pts
= AV_NOPTS_VALUE
;
123 case AV_STEREO3D_COLUMNS
:
124 case AV_STEREO3D_SIDEBYSIDE
:
127 case AV_STEREO3D_LINES
:
128 case AV_STEREO3D_TOPBOTTOM
:
132 av_log(ctx
, AV_LOG_ERROR
, "Unknown packing mode.");
133 return AVERROR_INVALIDDATA
;
138 outlink
->time_base
= time_base
;
139 outlink
->frame_rate
= frame_rate
;
144 static void horizontal_frame_pack(AVFilterLink
*outlink
,
148 AVFilterContext
*ctx
= outlink
->src
;
149 FramepackContext
*s
= ctx
->priv
;
153 const uint8_t *leftp
= s
->input_views
[LEFT
]->data
[0];
154 const uint8_t *rightp
= s
->input_views
[RIGHT
]->data
[0];
155 uint8_t *dstp
= out
->data
[0];
156 int length
= out
->width
/ 2;
157 int lines
= out
->height
;
159 for (plane
= 0; plane
< s
->pix_desc
->nb_components
; plane
++) {
160 if (plane
== 1 || plane
== 2) {
161 length
= -(-(out
->width
/ 2) >> s
->pix_desc
->log2_chroma_w
);
162 lines
= -(-(out
->height
) >> s
->pix_desc
->log2_chroma_h
);
164 for (i
= 0; i
< lines
; i
++) {
166 leftp
= s
->input_views
[LEFT
]->data
[plane
] +
167 s
->input_views
[LEFT
]->linesize
[plane
] * i
;
168 rightp
= s
->input_views
[RIGHT
]->data
[plane
] +
169 s
->input_views
[RIGHT
]->linesize
[plane
] * i
;
170 dstp
= out
->data
[plane
] + out
->linesize
[plane
] * i
;
171 for (j
= 0; j
< length
; j
++) {
172 // interpolate chroma as necessary
173 if ((s
->pix_desc
->log2_chroma_w
||
174 s
->pix_desc
->log2_chroma_h
) &&
175 (plane
== 1 || plane
== 2)) {
176 *dstp
++ = (*leftp
+ *rightp
) / 2;
177 *dstp
++ = (*leftp
+ *rightp
) / 2;
188 for (i
= 0; i
< 2; i
++) {
189 const uint8_t *src
[4];
191 int sub_w
= s
->input_views
[i
]->width
>> s
->pix_desc
->log2_chroma_w
;
193 src
[0] = s
->input_views
[i
]->data
[0];
194 src
[1] = s
->input_views
[i
]->data
[1];
195 src
[2] = s
->input_views
[i
]->data
[2];
197 dst
[0] = out
->data
[0] + i
* s
->input_views
[i
]->width
;
198 dst
[1] = out
->data
[1] + i
* sub_w
;
199 dst
[2] = out
->data
[2] + i
* sub_w
;
201 av_image_copy(dst
, out
->linesize
, src
, s
->input_views
[i
]->linesize
,
202 s
->input_views
[i
]->format
,
203 s
->input_views
[i
]->width
,
204 s
->input_views
[i
]->height
);
209 static void vertical_frame_pack(AVFilterLink
*outlink
,
213 AVFilterContext
*ctx
= outlink
->src
;
214 FramepackContext
*s
= ctx
->priv
;
217 for (i
= 0; i
< 2; i
++) {
218 const uint8_t *src
[4];
221 int sub_h
= s
->input_views
[i
]->height
>> s
->pix_desc
->log2_chroma_h
;
223 src
[0] = s
->input_views
[i
]->data
[0];
224 src
[1] = s
->input_views
[i
]->data
[1];
225 src
[2] = s
->input_views
[i
]->data
[2];
227 dst
[0] = out
->data
[0] + i
* out
->linesize
[0] *
228 (interleaved
+ s
->input_views
[i
]->height
* (1 - interleaved
));
229 dst
[1] = out
->data
[1] + i
* out
->linesize
[1] *
230 (interleaved
+ sub_h
* (1 - interleaved
));
231 dst
[2] = out
->data
[2] + i
* out
->linesize
[2] *
232 (interleaved
+ sub_h
* (1 - interleaved
));
234 linesizes
[0] = out
->linesize
[0] +
235 interleaved
* out
->linesize
[0];
236 linesizes
[1] = out
->linesize
[1] +
237 interleaved
* out
->linesize
[1];
238 linesizes
[2] = out
->linesize
[2] +
239 interleaved
* out
->linesize
[2];
241 av_image_copy(dst
, linesizes
, src
, s
->input_views
[i
]->linesize
,
242 s
->input_views
[i
]->format
,
243 s
->input_views
[i
]->width
,
244 s
->input_views
[i
]->height
);
248 static av_always_inline
void spatial_frame_pack(AVFilterLink
*outlink
,
251 AVFilterContext
*ctx
= outlink
->src
;
252 FramepackContext
*s
= ctx
->priv
;
254 case AV_STEREO3D_SIDEBYSIDE
:
255 horizontal_frame_pack(outlink
, dst
, 0);
257 case AV_STEREO3D_COLUMNS
:
258 horizontal_frame_pack(outlink
, dst
, 1);
260 case AV_STEREO3D_TOPBOTTOM
:
261 vertical_frame_pack(outlink
, dst
, 0);
263 case AV_STEREO3D_LINES
:
264 vertical_frame_pack(outlink
, dst
, 1);
269 static int filter_frame_left(AVFilterLink
*inlink
, AVFrame
*frame
)
271 FramepackContext
*s
= inlink
->dst
->priv
;
272 s
->input_views
[LEFT
] = frame
;
276 static int filter_frame_right(AVFilterLink
*inlink
, AVFrame
*frame
)
278 FramepackContext
*s
= inlink
->dst
->priv
;
279 s
->input_views
[RIGHT
] = frame
;
283 static int request_frame(AVFilterLink
*outlink
)
285 AVFilterContext
*ctx
= outlink
->src
;
286 FramepackContext
*s
= ctx
->priv
;
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
]);
299 if (s
->format
== AV_STEREO3D_FRAMESEQUENCE
) {
300 if (s
->double_pts
== AV_NOPTS_VALUE
)
301 s
->double_pts
= s
->input_views
[LEFT
]->pts
;
303 for (i
= 0; i
< 2; i
++) {
304 // set correct timestamps
305 s
->input_views
[i
]->pts
= s
->double_pts
++;
307 // set stereo3d side data
308 stereo
= av_stereo3d_create_side_data(s
->input_views
[i
]);
310 return AVERROR(ENOMEM
);
311 stereo
->type
= s
->format
;
313 // filter the frame and immediately relinquish its pointer
314 ret
= ff_filter_frame(outlink
, s
->input_views
[i
]);
315 s
->input_views
[i
] = NULL
;
321 AVFrame
*dst
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
323 return AVERROR(ENOMEM
);
325 spatial_frame_pack(outlink
, dst
);
327 // get any property from the original frame
328 ret
= av_frame_copy_props(dst
, s
->input_views
[LEFT
]);
334 for (i
= 0; i
< 2; i
++)
335 av_frame_free(&s
->input_views
[i
]);
337 // set stereo3d side data
338 stereo
= av_stereo3d_create_side_data(dst
);
341 return AVERROR(ENOMEM
);
343 stereo
->type
= s
->format
;
345 return ff_filter_frame(outlink
, dst
);
349 #define OFFSET(x) offsetof(FramepackContext, x)
350 #define V AV_OPT_FLAG_VIDEO_PARAM
351 static const AVOption options
[] = {
352 { "format", "Frame pack output format", OFFSET(format
), AV_OPT_TYPE_INT
,
353 { .i64
= AV_STEREO3D_SIDEBYSIDE
}, 0, INT_MAX
, .flags
= V
, .unit
= "format" },
354 { "sbs", "Views are packed next to each other", 0, AV_OPT_TYPE_CONST
,
355 { .i64
= AV_STEREO3D_SIDEBYSIDE
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
356 { "tab", "Views are packed on top of each other", 0, AV_OPT_TYPE_CONST
,
357 { .i64
= AV_STEREO3D_TOPBOTTOM
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
358 { "frameseq", "Views are one after the other", 0, AV_OPT_TYPE_CONST
,
359 { .i64
= AV_STEREO3D_FRAMESEQUENCE
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
360 { "lines", "Views are interleaved by lines", 0, AV_OPT_TYPE_CONST
,
361 { .i64
= AV_STEREO3D_LINES
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
362 { "columns", "Views are interleaved by columns", 0, AV_OPT_TYPE_CONST
,
363 { .i64
= AV_STEREO3D_COLUMNS
}, INT_MIN
, INT_MAX
, .flags
= V
, .unit
= "format" },
367 static const AVClass framepack_class
= {
368 .class_name
= "framepack",
369 .item_name
= av_default_item_name
,
371 .version
= LIBAVUTIL_VERSION_INT
,
374 static const AVFilterPad framepack_inputs
[] = {
377 .type
= AVMEDIA_TYPE_VIDEO
,
378 .filter_frame
= filter_frame_left
,
383 .type
= AVMEDIA_TYPE_VIDEO
,
384 .filter_frame
= filter_frame_right
,
390 static const AVFilterPad framepack_outputs
[] = {
393 .type
= AVMEDIA_TYPE_VIDEO
,
394 .config_props
= config_output
,
395 .request_frame
= request_frame
,
400 AVFilter ff_vf_framepack
= {
402 .description
= NULL_IF_CONFIG_SMALL("Generate a frame packed stereoscopic video."),
403 .priv_size
= sizeof(FramepackContext
),
404 .priv_class
= &framepack_class
,
405 .query_formats
= query_formats
,
406 .inputs
= framepack_inputs
,
407 .outputs
= framepack_outputs
,
408 .uninit
= framepack_uninit
,