FFmpeg
vf_v360.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 Eugene Lyapustin
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  * 360 video conversion filter.
24  * Principle of operation:
25  *
26  * (for each pixel in output frame)
27  * 1) Calculate OpenGL-like coordinates (x, y, z) for pixel position (i, j)
28  * 2) Apply 360 operations (rotation, mirror) to (x, y, z)
29  * 3) Calculate pixel position (u, v) in input frame
30  * 4) Calculate interpolation window and weight for each pixel
31  *
32  * (for each frame)
33  * 5) Remap input frame to output frame using precalculated data
34  */
35 
36 #include <math.h>
37 
38 #include "libavutil/avassert.h"
39 #include "libavutil/imgutils.h"
40 #include "libavutil/pixdesc.h"
41 #include "libavutil/opt.h"
42 #include "avfilter.h"
43 #include "formats.h"
44 #include "internal.h"
45 #include "video.h"
46 #include "v360.h"
47 
48 typedef struct ThreadData {
49  AVFrame *in;
50  AVFrame *out;
51 } ThreadData;
52 
53 #define OFFSET(x) offsetof(V360Context, x)
54 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
55 
56 static const AVOption v360_options[] = {
57  { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" },
58  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
59  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
60  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "in" },
61  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" },
62  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" },
63  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
64  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
65  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
66  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" },
67  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
68  { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
69  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
70  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
71  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
72  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
73  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
74  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
75  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
76  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
77  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
78  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
79  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
80  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" },
81  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
82  { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" },
83  { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
84  { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
85  { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
86  { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
87  { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
88  { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
89  { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
90  { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
91  { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
92  { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
93  { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
94  {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
95  { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
96  { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
97  { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
98  { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
99  {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
100  { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
101  { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"},
102  { "in_pad", "input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"},
103  { "out_pad", "output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"},
104  { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "yaw"},
105  { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "pitch"},
106  { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "roll"},
107  { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"},
108  { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"},
109  { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"},
110  { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"},
111  { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"},
112  { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"},
113  { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"},
114  { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "ih_flip"},
115  { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"},
116  { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"},
117  { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"},
118  { NULL }
119 };
120 
122 
124 {
125  static const enum AVPixelFormat pix_fmts[] = {
126  // YUVA444
130 
131  // YUVA422
135 
136  // YUVA420
139 
140  // YUVJ
144 
145  // YUV444
149 
150  // YUV440
153 
154  // YUV422
158 
159  // YUV420
163 
164  // YUV411
166 
167  // YUV410
169 
170  // GBR
174 
175  // GBRA
178 
179  // GRAY
183 
185  };
186 
187  AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
188  if (!fmts_list)
189  return AVERROR(ENOMEM);
190  return ff_set_common_formats(ctx, fmts_list);
191 }
192 
193 #define DEFINE_REMAP1_LINE(bits, div) \
194 static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
195  ptrdiff_t in_linesize, \
196  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
197 { \
198  const uint##bits##_t *s = (const uint##bits##_t *)src; \
199  uint##bits##_t *d = (uint##bits##_t *)dst; \
200  \
201  in_linesize /= div; \
202  \
203  for (int x = 0; x < width; x++) \
204  d[x] = s[v[x] * in_linesize + u[x]]; \
205 }
206 
207 DEFINE_REMAP1_LINE( 8, 1)
208 DEFINE_REMAP1_LINE(16, 2)
209 
210 typedef struct XYRemap {
211  uint16_t u[4][4];
212  uint16_t v[4][4];
213  float ker[4][4];
214 } XYRemap;
215 
216 /**
217  * Generate remapping function with a given window size and pixel depth.
218  *
219  * @param ws size of interpolation window
220  * @param bits number of bits per pixel
221  */
222 #define DEFINE_REMAP(ws, bits) \
223 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
224 { \
225  ThreadData *td = (ThreadData*)arg; \
226  const V360Context *s = ctx->priv; \
227  const AVFrame *in = td->in; \
228  AVFrame *out = td->out; \
229  \
230  for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
231  for (int plane = 0; plane < s->nb_planes; plane++) { \
232  const int in_linesize = in->linesize[plane]; \
233  const int out_linesize = out->linesize[plane]; \
234  const int uv_linesize = s->uv_linesize[plane]; \
235  const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
236  const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
237  const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
238  const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
239  const uint8_t *src = in->data[plane] + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
240  uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
241  const int width = s->pr_width[plane]; \
242  const int height = s->pr_height[plane]; \
243  \
244  const int slice_start = (height * jobnr ) / nb_jobs; \
245  const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
246  \
247  for (int y = slice_start; y < slice_end; y++) { \
248  const unsigned map = s->map[plane]; \
249  const uint16_t *u = s->u[map] + y * uv_linesize * ws * ws; \
250  const uint16_t *v = s->v[map] + y * uv_linesize * ws * ws; \
251  const int16_t *ker = s->ker[map] + y * uv_linesize * ws * ws; \
252  \
253  s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
254  } \
255  } \
256  } \
257  \
258  return 0; \
259 }
260 
261 DEFINE_REMAP(1, 8)
262 DEFINE_REMAP(2, 8)
263 DEFINE_REMAP(4, 8)
264 DEFINE_REMAP(1, 16)
265 DEFINE_REMAP(2, 16)
266 DEFINE_REMAP(4, 16)
267 
268 #define DEFINE_REMAP_LINE(ws, bits, div) \
269 static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
270  ptrdiff_t in_linesize, \
271  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
272 { \
273  const uint##bits##_t *s = (const uint##bits##_t *)src; \
274  uint##bits##_t *d = (uint##bits##_t *)dst; \
275  \
276  in_linesize /= div; \
277  \
278  for (int x = 0; x < width; x++) { \
279  const uint16_t *uu = u + x * ws * ws; \
280  const uint16_t *vv = v + x * ws * ws; \
281  const int16_t *kker = ker + x * ws * ws; \
282  int tmp = 0; \
283  \
284  for (int i = 0; i < ws; i++) { \
285  for (int j = 0; j < ws; j++) { \
286  tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \
287  } \
288  } \
289  \
290  d[x] = av_clip_uint##bits(tmp >> 14); \
291  } \
292 }
293 
294 DEFINE_REMAP_LINE(2, 8, 1)
295 DEFINE_REMAP_LINE(4, 8, 1)
296 DEFINE_REMAP_LINE(2, 16, 2)
297 DEFINE_REMAP_LINE(4, 16, 2)
298 
299 void ff_v360_init(V360Context *s, int depth)
300 {
301  switch (s->interp) {
302  case NEAREST:
303  s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c;
304  break;
305  case BILINEAR:
306  s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c;
307  break;
308  case BICUBIC:
309  case LANCZOS:
310  s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c;
311  break;
312  }
313 
314  if (ARCH_X86)
315  ff_v360_init_x86(s, depth);
316 }
317 
318 /**
319  * Save nearest pixel coordinates for remapping.
320  *
321  * @param du horizontal relative coordinate
322  * @param dv vertical relative coordinate
323  * @param r_tmp calculated 4x4 window
324  * @param u u remap data
325  * @param v v remap data
326  * @param ker ker remap data
327  */
328 static void nearest_kernel(float du, float dv, const XYRemap *r_tmp,
329  uint16_t *u, uint16_t *v, int16_t *ker)
330 {
331  const int i = roundf(dv) + 1;
332  const int j = roundf(du) + 1;
333 
334  u[0] = r_tmp->u[i][j];
335  v[0] = r_tmp->v[i][j];
336 }
337 
338 /**
339  * Calculate kernel for bilinear interpolation.
340  *
341  * @param du horizontal relative coordinate
342  * @param dv vertical relative coordinate
343  * @param r_tmp calculated 4x4 window
344  * @param u u remap data
345  * @param v v remap data
346  * @param ker ker remap data
347  */
348 static void bilinear_kernel(float du, float dv, const XYRemap *r_tmp,
349  uint16_t *u, uint16_t *v, int16_t *ker)
350 {
351  for (int i = 0; i < 2; i++) {
352  for (int j = 0; j < 2; j++) {
353  u[i * 2 + j] = r_tmp->u[i + 1][j + 1];
354  v[i * 2 + j] = r_tmp->v[i + 1][j + 1];
355  }
356  }
357 
358  ker[0] = (1.f - du) * (1.f - dv) * 16384;
359  ker[1] = du * (1.f - dv) * 16384;
360  ker[2] = (1.f - du) * dv * 16384;
361  ker[3] = du * dv * 16384;
362 }
363 
364 /**
365  * Calculate 1-dimensional cubic coefficients.
366  *
367  * @param t relative coordinate
368  * @param coeffs coefficients
369  */
370 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
371 {
372  const float tt = t * t;
373  const float ttt = t * t * t;
374 
375  coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f;
376  coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f;
377  coeffs[2] = t + tt / 2.f - ttt / 2.f;
378  coeffs[3] = - t / 6.f + ttt / 6.f;
379 }
380 
381 /**
382  * Calculate kernel for bicubic interpolation.
383  *
384  * @param du horizontal relative coordinate
385  * @param dv vertical relative coordinate
386  * @param r_tmp calculated 4x4 window
387  * @param u u remap data
388  * @param v v remap data
389  * @param ker ker remap data
390  */
391 static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp,
392  uint16_t *u, uint16_t *v, int16_t *ker)
393 {
394  float du_coeffs[4];
395  float dv_coeffs[4];
396 
397  calculate_bicubic_coeffs(du, du_coeffs);
398  calculate_bicubic_coeffs(dv, dv_coeffs);
399 
400  for (int i = 0; i < 4; i++) {
401  for (int j = 0; j < 4; j++) {
402  u[i * 4 + j] = r_tmp->u[i][j];
403  v[i * 4 + j] = r_tmp->v[i][j];
404  ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384;
405  }
406  }
407 }
408 
409 /**
410  * Calculate 1-dimensional lanczos coefficients.
411  *
412  * @param t relative coordinate
413  * @param coeffs coefficients
414  */
415 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
416 {
417  float sum = 0.f;
418 
419  for (int i = 0; i < 4; i++) {
420  const float x = M_PI * (t - i + 1);
421  if (x == 0.f) {
422  coeffs[i] = 1.f;
423  } else {
424  coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
425  }
426  sum += coeffs[i];
427  }
428 
429  for (int i = 0; i < 4; i++) {
430  coeffs[i] /= sum;
431  }
432 }
433 
434 /**
435  * Calculate kernel for lanczos interpolation.
436  *
437  * @param du horizontal relative coordinate
438  * @param dv vertical relative coordinate
439  * @param r_tmp calculated 4x4 window
440  * @param u u remap data
441  * @param v v remap data
442  * @param ker ker remap data
443  */
444 static void lanczos_kernel(float du, float dv, const XYRemap *r_tmp,
445  uint16_t *u, uint16_t *v, int16_t *ker)
446 {
447  float du_coeffs[4];
448  float dv_coeffs[4];
449 
450  calculate_lanczos_coeffs(du, du_coeffs);
451  calculate_lanczos_coeffs(dv, dv_coeffs);
452 
453  for (int i = 0; i < 4; i++) {
454  for (int j = 0; j < 4; j++) {
455  u[i * 4 + j] = r_tmp->u[i][j];
456  v[i * 4 + j] = r_tmp->v[i][j];
457  ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384;
458  }
459  }
460 }
461 
462 /**
463  * Modulo operation with only positive remainders.
464  *
465  * @param a dividend
466  * @param b divisor
467  *
468  * @return positive remainder of (a / b)
469  */
470 static inline int mod(int a, int b)
471 {
472  const int res = a % b;
473  if (res < 0) {
474  return res + b;
475  } else {
476  return res;
477  }
478 }
479 
480 /**
481  * Convert char to corresponding direction.
482  * Used for cubemap options.
483  */
484 static int get_direction(char c)
485 {
486  switch (c) {
487  case 'r':
488  return RIGHT;
489  case 'l':
490  return LEFT;
491  case 'u':
492  return UP;
493  case 'd':
494  return DOWN;
495  case 'f':
496  return FRONT;
497  case 'b':
498  return BACK;
499  default:
500  return -1;
501  }
502 }
503 
504 /**
505  * Convert char to corresponding rotation angle.
506  * Used for cubemap options.
507  */
508 static int get_rotation(char c)
509 {
510  switch (c) {
511  case '0':
512  return ROT_0;
513  case '1':
514  return ROT_90;
515  case '2':
516  return ROT_180;
517  case '3':
518  return ROT_270;
519  default:
520  return -1;
521  }
522 }
523 
524 /**
525  * Convert char to corresponding rotation order.
526  */
527 static int get_rorder(char c)
528 {
529  switch (c) {
530  case 'Y':
531  case 'y':
532  return YAW;
533  case 'P':
534  case 'p':
535  return PITCH;
536  case 'R':
537  case 'r':
538  return ROLL;
539  default:
540  return -1;
541  }
542 }
543 
544 /**
545  * Prepare data for processing cubemap input format.
546  *
547  * @param ctx filter context
548  *
549  * @return error code
550  */
552 {
553  V360Context *s = ctx->priv;
554 
555  for (int face = 0; face < NB_FACES; face++) {
556  const char c = s->in_forder[face];
557  int direction;
558 
559  if (c == '\0') {
560  av_log(ctx, AV_LOG_ERROR,
561  "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
562  return AVERROR(EINVAL);
563  }
564 
565  direction = get_direction(c);
566  if (direction == -1) {
567  av_log(ctx, AV_LOG_ERROR,
568  "Incorrect direction symbol '%c' in in_forder option.\n", c);
569  return AVERROR(EINVAL);
570  }
571 
572  s->in_cubemap_face_order[direction] = face;
573  }
574 
575  for (int face = 0; face < NB_FACES; face++) {
576  const char c = s->in_frot[face];
577  int rotation;
578 
579  if (c == '\0') {
580  av_log(ctx, AV_LOG_ERROR,
581  "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
582  return AVERROR(EINVAL);
583  }
584 
585  rotation = get_rotation(c);
586  if (rotation == -1) {
587  av_log(ctx, AV_LOG_ERROR,
588  "Incorrect rotation symbol '%c' in in_frot option.\n", c);
589  return AVERROR(EINVAL);
590  }
591 
592  s->in_cubemap_face_rotation[face] = rotation;
593  }
594 
595  return 0;
596 }
597 
598 /**
599  * Prepare data for processing cubemap output format.
600  *
601  * @param ctx filter context
602  *
603  * @return error code
604  */
606 {
607  V360Context *s = ctx->priv;
608 
609  for (int face = 0; face < NB_FACES; face++) {
610  const char c = s->out_forder[face];
611  int direction;
612 
613  if (c == '\0') {
614  av_log(ctx, AV_LOG_ERROR,
615  "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
616  return AVERROR(EINVAL);
617  }
618 
619  direction = get_direction(c);
620  if (direction == -1) {
621  av_log(ctx, AV_LOG_ERROR,
622  "Incorrect direction symbol '%c' in out_forder option.\n", c);
623  return AVERROR(EINVAL);
624  }
625 
626  s->out_cubemap_direction_order[face] = direction;
627  }
628 
629  for (int face = 0; face < NB_FACES; face++) {
630  const char c = s->out_frot[face];
631  int rotation;
632 
633  if (c == '\0') {
634  av_log(ctx, AV_LOG_ERROR,
635  "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
636  return AVERROR(EINVAL);
637  }
638 
639  rotation = get_rotation(c);
640  if (rotation == -1) {
641  av_log(ctx, AV_LOG_ERROR,
642  "Incorrect rotation symbol '%c' in out_frot option.\n", c);
643  return AVERROR(EINVAL);
644  }
645 
646  s->out_cubemap_face_rotation[face] = rotation;
647  }
648 
649  return 0;
650 }
651 
652 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
653 {
654  float tmp;
655 
656  switch (rotation) {
657  case ROT_0:
658  break;
659  case ROT_90:
660  tmp = *uf;
661  *uf = -*vf;
662  *vf = tmp;
663  break;
664  case ROT_180:
665  *uf = -*uf;
666  *vf = -*vf;
667  break;
668  case ROT_270:
669  tmp = -*uf;
670  *uf = *vf;
671  *vf = tmp;
672  break;
673  default:
674  av_assert0(0);
675  }
676 }
677 
678 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
679 {
680  float tmp;
681 
682  switch (rotation) {
683  case ROT_0:
684  break;
685  case ROT_90:
686  tmp = -*uf;
687  *uf = *vf;
688  *vf = tmp;
689  break;
690  case ROT_180:
691  *uf = -*uf;
692  *vf = -*vf;
693  break;
694  case ROT_270:
695  tmp = *uf;
696  *uf = -*vf;
697  *vf = tmp;
698  break;
699  default:
700  av_assert0(0);
701  }
702 }
703 
704 /**
705  * Normalize vector.
706  *
707  * @param vec vector
708  */
709 static void normalize_vector(float *vec)
710 {
711  const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
712 
713  vec[0] /= norm;
714  vec[1] /= norm;
715  vec[2] /= norm;
716 }
717 
718 /**
719  * Calculate 3D coordinates on sphere for corresponding cubemap position.
720  * Common operation for every cubemap.
721  *
722  * @param s filter context
723  * @param uf horizontal cubemap coordinate [0, 1)
724  * @param vf vertical cubemap coordinate [0, 1)
725  * @param face face of cubemap
726  * @param vec coordinates on sphere
727  */
728 static void cube_to_xyz(const V360Context *s,
729  float uf, float vf, int face,
730  float *vec)
731 {
732  const int direction = s->out_cubemap_direction_order[face];
733  float l_x, l_y, l_z;
734 
735  uf /= (1.f - s->out_pad);
736  vf /= (1.f - s->out_pad);
737 
739 
740  switch (direction) {
741  case RIGHT:
742  l_x = 1.f;
743  l_y = -vf;
744  l_z = uf;
745  break;
746  case LEFT:
747  l_x = -1.f;
748  l_y = -vf;
749  l_z = -uf;
750  break;
751  case UP:
752  l_x = uf;
753  l_y = 1.f;
754  l_z = -vf;
755  break;
756  case DOWN:
757  l_x = uf;
758  l_y = -1.f;
759  l_z = vf;
760  break;
761  case FRONT:
762  l_x = uf;
763  l_y = -vf;
764  l_z = -1.f;
765  break;
766  case BACK:
767  l_x = -uf;
768  l_y = -vf;
769  l_z = 1.f;
770  break;
771  default:
772  av_assert0(0);
773  }
774 
775  vec[0] = l_x;
776  vec[1] = l_y;
777  vec[2] = l_z;
778 
779  normalize_vector(vec);
780 }
781 
782 /**
783  * Calculate cubemap position for corresponding 3D coordinates on sphere.
784  * Common operation for every cubemap.
785  *
786  * @param s filter context
787  * @param vec coordinated on sphere
788  * @param uf horizontal cubemap coordinate [0, 1)
789  * @param vf vertical cubemap coordinate [0, 1)
790  * @param direction direction of view
791  */
792 static void xyz_to_cube(const V360Context *s,
793  const float *vec,
794  float *uf, float *vf, int *direction)
795 {
796  const float phi = atan2f(vec[0], -vec[2]);
797  const float theta = asinf(-vec[1]);
798  float phi_norm, theta_threshold;
799  int face;
800 
801  if (phi >= -M_PI_4 && phi < M_PI_4) {
802  *direction = FRONT;
803  phi_norm = phi;
804  } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
805  *direction = LEFT;
806  phi_norm = phi + M_PI_2;
807  } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
808  *direction = RIGHT;
809  phi_norm = phi - M_PI_2;
810  } else {
811  *direction = BACK;
812  phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
813  }
814 
815  theta_threshold = atanf(cosf(phi_norm));
816  if (theta > theta_threshold) {
817  *direction = DOWN;
818  } else if (theta < -theta_threshold) {
819  *direction = UP;
820  }
821 
822  switch (*direction) {
823  case RIGHT:
824  *uf = vec[2] / vec[0];
825  *vf = -vec[1] / vec[0];
826  break;
827  case LEFT:
828  *uf = vec[2] / vec[0];
829  *vf = vec[1] / vec[0];
830  break;
831  case UP:
832  *uf = vec[0] / vec[1];
833  *vf = -vec[2] / vec[1];
834  break;
835  case DOWN:
836  *uf = -vec[0] / vec[1];
837  *vf = -vec[2] / vec[1];
838  break;
839  case FRONT:
840  *uf = -vec[0] / vec[2];
841  *vf = vec[1] / vec[2];
842  break;
843  case BACK:
844  *uf = -vec[0] / vec[2];
845  *vf = -vec[1] / vec[2];
846  break;
847  default:
848  av_assert0(0);
849  }
850 
851  face = s->in_cubemap_face_order[*direction];
852  rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
853 
854  (*uf) *= s->input_mirror_modifier[0];
855  (*vf) *= s->input_mirror_modifier[1];
856 }
857 
858 /**
859  * Find position on another cube face in case of overflow/underflow.
860  * Used for calculation of interpolation window.
861  *
862  * @param s filter context
863  * @param uf horizontal cubemap coordinate
864  * @param vf vertical cubemap coordinate
865  * @param direction direction of view
866  * @param new_uf new horizontal cubemap coordinate
867  * @param new_vf new vertical cubemap coordinate
868  * @param face face position on cubemap
869  */
871  float uf, float vf, int direction,
872  float *new_uf, float *new_vf, int *face)
873 {
874  /*
875  * Cubemap orientation
876  *
877  * width
878  * <------->
879  * +-------+
880  * | | U
881  * | up | h ------->
882  * +-------+-------+-------+-------+ ^ e |
883  * | | | | | | i V |
884  * | left | front | right | back | | g |
885  * +-------+-------+-------+-------+ v h v
886  * | | t
887  * | down |
888  * +-------+
889  */
890 
891  *face = s->in_cubemap_face_order[direction];
893 
894  if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
895  // There are no pixels to use in this case
896  *new_uf = uf;
897  *new_vf = vf;
898  } else if (uf < -1.f) {
899  uf += 2.f;
900  switch (direction) {
901  case RIGHT:
902  direction = FRONT;
903  *new_uf = uf;
904  *new_vf = vf;
905  break;
906  case LEFT:
907  direction = BACK;
908  *new_uf = uf;
909  *new_vf = vf;
910  break;
911  case UP:
912  direction = LEFT;
913  *new_uf = vf;
914  *new_vf = -uf;
915  break;
916  case DOWN:
917  direction = LEFT;
918  *new_uf = -vf;
919  *new_vf = uf;
920  break;
921  case FRONT:
922  direction = LEFT;
923  *new_uf = uf;
924  *new_vf = vf;
925  break;
926  case BACK:
927  direction = RIGHT;
928  *new_uf = uf;
929  *new_vf = vf;
930  break;
931  default:
932  av_assert0(0);
933  }
934  } else if (uf >= 1.f) {
935  uf -= 2.f;
936  switch (direction) {
937  case RIGHT:
938  direction = BACK;
939  *new_uf = uf;
940  *new_vf = vf;
941  break;
942  case LEFT:
943  direction = FRONT;
944  *new_uf = uf;
945  *new_vf = vf;
946  break;
947  case UP:
948  direction = RIGHT;
949  *new_uf = -vf;
950  *new_vf = uf;
951  break;
952  case DOWN:
953  direction = RIGHT;
954  *new_uf = vf;
955  *new_vf = -uf;
956  break;
957  case FRONT:
958  direction = RIGHT;
959  *new_uf = uf;
960  *new_vf = vf;
961  break;
962  case BACK:
963  direction = LEFT;
964  *new_uf = uf;
965  *new_vf = vf;
966  break;
967  default:
968  av_assert0(0);
969  }
970  } else if (vf < -1.f) {
971  vf += 2.f;
972  switch (direction) {
973  case RIGHT:
974  direction = UP;
975  *new_uf = vf;
976  *new_vf = -uf;
977  break;
978  case LEFT:
979  direction = UP;
980  *new_uf = -vf;
981  *new_vf = uf;
982  break;
983  case UP:
984  direction = BACK;
985  *new_uf = -uf;
986  *new_vf = -vf;
987  break;
988  case DOWN:
989  direction = FRONT;
990  *new_uf = uf;
991  *new_vf = vf;
992  break;
993  case FRONT:
994  direction = UP;
995  *new_uf = uf;
996  *new_vf = vf;
997  break;
998  case BACK:
999  direction = UP;
1000  *new_uf = -uf;
1001  *new_vf = -vf;
1002  break;
1003  default:
1004  av_assert0(0);
1005  }
1006  } else if (vf >= 1.f) {
1007  vf -= 2.f;
1008  switch (direction) {
1009  case RIGHT:
1010  direction = DOWN;
1011  *new_uf = -vf;
1012  *new_vf = uf;
1013  break;
1014  case LEFT:
1015  direction = DOWN;
1016  *new_uf = vf;
1017  *new_vf = -uf;
1018  break;
1019  case UP:
1020  direction = FRONT;
1021  *new_uf = uf;
1022  *new_vf = vf;
1023  break;
1024  case DOWN:
1025  direction = BACK;
1026  *new_uf = -uf;
1027  *new_vf = -vf;
1028  break;
1029  case FRONT:
1030  direction = DOWN;
1031  *new_uf = uf;
1032  *new_vf = vf;
1033  break;
1034  case BACK:
1035  direction = DOWN;
1036  *new_uf = -uf;
1037  *new_vf = -vf;
1038  break;
1039  default:
1040  av_assert0(0);
1041  }
1042  } else {
1043  // Inside cube face
1044  *new_uf = uf;
1045  *new_vf = vf;
1046  }
1047 
1048  *face = s->in_cubemap_face_order[direction];
1049  rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1050 }
1051 
1052 /**
1053  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1054  *
1055  * @param s filter context
1056  * @param i horizontal position on frame [0, width)
1057  * @param j vertical position on frame [0, height)
1058  * @param width frame width
1059  * @param height frame height
1060  * @param vec coordinates on sphere
1061  */
1062 static void cube3x2_to_xyz(const V360Context *s,
1063  int i, int j, int width, int height,
1064  float *vec)
1065 {
1066  const float ew = width / 3.f;
1067  const float eh = height / 2.f;
1068 
1069  const int u_face = floorf(i / ew);
1070  const int v_face = floorf(j / eh);
1071  const int face = u_face + 3 * v_face;
1072 
1073  const int u_shift = ceilf(ew * u_face);
1074  const int v_shift = ceilf(eh * v_face);
1075  const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1076  const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1077 
1078  const float uf = 2.f * (i - u_shift) / ewi - 1.f;
1079  const float vf = 2.f * (j - v_shift) / ehi - 1.f;
1080 
1081  cube_to_xyz(s, uf, vf, face, vec);
1082 }
1083 
1084 /**
1085  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1086  *
1087  * @param s filter context
1088  * @param vec coordinates on sphere
1089  * @param width frame width
1090  * @param height frame height
1091  * @param us horizontal coordinates for interpolation window
1092  * @param vs vertical coordinates for interpolation window
1093  * @param du horizontal relative coordinate
1094  * @param dv vertical relative coordinate
1095  */
1096 static void xyz_to_cube3x2(const V360Context *s,
1097  const float *vec, int width, int height,
1098  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1099 {
1100  const float ew = width / 3.f;
1101  const float eh = height / 2.f;
1102  float uf, vf;
1103  int ui, vi;
1104  int ewi, ehi;
1105  int direction, face;
1106  int u_face, v_face;
1107 
1108  xyz_to_cube(s, vec, &uf, &vf, &direction);
1109 
1110  uf *= (1.f - s->in_pad);
1111  vf *= (1.f - s->in_pad);
1112 
1113  face = s->in_cubemap_face_order[direction];
1114  u_face = face % 3;
1115  v_face = face / 3;
1116  ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1117  ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1118 
1119  uf = 0.5f * ewi * (uf + 1.f);
1120  vf = 0.5f * ehi * (vf + 1.f);
1121 
1122  ui = floorf(uf);
1123  vi = floorf(vf);
1124 
1125  *du = uf - ui;
1126  *dv = vf - vi;
1127 
1128  for (int i = -1; i < 3; i++) {
1129  for (int j = -1; j < 3; j++) {
1130  int new_ui = ui + j;
1131  int new_vi = vi + i;
1132  int u_shift, v_shift;
1133  int new_ewi, new_ehi;
1134 
1135  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1136  face = s->in_cubemap_face_order[direction];
1137 
1138  u_face = face % 3;
1139  v_face = face / 3;
1140  u_shift = ceilf(ew * u_face);
1141  v_shift = ceilf(eh * v_face);
1142  } else {
1143  uf = 2.f * new_ui / ewi - 1.f;
1144  vf = 2.f * new_vi / ehi - 1.f;
1145 
1146  uf /= (1.f - s->in_pad);
1147  vf /= (1.f - s->in_pad);
1148 
1149  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1150 
1151  uf *= (1.f - s->in_pad);
1152  vf *= (1.f - s->in_pad);
1153 
1154  u_face = face % 3;
1155  v_face = face / 3;
1156  u_shift = ceilf(ew * u_face);
1157  v_shift = ceilf(eh * v_face);
1158  new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1159  new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1160 
1161  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1162  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1163  }
1164 
1165  us[i + 1][j + 1] = u_shift + new_ui;
1166  vs[i + 1][j + 1] = v_shift + new_vi;
1167  }
1168  }
1169 }
1170 
1171 /**
1172  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1173  *
1174  * @param s filter context
1175  * @param i horizontal position on frame [0, width)
1176  * @param j vertical position on frame [0, height)
1177  * @param width frame width
1178  * @param height frame height
1179  * @param vec coordinates on sphere
1180  */
1181 static void cube1x6_to_xyz(const V360Context *s,
1182  int i, int j, int width, int height,
1183  float *vec)
1184 {
1185  const float ew = width;
1186  const float eh = height / 6.f;
1187 
1188  const int face = floorf(j / eh);
1189 
1190  const int v_shift = ceilf(eh * face);
1191  const int ehi = ceilf(eh * (face + 1)) - v_shift;
1192 
1193  const float uf = 2.f * i / ew - 1.f;
1194  const float vf = 2.f * (j - v_shift) / ehi - 1.f;
1195 
1196  cube_to_xyz(s, uf, vf, face, vec);
1197 }
1198 
1199 /**
1200  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1201  *
1202  * @param s filter context
1203  * @param i horizontal position on frame [0, width)
1204  * @param j vertical position on frame [0, height)
1205  * @param width frame width
1206  * @param height frame height
1207  * @param vec coordinates on sphere
1208  */
1209 static void cube6x1_to_xyz(const V360Context *s,
1210  int i, int j, int width, int height,
1211  float *vec)
1212 {
1213  const float ew = width / 6.f;
1214  const float eh = height;
1215 
1216  const int face = floorf(i / ew);
1217 
1218  const int u_shift = ceilf(ew * face);
1219  const int ewi = ceilf(ew * (face + 1)) - u_shift;
1220 
1221  const float uf = 2.f * (i - u_shift) / ewi - 1.f;
1222  const float vf = 2.f * j / eh - 1.f;
1223 
1224  cube_to_xyz(s, uf, vf, face, vec);
1225 }
1226 
1227 /**
1228  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1229  *
1230  * @param s filter context
1231  * @param vec coordinates on sphere
1232  * @param width frame width
1233  * @param height frame height
1234  * @param us horizontal coordinates for interpolation window
1235  * @param vs vertical coordinates for interpolation window
1236  * @param du horizontal relative coordinate
1237  * @param dv vertical relative coordinate
1238  */
1239 static void xyz_to_cube1x6(const V360Context *s,
1240  const float *vec, int width, int height,
1241  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1242 {
1243  const float eh = height / 6.f;
1244  const int ewi = width;
1245  float uf, vf;
1246  int ui, vi;
1247  int ehi;
1248  int direction, face;
1249 
1250  xyz_to_cube(s, vec, &uf, &vf, &direction);
1251 
1252  uf *= (1.f - s->in_pad);
1253  vf *= (1.f - s->in_pad);
1254 
1255  face = s->in_cubemap_face_order[direction];
1256  ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1257 
1258  uf = 0.5f * ewi * (uf + 1.f);
1259  vf = 0.5f * ehi * (vf + 1.f);
1260 
1261  ui = floorf(uf);
1262  vi = floorf(vf);
1263 
1264  *du = uf - ui;
1265  *dv = vf - vi;
1266 
1267  for (int i = -1; i < 3; i++) {
1268  for (int j = -1; j < 3; j++) {
1269  int new_ui = ui + j;
1270  int new_vi = vi + i;
1271  int v_shift;
1272  int new_ehi;
1273 
1274  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1275  face = s->in_cubemap_face_order[direction];
1276 
1277  v_shift = ceilf(eh * face);
1278  } else {
1279  uf = 2.f * new_ui / ewi - 1.f;
1280  vf = 2.f * new_vi / ehi - 1.f;
1281 
1282  uf /= (1.f - s->in_pad);
1283  vf /= (1.f - s->in_pad);
1284 
1285  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1286 
1287  uf *= (1.f - s->in_pad);
1288  vf *= (1.f - s->in_pad);
1289 
1290  v_shift = ceilf(eh * face);
1291  new_ehi = ceilf(eh * (face + 1)) - v_shift;
1292 
1293  new_ui = av_clip(roundf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1);
1294  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1295  }
1296 
1297  us[i + 1][j + 1] = new_ui;
1298  vs[i + 1][j + 1] = v_shift + new_vi;
1299  }
1300  }
1301 }
1302 
1303 /**
1304  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1305  *
1306  * @param s filter context
1307  * @param vec coordinates on sphere
1308  * @param width frame width
1309  * @param height frame height
1310  * @param us horizontal coordinates for interpolation window
1311  * @param vs vertical coordinates for interpolation window
1312  * @param du horizontal relative coordinate
1313  * @param dv vertical relative coordinate
1314  */
1315 static void xyz_to_cube6x1(const V360Context *s,
1316  const float *vec, int width, int height,
1317  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1318 {
1319  const float ew = width / 6.f;
1320  const int ehi = height;
1321  float uf, vf;
1322  int ui, vi;
1323  int ewi;
1324  int direction, face;
1325 
1326  xyz_to_cube(s, vec, &uf, &vf, &direction);
1327 
1328  uf *= (1.f - s->in_pad);
1329  vf *= (1.f - s->in_pad);
1330 
1331  face = s->in_cubemap_face_order[direction];
1332  ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1333 
1334  uf = 0.5f * ewi * (uf + 1.f);
1335  vf = 0.5f * ehi * (vf + 1.f);
1336 
1337  ui = floorf(uf);
1338  vi = floorf(vf);
1339 
1340  *du = uf - ui;
1341  *dv = vf - vi;
1342 
1343  for (int i = -1; i < 3; i++) {
1344  for (int j = -1; j < 3; j++) {
1345  int new_ui = ui + j;
1346  int new_vi = vi + i;
1347  int u_shift;
1348  int new_ewi;
1349 
1350  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1351  face = s->in_cubemap_face_order[direction];
1352 
1353  u_shift = ceilf(ew * face);
1354  } else {
1355  uf = 2.f * new_ui / ewi - 1.f;
1356  vf = 2.f * new_vi / ehi - 1.f;
1357 
1358  uf /= (1.f - s->in_pad);
1359  vf /= (1.f - s->in_pad);
1360 
1361  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1362 
1363  uf *= (1.f - s->in_pad);
1364  vf *= (1.f - s->in_pad);
1365 
1366  u_shift = ceilf(ew * face);
1367  new_ewi = ceilf(ew * (face + 1)) - u_shift;
1368 
1369  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1370  new_vi = av_clip(roundf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1);
1371  }
1372 
1373  us[i + 1][j + 1] = u_shift + new_ui;
1374  vs[i + 1][j + 1] = new_vi;
1375  }
1376  }
1377 }
1378 
1379 /**
1380  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1381  *
1382  * @param s filter context
1383  * @param i horizontal position on frame [0, width)
1384  * @param j vertical position on frame [0, height)
1385  * @param width frame width
1386  * @param height frame height
1387  * @param vec coordinates on sphere
1388  */
1389 static void equirect_to_xyz(const V360Context *s,
1390  int i, int j, int width, int height,
1391  float *vec)
1392 {
1393  const float phi = ((2.f * i) / width - 1.f) * M_PI;
1394  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1395 
1396  const float sin_phi = sinf(phi);
1397  const float cos_phi = cosf(phi);
1398  const float sin_theta = sinf(theta);
1399  const float cos_theta = cosf(theta);
1400 
1401  vec[0] = cos_theta * sin_phi;
1402  vec[1] = -sin_theta;
1403  vec[2] = -cos_theta * cos_phi;
1404 }
1405 
1406 /**
1407  * Prepare data for processing stereographic output format.
1408  *
1409  * @param ctx filter context
1410  *
1411  * @return error code
1412  */
1414 {
1415  V360Context *s = ctx->priv;
1416 
1417  const float h_angle = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
1418  const float v_angle = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
1419 
1420  s->flat_range[0] = h_angle;
1421  s->flat_range[1] = v_angle;
1422 
1423  return 0;
1424 }
1425 
1426 /**
1427  * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
1428  *
1429  * @param s filter context
1430  * @param i horizontal position on frame [0, width)
1431  * @param j vertical position on frame [0, height)
1432  * @param width frame width
1433  * @param height frame height
1434  * @param vec coordinates on sphere
1435  */
1437  int i, int j, int width, int height,
1438  float *vec)
1439 {
1440  const float x = ((2.f * i) / width - 1.f) * s->flat_range[0];
1441  const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
1442  const float xy = x * x + y * y;
1443 
1444  vec[0] = 2.f * x / (1.f + xy);
1445  vec[1] = (-1.f + xy) / (1.f + xy);
1446  vec[2] = 2.f * y / (1.f + xy);
1447 
1448  normalize_vector(vec);
1449 }
1450 
1451 /**
1452  * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
1453  *
1454  * @param s filter context
1455  * @param vec coordinates on sphere
1456  * @param width frame width
1457  * @param height frame height
1458  * @param us horizontal coordinates for interpolation window
1459  * @param vs vertical coordinates for interpolation window
1460  * @param du horizontal relative coordinate
1461  * @param dv vertical relative coordinate
1462  */
1464  const float *vec, int width, int height,
1465  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1466 {
1467  const float x = av_clipf(vec[0] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[0];
1468  const float y = av_clipf(vec[2] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[1];
1469  float uf, vf;
1470  int ui, vi;
1471 
1472  uf = (x + 1.f) * width / 2.f;
1473  vf = (y + 1.f) * height / 2.f;
1474  ui = floorf(uf);
1475  vi = floorf(vf);
1476 
1477  *du = uf - ui;
1478  *dv = vf - vi;
1479 
1480  for (int i = -1; i < 3; i++) {
1481  for (int j = -1; j < 3; j++) {
1482  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1483  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1484  }
1485  }
1486 }
1487 
1488 /**
1489  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
1490  *
1491  * @param s filter context
1492  * @param vec coordinates on sphere
1493  * @param width frame width
1494  * @param height frame height
1495  * @param us horizontal coordinates for interpolation window
1496  * @param vs vertical coordinates for interpolation window
1497  * @param du horizontal relative coordinate
1498  * @param dv vertical relative coordinate
1499  */
1500 static void xyz_to_equirect(const V360Context *s,
1501  const float *vec, int width, int height,
1502  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1503 {
1504  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1505  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1506  float uf, vf;
1507  int ui, vi;
1508 
1509  uf = (phi / M_PI + 1.f) * width / 2.f;
1510  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1511  ui = floorf(uf);
1512  vi = floorf(vf);
1513 
1514  *du = uf - ui;
1515  *dv = vf - vi;
1516 
1517  for (int i = -1; i < 3; i++) {
1518  for (int j = -1; j < 3; j++) {
1519  us[i + 1][j + 1] = mod(ui + j, width);
1520  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1521  }
1522  }
1523 }
1524 
1525 /**
1526  * Prepare data for processing equi-angular cubemap input format.
1527  *
1528  * @param ctx filter context
1529 
1530  * @return error code
1531  */
1533 {
1534  V360Context *s = ctx->priv;
1535 
1536  if (s->ih_flip && s->iv_flip) {
1543  } else if (s->ih_flip) {
1550  } else if (s->iv_flip) {
1557  } else {
1564  }
1565 
1566  if (s->iv_flip) {
1573  } else {
1580  }
1581 
1582  return 0;
1583 }
1584 
1585 /**
1586  * Prepare data for processing equi-angular cubemap output format.
1587  *
1588  * @param ctx filter context
1589  *
1590  * @return error code
1591  */
1593 {
1594  V360Context *s = ctx->priv;
1595 
1602 
1609 
1610  return 0;
1611 }
1612 
1613 /**
1614  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
1615  *
1616  * @param s filter context
1617  * @param i horizontal position on frame [0, width)
1618  * @param j vertical position on frame [0, height)
1619  * @param width frame width
1620  * @param height frame height
1621  * @param vec coordinates on sphere
1622  */
1623 static void eac_to_xyz(const V360Context *s,
1624  int i, int j, int width, int height,
1625  float *vec)
1626 {
1627  const float pixel_pad = 2;
1628  const float u_pad = pixel_pad / width;
1629  const float v_pad = pixel_pad / height;
1630 
1631  int u_face, v_face, face;
1632 
1633  float l_x, l_y, l_z;
1634 
1635  float uf = (float)i / width;
1636  float vf = (float)j / height;
1637 
1638  // EAC has 2-pixel padding on faces except between faces on the same row
1639  // Padding pixels seems not to be stretched with tangent as regular pixels
1640  // Formulas below approximate original padding as close as I could get experimentally
1641 
1642  // Horizontal padding
1643  uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
1644  if (uf < 0.f) {
1645  u_face = 0;
1646  uf -= 0.5f;
1647  } else if (uf >= 3.f) {
1648  u_face = 2;
1649  uf -= 2.5f;
1650  } else {
1651  u_face = floorf(uf);
1652  uf = fmodf(uf, 1.f) - 0.5f;
1653  }
1654 
1655  // Vertical padding
1656  v_face = floorf(vf * 2.f);
1657  vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
1658 
1659  if (uf >= -0.5f && uf < 0.5f) {
1660  uf = tanf(M_PI_2 * uf);
1661  } else {
1662  uf = 2.f * uf;
1663  }
1664  if (vf >= -0.5f && vf < 0.5f) {
1665  vf = tanf(M_PI_2 * vf);
1666  } else {
1667  vf = 2.f * vf;
1668  }
1669 
1670  face = u_face + 3 * v_face;
1671 
1672  switch (face) {
1673  case TOP_LEFT:
1674  l_x = -1.f;
1675  l_y = -vf;
1676  l_z = -uf;
1677  break;
1678  case TOP_MIDDLE:
1679  l_x = uf;
1680  l_y = -vf;
1681  l_z = -1.f;
1682  break;
1683  case TOP_RIGHT:
1684  l_x = 1.f;
1685  l_y = -vf;
1686  l_z = uf;
1687  break;
1688  case BOTTOM_LEFT:
1689  l_x = -vf;
1690  l_y = -1.f;
1691  l_z = uf;
1692  break;
1693  case BOTTOM_MIDDLE:
1694  l_x = -vf;
1695  l_y = uf;
1696  l_z = 1.f;
1697  break;
1698  case BOTTOM_RIGHT:
1699  l_x = -vf;
1700  l_y = 1.f;
1701  l_z = -uf;
1702  break;
1703  default:
1704  av_assert0(0);
1705  }
1706 
1707  vec[0] = l_x;
1708  vec[1] = l_y;
1709  vec[2] = l_z;
1710 
1711  normalize_vector(vec);
1712 }
1713 
1714 /**
1715  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
1716  *
1717  * @param s filter context
1718  * @param vec coordinates on sphere
1719  * @param width frame width
1720  * @param height frame height
1721  * @param us horizontal coordinates for interpolation window
1722  * @param vs vertical coordinates for interpolation window
1723  * @param du horizontal relative coordinate
1724  * @param dv vertical relative coordinate
1725  */
1726 static void xyz_to_eac(const V360Context *s,
1727  const float *vec, int width, int height,
1728  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1729 {
1730  const float pixel_pad = 2;
1731  const float u_pad = pixel_pad / width;
1732  const float v_pad = pixel_pad / height;
1733 
1734  float uf, vf;
1735  int ui, vi;
1736  int direction, face;
1737  int u_face, v_face;
1738 
1739  xyz_to_cube(s, vec, &uf, &vf, &direction);
1740 
1741  face = s->in_cubemap_face_order[direction];
1742  u_face = face % 3;
1743  v_face = face / 3;
1744 
1745  uf = M_2_PI * atanf(uf) + 0.5f;
1746  vf = M_2_PI * atanf(vf) + 0.5f;
1747 
1748  // These formulas are inversed from eac_to_xyz ones
1749  uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
1750  vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
1751 
1752  uf *= width;
1753  vf *= height;
1754 
1755  ui = floorf(uf);
1756  vi = floorf(vf);
1757 
1758  *du = uf - ui;
1759  *dv = vf - vi;
1760 
1761  for (int i = -1; i < 3; i++) {
1762  for (int j = -1; j < 3; j++) {
1763  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1764  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1765  }
1766  }
1767 }
1768 
1769 /**
1770  * Prepare data for processing flat output format.
1771  *
1772  * @param ctx filter context
1773  *
1774  * @return error code
1775  */
1777 {
1778  V360Context *s = ctx->priv;
1779 
1780  const float h_angle = 0.5f * s->h_fov * M_PI / 180.f;
1781  const float v_angle = 0.5f * s->v_fov * M_PI / 180.f;
1782 
1783  s->flat_range[0] = tanf(h_angle);
1784  s->flat_range[1] = tanf(v_angle);
1785  s->flat_range[2] = -1.f;
1786 
1787  return 0;
1788 }
1789 
1790 /**
1791  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
1792  *
1793  * @param s filter context
1794  * @param i horizontal position on frame [0, width)
1795  * @param j vertical position on frame [0, height)
1796  * @param width frame width
1797  * @param height frame height
1798  * @param vec coordinates on sphere
1799  */
1800 static void flat_to_xyz(const V360Context *s,
1801  int i, int j, int width, int height,
1802  float *vec)
1803 {
1804  const float l_x = s->flat_range[0] * (2.f * i / width - 1.f);
1805  const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
1806  const float l_z = s->flat_range[2];
1807 
1808  vec[0] = l_x;
1809  vec[1] = l_y;
1810  vec[2] = l_z;
1811 
1812  normalize_vector(vec);
1813 }
1814 
1815 /**
1816  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
1817  *
1818  * @param s filter context
1819  * @param i horizontal position on frame [0, width)
1820  * @param j vertical position on frame [0, height)
1821  * @param width frame width
1822  * @param height frame height
1823  * @param vec coordinates on sphere
1824  */
1825 static void dfisheye_to_xyz(const V360Context *s,
1826  int i, int j, int width, int height,
1827  float *vec)
1828 {
1829  const float scale = 1.f + s->out_pad;
1830 
1831  const float ew = width / 2.f;
1832  const float eh = height;
1833 
1834  const int ei = i >= ew ? i - ew : i;
1835  const float m = i >= ew ? -1.f : 1.f;
1836 
1837  const float uf = ((2.f * ei) / ew - 1.f) * scale;
1838  const float vf = ((2.f * j) / eh - 1.f) * scale;
1839 
1840  const float phi = M_PI + atan2f(vf, uf * m);
1841  const float theta = m * M_PI_2 * (1.f - hypotf(uf, vf));
1842 
1843  const float sin_phi = sinf(phi);
1844  const float cos_phi = cosf(phi);
1845  const float sin_theta = sinf(theta);
1846  const float cos_theta = cosf(theta);
1847 
1848  vec[0] = cos_theta * cos_phi;
1849  vec[1] = cos_theta * sin_phi;
1850  vec[2] = sin_theta;
1851 
1852  normalize_vector(vec);
1853 }
1854 
1855 /**
1856  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
1857  *
1858  * @param s filter context
1859  * @param vec coordinates on sphere
1860  * @param width frame width
1861  * @param height frame height
1862  * @param us horizontal coordinates for interpolation window
1863  * @param vs vertical coordinates for interpolation window
1864  * @param du horizontal relative coordinate
1865  * @param dv vertical relative coordinate
1866  */
1867 static void xyz_to_dfisheye(const V360Context *s,
1868  const float *vec, int width, int height,
1869  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1870 {
1871  const float scale = 1.f - s->in_pad;
1872 
1873  const float ew = width / 2.f;
1874  const float eh = height;
1875 
1876  const float phi = atan2f(-vec[1], -vec[0]) * s->input_mirror_modifier[0];
1877  const float theta = acosf(fabsf(vec[2])) / M_PI * s->input_mirror_modifier[1];
1878 
1879  float uf = (theta * cosf(phi) * scale + 0.5f) * ew;
1880  float vf = (theta * sinf(phi) * scale + 0.5f) * eh;
1881 
1882  int ui, vi;
1883  int u_shift;
1884 
1885  if (vec[2] >= 0) {
1886  u_shift = 0;
1887  } else {
1888  u_shift = ceilf(ew);
1889  uf = ew - uf;
1890  }
1891 
1892  ui = floorf(uf);
1893  vi = floorf(vf);
1894 
1895  *du = uf - ui;
1896  *dv = vf - vi;
1897 
1898  for (int i = -1; i < 3; i++) {
1899  for (int j = -1; j < 3; j++) {
1900  us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width - 1);
1901  vs[i + 1][j + 1] = av_clip( vi + i, 0, height - 1);
1902  }
1903  }
1904 }
1905 
1906 /**
1907  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
1908  *
1909  * @param s filter context
1910  * @param i horizontal position on frame [0, width)
1911  * @param j vertical position on frame [0, height)
1912  * @param width frame width
1913  * @param height frame height
1914  * @param vec coordinates on sphere
1915  */
1916 static void barrel_to_xyz(const V360Context *s,
1917  int i, int j, int width, int height,
1918  float *vec)
1919 {
1920  const float scale = 0.99f;
1921  float l_x, l_y, l_z;
1922 
1923  if (i < 4 * width / 5) {
1924  const float theta_range = M_PI_4;
1925 
1926  const int ew = 4 * width / 5;
1927  const int eh = height;
1928 
1929  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
1930  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
1931 
1932  const float sin_phi = sinf(phi);
1933  const float cos_phi = cosf(phi);
1934  const float sin_theta = sinf(theta);
1935  const float cos_theta = cosf(theta);
1936 
1937  l_x = cos_theta * sin_phi;
1938  l_y = -sin_theta;
1939  l_z = -cos_theta * cos_phi;
1940  } else {
1941  const int ew = width / 5;
1942  const int eh = height / 2;
1943 
1944  float uf, vf;
1945 
1946  if (j < eh) { // UP
1947  uf = 2.f * (i - 4 * ew) / ew - 1.f;
1948  vf = 2.f * (j ) / eh - 1.f;
1949 
1950  uf /= scale;
1951  vf /= scale;
1952 
1953  l_x = uf;
1954  l_y = 1.f;
1955  l_z = -vf;
1956  } else { // DOWN
1957  uf = 2.f * (i - 4 * ew) / ew - 1.f;
1958  vf = 2.f * (j - eh) / eh - 1.f;
1959 
1960  uf /= scale;
1961  vf /= scale;
1962 
1963  l_x = uf;
1964  l_y = -1.f;
1965  l_z = vf;
1966  }
1967  }
1968 
1969  vec[0] = l_x;
1970  vec[1] = l_y;
1971  vec[2] = l_z;
1972 
1973  normalize_vector(vec);
1974 }
1975 
1976 /**
1977  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
1978  *
1979  * @param s filter context
1980  * @param vec coordinates on sphere
1981  * @param width frame width
1982  * @param height frame height
1983  * @param us horizontal coordinates for interpolation window
1984  * @param vs vertical coordinates for interpolation window
1985  * @param du horizontal relative coordinate
1986  * @param dv vertical relative coordinate
1987  */
1988 static void xyz_to_barrel(const V360Context *s,
1989  const float *vec, int width, int height,
1990  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1991 {
1992  const float scale = 0.99f;
1993 
1994  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1995  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1996  const float theta_range = M_PI_4;
1997 
1998  int ew, eh;
1999  int u_shift, v_shift;
2000  float uf, vf;
2001  int ui, vi;
2002 
2003  if (theta > -theta_range && theta < theta_range) {
2004  ew = 4 * width / 5;
2005  eh = height;
2006 
2007  u_shift = s->ih_flip ? width / 5 : 0;
2008  v_shift = 0;
2009 
2010  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
2011  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
2012  } else {
2013  ew = width / 5;
2014  eh = height / 2;
2015 
2016  u_shift = s->ih_flip ? 0 : 4 * ew;
2017 
2018  if (theta < 0.f) { // UP
2019  uf = vec[0] / vec[1];
2020  vf = -vec[2] / vec[1];
2021  v_shift = 0;
2022  } else { // DOWN
2023  uf = -vec[0] / vec[1];
2024  vf = -vec[2] / vec[1];
2025  v_shift = eh;
2026  }
2027 
2028  uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
2029  vf *= s->input_mirror_modifier[1];
2030 
2031  uf = 0.5f * ew * (uf * scale + 1.f);
2032  vf = 0.5f * eh * (vf * scale + 1.f);
2033  }
2034 
2035  ui = floorf(uf);
2036  vi = floorf(vf);
2037 
2038  *du = uf - ui;
2039  *dv = vf - vi;
2040 
2041  for (int i = -1; i < 3; i++) {
2042  for (int j = -1; j < 3; j++) {
2043  us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
2044  vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
2045  }
2046  }
2047 }
2048 
2049 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
2050 {
2051  for (int i = 0; i < 3; i++) {
2052  for (int j = 0; j < 3; j++) {
2053  float sum = 0;
2054 
2055  for (int k = 0; k < 3; k++)
2056  sum += a[i][k] * b[k][j];
2057 
2058  c[i][j] = sum;
2059  }
2060  }
2061 }
2062 
2063 /**
2064  * Calculate rotation matrix for yaw/pitch/roll angles.
2065  */
2066 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
2067  float rot_mat[3][3],
2068  const int rotation_order[3])
2069 {
2070  const float yaw_rad = yaw * M_PI / 180.f;
2071  const float pitch_rad = pitch * M_PI / 180.f;
2072  const float roll_rad = roll * M_PI / 180.f;
2073 
2074  const float sin_yaw = sinf(-yaw_rad);
2075  const float cos_yaw = cosf(-yaw_rad);
2076  const float sin_pitch = sinf(pitch_rad);
2077  const float cos_pitch = cosf(pitch_rad);
2078  const float sin_roll = sinf(roll_rad);
2079  const float cos_roll = cosf(roll_rad);
2080 
2081  float m[3][3][3];
2082  float temp[3][3];
2083 
2084  m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw;
2085  m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0;
2086  m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw;
2087 
2088  m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0;
2089  m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch;
2090  m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch;
2091 
2092  m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0;
2093  m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0;
2094  m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1;
2095 
2096  multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
2097  multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
2098 }
2099 
2100 /**
2101  * Rotate vector with given rotation matrix.
2102  *
2103  * @param rot_mat rotation matrix
2104  * @param vec vector
2105  */
2106 static inline void rotate(const float rot_mat[3][3],
2107  float *vec)
2108 {
2109  const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
2110  const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
2111  const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
2112 
2113  vec[0] = x_tmp;
2114  vec[1] = y_tmp;
2115  vec[2] = z_tmp;
2116 }
2117 
2118 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
2119  float *modifier)
2120 {
2121  modifier[0] = h_flip ? -1.f : 1.f;
2122  modifier[1] = v_flip ? -1.f : 1.f;
2123  modifier[2] = d_flip ? -1.f : 1.f;
2124 }
2125 
2126 static inline void mirror(const float *modifier, float *vec)
2127 {
2128  vec[0] *= modifier[0];
2129  vec[1] *= modifier[1];
2130  vec[2] *= modifier[2];
2131 }
2132 
2133 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
2134 {
2135  s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2136  s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2137  if (!s->u[p] || !s->v[p])
2138  return AVERROR(ENOMEM);
2139  if (sizeof_ker) {
2140  s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
2141  if (!s->ker[p])
2142  return AVERROR(ENOMEM);
2143  }
2144 
2145  return 0;
2146 }
2147 
2148 static void fov_from_dfov(V360Context *s, float w, float h)
2149 {
2150  const float da = tanf(0.5 * FFMIN(s->d_fov, 359.f) * M_PI / 180.f);
2151  const float d = hypotf(w, h);
2152 
2153  s->h_fov = atan2f(da * w, d) * 360.f / M_PI;
2154  s->v_fov = atan2f(da * h, d) * 360.f / M_PI;
2155 
2156  if (s->h_fov < 0.f)
2157  s->h_fov += 360.f;
2158  if (s->v_fov < 0.f)
2159  s->v_fov += 360.f;
2160 }
2161 
2162 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
2163 {
2164  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
2165  outw[0] = outw[3] = w;
2166  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
2167  outh[0] = outh[3] = h;
2168 }
2169 
2170 static int config_output(AVFilterLink *outlink)
2171 {
2172  AVFilterContext *ctx = outlink->src;
2173  AVFilterLink *inlink = ctx->inputs[0];
2174  V360Context *s = ctx->priv;
2176  const int depth = desc->comp[0].depth;
2177  int sizeof_uv;
2178  int sizeof_ker;
2179  int elements;
2180  int err;
2181  int h, w;
2182  int in_offset_h, in_offset_w;
2183  int out_offset_h, out_offset_w;
2184  float hf, wf;
2185  float output_mirror_modifier[3];
2186  void (*in_transform)(const V360Context *s,
2187  const float *vec, int width, int height,
2188  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv);
2189  void (*out_transform)(const V360Context *s,
2190  int i, int j, int width, int height,
2191  float *vec);
2192  void (*calculate_kernel)(float du, float dv, const XYRemap *r_tmp,
2193  uint16_t *u, uint16_t *v, int16_t *ker);
2194  int (*prepare_out)(AVFilterContext *ctx);
2195  float rot_mat[3][3];
2196 
2197  s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
2198  s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
2199 
2200  switch (s->interp) {
2201  case NEAREST:
2202  calculate_kernel = nearest_kernel;
2203  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
2204  elements = 1;
2205  sizeof_uv = sizeof(uint16_t) * elements;
2206  sizeof_ker = 0;
2207  break;
2208  case BILINEAR:
2209  calculate_kernel = bilinear_kernel;
2210  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
2211  elements = 2 * 2;
2212  sizeof_uv = sizeof(uint16_t) * elements;
2213  sizeof_ker = sizeof(uint16_t) * elements;
2214  break;
2215  case BICUBIC:
2216  calculate_kernel = bicubic_kernel;
2217  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2218  elements = 4 * 4;
2219  sizeof_uv = sizeof(uint16_t) * elements;
2220  sizeof_ker = sizeof(uint16_t) * elements;
2221  break;
2222  case LANCZOS:
2223  calculate_kernel = lanczos_kernel;
2224  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2225  elements = 4 * 4;
2226  sizeof_uv = sizeof(uint16_t) * elements;
2227  sizeof_ker = sizeof(uint16_t) * elements;
2228  break;
2229  default:
2230  av_assert0(0);
2231  }
2232 
2233  ff_v360_init(s, depth);
2234 
2235  for (int order = 0; order < NB_RORDERS; order++) {
2236  const char c = s->rorder[order];
2237  int rorder;
2238 
2239  if (c == '\0') {
2240  av_log(ctx, AV_LOG_ERROR,
2241  "Incomplete rorder option. Direction for all 3 rotation orders should be specified.\n");
2242  return AVERROR(EINVAL);
2243  }
2244 
2245  rorder = get_rorder(c);
2246  if (rorder == -1) {
2247  av_log(ctx, AV_LOG_ERROR,
2248  "Incorrect rotation order symbol '%c' in rorder option.\n", c);
2249  return AVERROR(EINVAL);
2250  }
2251 
2252  s->rotation_order[order] = rorder;
2253  }
2254 
2255  switch (s->in_stereo) {
2256  case STEREO_2D:
2257  w = inlink->w;
2258  h = inlink->h;
2259  in_offset_w = in_offset_h = 0;
2260  break;
2261  case STEREO_SBS:
2262  w = inlink->w / 2;
2263  h = inlink->h;
2264  in_offset_w = w;
2265  in_offset_h = 0;
2266  break;
2267  case STEREO_TB:
2268  w = inlink->w;
2269  h = inlink->h / 2;
2270  in_offset_w = 0;
2271  in_offset_h = h;
2272  break;
2273  default:
2274  av_assert0(0);
2275  }
2276 
2277  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
2278  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
2279 
2280  switch (s->in) {
2281  case EQUIRECTANGULAR:
2282  in_transform = xyz_to_equirect;
2283  err = 0;
2284  wf = w;
2285  hf = h;
2286  break;
2287  case CUBEMAP_3_2:
2288  in_transform = xyz_to_cube3x2;
2289  err = prepare_cube_in(ctx);
2290  wf = w / 3.f * 4.f;
2291  hf = h;
2292  break;
2293  case CUBEMAP_1_6:
2294  in_transform = xyz_to_cube1x6;
2295  err = prepare_cube_in(ctx);
2296  wf = w * 4.f;
2297  hf = h / 3.f;
2298  break;
2299  case CUBEMAP_6_1:
2300  in_transform = xyz_to_cube6x1;
2301  err = prepare_cube_in(ctx);
2302  wf = w / 3.f * 2.f;
2303  hf = h * 2.f;
2304  break;
2305  case EQUIANGULAR:
2306  in_transform = xyz_to_eac;
2307  err = prepare_eac_in(ctx);
2308  wf = w;
2309  hf = h / 9.f * 8.f;
2310  break;
2311  case FLAT:
2312  av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n");
2313  return AVERROR(EINVAL);
2314  case DUAL_FISHEYE:
2315  in_transform = xyz_to_dfisheye;
2316  err = 0;
2317  wf = w;
2318  hf = h;
2319  break;
2320  case BARREL:
2321  in_transform = xyz_to_barrel;
2322  err = 0;
2323  wf = w / 5.f * 4.f;
2324  hf = h;
2325  break;
2326  case STEREOGRAPHIC:
2327  in_transform = xyz_to_stereographic;
2328  err = 0;
2329  wf = w;
2330  hf = h / 2.f;
2331  break;
2332  default:
2333  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
2334  return AVERROR_BUG;
2335  }
2336 
2337  if (err != 0) {
2338  return err;
2339  }
2340 
2341  switch (s->out) {
2342  case EQUIRECTANGULAR:
2343  out_transform = equirect_to_xyz;
2344  prepare_out = NULL;
2345  w = roundf(wf);
2346  h = roundf(hf);
2347  break;
2348  case CUBEMAP_3_2:
2349  out_transform = cube3x2_to_xyz;
2350  prepare_out = prepare_cube_out;
2351  w = roundf(wf / 4.f * 3.f);
2352  h = roundf(hf);
2353  break;
2354  case CUBEMAP_1_6:
2355  out_transform = cube1x6_to_xyz;
2356  prepare_out = prepare_cube_out;
2357  w = roundf(wf / 4.f);
2358  h = roundf(hf * 3.f);
2359  break;
2360  case CUBEMAP_6_1:
2361  out_transform = cube6x1_to_xyz;
2362  prepare_out = prepare_cube_out;
2363  w = roundf(wf / 2.f * 3.f);
2364  h = roundf(hf / 2.f);
2365  break;
2366  case EQUIANGULAR:
2367  out_transform = eac_to_xyz;
2368  prepare_out = prepare_eac_out;
2369  w = roundf(wf);
2370  h = roundf(hf / 8.f * 9.f);
2371  break;
2372  case FLAT:
2373  out_transform = flat_to_xyz;
2374  prepare_out = prepare_flat_out;
2375  w = roundf(wf);
2376  h = roundf(hf);
2377  break;
2378  case DUAL_FISHEYE:
2379  out_transform = dfisheye_to_xyz;
2380  prepare_out = NULL;
2381  w = roundf(wf);
2382  h = roundf(hf);
2383  break;
2384  case BARREL:
2385  out_transform = barrel_to_xyz;
2386  prepare_out = NULL;
2387  w = roundf(wf / 4.f * 5.f);
2388  h = roundf(hf);
2389  break;
2390  case STEREOGRAPHIC:
2391  out_transform = stereographic_to_xyz;
2392  prepare_out = prepare_stereographic_out;
2393  w = roundf(wf);
2394  h = roundf(hf * 2.f);
2395  break;
2396  default:
2397  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
2398  return AVERROR_BUG;
2399  }
2400 
2401  // Override resolution with user values if specified
2402  if (s->width > 0 && s->height > 0) {
2403  w = s->width;
2404  h = s->height;
2405  } else if (s->width > 0 || s->height > 0) {
2406  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
2407  return AVERROR(EINVAL);
2408  } else {
2409  if (s->out_transpose)
2410  FFSWAP(int, w, h);
2411 
2412  if (s->in_transpose)
2413  FFSWAP(int, w, h);
2414  }
2415 
2416  if (s->d_fov > 0.f)
2417  fov_from_dfov(s, w, h);
2418 
2419  if (prepare_out) {
2420  err = prepare_out(ctx);
2421  if (err != 0)
2422  return err;
2423  }
2424 
2425  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
2426 
2427  switch (s->out_stereo) {
2428  case STEREO_2D:
2429  out_offset_w = out_offset_h = 0;
2430  break;
2431  case STEREO_SBS:
2432  out_offset_w = w;
2433  out_offset_h = 0;
2434  w *= 2;
2435  break;
2436  case STEREO_TB:
2437  out_offset_w = 0;
2438  out_offset_h = h;
2439  h *= 2;
2440  break;
2441  default:
2442  av_assert0(0);
2443  }
2444 
2445  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
2446  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
2447 
2448  for (int i = 0; i < 4; i++)
2449  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
2450 
2451  outlink->h = h;
2452  outlink->w = w;
2453 
2455 
2456  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
2457  s->nb_allocated = 1;
2458  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
2459  allocate_plane(s, sizeof_uv, sizeof_ker, 0);
2460  } else {
2461  s->nb_allocated = 2;
2462  s->map[0] = 0;
2463  s->map[1] = s->map[2] = 1;
2464  s->map[3] = 0;
2465  allocate_plane(s, sizeof_uv, sizeof_ker, 0);
2466  allocate_plane(s, sizeof_uv, sizeof_ker, 1);
2467  }
2468 
2469  calculate_rotation_matrix(s->yaw, s->pitch, s->roll, rot_mat, s->rotation_order);
2470  set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, output_mirror_modifier);
2471 
2472  // Calculate remap data
2473  for (int p = 0; p < s->nb_allocated; p++) {
2474  const int width = s->pr_width[p];
2475  const int uv_linesize = s->uv_linesize[p];
2476  const int height = s->pr_height[p];
2477  const int in_width = s->inplanewidth[p];
2478  const int in_height = s->inplaneheight[p];
2479  float du, dv;
2480  float vec[3];
2481  XYRemap r_tmp;
2482 
2483  for (int i = 0; i < width; i++) {
2484  for (int j = 0; j < height; j++) {
2485  uint16_t *u = s->u[p] + (j * uv_linesize + i) * elements;
2486  uint16_t *v = s->v[p] + (j * uv_linesize + i) * elements;
2487  int16_t *ker = s->ker[p] + (j * uv_linesize + i) * elements;
2488 
2489  if (s->out_transpose)
2490  out_transform(s, j, i, height, width, vec);
2491  else
2492  out_transform(s, i, j, width, height, vec);
2493  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2494  rotate(rot_mat, vec);
2495  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2496  normalize_vector(vec);
2497  mirror(output_mirror_modifier, vec);
2498  if (s->in_transpose)
2499  in_transform(s, vec, in_height, in_width, r_tmp.v, r_tmp.u, &du, &dv);
2500  else
2501  in_transform(s, vec, in_width, in_height, r_tmp.u, r_tmp.v, &du, &dv);
2502  av_assert1(!isnan(du) && !isnan(dv));
2503  calculate_kernel(du, dv, &r_tmp, u, v, ker);
2504  }
2505  }
2506  }
2507 
2508  return 0;
2509 }
2510 
2512 {
2513  AVFilterContext *ctx = inlink->dst;
2514  AVFilterLink *outlink = ctx->outputs[0];
2515  V360Context *s = ctx->priv;
2516  AVFrame *out;
2517  ThreadData td;
2518 
2519  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
2520  if (!out) {
2521  av_frame_free(&in);
2522  return AVERROR(ENOMEM);
2523  }
2524  av_frame_copy_props(out, in);
2525 
2526  td.in = in;
2527  td.out = out;
2528 
2529  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2530 
2531  av_frame_free(&in);
2532  return ff_filter_frame(outlink, out);
2533 }
2534 
2536 {
2537  V360Context *s = ctx->priv;
2538 
2539  for (int p = 0; p < s->nb_allocated; p++) {
2540  av_freep(&s->u[p]);
2541  av_freep(&s->v[p]);
2542  av_freep(&s->ker[p]);
2543  }
2544 }
2545 
2546 static const AVFilterPad inputs[] = {
2547  {
2548  .name = "default",
2549  .type = AVMEDIA_TYPE_VIDEO,
2550  .filter_frame = filter_frame,
2551  },
2552  { NULL }
2553 };
2554 
2555 static const AVFilterPad outputs[] = {
2556  {
2557  .name = "default",
2558  .type = AVMEDIA_TYPE_VIDEO,
2559  .config_props = config_output,
2560  },
2561  { NULL }
2562 };
2563 
2565  .name = "v360",
2566  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
2567  .priv_size = sizeof(V360Context),
2568  .uninit = uninit,
2570  .inputs = inputs,
2571  .outputs = outputs,
2572  .priv_class = &v360_class,
2574 };
Axis +Z.
Definition: v360.h:69
#define NULL
Definition: coverity.c:32
static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
Definition: vf_v360.c:2049
static const AVOption v360_options[]
Definition: vf_v360.c:56
#define AV_PIX_FMT_YUVA422P16
Definition: pixfmt.h:430
static void flat_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in flat format.
Definition: vf_v360.c:1800
AVFrame * out
Definition: af_adeclick.c:488
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:389
uint16_t v[4][4]
Definition: vf_v360.c:212
static void cube3x2_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format...
Definition: vf_v360.c:1062
#define AV_PIX_FMT_YUVA422P9
Definition: pixfmt.h:422
static void xyz_to_barrel(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in barrel facebook&#39;s format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1988
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2522
int nb_planes
Definition: v360.h:128
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
#define TOP_LEFT
Definition: movtextdec.c:47
int in_transpose
Definition: v360.h:113
AVOption.
Definition: opt.h:246
float input_mirror_modifier[2]
Definition: v360.h:118
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:424
int h_flip
Definition: v360.h:112
#define AV_PIX_FMT_YUV444P14
Definition: pixfmt.h:397
static void calculate_lanczos_coeffs(float t, float *coeffs)
Calculate 1-dimensional lanczos coefficients.
Definition: vf_v360.c:415
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:407
static int prepare_stereographic_out(AVFilterContext *ctx)
Prepare data for processing stereographic output format.
Definition: vf_v360.c:1413
#define AV_PIX_FMT_YUVA422P10
Definition: pixfmt.h:425
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:71
Definition: v360.h:28
misc image utilities
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2562
Main libavfilter public API header.
else temp
Definition: vf_mcdeint.c:256
static int prepare_eac_out(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap output format.
Definition: vf_v360.c:1592
static void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
Definition: vf_v360.c:678
static void eac_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format...
Definition: vf_v360.c:1623
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
static void rotate_cube_face(float *uf, float *vf, int rotation)
Definition: vf_v360.c:652
int in
Definition: v360.h:90
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:403
static int get_rorder(char c)
Convert char to corresponding rotation order.
Definition: vf_v360.c:527
#define us(width, name, range_min, range_max, subs,...)
Definition: cbs_h2645.c:266
Definition: v360.h:37
#define AV_PIX_FMT_GRAY9
Definition: pixfmt.h:367
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:391
int inplanewidth[4]
Definition: v360.h:126
int out_offset_w[4]
Definition: v360.h:123
static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bicubic interpolation.
Definition: vf_v360.c:391
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:99
static void set_mirror_modifier(int h_flip, int v_flip, int d_flip, float *modifier)
Definition: vf_v360.c:2118
char * in_frot
Definition: v360.h:95
int pr_height[4]
Definition: v360.h:120
static int config_output(AVFilterLink *outlink)
Definition: vf_v360.c:2170
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:92
static void xyz_to_equirect(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1500
uint16_t * u[4]
Definition: v360.h:131
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:244
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:283
#define AV_PIX_FMT_GRAY10
Definition: pixfmt.h:368
Axis +Y.
Definition: v360.h:66
const char * name
Pad name.
Definition: internal.h:60
#define AV_PIX_FMT_GRAY12
Definition: pixfmt.h:369
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:346
float pitch
Definition: v360.h:109
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1080
float roll
Definition: v360.h:109
static void xyz_to_cube6x1(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1315
planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
Definition: pixfmt.h:101
AVComponentDescriptor comp[4]
Parameters that describe how pixels are packed.
Definition: pixdesc.h:117
float yaw
Definition: v360.h:109
#define av_cold
Definition: attributes.h:82
AVOptions.
int out_transpose
Definition: v360.h:113
int planeheight[4]
Definition: v360.h:125
static void xyz_to_eac(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1726
#define f(width, name)
Definition: cbs_vp9.c:255
Axis -Y.
Definition: v360.h:67
static void cube1x6_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format...
Definition: vf_v360.c:1181
char * out_frot
Definition: v360.h:96
static void equirect_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format...
Definition: vf_v360.c:1389
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:252
#define AV_PIX_FMT_YUVA420P9
Definition: pixfmt.h:421
Definition: v360.h:84
#define cosf(x)
Definition: libm.h:78
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:402
int height
Definition: vf_avgblur.c:61
static void xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1867
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
Definition: vf_v360.c:2133
#define atanf(x)
Definition: libm.h:40
AVFilter ff_vf_v360
Definition: vf_v360.c:2564
static void barrel_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook&#39;s format...
Definition: vf_v360.c:1916
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range...
Definition: pixfmt.h:100
int16_t * ker[4]
Definition: v360.h:132
int height
Definition: v360.h:92
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition: pixfmt.h:79
#define atan2f(y, x)
Definition: libm.h:45
int width
Definition: v360.h:92
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:400
#define AV_PIX_FMT_YUV422P12
Definition: pixfmt.h:392
#define AV_PIX_FMT_YUVA420P16
Definition: pixfmt.h:429
static void xyz_to_cube(const V360Context *s, const float *vec, float *uf, float *vf, int *direction)
Calculate cubemap position for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:792
#define FFALIGN(x, a)
Definition: macros.h:48
int rotation_order[3]
Definition: v360.h:103
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
int in_stereo
Definition: v360.h:105
void ff_v360_init(V360Context *s, int depth)
Definition: vf_v360.c:299
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition: pixfmt.h:176
const AVS_VideoInfo * vi
Definition: avisynth_c.h:887
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:568
#define td
Definition: regdef.h:70
Definition: v360.h:82
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition: pixdesc.h:101
static void mirror(const float *modifier, float *vec)
Definition: vf_v360.c:2126
int out_offset_h[4]
Definition: v360.h:123
#define DEFINE_REMAP1_LINE(bits, div)
Definition: vf_v360.c:193
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:202
#define FF_CEIL_RSHIFT
Definition: common.h:61
static void calculate_bicubic_coeffs(float t, float *coeffs)
Calculate 1-dimensional cubic coefficients.
Definition: vf_v360.c:370
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
#define FLAGS
Definition: vf_v360.c:54
static void xyz_to_stereographic(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in stereographic format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1463
void * priv
private data for use by the filter
Definition: avfilter.h:353
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:116
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:431
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:408
simple assert() macros that are a bit more flexible than ISO C assert().
static int prepare_eac_in(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap input format.
Definition: vf_v360.c:1532
Definition: v360.h:77
static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
Definition: vf_v360.c:2162
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:390
Definition: v360.h:46
int in_offset_w[4]
Definition: v360.h:122
int out
Definition: v360.h:90
int out_stereo
Definition: v360.h:105
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:409
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:70
Definition: v360.h:48
int out_cubemap_face_rotation[6]
Definition: v360.h:102
int in_offset_h[4]
Definition: v360.h:122
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:385
unsigned map[4]
Definition: v360.h:133
int ih_flip
Definition: v360.h:111
int interp
Definition: v360.h:91
#define OFFSET(x)
Definition: vf_v360.c:53
Definition: v360.h:75
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:406
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:371
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:53
float in_pad
Definition: v360.h:107
#define FFMIN(a, b)
Definition: common.h:96
AVFrame * d
float out_pad
Definition: v360.h:107
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:78
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:428
Definition: v360.h:26
static const ElemCat * elements[ELEMENT_COUNT]
Definition: signature.h:566
typedef void(APIENTRY *FF_PFNGLACTIVETEXTUREPROC)(GLenum texture)
static int query_formats(AVFilterContext *ctx)
Definition: vf_v360.c:123
#define M_PI_2
Definition: mathematics.h:55
int inplaneheight[4]
Definition: v360.h:126
AVFrame * m
Definition: v360.h:47
AVFormatContext * ctx
Definition: movenc.c:48
Definition: v360.h:49
AVFILTER_DEFINE_CLASS(v360)
int in_cubemap_face_order[6]
Definition: v360.h:99
#define AV_PIX_FMT_YUVA444P10
Definition: pixfmt.h:426
static void process_cube_coordinates(const V360Context *s, float uf, float vf, int direction, float *new_uf, float *new_vf, int *face)
Find position on another cube face in case of overflow/underflow.
Definition: vf_v360.c:870
static const AVFilterPad outputs[]
Definition: vf_v360.c:2555
int(* remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: v360.h:135
#define AV_PIX_FMT_YUV444P9
Definition: pixfmt.h:386
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:405
float flat_range[3]
Definition: v360.h:116
#define AV_PIX_FMT_YUV420P16
Definition: pixfmt.h:398
float v_fov
Definition: v360.h:115
static void nearest_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker)
Save nearest pixel coordinates for remapping.
Definition: vf_v360.c:328
Definition: v360.h:83
#define sinf(x)
Definition: libm.h:419
#define DEFINE_REMAP_LINE(ws, bits, div)
Definition: vf_v360.c:268
#define TOP_RIGHT
Definition: movtextdec.c:49
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_v360.c:2535
#define AV_PIX_FMT_YUV420P14
Definition: pixfmt.h:395
static int prepare_flat_out(AVFilterContext *ctx)
Prepare data for processing flat output format.
Definition: vf_v360.c:1776
int planewidth[4]
Definition: v360.h:125
Used for passing data between threads.
Definition: dsddec.c:64
int in_cubemap_face_rotation[6]
Definition: v360.h:101
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition: pixfmt.h:177
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:81
static int mod(int a, int b)
Modulo operation with only positive remainders.
Definition: vf_v360.c:470
int nb_allocated
Definition: v360.h:129
#define AV_PIX_FMT_GRAY14
Definition: pixfmt.h:370
Definition: v360.h:74
static int prepare_cube_in(AVFilterContext *ctx)
Prepare data for processing cubemap input format.
Definition: vf_v360.c:551
int d_flip
Definition: v360.h:112
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_v360.c:2511
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
static void fov_from_dfov(V360Context *s, float w, float h)
Definition: vf_v360.c:2148
int out_cubemap_direction_order[6]
Definition: v360.h:100
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:387
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:72
static void dfisheye_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format...
Definition: vf_v360.c:1825
static const AVFilterPad inputs[]
Definition: vf_v360.c:2546
Filter definition.
Definition: avfilter.h:144
static void calculate_rotation_matrix(float yaw, float pitch, float roll, float rot_mat[3][3], const int rotation_order[3])
Calculate rotation matrix for yaw/pitch/roll angles.
Definition: vf_v360.c:2066
#define isnan(x)
Definition: libm.h:340
AVFrame * b
int v_flip
Definition: v360.h:112
#define DEFINE_REMAP(ws, bits)
Generate remapping function with a given window size and pixel depth.
Definition: vf_v360.c:222
const char * name
Filter name.
Definition: avfilter.h:148
#define AV_PIX_FMT_YUV440P12
Definition: pixfmt.h:393
#define AV_PIX_FMT_YUV420P9
Definition: pixfmt.h:384
char * in_forder
Definition: v360.h:93
static int prepare_cube_out(AVFilterContext *ctx)
Prepare data for processing cubemap output format.
Definition: vf_v360.c:605
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:350
int outw
Definition: vf_rotate.c:88
static av_always_inline av_const float roundf(float x)
Definition: libm.h:451
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:275
#define AV_PIX_FMT_YUV422P14
Definition: pixfmt.h:396
int pr_width[4]
Definition: v360.h:120
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:404
static void stereographic_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in stereographic format...
Definition: vf_v360.c:1436
#define flags(name, subs,...)
Definition: cbs_av1.c:561
AVFilterInternal * internal
An opaque struct for libavfilter internal use.
Definition: avfilter.h:378
#define AV_PIX_FMT_YUV422P10
Definition: pixfmt.h:388
int uv_linesize[4]
Definition: v360.h:127
#define AV_PIX_FMT_YUV444P12
Definition: pixfmt.h:394
static void xyz_to_cube1x6(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1239
float h_fov
Definition: v360.h:115
void ff_v360_init_x86(V360Context *s, int depth)
Definition: vf_v360_init.c:41
int
static void normalize_vector(float *vec)
Normalize vector.
Definition: vf_v360.c:709
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:66
Y , 8bpp.
Definition: pixfmt.h:74
static void rotate(const float rot_mat[3][3], float *vec)
Rotate vector with given rotation matrix.
Definition: vf_v360.c:2106
Definition: v360.h:76
int iv_flip
Definition: v360.h:111
static int get_rotation(char c)
Convert char to corresponding rotation angle.
Definition: vf_v360.c:508
planar GBRA 4:4:4:4 32bpp
Definition: pixfmt.h:215
#define AV_PIX_FMT_YUVA444P9
Definition: pixfmt.h:423
#define ui(width, name)
Definition: cbs_mpeg2.c:43
Definition: v360.h:60
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition: pixfmt.h:80
static void cube_to_xyz(const V360Context *s, float uf, float vf, int face, float *vec)
Calculate 3D coordinates on sphere for corresponding cubemap position.
Definition: vf_v360.c:728
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition: pixfmt.h:73
char * out_forder
Definition: v360.h:94
avfilter_execute_func * execute
Definition: internal.h:155
static void cube6x1_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format...
Definition: vf_v360.c:1209
Axis -Z.
Definition: v360.h:68
static int get_direction(char c)
Convert char to corresponding direction.
Definition: vf_v360.c:484
const AVPixFmtDescriptor * desc
Definition: vf_tonemap.c:196
A list of supported formats for one end of a filter link.
Definition: formats.h:64
static void bilinear_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bilinear interpolation.
Definition: vf_v360.c:348
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition: pixfmt.h:258
uint16_t u[4][4]
Definition: vf_v360.c:211
An instance of a filter.
Definition: avfilter.h:338
#define RIGHT
Definition: cdgraphics.c:167
static void xyz_to_cube3x2(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1096
#define av_freep(p)
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition: pixfmt.h:99
#define M_PI
Definition: mathematics.h:52
Definition: v360.h:39
uint16_t * v[4]
Definition: v360.h:131
AVFrame * in
Definition: af_afftdn.c:1082
#define FFSWAP(type, a, b)
Definition: common.h:99
int outh
Definition: vf_rotate.c:88
#define LEFT
Definition: cdgraphics.c:166
AVFilterLink * inlink
Definition: vf_blend.c:56
internal API functions
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
char * rorder
Definition: v360.h:97
int depth
Number of bits in the component.
Definition: pixdesc.h:58
AVFrame * a
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
float d_fov
Definition: v360.h:115
#define AV_PIX_FMT_YUV422P16
Definition: pixfmt.h:399
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:654
#define AV_PIX_FMT_YUVA422P12
Definition: pixfmt.h:427
static void lanczos_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for lanczos interpolation.
Definition: vf_v360.c:444
static uint8_t tmp[11]
Definition: aes_ctr.c:26