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  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" },
69  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" },
70  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" },
71  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" },
72  { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
73  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
74  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
75  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
76  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
77  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
78  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
79  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
80  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
81  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
82  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
83  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
84  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" },
85  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
86  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" },
87  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" },
88  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" },
89  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" },
90  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" },
91  { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "out" },
92  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "out" },
93  { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" },
94  { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
95  { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
96  { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
97  { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
98  { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
99  { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
100  { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
101  { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
102  { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
103  { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
104  { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
105  {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
106  { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
107  { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
108  { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
109  { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
110  {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
111  { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
112  { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"},
113  { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"},
114  { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"},
115  { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fin_pad"},
116  { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fout_pad"},
117  { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "yaw"},
118  { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "pitch"},
119  { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "roll"},
120  { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"},
121  { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"},
122  { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"},
123  { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"},
124  { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"},
125  { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"},
126  { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"},
127  { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "ih_flip"},
128  { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"},
129  { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"},
130  { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"},
131  { NULL }
132 };
133 
135 
137 {
138  static const enum AVPixelFormat pix_fmts[] = {
139  // YUVA444
143 
144  // YUVA422
148 
149  // YUVA420
152 
153  // YUVJ
157 
158  // YUV444
162 
163  // YUV440
166 
167  // YUV422
171 
172  // YUV420
176 
177  // YUV411
179 
180  // YUV410
182 
183  // GBR
187 
188  // GBRA
191 
192  // GRAY
196 
198  };
199 
200  AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
201  if (!fmts_list)
202  return AVERROR(ENOMEM);
203  return ff_set_common_formats(ctx, fmts_list);
204 }
205 
206 #define DEFINE_REMAP1_LINE(bits, div) \
207 static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
208  ptrdiff_t in_linesize, \
209  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
210 { \
211  const uint##bits##_t *s = (const uint##bits##_t *)src; \
212  uint##bits##_t *d = (uint##bits##_t *)dst; \
213  \
214  in_linesize /= div; \
215  \
216  for (int x = 0; x < width; x++) \
217  d[x] = s[v[x] * in_linesize + u[x]]; \
218 }
219 
220 DEFINE_REMAP1_LINE( 8, 1)
221 DEFINE_REMAP1_LINE(16, 2)
222 
223 /**
224  * Generate remapping function with a given window size and pixel depth.
225  *
226  * @param ws size of interpolation window
227  * @param bits number of bits per pixel
228  */
229 #define DEFINE_REMAP(ws, bits) \
230 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
231 { \
232  ThreadData *td = arg; \
233  const V360Context *s = ctx->priv; \
234  const AVFrame *in = td->in; \
235  AVFrame *out = td->out; \
236  \
237  for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
238  for (int plane = 0; plane < s->nb_planes; plane++) { \
239  const int in_linesize = in->linesize[plane]; \
240  const int out_linesize = out->linesize[plane]; \
241  const int uv_linesize = s->uv_linesize[plane]; \
242  const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
243  const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
244  const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
245  const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
246  const uint8_t *src = in->data[plane] + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
247  uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
248  const int width = s->pr_width[plane]; \
249  const int height = s->pr_height[plane]; \
250  \
251  const int slice_start = (height * jobnr ) / nb_jobs; \
252  const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
253  \
254  for (int y = slice_start; y < slice_end; y++) { \
255  const unsigned map = s->map[plane]; \
256  const uint16_t *u = s->u[map] + y * uv_linesize * ws * ws; \
257  const uint16_t *v = s->v[map] + y * uv_linesize * ws * ws; \
258  const int16_t *ker = s->ker[map] + y * uv_linesize * ws * ws; \
259  \
260  s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
261  } \
262  } \
263  } \
264  \
265  return 0; \
266 }
267 
268 DEFINE_REMAP(1, 8)
269 DEFINE_REMAP(2, 8)
270 DEFINE_REMAP(4, 8)
271 DEFINE_REMAP(1, 16)
272 DEFINE_REMAP(2, 16)
273 DEFINE_REMAP(4, 16)
274 
275 #define DEFINE_REMAP_LINE(ws, bits, div) \
276 static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
277  ptrdiff_t in_linesize, \
278  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
279 { \
280  const uint##bits##_t *s = (const uint##bits##_t *)src; \
281  uint##bits##_t *d = (uint##bits##_t *)dst; \
282  \
283  in_linesize /= div; \
284  \
285  for (int x = 0; x < width; x++) { \
286  const uint16_t *uu = u + x * ws * ws; \
287  const uint16_t *vv = v + x * ws * ws; \
288  const int16_t *kker = ker + x * ws * ws; \
289  int tmp = 0; \
290  \
291  for (int i = 0; i < ws; i++) { \
292  for (int j = 0; j < ws; j++) { \
293  tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \
294  } \
295  } \
296  \
297  d[x] = av_clip_uint##bits(tmp >> 14); \
298  } \
299 }
300 
301 DEFINE_REMAP_LINE(2, 8, 1)
302 DEFINE_REMAP_LINE(4, 8, 1)
303 DEFINE_REMAP_LINE(2, 16, 2)
304 DEFINE_REMAP_LINE(4, 16, 2)
305 
306 void ff_v360_init(V360Context *s, int depth)
307 {
308  switch (s->interp) {
309  case NEAREST:
310  s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c;
311  break;
312  case BILINEAR:
313  s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c;
314  break;
315  case BICUBIC:
316  case LANCZOS:
317  s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c;
318  break;
319  }
320 
321  if (ARCH_X86)
322  ff_v360_init_x86(s, depth);
323 }
324 
325 /**
326  * Save nearest pixel coordinates for remapping.
327  *
328  * @param du horizontal relative coordinate
329  * @param dv vertical relative coordinate
330  * @param rmap calculated 4x4 window
331  * @param u u remap data
332  * @param v v remap data
333  * @param ker ker remap data
334  */
335 static void nearest_kernel(float du, float dv, const XYRemap *rmap,
336  uint16_t *u, uint16_t *v, int16_t *ker)
337 {
338  const int i = roundf(dv) + 1;
339  const int j = roundf(du) + 1;
340 
341  u[0] = rmap->u[i][j];
342  v[0] = rmap->v[i][j];
343 }
344 
345 /**
346  * Calculate kernel for bilinear interpolation.
347  *
348  * @param du horizontal relative coordinate
349  * @param dv vertical relative coordinate
350  * @param rmap calculated 4x4 window
351  * @param u u remap data
352  * @param v v remap data
353  * @param ker ker remap data
354  */
355 static void bilinear_kernel(float du, float dv, const XYRemap *rmap,
356  uint16_t *u, uint16_t *v, int16_t *ker)
357 {
358  for (int i = 0; i < 2; i++) {
359  for (int j = 0; j < 2; j++) {
360  u[i * 2 + j] = rmap->u[i + 1][j + 1];
361  v[i * 2 + j] = rmap->v[i + 1][j + 1];
362  }
363  }
364 
365  ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f);
366  ker[1] = lrintf( du * (1.f - dv) * 16385.f);
367  ker[2] = lrintf((1.f - du) * dv * 16385.f);
368  ker[3] = lrintf( du * dv * 16385.f);
369 }
370 
371 /**
372  * Calculate 1-dimensional cubic coefficients.
373  *
374  * @param t relative coordinate
375  * @param coeffs coefficients
376  */
377 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
378 {
379  const float tt = t * t;
380  const float ttt = t * t * t;
381 
382  coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f;
383  coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f;
384  coeffs[2] = t + tt / 2.f - ttt / 2.f;
385  coeffs[3] = - t / 6.f + ttt / 6.f;
386 }
387 
388 /**
389  * Calculate kernel for bicubic interpolation.
390  *
391  * @param du horizontal relative coordinate
392  * @param dv vertical relative coordinate
393  * @param rmap calculated 4x4 window
394  * @param u u remap data
395  * @param v v remap data
396  * @param ker ker remap data
397  */
398 static void bicubic_kernel(float du, float dv, const XYRemap *rmap,
399  uint16_t *u, uint16_t *v, int16_t *ker)
400 {
401  float du_coeffs[4];
402  float dv_coeffs[4];
403 
404  calculate_bicubic_coeffs(du, du_coeffs);
405  calculate_bicubic_coeffs(dv, dv_coeffs);
406 
407  for (int i = 0; i < 4; i++) {
408  for (int j = 0; j < 4; j++) {
409  u[i * 4 + j] = rmap->u[i][j];
410  v[i * 4 + j] = rmap->v[i][j];
411  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
412  }
413  }
414 }
415 
416 /**
417  * Calculate 1-dimensional lanczos coefficients.
418  *
419  * @param t relative coordinate
420  * @param coeffs coefficients
421  */
422 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
423 {
424  float sum = 0.f;
425 
426  for (int i = 0; i < 4; i++) {
427  const float x = M_PI * (t - i + 1);
428  if (x == 0.f) {
429  coeffs[i] = 1.f;
430  } else {
431  coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
432  }
433  sum += coeffs[i];
434  }
435 
436  for (int i = 0; i < 4; i++) {
437  coeffs[i] /= sum;
438  }
439 }
440 
441 /**
442  * Calculate kernel for lanczos interpolation.
443  *
444  * @param du horizontal relative coordinate
445  * @param dv vertical relative coordinate
446  * @param rmap calculated 4x4 window
447  * @param u u remap data
448  * @param v v remap data
449  * @param ker ker remap data
450  */
451 static void lanczos_kernel(float du, float dv, const XYRemap *rmap,
452  uint16_t *u, uint16_t *v, int16_t *ker)
453 {
454  float du_coeffs[4];
455  float dv_coeffs[4];
456 
457  calculate_lanczos_coeffs(du, du_coeffs);
458  calculate_lanczos_coeffs(dv, dv_coeffs);
459 
460  for (int i = 0; i < 4; i++) {
461  for (int j = 0; j < 4; j++) {
462  u[i * 4 + j] = rmap->u[i][j];
463  v[i * 4 + j] = rmap->v[i][j];
464  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
465  }
466  }
467 }
468 
469 /**
470  * Modulo operation with only positive remainders.
471  *
472  * @param a dividend
473  * @param b divisor
474  *
475  * @return positive remainder of (a / b)
476  */
477 static inline int mod(int a, int b)
478 {
479  const int res = a % b;
480  if (res < 0) {
481  return res + b;
482  } else {
483  return res;
484  }
485 }
486 
487 /**
488  * Convert char to corresponding direction.
489  * Used for cubemap options.
490  */
491 static int get_direction(char c)
492 {
493  switch (c) {
494  case 'r':
495  return RIGHT;
496  case 'l':
497  return LEFT;
498  case 'u':
499  return UP;
500  case 'd':
501  return DOWN;
502  case 'f':
503  return FRONT;
504  case 'b':
505  return BACK;
506  default:
507  return -1;
508  }
509 }
510 
511 /**
512  * Convert char to corresponding rotation angle.
513  * Used for cubemap options.
514  */
515 static int get_rotation(char c)
516 {
517  switch (c) {
518  case '0':
519  return ROT_0;
520  case '1':
521  return ROT_90;
522  case '2':
523  return ROT_180;
524  case '3':
525  return ROT_270;
526  default:
527  return -1;
528  }
529 }
530 
531 /**
532  * Convert char to corresponding rotation order.
533  */
534 static int get_rorder(char c)
535 {
536  switch (c) {
537  case 'Y':
538  case 'y':
539  return YAW;
540  case 'P':
541  case 'p':
542  return PITCH;
543  case 'R':
544  case 'r':
545  return ROLL;
546  default:
547  return -1;
548  }
549 }
550 
551 /**
552  * Prepare data for processing cubemap input format.
553  *
554  * @param ctx filter context
555  *
556  * @return error code
557  */
559 {
560  V360Context *s = ctx->priv;
561 
562  for (int face = 0; face < NB_FACES; face++) {
563  const char c = s->in_forder[face];
564  int direction;
565 
566  if (c == '\0') {
567  av_log(ctx, AV_LOG_ERROR,
568  "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
569  return AVERROR(EINVAL);
570  }
571 
572  direction = get_direction(c);
573  if (direction == -1) {
574  av_log(ctx, AV_LOG_ERROR,
575  "Incorrect direction symbol '%c' in in_forder option.\n", c);
576  return AVERROR(EINVAL);
577  }
578 
579  s->in_cubemap_face_order[direction] = face;
580  }
581 
582  for (int face = 0; face < NB_FACES; face++) {
583  const char c = s->in_frot[face];
584  int rotation;
585 
586  if (c == '\0') {
587  av_log(ctx, AV_LOG_ERROR,
588  "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
589  return AVERROR(EINVAL);
590  }
591 
592  rotation = get_rotation(c);
593  if (rotation == -1) {
594  av_log(ctx, AV_LOG_ERROR,
595  "Incorrect rotation symbol '%c' in in_frot option.\n", c);
596  return AVERROR(EINVAL);
597  }
598 
599  s->in_cubemap_face_rotation[face] = rotation;
600  }
601 
602  return 0;
603 }
604 
605 /**
606  * Prepare data for processing cubemap output format.
607  *
608  * @param ctx filter context
609  *
610  * @return error code
611  */
613 {
614  V360Context *s = ctx->priv;
615 
616  for (int face = 0; face < NB_FACES; face++) {
617  const char c = s->out_forder[face];
618  int direction;
619 
620  if (c == '\0') {
621  av_log(ctx, AV_LOG_ERROR,
622  "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
623  return AVERROR(EINVAL);
624  }
625 
626  direction = get_direction(c);
627  if (direction == -1) {
628  av_log(ctx, AV_LOG_ERROR,
629  "Incorrect direction symbol '%c' in out_forder option.\n", c);
630  return AVERROR(EINVAL);
631  }
632 
633  s->out_cubemap_direction_order[face] = direction;
634  }
635 
636  for (int face = 0; face < NB_FACES; face++) {
637  const char c = s->out_frot[face];
638  int rotation;
639 
640  if (c == '\0') {
641  av_log(ctx, AV_LOG_ERROR,
642  "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
643  return AVERROR(EINVAL);
644  }
645 
646  rotation = get_rotation(c);
647  if (rotation == -1) {
648  av_log(ctx, AV_LOG_ERROR,
649  "Incorrect rotation symbol '%c' in out_frot option.\n", c);
650  return AVERROR(EINVAL);
651  }
652 
653  s->out_cubemap_face_rotation[face] = rotation;
654  }
655 
656  return 0;
657 }
658 
659 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
660 {
661  float tmp;
662 
663  switch (rotation) {
664  case ROT_0:
665  break;
666  case ROT_90:
667  tmp = *uf;
668  *uf = -*vf;
669  *vf = tmp;
670  break;
671  case ROT_180:
672  *uf = -*uf;
673  *vf = -*vf;
674  break;
675  case ROT_270:
676  tmp = -*uf;
677  *uf = *vf;
678  *vf = tmp;
679  break;
680  default:
681  av_assert0(0);
682  }
683 }
684 
685 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
686 {
687  float tmp;
688 
689  switch (rotation) {
690  case ROT_0:
691  break;
692  case ROT_90:
693  tmp = -*uf;
694  *uf = *vf;
695  *vf = tmp;
696  break;
697  case ROT_180:
698  *uf = -*uf;
699  *vf = -*vf;
700  break;
701  case ROT_270:
702  tmp = *uf;
703  *uf = -*vf;
704  *vf = tmp;
705  break;
706  default:
707  av_assert0(0);
708  }
709 }
710 
711 /**
712  * Normalize vector.
713  *
714  * @param vec vector
715  */
716 static void normalize_vector(float *vec)
717 {
718  const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
719 
720  vec[0] /= norm;
721  vec[1] /= norm;
722  vec[2] /= norm;
723 }
724 
725 /**
726  * Calculate 3D coordinates on sphere for corresponding cubemap position.
727  * Common operation for every cubemap.
728  *
729  * @param s filter private context
730  * @param uf horizontal cubemap coordinate [0, 1)
731  * @param vf vertical cubemap coordinate [0, 1)
732  * @param face face of cubemap
733  * @param vec coordinates on sphere
734  * @param scalew scale for uf
735  * @param scaleh scale for vf
736  */
737 static void cube_to_xyz(const V360Context *s,
738  float uf, float vf, int face,
739  float *vec, float scalew, float scaleh)
740 {
741  const int direction = s->out_cubemap_direction_order[face];
742  float l_x, l_y, l_z;
743 
744  uf /= scalew;
745  vf /= scaleh;
746 
748 
749  switch (direction) {
750  case RIGHT:
751  l_x = 1.f;
752  l_y = -vf;
753  l_z = uf;
754  break;
755  case LEFT:
756  l_x = -1.f;
757  l_y = -vf;
758  l_z = -uf;
759  break;
760  case UP:
761  l_x = uf;
762  l_y = 1.f;
763  l_z = -vf;
764  break;
765  case DOWN:
766  l_x = uf;
767  l_y = -1.f;
768  l_z = vf;
769  break;
770  case FRONT:
771  l_x = uf;
772  l_y = -vf;
773  l_z = -1.f;
774  break;
775  case BACK:
776  l_x = -uf;
777  l_y = -vf;
778  l_z = 1.f;
779  break;
780  default:
781  av_assert0(0);
782  }
783 
784  vec[0] = l_x;
785  vec[1] = l_y;
786  vec[2] = l_z;
787 
788  normalize_vector(vec);
789 }
790 
791 /**
792  * Calculate cubemap position for corresponding 3D coordinates on sphere.
793  * Common operation for every cubemap.
794  *
795  * @param s filter private context
796  * @param vec coordinated on sphere
797  * @param uf horizontal cubemap coordinate [0, 1)
798  * @param vf vertical cubemap coordinate [0, 1)
799  * @param direction direction of view
800  */
801 static void xyz_to_cube(const V360Context *s,
802  const float *vec,
803  float *uf, float *vf, int *direction)
804 {
805  const float phi = atan2f(vec[0], -vec[2]);
806  const float theta = asinf(-vec[1]);
807  float phi_norm, theta_threshold;
808  int face;
809 
810  if (phi >= -M_PI_4 && phi < M_PI_4) {
811  *direction = FRONT;
812  phi_norm = phi;
813  } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
814  *direction = LEFT;
815  phi_norm = phi + M_PI_2;
816  } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
817  *direction = RIGHT;
818  phi_norm = phi - M_PI_2;
819  } else {
820  *direction = BACK;
821  phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
822  }
823 
824  theta_threshold = atanf(cosf(phi_norm));
825  if (theta > theta_threshold) {
826  *direction = DOWN;
827  } else if (theta < -theta_threshold) {
828  *direction = UP;
829  }
830 
831  switch (*direction) {
832  case RIGHT:
833  *uf = vec[2] / vec[0];
834  *vf = -vec[1] / vec[0];
835  break;
836  case LEFT:
837  *uf = vec[2] / vec[0];
838  *vf = vec[1] / vec[0];
839  break;
840  case UP:
841  *uf = vec[0] / vec[1];
842  *vf = -vec[2] / vec[1];
843  break;
844  case DOWN:
845  *uf = -vec[0] / vec[1];
846  *vf = -vec[2] / vec[1];
847  break;
848  case FRONT:
849  *uf = -vec[0] / vec[2];
850  *vf = vec[1] / vec[2];
851  break;
852  case BACK:
853  *uf = -vec[0] / vec[2];
854  *vf = -vec[1] / vec[2];
855  break;
856  default:
857  av_assert0(0);
858  }
859 
860  face = s->in_cubemap_face_order[*direction];
861  rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
862 
863  (*uf) *= s->input_mirror_modifier[0];
864  (*vf) *= s->input_mirror_modifier[1];
865 }
866 
867 /**
868  * Find position on another cube face in case of overflow/underflow.
869  * Used for calculation of interpolation window.
870  *
871  * @param s filter private context
872  * @param uf horizontal cubemap coordinate
873  * @param vf vertical cubemap coordinate
874  * @param direction direction of view
875  * @param new_uf new horizontal cubemap coordinate
876  * @param new_vf new vertical cubemap coordinate
877  * @param face face position on cubemap
878  */
880  float uf, float vf, int direction,
881  float *new_uf, float *new_vf, int *face)
882 {
883  /*
884  * Cubemap orientation
885  *
886  * width
887  * <------->
888  * +-------+
889  * | | U
890  * | up | h ------->
891  * +-------+-------+-------+-------+ ^ e |
892  * | | | | | | i V |
893  * | left | front | right | back | | g |
894  * +-------+-------+-------+-------+ v h v
895  * | | t
896  * | down |
897  * +-------+
898  */
899 
900  *face = s->in_cubemap_face_order[direction];
902 
903  if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
904  // There are no pixels to use in this case
905  *new_uf = uf;
906  *new_vf = vf;
907  } else if (uf < -1.f) {
908  uf += 2.f;
909  switch (direction) {
910  case RIGHT:
911  direction = FRONT;
912  *new_uf = uf;
913  *new_vf = vf;
914  break;
915  case LEFT:
916  direction = BACK;
917  *new_uf = uf;
918  *new_vf = vf;
919  break;
920  case UP:
921  direction = LEFT;
922  *new_uf = vf;
923  *new_vf = -uf;
924  break;
925  case DOWN:
926  direction = LEFT;
927  *new_uf = -vf;
928  *new_vf = uf;
929  break;
930  case FRONT:
931  direction = LEFT;
932  *new_uf = uf;
933  *new_vf = vf;
934  break;
935  case BACK:
936  direction = RIGHT;
937  *new_uf = uf;
938  *new_vf = vf;
939  break;
940  default:
941  av_assert0(0);
942  }
943  } else if (uf >= 1.f) {
944  uf -= 2.f;
945  switch (direction) {
946  case RIGHT:
947  direction = BACK;
948  *new_uf = uf;
949  *new_vf = vf;
950  break;
951  case LEFT:
952  direction = FRONT;
953  *new_uf = uf;
954  *new_vf = vf;
955  break;
956  case UP:
957  direction = RIGHT;
958  *new_uf = -vf;
959  *new_vf = uf;
960  break;
961  case DOWN:
962  direction = RIGHT;
963  *new_uf = vf;
964  *new_vf = -uf;
965  break;
966  case FRONT:
967  direction = RIGHT;
968  *new_uf = uf;
969  *new_vf = vf;
970  break;
971  case BACK:
972  direction = LEFT;
973  *new_uf = uf;
974  *new_vf = vf;
975  break;
976  default:
977  av_assert0(0);
978  }
979  } else if (vf < -1.f) {
980  vf += 2.f;
981  switch (direction) {
982  case RIGHT:
983  direction = UP;
984  *new_uf = vf;
985  *new_vf = -uf;
986  break;
987  case LEFT:
988  direction = UP;
989  *new_uf = -vf;
990  *new_vf = uf;
991  break;
992  case UP:
993  direction = BACK;
994  *new_uf = -uf;
995  *new_vf = -vf;
996  break;
997  case DOWN:
998  direction = FRONT;
999  *new_uf = uf;
1000  *new_vf = vf;
1001  break;
1002  case FRONT:
1003  direction = UP;
1004  *new_uf = uf;
1005  *new_vf = vf;
1006  break;
1007  case BACK:
1008  direction = UP;
1009  *new_uf = -uf;
1010  *new_vf = -vf;
1011  break;
1012  default:
1013  av_assert0(0);
1014  }
1015  } else if (vf >= 1.f) {
1016  vf -= 2.f;
1017  switch (direction) {
1018  case RIGHT:
1019  direction = DOWN;
1020  *new_uf = -vf;
1021  *new_vf = uf;
1022  break;
1023  case LEFT:
1024  direction = DOWN;
1025  *new_uf = vf;
1026  *new_vf = -uf;
1027  break;
1028  case UP:
1029  direction = FRONT;
1030  *new_uf = uf;
1031  *new_vf = vf;
1032  break;
1033  case DOWN:
1034  direction = BACK;
1035  *new_uf = -uf;
1036  *new_vf = -vf;
1037  break;
1038  case FRONT:
1039  direction = DOWN;
1040  *new_uf = uf;
1041  *new_vf = vf;
1042  break;
1043  case BACK:
1044  direction = DOWN;
1045  *new_uf = -uf;
1046  *new_vf = -vf;
1047  break;
1048  default:
1049  av_assert0(0);
1050  }
1051  } else {
1052  // Inside cube face
1053  *new_uf = uf;
1054  *new_vf = vf;
1055  }
1056 
1057  *face = s->in_cubemap_face_order[direction];
1058  rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1059 }
1060 
1061 /**
1062  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1063  *
1064  * @param s filter private context
1065  * @param i horizontal position on frame [0, width)
1066  * @param j vertical position on frame [0, height)
1067  * @param width frame width
1068  * @param height frame height
1069  * @param vec coordinates on sphere
1070  */
1071 static void cube3x2_to_xyz(const V360Context *s,
1072  int i, int j, int width, int height,
1073  float *vec)
1074 {
1075  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 3.f) : 1.f - s->out_pad;
1076  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 2.f) : 1.f - s->out_pad;
1077 
1078  const float ew = width / 3.f;
1079  const float eh = height / 2.f;
1080 
1081  const int u_face = floorf(i / ew);
1082  const int v_face = floorf(j / eh);
1083  const int face = u_face + 3 * v_face;
1084 
1085  const int u_shift = ceilf(ew * u_face);
1086  const int v_shift = ceilf(eh * v_face);
1087  const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1088  const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1089 
1090  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1091  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1092 
1093  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1094 }
1095 
1096 /**
1097  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1098  *
1099  * @param s filter private context
1100  * @param vec coordinates on sphere
1101  * @param width frame width
1102  * @param height frame height
1103  * @param us horizontal coordinates for interpolation window
1104  * @param vs vertical coordinates for interpolation window
1105  * @param du horizontal relative coordinate
1106  * @param dv vertical relative coordinate
1107  */
1108 static void xyz_to_cube3x2(const V360Context *s,
1109  const float *vec, int width, int height,
1110  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1111 {
1112  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 3.f) : 1.f - s->in_pad;
1113  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 2.f) : 1.f - s->in_pad;
1114  const float ew = width / 3.f;
1115  const float eh = height / 2.f;
1116  float uf, vf;
1117  int ui, vi;
1118  int ewi, ehi;
1119  int direction, face;
1120  int u_face, v_face;
1121 
1122  xyz_to_cube(s, vec, &uf, &vf, &direction);
1123 
1124  uf *= scalew;
1125  vf *= scaleh;
1126 
1127  face = s->in_cubemap_face_order[direction];
1128  u_face = face % 3;
1129  v_face = face / 3;
1130  ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1131  ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1132 
1133  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1134  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1135 
1136  ui = floorf(uf);
1137  vi = floorf(vf);
1138 
1139  *du = uf - ui;
1140  *dv = vf - vi;
1141 
1142  for (int i = -1; i < 3; i++) {
1143  for (int j = -1; j < 3; j++) {
1144  int new_ui = ui + j;
1145  int new_vi = vi + i;
1146  int u_shift, v_shift;
1147  int new_ewi, new_ehi;
1148 
1149  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1150  face = s->in_cubemap_face_order[direction];
1151 
1152  u_face = face % 3;
1153  v_face = face / 3;
1154  u_shift = ceilf(ew * u_face);
1155  v_shift = ceilf(eh * v_face);
1156  } else {
1157  uf = 2.f * new_ui / ewi - 1.f;
1158  vf = 2.f * new_vi / ehi - 1.f;
1159 
1160  uf /= scalew;
1161  vf /= scaleh;
1162 
1163  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1164 
1165  uf *= scalew;
1166  vf *= scaleh;
1167 
1168  u_face = face % 3;
1169  v_face = face / 3;
1170  u_shift = ceilf(ew * u_face);
1171  v_shift = ceilf(eh * v_face);
1172  new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1173  new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1174 
1175  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1176  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1177  }
1178 
1179  us[i + 1][j + 1] = u_shift + new_ui;
1180  vs[i + 1][j + 1] = v_shift + new_vi;
1181  }
1182  }
1183 }
1184 
1185 /**
1186  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1187  *
1188  * @param s filter private context
1189  * @param i horizontal position on frame [0, width)
1190  * @param j vertical position on frame [0, height)
1191  * @param width frame width
1192  * @param height frame height
1193  * @param vec coordinates on sphere
1194  */
1195 static void cube1x6_to_xyz(const V360Context *s,
1196  int i, int j, int width, int height,
1197  float *vec)
1198 {
1199  const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_width : 1.f - s->out_pad;
1200  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 6.f) : 1.f - s->out_pad;
1201 
1202  const float ew = width;
1203  const float eh = height / 6.f;
1204 
1205  const int face = floorf(j / eh);
1206 
1207  const int v_shift = ceilf(eh * face);
1208  const int ehi = ceilf(eh * (face + 1)) - v_shift;
1209 
1210  const float uf = 2.f * (i + 0.5f) / ew - 1.f;
1211  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1212 
1213  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1214 }
1215 
1216 /**
1217  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1218  *
1219  * @param s filter private context
1220  * @param i horizontal position on frame [0, width)
1221  * @param j vertical position on frame [0, height)
1222  * @param width frame width
1223  * @param height frame height
1224  * @param vec coordinates on sphere
1225  */
1226 static void cube6x1_to_xyz(const V360Context *s,
1227  int i, int j, int width, int height,
1228  float *vec)
1229 {
1230  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 6.f) : 1.f - s->out_pad;
1231  const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_height : 1.f - s->out_pad;
1232 
1233  const float ew = width / 6.f;
1234  const float eh = height;
1235 
1236  const int face = floorf(i / ew);
1237 
1238  const int u_shift = ceilf(ew * face);
1239  const int ewi = ceilf(ew * (face + 1)) - u_shift;
1240 
1241  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1242  const float vf = 2.f * (j + 0.5f) / eh - 1.f;
1243 
1244  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1245 }
1246 
1247 /**
1248  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1249  *
1250  * @param s filter private context
1251  * @param vec coordinates on sphere
1252  * @param width frame width
1253  * @param height frame height
1254  * @param us horizontal coordinates for interpolation window
1255  * @param vs vertical coordinates for interpolation window
1256  * @param du horizontal relative coordinate
1257  * @param dv vertical relative coordinate
1258  */
1259 static void xyz_to_cube1x6(const V360Context *s,
1260  const float *vec, int width, int height,
1261  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1262 {
1263  const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_width : 1.f - s->in_pad;
1264  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 6.f) : 1.f - s->in_pad;
1265  const float eh = height / 6.f;
1266  const int ewi = width;
1267  float uf, vf;
1268  int ui, vi;
1269  int ehi;
1270  int direction, face;
1271 
1272  xyz_to_cube(s, vec, &uf, &vf, &direction);
1273 
1274  uf *= scalew;
1275  vf *= scaleh;
1276 
1277  face = s->in_cubemap_face_order[direction];
1278  ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1279 
1280  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1281  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1282 
1283  ui = floorf(uf);
1284  vi = floorf(vf);
1285 
1286  *du = uf - ui;
1287  *dv = vf - vi;
1288 
1289  for (int i = -1; i < 3; i++) {
1290  for (int j = -1; j < 3; j++) {
1291  int new_ui = ui + j;
1292  int new_vi = vi + i;
1293  int v_shift;
1294  int new_ehi;
1295 
1296  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1297  face = s->in_cubemap_face_order[direction];
1298 
1299  v_shift = ceilf(eh * face);
1300  } else {
1301  uf = 2.f * new_ui / ewi - 1.f;
1302  vf = 2.f * new_vi / ehi - 1.f;
1303 
1304  uf /= scalew;
1305  vf /= scaleh;
1306 
1307  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1308 
1309  uf *= scalew;
1310  vf *= scaleh;
1311 
1312  v_shift = ceilf(eh * face);
1313  new_ehi = ceilf(eh * (face + 1)) - v_shift;
1314 
1315  new_ui = av_clip(roundf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1);
1316  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1317  }
1318 
1319  us[i + 1][j + 1] = new_ui;
1320  vs[i + 1][j + 1] = v_shift + new_vi;
1321  }
1322  }
1323 }
1324 
1325 /**
1326  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1327  *
1328  * @param s filter private context
1329  * @param vec coordinates on sphere
1330  * @param width frame width
1331  * @param height frame height
1332  * @param us horizontal coordinates for interpolation window
1333  * @param vs vertical coordinates for interpolation window
1334  * @param du horizontal relative coordinate
1335  * @param dv vertical relative coordinate
1336  */
1337 static void xyz_to_cube6x1(const V360Context *s,
1338  const float *vec, int width, int height,
1339  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1340 {
1341  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 6.f) : 1.f - s->in_pad;
1342  const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_height : 1.f - s->in_pad;
1343  const float ew = width / 6.f;
1344  const int ehi = height;
1345  float uf, vf;
1346  int ui, vi;
1347  int ewi;
1348  int direction, face;
1349 
1350  xyz_to_cube(s, vec, &uf, &vf, &direction);
1351 
1352  uf *= scalew;
1353  vf *= scaleh;
1354 
1355  face = s->in_cubemap_face_order[direction];
1356  ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1357 
1358  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1359  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1360 
1361  ui = floorf(uf);
1362  vi = floorf(vf);
1363 
1364  *du = uf - ui;
1365  *dv = vf - vi;
1366 
1367  for (int i = -1; i < 3; i++) {
1368  for (int j = -1; j < 3; j++) {
1369  int new_ui = ui + j;
1370  int new_vi = vi + i;
1371  int u_shift;
1372  int new_ewi;
1373 
1374  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1375  face = s->in_cubemap_face_order[direction];
1376 
1377  u_shift = ceilf(ew * face);
1378  } else {
1379  uf = 2.f * new_ui / ewi - 1.f;
1380  vf = 2.f * new_vi / ehi - 1.f;
1381 
1382  uf /= scalew;
1383  vf /= scaleh;
1384 
1385  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1386 
1387  uf *= scalew;
1388  vf *= scaleh;
1389 
1390  u_shift = ceilf(ew * face);
1391  new_ewi = ceilf(ew * (face + 1)) - u_shift;
1392 
1393  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1394  new_vi = av_clip(roundf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1);
1395  }
1396 
1397  us[i + 1][j + 1] = u_shift + new_ui;
1398  vs[i + 1][j + 1] = new_vi;
1399  }
1400  }
1401 }
1402 
1403 /**
1404  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1405  *
1406  * @param s filter private context
1407  * @param i horizontal position on frame [0, width)
1408  * @param j vertical position on frame [0, height)
1409  * @param width frame width
1410  * @param height frame height
1411  * @param vec coordinates on sphere
1412  */
1413 static void equirect_to_xyz(const V360Context *s,
1414  int i, int j, int width, int height,
1415  float *vec)
1416 {
1417  const float phi = ((2.f * i) / width - 1.f) * M_PI;
1418  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1419 
1420  const float sin_phi = sinf(phi);
1421  const float cos_phi = cosf(phi);
1422  const float sin_theta = sinf(theta);
1423  const float cos_theta = cosf(theta);
1424 
1425  vec[0] = cos_theta * sin_phi;
1426  vec[1] = -sin_theta;
1427  vec[2] = -cos_theta * cos_phi;
1428 }
1429 
1430 /**
1431  * Prepare data for processing stereographic output format.
1432  *
1433  * @param ctx filter context
1434  *
1435  * @return error code
1436  */
1438 {
1439  V360Context *s = ctx->priv;
1440 
1441  s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
1442  s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
1443 
1444  return 0;
1445 }
1446 
1447 /**
1448  * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
1449  *
1450  * @param s filter private context
1451  * @param i horizontal position on frame [0, width)
1452  * @param j vertical position on frame [0, height)
1453  * @param width frame width
1454  * @param height frame height
1455  * @param vec coordinates on sphere
1456  */
1458  int i, int j, int width, int height,
1459  float *vec)
1460 {
1461  const float x = ((2.f * i) / width - 1.f) * s->flat_range[0];
1462  const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
1463  const float xy = x * x + y * y;
1464 
1465  vec[0] = 2.f * x / (1.f + xy);
1466  vec[1] = (-1.f + xy) / (1.f + xy);
1467  vec[2] = 2.f * y / (1.f + xy);
1468 
1469  normalize_vector(vec);
1470 }
1471 
1472 /**
1473  * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
1474  *
1475  * @param s filter private context
1476  * @param vec coordinates on sphere
1477  * @param width frame width
1478  * @param height frame height
1479  * @param us horizontal coordinates for interpolation window
1480  * @param vs vertical coordinates for interpolation window
1481  * @param du horizontal relative coordinate
1482  * @param dv vertical relative coordinate
1483  */
1485  const float *vec, int width, int height,
1486  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1487 {
1488  const float x = av_clipf(vec[0] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[0];
1489  const float y = av_clipf(vec[2] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[1];
1490  float uf, vf;
1491  int ui, vi;
1492 
1493  uf = (x + 1.f) * width / 2.f;
1494  vf = (y + 1.f) * height / 2.f;
1495  ui = floorf(uf);
1496  vi = floorf(vf);
1497 
1498  *du = uf - ui;
1499  *dv = vf - vi;
1500 
1501  for (int i = -1; i < 3; i++) {
1502  for (int j = -1; j < 3; j++) {
1503  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1504  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1505  }
1506  }
1507 }
1508 
1509 /**
1510  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
1511  *
1512  * @param s filter private context
1513  * @param vec coordinates on sphere
1514  * @param width frame width
1515  * @param height frame height
1516  * @param us horizontal coordinates for interpolation window
1517  * @param vs vertical coordinates for interpolation window
1518  * @param du horizontal relative coordinate
1519  * @param dv vertical relative coordinate
1520  */
1521 static void xyz_to_equirect(const V360Context *s,
1522  const float *vec, int width, int height,
1523  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1524 {
1525  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1526  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1527  float uf, vf;
1528  int ui, vi;
1529 
1530  uf = (phi / M_PI + 1.f) * width / 2.f;
1531  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1532  ui = floorf(uf);
1533  vi = floorf(vf);
1534 
1535  *du = uf - ui;
1536  *dv = vf - vi;
1537 
1538  for (int i = -1; i < 3; i++) {
1539  for (int j = -1; j < 3; j++) {
1540  us[i + 1][j + 1] = mod(ui + j, width);
1541  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1542  }
1543  }
1544 }
1545 
1546 /**
1547  * Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
1548  *
1549  * @param s filter private context
1550  * @param vec coordinates on sphere
1551  * @param width frame width
1552  * @param height frame height
1553  * @param us horizontal coordinates for interpolation window
1554  * @param vs vertical coordinates for interpolation window
1555  * @param du horizontal relative coordinate
1556  * @param dv vertical relative coordinate
1557  */
1558 static void xyz_to_mercator(const V360Context *s,
1559  const float *vec, int width, int height,
1560  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1561 {
1562  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1563  const float theta = -vec[1] * s->input_mirror_modifier[1];
1564  float uf, vf;
1565  int ui, vi;
1566 
1567  uf = (phi / M_PI + 1.f) * width / 2.f;
1568  vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f;
1569  ui = floorf(uf);
1570  vi = floorf(vf);
1571 
1572  *du = uf - ui;
1573  *dv = vf - vi;
1574 
1575  for (int i = -1; i < 3; i++) {
1576  for (int j = -1; j < 3; j++) {
1577  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1578  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1579  }
1580  }
1581 }
1582 
1583 /**
1584  * Calculate 3D coordinates on sphere for corresponding frame position in mercator format.
1585  *
1586  * @param s filter private context
1587  * @param i horizontal position on frame [0, width)
1588  * @param j vertical position on frame [0, height)
1589  * @param width frame width
1590  * @param height frame height
1591  * @param vec coordinates on sphere
1592  */
1593 static void mercator_to_xyz(const V360Context *s,
1594  int i, int j, int width, int height,
1595  float *vec)
1596 {
1597  const float phi = ((2.f * i) / width - 1.f) * M_PI + M_PI_2;
1598  const float y = ((2.f * j) / height - 1.f) * M_PI;
1599  const float div = expf(2.f * y) + 1.f;
1600 
1601  const float sin_phi = sinf(phi);
1602  const float cos_phi = cosf(phi);
1603  const float sin_theta = -2.f * expf(y) / div;
1604  const float cos_theta = -(expf(2.f * y) - 1.f) / div;
1605 
1606  vec[0] = sin_theta * cos_phi;
1607  vec[1] = cos_theta;
1608  vec[2] = sin_theta * sin_phi;
1609 }
1610 
1611 /**
1612  * Calculate frame position in ball format for corresponding 3D coordinates on sphere.
1613  *
1614  * @param s filter private context
1615  * @param vec coordinates on sphere
1616  * @param width frame width
1617  * @param height frame height
1618  * @param us horizontal coordinates for interpolation window
1619  * @param vs vertical coordinates for interpolation window
1620  * @param du horizontal relative coordinate
1621  * @param dv vertical relative coordinate
1622  */
1623 static void xyz_to_ball(const V360Context *s,
1624  const float *vec, int width, int height,
1625  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1626 {
1627  const float l = hypotf(vec[0], vec[1]);
1628  const float r = sqrtf(1.f + vec[2]) / M_SQRT2;
1629  float uf, vf;
1630  int ui, vi;
1631 
1632  uf = (1.f + r * vec[0] * s->input_mirror_modifier[0] / (l > 0.f ? l : 1.f)) * width * 0.5f;
1633  vf = (1.f - r * vec[1] * s->input_mirror_modifier[1] / (l > 0.f ? l : 1.f)) * height * 0.5f;
1634 
1635  ui = floorf(uf);
1636  vi = floorf(vf);
1637 
1638  *du = uf - ui;
1639  *dv = vf - vi;
1640 
1641  for (int i = -1; i < 3; i++) {
1642  for (int j = -1; j < 3; j++) {
1643  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1644  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1645  }
1646  }
1647 }
1648 
1649 /**
1650  * Calculate 3D coordinates on sphere for corresponding frame position in ball format.
1651  *
1652  * @param s filter private context
1653  * @param i horizontal position on frame [0, width)
1654  * @param j vertical position on frame [0, height)
1655  * @param width frame width
1656  * @param height frame height
1657  * @param vec coordinates on sphere
1658  */
1659 static void ball_to_xyz(const V360Context *s,
1660  int i, int j, int width, int height,
1661  float *vec)
1662 {
1663  const float x = (2.f * i) / width - 1.f;
1664  const float y = (2.f * j) / height - 1.f;
1665  const float l = hypotf(x, y);
1666 
1667  if (l <= 1.f) {
1668  const float z = 2.f * l * sqrtf(1.f - l * l);
1669 
1670  vec[0] = z * x / (l > 0.f ? l : 1.f);
1671  vec[1] = -z * y / (l > 0.f ? l : 1.f);
1672  vec[2] = -1.f + 2.f * l * l;
1673  } else {
1674  vec[0] = 0.f;
1675  vec[1] = -1.f;
1676  vec[2] = 0.f;
1677  }
1678 }
1679 
1680 /**
1681  * Calculate 3D coordinates on sphere for corresponding frame position in hammer format.
1682  *
1683  * @param s filter private context
1684  * @param i horizontal position on frame [0, width)
1685  * @param j vertical position on frame [0, height)
1686  * @param width frame width
1687  * @param height frame height
1688  * @param vec coordinates on sphere
1689  */
1690 static void hammer_to_xyz(const V360Context *s,
1691  int i, int j, int width, int height,
1692  float *vec)
1693 {
1694  const float x = ((2.f * i) / width - 1.f);
1695  const float y = ((2.f * j) / height - 1.f);
1696 
1697  const float xx = x * x;
1698  const float yy = y * y;
1699 
1700  const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f);
1701 
1702  const float a = M_SQRT2 * x * z;
1703  const float b = 2.f * z * z - 1.f;
1704 
1705  const float aa = a * a;
1706  const float bb = b * b;
1707 
1708  const float w = sqrtf(1.f - 2.f * yy * z * z);
1709 
1710  vec[0] = w * 2.f * a * b / (aa + bb);
1711  vec[1] = -M_SQRT2 * y * z;
1712  vec[2] = -w * (bb - aa) / (aa + bb);
1713 
1714  normalize_vector(vec);
1715 }
1716 
1717 /**
1718  * Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
1719  *
1720  * @param s filter private context
1721  * @param vec coordinates on sphere
1722  * @param width frame width
1723  * @param height frame height
1724  * @param us horizontal coordinates for interpolation window
1725  * @param vs vertical coordinates for interpolation window
1726  * @param du horizontal relative coordinate
1727  * @param dv vertical relative coordinate
1728  */
1729 static void xyz_to_hammer(const V360Context *s,
1730  const float *vec, int width, int height,
1731  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1732 {
1733  const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1734 
1735  const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f));
1736  const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z;
1737  const float y = -vec[1] / z * s->input_mirror_modifier[1];
1738  float uf, vf;
1739  int ui, vi;
1740 
1741  uf = (x + 1.f) * width / 2.f;
1742  vf = (y + 1.f) * height / 2.f;
1743  ui = floorf(uf);
1744  vi = floorf(vf);
1745 
1746  *du = uf - ui;
1747  *dv = vf - vi;
1748 
1749  for (int i = -1; i < 3; i++) {
1750  for (int j = -1; j < 3; j++) {
1751  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1752  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1753  }
1754  }
1755 }
1756 
1757 /**
1758  * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format.
1759  *
1760  * @param s filter private context
1761  * @param i horizontal position on frame [0, width)
1762  * @param j vertical position on frame [0, height)
1763  * @param width frame width
1764  * @param height frame height
1765  * @param vec coordinates on sphere
1766  */
1767 static void sinusoidal_to_xyz(const V360Context *s,
1768  int i, int j, int width, int height,
1769  float *vec)
1770 {
1771  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1772  const float phi = ((2.f * i) / width - 1.f) * M_PI / cosf(theta);
1773 
1774  const float sin_phi = sinf(phi);
1775  const float cos_phi = cosf(phi);
1776  const float sin_theta = sinf(theta);
1777  const float cos_theta = cosf(theta);
1778 
1779  vec[0] = cos_theta * sin_phi;
1780  vec[1] = -sin_theta;
1781  vec[2] = -cos_theta * cos_phi;
1782 
1783  normalize_vector(vec);
1784 }
1785 
1786 /**
1787  * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere.
1788  *
1789  * @param s filter private context
1790  * @param vec coordinates on sphere
1791  * @param width frame width
1792  * @param height frame height
1793  * @param us horizontal coordinates for interpolation window
1794  * @param vs vertical coordinates for interpolation window
1795  * @param du horizontal relative coordinate
1796  * @param dv vertical relative coordinate
1797  */
1798 static void xyz_to_sinusoidal(const V360Context *s,
1799  const float *vec, int width, int height,
1800  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1801 {
1802  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1803  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] * cosf(theta);
1804  float uf, vf;
1805  int ui, vi;
1806 
1807  uf = (phi / M_PI + 1.f) * width / 2.f;
1808  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1809  ui = floorf(uf);
1810  vi = floorf(vf);
1811 
1812  *du = uf - ui;
1813  *dv = vf - vi;
1814 
1815  for (int i = -1; i < 3; i++) {
1816  for (int j = -1; j < 3; j++) {
1817  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1818  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1819  }
1820  }
1821 }
1822 
1823 /**
1824  * Prepare data for processing equi-angular cubemap input format.
1825  *
1826  * @param ctx filter context
1827  *
1828  * @return error code
1829  */
1831 {
1832  V360Context *s = ctx->priv;
1833 
1834  if (s->ih_flip && s->iv_flip) {
1841  } else if (s->ih_flip) {
1848  } else if (s->iv_flip) {
1855  } else {
1862  }
1863 
1864  if (s->iv_flip) {
1871  } else {
1878  }
1879 
1880  return 0;
1881 }
1882 
1883 /**
1884  * Prepare data for processing equi-angular cubemap output format.
1885  *
1886  * @param ctx filter context
1887  *
1888  * @return error code
1889  */
1891 {
1892  V360Context *s = ctx->priv;
1893 
1900 
1907 
1908  return 0;
1909 }
1910 
1911 /**
1912  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
1913  *
1914  * @param s filter private context
1915  * @param i horizontal position on frame [0, width)
1916  * @param j vertical position on frame [0, height)
1917  * @param width frame width
1918  * @param height frame height
1919  * @param vec coordinates on sphere
1920  */
1921 static void eac_to_xyz(const V360Context *s,
1922  int i, int j, int width, int height,
1923  float *vec)
1924 {
1925  const float pixel_pad = 2;
1926  const float u_pad = pixel_pad / width;
1927  const float v_pad = pixel_pad / height;
1928 
1929  int u_face, v_face, face;
1930 
1931  float l_x, l_y, l_z;
1932 
1933  float uf = (i + 0.5f) / width;
1934  float vf = (j + 0.5f) / height;
1935 
1936  // EAC has 2-pixel padding on faces except between faces on the same row
1937  // Padding pixels seems not to be stretched with tangent as regular pixels
1938  // Formulas below approximate original padding as close as I could get experimentally
1939 
1940  // Horizontal padding
1941  uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
1942  if (uf < 0.f) {
1943  u_face = 0;
1944  uf -= 0.5f;
1945  } else if (uf >= 3.f) {
1946  u_face = 2;
1947  uf -= 2.5f;
1948  } else {
1949  u_face = floorf(uf);
1950  uf = fmodf(uf, 1.f) - 0.5f;
1951  }
1952 
1953  // Vertical padding
1954  v_face = floorf(vf * 2.f);
1955  vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
1956 
1957  if (uf >= -0.5f && uf < 0.5f) {
1958  uf = tanf(M_PI_2 * uf);
1959  } else {
1960  uf = 2.f * uf;
1961  }
1962  if (vf >= -0.5f && vf < 0.5f) {
1963  vf = tanf(M_PI_2 * vf);
1964  } else {
1965  vf = 2.f * vf;
1966  }
1967 
1968  face = u_face + 3 * v_face;
1969 
1970  switch (face) {
1971  case TOP_LEFT:
1972  l_x = -1.f;
1973  l_y = -vf;
1974  l_z = -uf;
1975  break;
1976  case TOP_MIDDLE:
1977  l_x = uf;
1978  l_y = -vf;
1979  l_z = -1.f;
1980  break;
1981  case TOP_RIGHT:
1982  l_x = 1.f;
1983  l_y = -vf;
1984  l_z = uf;
1985  break;
1986  case BOTTOM_LEFT:
1987  l_x = -vf;
1988  l_y = -1.f;
1989  l_z = uf;
1990  break;
1991  case BOTTOM_MIDDLE:
1992  l_x = -vf;
1993  l_y = uf;
1994  l_z = 1.f;
1995  break;
1996  case BOTTOM_RIGHT:
1997  l_x = -vf;
1998  l_y = 1.f;
1999  l_z = -uf;
2000  break;
2001  default:
2002  av_assert0(0);
2003  }
2004 
2005  vec[0] = l_x;
2006  vec[1] = l_y;
2007  vec[2] = l_z;
2008 
2009  normalize_vector(vec);
2010 }
2011 
2012 /**
2013  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
2014  *
2015  * @param s filter private context
2016  * @param vec coordinates on sphere
2017  * @param width frame width
2018  * @param height frame height
2019  * @param us horizontal coordinates for interpolation window
2020  * @param vs vertical coordinates for interpolation window
2021  * @param du horizontal relative coordinate
2022  * @param dv vertical relative coordinate
2023  */
2024 static void xyz_to_eac(const V360Context *s,
2025  const float *vec, int width, int height,
2026  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2027 {
2028  const float pixel_pad = 2;
2029  const float u_pad = pixel_pad / width;
2030  const float v_pad = pixel_pad / height;
2031 
2032  float uf, vf;
2033  int ui, vi;
2034  int direction, face;
2035  int u_face, v_face;
2036 
2037  xyz_to_cube(s, vec, &uf, &vf, &direction);
2038 
2039  face = s->in_cubemap_face_order[direction];
2040  u_face = face % 3;
2041  v_face = face / 3;
2042 
2043  uf = M_2_PI * atanf(uf) + 0.5f;
2044  vf = M_2_PI * atanf(vf) + 0.5f;
2045 
2046  // These formulas are inversed from eac_to_xyz ones
2047  uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
2048  vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
2049 
2050  uf *= width;
2051  vf *= height;
2052 
2053  uf -= 0.5f;
2054  vf -= 0.5f;
2055 
2056  ui = floorf(uf);
2057  vi = floorf(vf);
2058 
2059  *du = uf - ui;
2060  *dv = vf - vi;
2061 
2062  for (int i = -1; i < 3; i++) {
2063  for (int j = -1; j < 3; j++) {
2064  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
2065  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2066  }
2067  }
2068 }
2069 
2070 /**
2071  * Prepare data for processing flat output format.
2072  *
2073  * @param ctx filter context
2074  *
2075  * @return error code
2076  */
2078 {
2079  V360Context *s = ctx->priv;
2080 
2081  s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
2082  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2083 
2084  return 0;
2085 }
2086 
2087 /**
2088  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
2089  *
2090  * @param s filter private context
2091  * @param i horizontal position on frame [0, width)
2092  * @param j vertical position on frame [0, height)
2093  * @param width frame width
2094  * @param height frame height
2095  * @param vec coordinates on sphere
2096  */
2097 static void flat_to_xyz(const V360Context *s,
2098  int i, int j, int width, int height,
2099  float *vec)
2100 {
2101  const float l_x = s->flat_range[0] * (2.f * i / width - 1.f);
2102  const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
2103 
2104  vec[0] = l_x;
2105  vec[1] = l_y;
2106  vec[2] = -1.f;
2107 
2108  normalize_vector(vec);
2109 }
2110 
2111 /**
2112  * Prepare data for processing fisheye output format.
2113  *
2114  * @param ctx filter context
2115  *
2116  * @return error code
2117  */
2119 {
2120  V360Context *s = ctx->priv;
2121 
2122  s->flat_range[0] = s->h_fov / 180.f;
2123  s->flat_range[1] = s->v_fov / 180.f;
2124 
2125  return 0;
2126 }
2127 
2128 /**
2129  * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format.
2130  *
2131  * @param s filter private context
2132  * @param i horizontal position on frame [0, width)
2133  * @param j vertical position on frame [0, height)
2134  * @param width frame width
2135  * @param height frame height
2136  * @param vec coordinates on sphere
2137  */
2138 static void fisheye_to_xyz(const V360Context *s,
2139  int i, int j, int width, int height,
2140  float *vec)
2141 {
2142  const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
2143  const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
2144 
2145  const float phi = -atan2f(vf, uf);
2146  const float theta = -M_PI_2 * (1.f - hypotf(uf, vf));
2147 
2148  vec[0] = cosf(theta) * cosf(phi);
2149  vec[1] = cosf(theta) * sinf(phi);
2150  vec[2] = sinf(theta);
2151 
2152  normalize_vector(vec);
2153 }
2154 
2155 /**
2156  * Calculate 3D coordinates on sphere for corresponding frame position in pannini format.
2157  *
2158  * @param s filter private context
2159  * @param i horizontal position on frame [0, width)
2160  * @param j vertical position on frame [0, height)
2161  * @param width frame width
2162  * @param height frame height
2163  * @param vec coordinates on sphere
2164  */
2165 static void pannini_to_xyz(const V360Context *s,
2166  int i, int j, int width, int height,
2167  float *vec)
2168 {
2169  const float uf = ((2.f * i) / width - 1.f);
2170  const float vf = ((2.f * j) / height - 1.f);
2171 
2172  const float d = s->h_fov;
2173  float k = uf * uf / ((d + 1.f) * (d + 1.f));
2174  float dscr = k * k * d * d - (k + 1) * (k * d * d - 1.f);
2175  float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
2176  float S = (d + 1.f) / (d + clon);
2177  float lon = -(M_PI + atan2f(uf, S * clon));
2178  float lat = -atan2f(vf, S);
2179 
2180  vec[0] = sinf(lon) * cosf(lat);
2181  vec[1] = sinf(lat);
2182  vec[2] = cosf(lon) * cosf(lat);
2183 
2184  normalize_vector(vec);
2185 }
2186 
2187 /**
2188  * Prepare data for processing cylindrical output format.
2189  *
2190  * @param ctx filter context
2191  *
2192  * @return error code
2193  */
2195 {
2196  V360Context *s = ctx->priv;
2197 
2198  s->flat_range[0] = M_PI * s->h_fov / 360.f;
2199  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2200 
2201  return 0;
2202 }
2203 
2204 /**
2205  * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format.
2206  *
2207  * @param s filter private context
2208  * @param i horizontal position on frame [0, width)
2209  * @param j vertical position on frame [0, height)
2210  * @param width frame width
2211  * @param height frame height
2212  * @param vec coordinates on sphere
2213  */
2214 static void cylindrical_to_xyz(const V360Context *s,
2215  int i, int j, int width, int height,
2216  float *vec)
2217 {
2218  const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
2219  const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
2220 
2221  const float phi = uf;
2222  const float theta = atanf(vf);
2223 
2224  const float sin_phi = sinf(phi);
2225  const float cos_phi = cosf(phi);
2226  const float sin_theta = sinf(theta);
2227  const float cos_theta = cosf(theta);
2228 
2229  vec[0] = cos_theta * sin_phi;
2230  vec[1] = -sin_theta;
2231  vec[2] = -cos_theta * cos_phi;
2232 
2233  normalize_vector(vec);
2234 }
2235 
2236 /**
2237  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
2238  *
2239  * @param s filter private context
2240  * @param i horizontal position on frame [0, width)
2241  * @param j vertical position on frame [0, height)
2242  * @param width frame width
2243  * @param height frame height
2244  * @param vec coordinates on sphere
2245  */
2246 static void dfisheye_to_xyz(const V360Context *s,
2247  int i, int j, int width, int height,
2248  float *vec)
2249 {
2250  const float scale = 1.f + s->out_pad;
2251 
2252  const float ew = width / 2.f;
2253  const float eh = height;
2254 
2255  const int ei = i >= ew ? i - ew : i;
2256  const float m = i >= ew ? -1.f : 1.f;
2257 
2258  const float uf = ((2.f * ei) / ew - 1.f) * scale;
2259  const float vf = ((2.f * j) / eh - 1.f) * scale;
2260 
2261  const float h = hypotf(uf, vf);
2262  const float lh = h > 0.f ? h : 1.f;
2263  const float theta = m * M_PI_2 * (1.f - h);
2264 
2265  const float sin_theta = sinf(theta);
2266  const float cos_theta = cosf(theta);
2267 
2268  vec[0] = cos_theta * m * -uf / lh;
2269  vec[1] = cos_theta * -vf / lh;
2270  vec[2] = sin_theta;
2271 
2272  normalize_vector(vec);
2273 }
2274 
2275 /**
2276  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
2277  *
2278  * @param s filter private context
2279  * @param vec coordinates on sphere
2280  * @param width frame width
2281  * @param height frame height
2282  * @param us horizontal coordinates for interpolation window
2283  * @param vs vertical coordinates for interpolation window
2284  * @param du horizontal relative coordinate
2285  * @param dv vertical relative coordinate
2286  */
2287 static void xyz_to_dfisheye(const V360Context *s,
2288  const float *vec, int width, int height,
2289  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2290 {
2291  const float scale = 1.f - s->in_pad;
2292 
2293  const float ew = width / 2.f;
2294  const float eh = height;
2295 
2296  const float h = hypotf(vec[0], vec[1]);
2297  const float lh = h > 0.f ? h : 1.f;
2298  const float theta = acosf(fabsf(vec[2])) / M_PI;
2299 
2300  float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
2301  float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
2302 
2303  int ui, vi;
2304  int u_shift;
2305 
2306  if (vec[2] >= 0.f) {
2307  u_shift = 0;
2308  } else {
2309  u_shift = ceilf(ew);
2310  uf = ew - uf;
2311  }
2312 
2313  ui = floorf(uf);
2314  vi = floorf(vf);
2315 
2316  *du = uf - ui;
2317  *dv = vf - vi;
2318 
2319  for (int i = -1; i < 3; i++) {
2320  for (int j = -1; j < 3; j++) {
2321  us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width - 1);
2322  vs[i + 1][j + 1] = av_clip( vi + i, 0, height - 1);
2323  }
2324  }
2325 }
2326 
2327 /**
2328  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
2329  *
2330  * @param s filter private context
2331  * @param i horizontal position on frame [0, width)
2332  * @param j vertical position on frame [0, height)
2333  * @param width frame width
2334  * @param height frame height
2335  * @param vec coordinates on sphere
2336  */
2337 static void barrel_to_xyz(const V360Context *s,
2338  int i, int j, int width, int height,
2339  float *vec)
2340 {
2341  const float scale = 0.99f;
2342  float l_x, l_y, l_z;
2343 
2344  if (i < 4 * width / 5) {
2345  const float theta_range = M_PI_4;
2346 
2347  const int ew = 4 * width / 5;
2348  const int eh = height;
2349 
2350  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
2351  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
2352 
2353  const float sin_phi = sinf(phi);
2354  const float cos_phi = cosf(phi);
2355  const float sin_theta = sinf(theta);
2356  const float cos_theta = cosf(theta);
2357 
2358  l_x = cos_theta * sin_phi;
2359  l_y = -sin_theta;
2360  l_z = -cos_theta * cos_phi;
2361  } else {
2362  const int ew = width / 5;
2363  const int eh = height / 2;
2364 
2365  float uf, vf;
2366 
2367  if (j < eh) { // UP
2368  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2369  vf = 2.f * (j ) / eh - 1.f;
2370 
2371  uf /= scale;
2372  vf /= scale;
2373 
2374  l_x = uf;
2375  l_y = 1.f;
2376  l_z = -vf;
2377  } else { // DOWN
2378  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2379  vf = 2.f * (j - eh) / eh - 1.f;
2380 
2381  uf /= scale;
2382  vf /= scale;
2383 
2384  l_x = uf;
2385  l_y = -1.f;
2386  l_z = vf;
2387  }
2388  }
2389 
2390  vec[0] = l_x;
2391  vec[1] = l_y;
2392  vec[2] = l_z;
2393 
2394  normalize_vector(vec);
2395 }
2396 
2397 /**
2398  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
2399  *
2400  * @param s filter private context
2401  * @param vec coordinates on sphere
2402  * @param width frame width
2403  * @param height frame height
2404  * @param us horizontal coordinates for interpolation window
2405  * @param vs vertical coordinates for interpolation window
2406  * @param du horizontal relative coordinate
2407  * @param dv vertical relative coordinate
2408  */
2409 static void xyz_to_barrel(const V360Context *s,
2410  const float *vec, int width, int height,
2411  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2412 {
2413  const float scale = 0.99f;
2414 
2415  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
2416  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
2417  const float theta_range = M_PI_4;
2418 
2419  int ew, eh;
2420  int u_shift, v_shift;
2421  float uf, vf;
2422  int ui, vi;
2423 
2424  if (theta > -theta_range && theta < theta_range) {
2425  ew = 4 * width / 5;
2426  eh = height;
2427 
2428  u_shift = s->ih_flip ? width / 5 : 0;
2429  v_shift = 0;
2430 
2431  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
2432  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
2433  } else {
2434  ew = width / 5;
2435  eh = height / 2;
2436 
2437  u_shift = s->ih_flip ? 0 : 4 * ew;
2438 
2439  if (theta < 0.f) { // UP
2440  uf = vec[0] / vec[1];
2441  vf = -vec[2] / vec[1];
2442  v_shift = 0;
2443  } else { // DOWN
2444  uf = -vec[0] / vec[1];
2445  vf = -vec[2] / vec[1];
2446  v_shift = eh;
2447  }
2448 
2449  uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
2450  vf *= s->input_mirror_modifier[1];
2451 
2452  uf = 0.5f * ew * (uf * scale + 1.f);
2453  vf = 0.5f * eh * (vf * scale + 1.f);
2454  }
2455 
2456  ui = floorf(uf);
2457  vi = floorf(vf);
2458 
2459  *du = uf - ui;
2460  *dv = vf - vi;
2461 
2462  for (int i = -1; i < 3; i++) {
2463  for (int j = -1; j < 3; j++) {
2464  us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
2465  vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
2466  }
2467  }
2468 }
2469 
2470 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
2471 {
2472  for (int i = 0; i < 3; i++) {
2473  for (int j = 0; j < 3; j++) {
2474  float sum = 0;
2475 
2476  for (int k = 0; k < 3; k++)
2477  sum += a[i][k] * b[k][j];
2478 
2479  c[i][j] = sum;
2480  }
2481  }
2482 }
2483 
2484 /**
2485  * Calculate rotation matrix for yaw/pitch/roll angles.
2486  */
2487 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
2488  float rot_mat[3][3],
2489  const int rotation_order[3])
2490 {
2491  const float yaw_rad = yaw * M_PI / 180.f;
2492  const float pitch_rad = pitch * M_PI / 180.f;
2493  const float roll_rad = roll * M_PI / 180.f;
2494 
2495  const float sin_yaw = sinf(-yaw_rad);
2496  const float cos_yaw = cosf(-yaw_rad);
2497  const float sin_pitch = sinf(pitch_rad);
2498  const float cos_pitch = cosf(pitch_rad);
2499  const float sin_roll = sinf(roll_rad);
2500  const float cos_roll = cosf(roll_rad);
2501 
2502  float m[3][3][3];
2503  float temp[3][3];
2504 
2505  m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw;
2506  m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0;
2507  m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw;
2508 
2509  m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0;
2510  m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch;
2511  m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch;
2512 
2513  m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0;
2514  m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0;
2515  m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1;
2516 
2517  multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
2518  multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
2519 }
2520 
2521 /**
2522  * Rotate vector with given rotation matrix.
2523  *
2524  * @param rot_mat rotation matrix
2525  * @param vec vector
2526  */
2527 static inline void rotate(const float rot_mat[3][3],
2528  float *vec)
2529 {
2530  const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
2531  const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
2532  const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
2533 
2534  vec[0] = x_tmp;
2535  vec[1] = y_tmp;
2536  vec[2] = z_tmp;
2537 }
2538 
2539 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
2540  float *modifier)
2541 {
2542  modifier[0] = h_flip ? -1.f : 1.f;
2543  modifier[1] = v_flip ? -1.f : 1.f;
2544  modifier[2] = d_flip ? -1.f : 1.f;
2545 }
2546 
2547 static inline void mirror(const float *modifier, float *vec)
2548 {
2549  vec[0] *= modifier[0];
2550  vec[1] *= modifier[1];
2551  vec[2] *= modifier[2];
2552 }
2553 
2554 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
2555 {
2556  s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2557  s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2558  if (!s->u[p] || !s->v[p])
2559  return AVERROR(ENOMEM);
2560  if (sizeof_ker) {
2561  s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
2562  if (!s->ker[p])
2563  return AVERROR(ENOMEM);
2564  }
2565 
2566  return 0;
2567 }
2568 
2569 static void fov_from_dfov(V360Context *s, float w, float h)
2570 {
2571  const float da = tanf(0.5 * FFMIN(s->d_fov, 359.f) * M_PI / 180.f);
2572  const float d = hypotf(w, h);
2573 
2574  s->h_fov = atan2f(da * w, d) * 360.f / M_PI;
2575  s->v_fov = atan2f(da * h, d) * 360.f / M_PI;
2576 
2577  if (s->h_fov < 0.f)
2578  s->h_fov += 360.f;
2579  if (s->v_fov < 0.f)
2580  s->v_fov += 360.f;
2581 }
2582 
2583 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
2584 {
2585  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
2586  outw[0] = outw[3] = w;
2587  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
2588  outh[0] = outh[3] = h;
2589 }
2590 
2591 // Calculate remap data
2592 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
2593 {
2594  V360Context *s = ctx->priv;
2595 
2596  for (int p = 0; p < s->nb_allocated; p++) {
2597  const int width = s->pr_width[p];
2598  const int uv_linesize = s->uv_linesize[p];
2599  const int height = s->pr_height[p];
2600  const int in_width = s->inplanewidth[p];
2601  const int in_height = s->inplaneheight[p];
2602  const int slice_start = (height * jobnr ) / nb_jobs;
2603  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
2604  float du, dv;
2605  float vec[3];
2606  XYRemap rmap;
2607 
2608  for (int j = slice_start; j < slice_end; j++) {
2609  for (int i = 0; i < width; i++) {
2610  uint16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
2611  uint16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
2612  int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
2613 
2614  if (s->out_transpose)
2615  s->out_transform(s, j, i, height, width, vec);
2616  else
2617  s->out_transform(s, i, j, width, height, vec);
2618  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2619  rotate(s->rot_mat, vec);
2620  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2621  normalize_vector(vec);
2622  mirror(s->output_mirror_modifier, vec);
2623  if (s->in_transpose)
2624  s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
2625  else
2626  s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
2627  av_assert1(!isnan(du) && !isnan(dv));
2628  s->calculate_kernel(du, dv, &rmap, u, v, ker);
2629  }
2630  }
2631  }
2632 
2633  return 0;
2634 }
2635 
2636 static int config_output(AVFilterLink *outlink)
2637 {
2638  AVFilterContext *ctx = outlink->src;
2639  AVFilterLink *inlink = ctx->inputs[0];
2640  V360Context *s = ctx->priv;
2642  const int depth = desc->comp[0].depth;
2643  int sizeof_uv;
2644  int sizeof_ker;
2645  int err;
2646  int h, w;
2647  int in_offset_h, in_offset_w;
2648  int out_offset_h, out_offset_w;
2649  float hf, wf;
2650  int (*prepare_out)(AVFilterContext *ctx);
2651 
2652  s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
2653  s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
2654 
2655  switch (s->interp) {
2656  case NEAREST:
2658  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
2659  s->elements = 1;
2660  sizeof_uv = sizeof(uint16_t) * s->elements;
2661  sizeof_ker = 0;
2662  break;
2663  case BILINEAR:
2665  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
2666  s->elements = 2 * 2;
2667  sizeof_uv = sizeof(uint16_t) * s->elements;
2668  sizeof_ker = sizeof(uint16_t) * s->elements;
2669  break;
2670  case BICUBIC:
2672  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2673  s->elements = 4 * 4;
2674  sizeof_uv = sizeof(uint16_t) * s->elements;
2675  sizeof_ker = sizeof(uint16_t) * s->elements;
2676  break;
2677  case LANCZOS:
2679  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2680  s->elements = 4 * 4;
2681  sizeof_uv = sizeof(uint16_t) * s->elements;
2682  sizeof_ker = sizeof(uint16_t) * s->elements;
2683  break;
2684  default:
2685  av_assert0(0);
2686  }
2687 
2688  ff_v360_init(s, depth);
2689 
2690  for (int order = 0; order < NB_RORDERS; order++) {
2691  const char c = s->rorder[order];
2692  int rorder;
2693 
2694  if (c == '\0') {
2695  av_log(ctx, AV_LOG_ERROR,
2696  "Incomplete rorder option. Direction for all 3 rotation orders should be specified.\n");
2697  return AVERROR(EINVAL);
2698  }
2699 
2700  rorder = get_rorder(c);
2701  if (rorder == -1) {
2702  av_log(ctx, AV_LOG_ERROR,
2703  "Incorrect rotation order symbol '%c' in rorder option.\n", c);
2704  return AVERROR(EINVAL);
2705  }
2706 
2707  s->rotation_order[order] = rorder;
2708  }
2709 
2710  switch (s->in_stereo) {
2711  case STEREO_2D:
2712  w = inlink->w;
2713  h = inlink->h;
2714  in_offset_w = in_offset_h = 0;
2715  break;
2716  case STEREO_SBS:
2717  w = inlink->w / 2;
2718  h = inlink->h;
2719  in_offset_w = w;
2720  in_offset_h = 0;
2721  break;
2722  case STEREO_TB:
2723  w = inlink->w;
2724  h = inlink->h / 2;
2725  in_offset_w = 0;
2726  in_offset_h = h;
2727  break;
2728  default:
2729  av_assert0(0);
2730  }
2731 
2732  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
2733  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
2734 
2735  s->in_width = s->inplanewidth[0];
2736  s->in_height = s->inplaneheight[0];
2737 
2738  if (s->in_transpose)
2739  FFSWAP(int, s->in_width, s->in_height);
2740 
2741  switch (s->in) {
2742  case EQUIRECTANGULAR:
2744  err = 0;
2745  wf = w;
2746  hf = h;
2747  break;
2748  case CUBEMAP_3_2:
2750  err = prepare_cube_in(ctx);
2751  wf = w / 3.f * 4.f;
2752  hf = h;
2753  break;
2754  case CUBEMAP_1_6:
2756  err = prepare_cube_in(ctx);
2757  wf = w * 4.f;
2758  hf = h / 3.f;
2759  break;
2760  case CUBEMAP_6_1:
2762  err = prepare_cube_in(ctx);
2763  wf = w / 3.f * 2.f;
2764  hf = h * 2.f;
2765  break;
2766  case EQUIANGULAR:
2767  s->in_transform = xyz_to_eac;
2768  err = prepare_eac_in(ctx);
2769  wf = w;
2770  hf = h / 9.f * 8.f;
2771  break;
2772  case CYLINDRICAL:
2773  case PANNINI:
2774  case FISHEYE:
2775  case FLAT:
2776  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
2777  return AVERROR(EINVAL);
2778  case DUAL_FISHEYE:
2780  err = 0;
2781  wf = w;
2782  hf = h;
2783  break;
2784  case BARREL:
2786  err = 0;
2787  wf = w / 5.f * 4.f;
2788  hf = h;
2789  break;
2790  case STEREOGRAPHIC:
2792  err = 0;
2793  wf = w;
2794  hf = h / 2.f;
2795  break;
2796  case MERCATOR:
2798  err = 0;
2799  wf = w;
2800  hf = h / 2.f;
2801  break;
2802  case BALL:
2804  err = 0;
2805  wf = w;
2806  hf = h / 2.f;
2807  break;
2808  case HAMMER:
2810  err = 0;
2811  wf = w;
2812  hf = h;
2813  break;
2814  case SINUSOIDAL:
2816  err = 0;
2817  wf = w;
2818  hf = h;
2819  break;
2820  default:
2821  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
2822  return AVERROR_BUG;
2823  }
2824 
2825  if (err != 0) {
2826  return err;
2827  }
2828 
2829  switch (s->out) {
2830  case EQUIRECTANGULAR:
2832  prepare_out = NULL;
2833  w = roundf(wf);
2834  h = roundf(hf);
2835  break;
2836  case CUBEMAP_3_2:
2838  prepare_out = prepare_cube_out;
2839  w = roundf(wf / 4.f * 3.f);
2840  h = roundf(hf);
2841  break;
2842  case CUBEMAP_1_6:
2844  prepare_out = prepare_cube_out;
2845  w = roundf(wf / 4.f);
2846  h = roundf(hf * 3.f);
2847  break;
2848  case CUBEMAP_6_1:
2850  prepare_out = prepare_cube_out;
2851  w = roundf(wf / 2.f * 3.f);
2852  h = roundf(hf / 2.f);
2853  break;
2854  case EQUIANGULAR:
2856  prepare_out = prepare_eac_out;
2857  w = roundf(wf);
2858  h = roundf(hf / 8.f * 9.f);
2859  break;
2860  case FLAT:
2862  prepare_out = prepare_flat_out;
2863  w = roundf(wf);
2864  h = roundf(hf);
2865  break;
2866  case DUAL_FISHEYE:
2868  prepare_out = NULL;
2869  w = roundf(wf);
2870  h = roundf(hf);
2871  break;
2872  case BARREL:
2874  prepare_out = NULL;
2875  w = roundf(wf / 4.f * 5.f);
2876  h = roundf(hf);
2877  break;
2878  case STEREOGRAPHIC:
2880  prepare_out = prepare_stereographic_out;
2881  w = roundf(wf);
2882  h = roundf(hf * 2.f);
2883  break;
2884  case MERCATOR:
2886  prepare_out = NULL;
2887  w = roundf(wf);
2888  h = roundf(hf * 2.f);
2889  break;
2890  case BALL:
2892  prepare_out = NULL;
2893  w = roundf(wf);
2894  h = roundf(hf * 2.f);
2895  break;
2896  case HAMMER:
2898  prepare_out = NULL;
2899  w = roundf(wf);
2900  h = roundf(hf);
2901  break;
2902  case SINUSOIDAL:
2904  prepare_out = NULL;
2905  w = roundf(wf);
2906  h = roundf(hf);
2907  break;
2908  case FISHEYE:
2910  prepare_out = prepare_fisheye_out;
2911  w = roundf(wf * 0.5f);
2912  h = roundf(hf);
2913  break;
2914  case PANNINI:
2916  prepare_out = NULL;
2917  w = roundf(wf);
2918  h = roundf(hf);
2919  break;
2920  case CYLINDRICAL:
2922  prepare_out = prepare_cylindrical_out;
2923  w = roundf(wf);
2924  h = roundf(hf * 0.5f);
2925  break;
2926  default:
2927  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
2928  return AVERROR_BUG;
2929  }
2930 
2931  // Override resolution with user values if specified
2932  if (s->width > 0 && s->height > 0) {
2933  w = s->width;
2934  h = s->height;
2935  } else if (s->width > 0 || s->height > 0) {
2936  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
2937  return AVERROR(EINVAL);
2938  } else {
2939  if (s->out_transpose)
2940  FFSWAP(int, w, h);
2941 
2942  if (s->in_transpose)
2943  FFSWAP(int, w, h);
2944  }
2945 
2946  if (s->d_fov > 0.f)
2947  fov_from_dfov(s, w, h);
2948 
2949  if (prepare_out) {
2950  err = prepare_out(ctx);
2951  if (err != 0)
2952  return err;
2953  }
2954 
2955  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
2956 
2957  s->out_width = s->pr_width[0];
2958  s->out_height = s->pr_height[0];
2959 
2960  if (s->out_transpose)
2961  FFSWAP(int, s->out_width, s->out_height);
2962 
2963  switch (s->out_stereo) {
2964  case STEREO_2D:
2965  out_offset_w = out_offset_h = 0;
2966  break;
2967  case STEREO_SBS:
2968  out_offset_w = w;
2969  out_offset_h = 0;
2970  w *= 2;
2971  break;
2972  case STEREO_TB:
2973  out_offset_w = 0;
2974  out_offset_h = h;
2975  h *= 2;
2976  break;
2977  default:
2978  av_assert0(0);
2979  }
2980 
2981  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
2982  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
2983 
2984  for (int i = 0; i < 4; i++)
2985  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
2986 
2987  outlink->h = h;
2988  outlink->w = w;
2989 
2991 
2992  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
2993  s->nb_allocated = 1;
2994  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
2995  } else {
2996  s->nb_allocated = 2;
2997  s->map[0] = 0;
2998  s->map[1] = s->map[2] = 1;
2999  s->map[3] = 0;
3000  }
3001 
3002  for (int i = 0; i < s->nb_allocated; i++)
3003  allocate_plane(s, sizeof_uv, sizeof_ker, i);
3004 
3007 
3008  ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3009 
3010  return 0;
3011 }
3012 
3014 {
3015  AVFilterContext *ctx = inlink->dst;
3016  AVFilterLink *outlink = ctx->outputs[0];
3017  V360Context *s = ctx->priv;
3018  AVFrame *out;
3019  ThreadData td;
3020 
3021  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
3022  if (!out) {
3023  av_frame_free(&in);
3024  return AVERROR(ENOMEM);
3025  }
3026  av_frame_copy_props(out, in);
3027 
3028  td.in = in;
3029  td.out = out;
3030 
3031  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3032 
3033  av_frame_free(&in);
3034  return ff_filter_frame(outlink, out);
3035 }
3036 
3038 {
3039  V360Context *s = ctx->priv;
3040 
3041  for (int p = 0; p < s->nb_allocated; p++) {
3042  av_freep(&s->u[p]);
3043  av_freep(&s->v[p]);
3044  av_freep(&s->ker[p]);
3045  }
3046 }
3047 
3048 static const AVFilterPad inputs[] = {
3049  {
3050  .name = "default",
3051  .type = AVMEDIA_TYPE_VIDEO,
3052  .filter_frame = filter_frame,
3053  },
3054  { NULL }
3055 };
3056 
3057 static const AVFilterPad outputs[] = {
3058  {
3059  .name = "default",
3060  .type = AVMEDIA_TYPE_VIDEO,
3061  .config_props = config_output,
3062  },
3063  { NULL }
3064 };
3065 
3067  .name = "v360",
3068  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
3069  .priv_size = sizeof(V360Context),
3070  .uninit = uninit,
3072  .inputs = inputs,
3073  .outputs = outputs,
3074  .priv_class = &v360_class,
3076 };
Definition: v360.h:42
Axis +Z.
Definition: v360.h:76
#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:2470
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:2097
AVFrame * out
Definition: af_adeclick.c:494
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:389
uint16_t v[4][4]
Definition: v360.h:97
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:1071
#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:2409
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2522
int nb_planes
Definition: v360.h:148
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:127
AVOption.
Definition: opt.h:246
float input_mirror_modifier[2]
Definition: v360.h:134
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:424
int h_flip
Definition: v360.h:126
#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:422
#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:1437
#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
int elements
Definition: v360.h:150
static int prepare_eac_out(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap output format.
Definition: vf_v360.c:1890
int out_height
Definition: v360.h:138
static void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
Definition: vf_v360.c:685
static void lanczos_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for lanczos interpolation.
Definition: vf_v360.c:451
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:1921
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:659
int in
Definition: v360.h:103
#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:534
#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
static void hammer_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 hammer format.
Definition: vf_v360.c:1690
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:391
int inplanewidth[4]
Definition: v360.h:146
int out_offset_w[4]
Definition: v360.h:143
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:2539
static void bicubic_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bicubic interpolation.
Definition: vf_v360.c:398
char * in_frot
Definition: v360.h:108
int pr_height[4]
Definition: v360.h:140
static int config_output(AVFilterLink *outlink)
Definition: vf_v360.c:2636
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:1521
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:244
static void xyz_to_mercator(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 mercator format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1558
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:73
Definition: v360.h:47
void(* out_transform)(const struct V360Context *s, int i, int j, int width, int height, float *vec)
Definition: v360.h:160
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:123
#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:1075
float roll
Definition: v360.h:123
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:1337
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:123
#define av_cold
Definition: attributes.h:82
AVOptions.
int out_transpose
Definition: v360.h:127
int planeheight[4]
Definition: v360.h:145
static void bilinear_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bilinear interpolation.
Definition: vf_v360.c:355
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:2024
#define f(width, name)
Definition: cbs_vp9.c:255
Axis -Y.
Definition: v360.h:74
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:1195
char * out_frot
Definition: v360.h:109
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:1413
#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:91
#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:2287
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
Definition: vf_v360.c:2554
static int prepare_cylindrical_out(AVFilterContext *ctx)
Prepare data for processing cylindrical output format.
Definition: vf_v360.c:2194
static void mercator_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 mercator format.
Definition: vf_v360.c:1593
#define atanf(x)
Definition: libm.h:40
AVFilter ff_vf_v360
Definition: vf_v360.c:3066
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:2337
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range...
Definition: pixfmt.h:100
int height
Definition: v360.h:105
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
#define lrintf(x)
Definition: libm_mips.h:70
int width
Definition: v360.h:105
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:400
static void cube_to_xyz(const V360Context *s, float uf, float vf, int face, float *vec, float scalew, float scaleh)
Calculate 3D coordinates on sphere for corresponding cubemap position.
Definition: vf_v360.c:737
#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:801
#define FFALIGN(x, a)
Definition: macros.h:48
int rotation_order[3]
Definition: v360.h:116
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
#define expf(x)
Definition: libm.h:283
int fin_pad
Definition: v360.h:121
static void xyz_to_sinusoidal(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 sinusoidal format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1798
int in_stereo
Definition: v360.h:118
void ff_v360_init(V360Context *s, int depth)
Definition: vf_v360.c:306
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
void(* in_transform)(const struct V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Definition: v360.h:156
#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:569
#define td
Definition: regdef.h:70
Definition: v360.h:89
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition: pixdesc.h:101
Definition: v360.h:44
Definition: v360.h:46
static void mirror(const float *modifier, float *vec)
Definition: vf_v360.c:2547
#define S(s, c, i)
static void sinusoidal_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 sinusoidal format...
Definition: vf_v360.c:1767
static int prepare_fisheye_out(AVFilterContext *ctx)
Prepare data for processing fisheye output format.
Definition: vf_v360.c:2118
int out_offset_h[4]
Definition: v360.h:143
#define DEFINE_REMAP1_LINE(bits, div)
Definition: vf_v360.c:206
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:377
#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:1484
const char * r
Definition: vf_curves.c:114
static void xyz_to_hammer(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 hammer format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1729
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
uint16_t * u[2]
Definition: v360.h:152
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:431
const char * arg
Definition: jacosubdec.c:66
#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:1830
Definition: v360.h:84
static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
Definition: vf_v360.c:2583
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:390
Definition: v360.h:53
int in_offset_w[4]
Definition: v360.h:142
int out
Definition: v360.h:103
int out_stereo
Definition: v360.h:118
#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:55
int out_cubemap_face_rotation[6]
Definition: v360.h:115
int16_t * ker[2]
Definition: v360.h:153
int in_offset_h[4]
Definition: v360.h:142
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:385
unsigned map[4]
Definition: v360.h:154
int ih_flip
Definition: v360.h:125
int interp
Definition: v360.h:104
int in_height
Definition: v360.h:137
#define OFFSET(x)
Definition: vf_v360.c:53
Definition: v360.h:82
#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:784
#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
static void nearest_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Save nearest pixel coordinates for remapping.
Definition: vf_v360.c:335
float in_pad
Definition: v360.h:120
#define FFMIN(a, b)
Definition: common.h:96
AVFrame * d
float out_pad
Definition: v360.h:120
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:78
static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_v360.c:2592
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:428
Definition: v360.h:26
static int query_formats(AVFilterContext *ctx)
Definition: vf_v360.c:136
#define M_PI_2
Definition: mathematics.h:55
int inplaneheight[4]
Definition: v360.h:146
AVFrame * m
Definition: v360.h:54
AVFormatContext * ctx
Definition: movenc.c:48
Definition: v360.h:56
AVFILTER_DEFINE_CLASS(v360)
int in_cubemap_face_order[6]
Definition: v360.h:112
#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:879
static const AVFilterPad outputs[]
Definition: vf_v360.c:3057
int(* remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: v360.h:167
#define AV_PIX_FMT_YUV444P9
Definition: pixfmt.h:386
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:405
if(ret)
#define AV_PIX_FMT_YUV420P16
Definition: pixfmt.h:398
float v_fov
Definition: v360.h:129
float output_mirror_modifier[3]
Definition: v360.h:135
Definition: v360.h:90
#define sinf(x)
Definition: libm.h:419
#define DEFINE_REMAP_LINE(ws, bits, div)
Definition: vf_v360.c:275
#define TOP_RIGHT
Definition: movtextdec.c:49
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_v360.c:3037
#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:2077
int planewidth[4]
Definition: v360.h:145
int out_width
Definition: v360.h:138
Used for passing data between threads.
Definition: dsddec.c:64
int in_cubemap_face_rotation[6]
Definition: v360.h:114
static void cylindrical_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 cylindrical format...
Definition: vf_v360.c:2214
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:477
int nb_allocated
Definition: v360.h:149
#define AV_PIX_FMT_GRAY14
Definition: pixfmt.h:370
Definition: v360.h:81
static int prepare_cube_in(AVFilterContext *ctx)
Prepare data for processing cubemap input format.
Definition: vf_v360.c:558
int d_flip
Definition: v360.h:126
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_v360.c:3013
#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:2569
int out_cubemap_direction_order[6]
Definition: v360.h:113
#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:2246
static const AVFilterPad inputs[]
Definition: vf_v360.c:3048
Definition: v360.h:95
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:2487
#define isnan(x)
Definition: libm.h:340
AVFrame * b
int v_flip
Definition: v360.h:126
#define DEFINE_REMAP(ws, bits)
Generate remapping function with a given window size and pixel depth.
Definition: vf_v360.c:229
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:106
static int prepare_cube_out(AVFilterContext *ctx)
Prepare data for processing cubemap output format.
Definition: vf_v360.c:612
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
float flat_range[2]
Definition: v360.h:130
#define AV_PIX_FMT_YUV422P14
Definition: pixfmt.h:396
int pr_width[4]
Definition: v360.h:140
#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:1457
#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
float rot_mat[3][3]
Definition: v360.h:132
int uv_linesize[4]
Definition: v360.h:147
#define AV_PIX_FMT_YUV444P12
Definition: pixfmt.h:394
uint16_t * v[2]
Definition: v360.h:152
#define M_SQRT2
Definition: mathematics.h:61
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:1259
float h_fov
Definition: v360.h:129
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:716
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:2527
Definition: v360.h:83
int iv_flip
Definition: v360.h:125
int fout_pad
Definition: v360.h:121
static int get_rotation(char c)
Convert char to corresponding rotation angle.
Definition: vf_v360.c:515
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:67
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition: pixfmt.h:80
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition: pixfmt.h:73
Definition: v360.h:43
char * out_forder
Definition: v360.h:107
avfilter_execute_func * execute
Definition: internal.h:144
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2035
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:1226
static void xyz_to_ball(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 ball format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1623
Axis -Z.
Definition: v360.h:75
static int get_direction(char c)
Convert char to corresponding direction.
Definition: vf_v360.c:491
const AVPixFmtDescriptor * desc
Definition: vf_tonemap.c:196
A list of supported formats for one end of a filter link.
Definition: formats.h:64
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: v360.h:96
An instance of a filter.
Definition: avfilter.h:338
static void fisheye_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 fisheye format.
Definition: vf_v360.c:2138
#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:1108
#define av_freep(p)
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition: pixfmt.h:99
#define av_always_inline
Definition: attributes.h:39
#define M_PI
Definition: mathematics.h:52
Definition: v360.h:39
AVFrame * in
Definition: af_afftdn.c:1083
static void ball_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 ball format.
Definition: vf_v360.c:1659
#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:110
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:129
int in_width
Definition: v360.h:137
#define AV_PIX_FMT_YUV422P16
Definition: pixfmt.h:399
void(* calculate_kernel)(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Definition: v360.h:164
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:654
static void pannini_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 pannini format.
Definition: vf_v360.c:2165
#define AV_PIX_FMT_YUVA422P12
Definition: pixfmt.h:427
static uint8_t tmp[11]
Definition: aes_ctr.c:26