FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_framepack.c
Go to the documentation of this file.
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
53 
54 static const enum AVPixelFormat formats_supported[] = {
59 };
60 
62 {
63  // this will ensure that formats are the same on all pads
65  return 0;
66 }
67 
69 {
70  FramepackContext *s = ctx->priv;
71 
72  // clean any leftover frame
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) {
118  time_base.den *= 2;
119  frame_rate.num *= 2;
120 
122  break;
123  case AV_STEREO3D_COLUMNS:
125  width *= 2;
126  break;
127  case AV_STEREO3D_LINES:
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 
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 
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 
217 {
218  switch (s->format) {
220  horizontal_frame_pack(s, dst, 0);
221  break;
222  case AV_STEREO3D_COLUMNS:
223  horizontal_frame_pack(s, dst, 1);
224  break;
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 
235 {
236  FramepackContext *s = inlink->dst->priv;
237  s->input_views[LEFT] = frame;
238  return 0;
239 }
240 
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
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 
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,
371  .inputs = framepack_inputs,
372  .outputs = framepack_outputs,
374 };