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  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
65  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
66  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
67  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
68  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
69  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" },
70  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
71  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" },
72  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" },
73  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" },
74  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" },
75  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" },
76  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "in" },
77  {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "in" },
78  { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
79  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
80  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
81  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
82  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
83  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
84  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
85  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
86  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
87  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
88  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
89  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
90  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" },
91  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
92  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" },
93  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" },
94  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" },
95  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" },
96  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" },
97  { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "out" },
98  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "out" },
99  {"perspective", "perspective", 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE}, 0, 0, FLAGS, "out" },
100  {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "out" },
101  { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" },
102  { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
103  { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
104  { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
105  { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
106  { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
107  { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
108  { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
109  { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
110  { "sp16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" },
111  { "spline16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" },
112  { "gauss", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" },
113  { "gaussian", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" },
114  { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
115  { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
116  { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
117  {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
118  { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
119  { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
120  { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
121  { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
122  {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
123  { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
124  { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"},
125  { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"},
126  { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"},
127  { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fin_pad"},
128  { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fout_pad"},
129  { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "yaw"},
130  { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "pitch"},
131  { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "roll"},
132  { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"},
133  { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"},
134  { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"},
135  { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"},
136  { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"},
137  { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"},
138  { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"},
139  { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "ih_flip"},
140  { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"},
141  { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"},
142  { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"},
143  { "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "ih_fov"},
144  { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "iv_fov"},
145  { "id_fov", "input diagonal field of view", OFFSET(id_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "id_fov"},
146  {"alpha_mask", "build mask in alpha plane", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "alpha"},
147  { NULL }
148 };
149 
151 
153 {
154  V360Context *s = ctx->priv;
155  static const enum AVPixelFormat pix_fmts[] = {
156  // YUVA444
160 
161  // YUVA422
165 
166  // YUVA420
169 
170  // YUVJ
174 
175  // YUV444
179 
180  // YUV440
183 
184  // YUV422
188 
189  // YUV420
193 
194  // YUV411
196 
197  // YUV410
199 
200  // GBR
204 
205  // GBRA
208 
209  // GRAY
213 
215  };
216  static const enum AVPixelFormat alpha_pix_fmts[] = {
227  AV_PIX_FMT_NONE
228  };
229 
230  AVFilterFormats *fmts_list = ff_make_format_list(s->alpha ? alpha_pix_fmts : pix_fmts);
231  if (!fmts_list)
232  return AVERROR(ENOMEM);
233  return ff_set_common_formats(ctx, fmts_list);
234 }
235 
236 #define DEFINE_REMAP1_LINE(bits, div) \
237 static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \
238  ptrdiff_t in_linesize, \
239  const int16_t *const u, const int16_t *const v, \
240  const int16_t *const ker) \
241 { \
242  const uint##bits##_t *const s = (const uint##bits##_t *const)src; \
243  uint##bits##_t *d = (uint##bits##_t *)dst; \
244  \
245  in_linesize /= div; \
246  \
247  for (int x = 0; x < width; x++) \
248  d[x] = s[v[x] * in_linesize + u[x]]; \
249 }
250 
251 DEFINE_REMAP1_LINE( 8, 1)
252 DEFINE_REMAP1_LINE(16, 2)
253 
254 /**
255  * Generate remapping function with a given window size and pixel depth.
256  *
257  * @param ws size of interpolation window
258  * @param bits number of bits per pixel
259  */
260 #define DEFINE_REMAP(ws, bits) \
261 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
262 { \
263  ThreadData *td = arg; \
264  const V360Context *s = ctx->priv; \
265  const AVFrame *in = td->in; \
266  AVFrame *out = td->out; \
267  \
268  for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
269  for (int plane = 0; plane < s->nb_planes; plane++) { \
270  const unsigned map = s->map[plane]; \
271  const int in_linesize = in->linesize[plane]; \
272  const int out_linesize = out->linesize[plane]; \
273  const int uv_linesize = s->uv_linesize[plane]; \
274  const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
275  const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
276  const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
277  const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
278  const uint8_t *const src = in->data[plane] + \
279  in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
280  uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
281  const uint8_t *mask = plane == 3 ? s->mask : NULL; \
282  const int width = s->pr_width[plane]; \
283  const int height = s->pr_height[plane]; \
284  \
285  const int slice_start = (height * jobnr ) / nb_jobs; \
286  const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
287  \
288  for (int y = slice_start; y < slice_end && !mask; y++) { \
289  const int16_t *const u = s->u[map] + y * uv_linesize * ws * ws; \
290  const int16_t *const v = s->v[map] + y * uv_linesize * ws * ws; \
291  const int16_t *const ker = s->ker[map] + y * uv_linesize * ws * ws; \
292  \
293  s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
294  } \
295  \
296  for (int y = slice_start; y < slice_end && mask; y++) { \
297  memcpy(dst + y * out_linesize, mask + y * width * (bits >> 3), width * (bits >> 3)); \
298  } \
299  } \
300  } \
301  \
302  return 0; \
303 }
304 
305 DEFINE_REMAP(1, 8)
306 DEFINE_REMAP(2, 8)
307 DEFINE_REMAP(4, 8)
308 DEFINE_REMAP(1, 16)
309 DEFINE_REMAP(2, 16)
310 DEFINE_REMAP(4, 16)
311 
312 #define DEFINE_REMAP_LINE(ws, bits, div) \
313 static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \
314  ptrdiff_t in_linesize, \
315  const int16_t *const u, const int16_t *const v, \
316  const int16_t *const ker) \
317 { \
318  const uint##bits##_t *const s = (const uint##bits##_t *const)src; \
319  uint##bits##_t *d = (uint##bits##_t *)dst; \
320  \
321  in_linesize /= div; \
322  \
323  for (int x = 0; x < width; x++) { \
324  const int16_t *const uu = u + x * ws * ws; \
325  const int16_t *const vv = v + x * ws * ws; \
326  const int16_t *const kker = ker + x * ws * ws; \
327  int tmp = 0; \
328  \
329  for (int i = 0; i < ws; i++) { \
330  for (int j = 0; j < ws; j++) { \
331  tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \
332  } \
333  } \
334  \
335  d[x] = av_clip_uint##bits(tmp >> 14); \
336  } \
337 }
338 
339 DEFINE_REMAP_LINE(2, 8, 1)
340 DEFINE_REMAP_LINE(4, 8, 1)
341 DEFINE_REMAP_LINE(2, 16, 2)
342 DEFINE_REMAP_LINE(4, 16, 2)
343 
344 void ff_v360_init(V360Context *s, int depth)
345 {
346  switch (s->interp) {
347  case NEAREST:
348  s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c;
349  break;
350  case BILINEAR:
351  s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c;
352  break;
353  case BICUBIC:
354  case LANCZOS:
355  case SPLINE16:
356  case GAUSSIAN:
357  s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c;
358  break;
359  }
360 
361  if (ARCH_X86)
362  ff_v360_init_x86(s, depth);
363 }
364 
365 /**
366  * Save nearest pixel coordinates for remapping.
367  *
368  * @param du horizontal relative coordinate
369  * @param dv vertical relative coordinate
370  * @param rmap calculated 4x4 window
371  * @param u u remap data
372  * @param v v remap data
373  * @param ker ker remap data
374  */
375 static void nearest_kernel(float du, float dv, const XYRemap *rmap,
376  int16_t *u, int16_t *v, int16_t *ker)
377 {
378  const int i = lrintf(dv) + 1;
379  const int j = lrintf(du) + 1;
380 
381  u[0] = rmap->u[i][j];
382  v[0] = rmap->v[i][j];
383 }
384 
385 /**
386  * Calculate kernel for bilinear interpolation.
387  *
388  * @param du horizontal relative coordinate
389  * @param dv vertical relative coordinate
390  * @param rmap calculated 4x4 window
391  * @param u u remap data
392  * @param v v remap data
393  * @param ker ker remap data
394  */
395 static void bilinear_kernel(float du, float dv, const XYRemap *rmap,
396  int16_t *u, int16_t *v, int16_t *ker)
397 {
398  for (int i = 0; i < 2; i++) {
399  for (int j = 0; j < 2; j++) {
400  u[i * 2 + j] = rmap->u[i + 1][j + 1];
401  v[i * 2 + j] = rmap->v[i + 1][j + 1];
402  }
403  }
404 
405  ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f);
406  ker[1] = lrintf( du * (1.f - dv) * 16385.f);
407  ker[2] = lrintf((1.f - du) * dv * 16385.f);
408  ker[3] = lrintf( du * dv * 16385.f);
409 }
410 
411 /**
412  * Calculate 1-dimensional cubic coefficients.
413  *
414  * @param t relative coordinate
415  * @param coeffs coefficients
416  */
417 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
418 {
419  const float tt = t * t;
420  const float ttt = t * t * t;
421 
422  coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f;
423  coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f;
424  coeffs[2] = t + tt / 2.f - ttt / 2.f;
425  coeffs[3] = - t / 6.f + ttt / 6.f;
426 }
427 
428 /**
429  * Calculate kernel for bicubic interpolation.
430  *
431  * @param du horizontal relative coordinate
432  * @param dv vertical relative coordinate
433  * @param rmap calculated 4x4 window
434  * @param u u remap data
435  * @param v v remap data
436  * @param ker ker remap data
437  */
438 static void bicubic_kernel(float du, float dv, const XYRemap *rmap,
439  int16_t *u, int16_t *v, int16_t *ker)
440 {
441  float du_coeffs[4];
442  float dv_coeffs[4];
443 
444  calculate_bicubic_coeffs(du, du_coeffs);
445  calculate_bicubic_coeffs(dv, dv_coeffs);
446 
447  for (int i = 0; i < 4; i++) {
448  for (int j = 0; j < 4; j++) {
449  u[i * 4 + j] = rmap->u[i][j];
450  v[i * 4 + j] = rmap->v[i][j];
451  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
452  }
453  }
454 }
455 
456 /**
457  * Calculate 1-dimensional lanczos coefficients.
458  *
459  * @param t relative coordinate
460  * @param coeffs coefficients
461  */
462 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
463 {
464  float sum = 0.f;
465 
466  for (int i = 0; i < 4; i++) {
467  const float x = M_PI * (t - i + 1);
468  if (x == 0.f) {
469  coeffs[i] = 1.f;
470  } else {
471  coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
472  }
473  sum += coeffs[i];
474  }
475 
476  for (int i = 0; i < 4; i++) {
477  coeffs[i] /= sum;
478  }
479 }
480 
481 /**
482  * Calculate kernel for lanczos interpolation.
483  *
484  * @param du horizontal relative coordinate
485  * @param dv vertical relative coordinate
486  * @param rmap calculated 4x4 window
487  * @param u u remap data
488  * @param v v remap data
489  * @param ker ker remap data
490  */
491 static void lanczos_kernel(float du, float dv, const XYRemap *rmap,
492  int16_t *u, int16_t *v, int16_t *ker)
493 {
494  float du_coeffs[4];
495  float dv_coeffs[4];
496 
497  calculate_lanczos_coeffs(du, du_coeffs);
498  calculate_lanczos_coeffs(dv, dv_coeffs);
499 
500  for (int i = 0; i < 4; i++) {
501  for (int j = 0; j < 4; j++) {
502  u[i * 4 + j] = rmap->u[i][j];
503  v[i * 4 + j] = rmap->v[i][j];
504  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
505  }
506  }
507 }
508 
509 /**
510  * Calculate 1-dimensional spline16 coefficients.
511  *
512  * @param t relative coordinate
513  * @param coeffs coefficients
514  */
515 static void calculate_spline16_coeffs(float t, float *coeffs)
516 {
517  coeffs[0] = ((-1.f / 3.f * t + 0.8f) * t - 7.f / 15.f) * t;
518  coeffs[1] = ((t - 9.f / 5.f) * t - 0.2f) * t + 1.f;
519  coeffs[2] = ((6.f / 5.f - t) * t + 0.8f) * t;
520  coeffs[3] = ((1.f / 3.f * t - 0.2f) * t - 2.f / 15.f) * t;
521 }
522 
523 /**
524  * Calculate kernel for spline16 interpolation.
525  *
526  * @param du horizontal relative coordinate
527  * @param dv vertical relative coordinate
528  * @param rmap calculated 4x4 window
529  * @param u u remap data
530  * @param v v remap data
531  * @param ker ker remap data
532  */
533 static void spline16_kernel(float du, float dv, const XYRemap *rmap,
534  int16_t *u, int16_t *v, int16_t *ker)
535 {
536  float du_coeffs[4];
537  float dv_coeffs[4];
538 
539  calculate_spline16_coeffs(du, du_coeffs);
540  calculate_spline16_coeffs(dv, dv_coeffs);
541 
542  for (int i = 0; i < 4; i++) {
543  for (int j = 0; j < 4; j++) {
544  u[i * 4 + j] = rmap->u[i][j];
545  v[i * 4 + j] = rmap->v[i][j];
546  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
547  }
548  }
549 }
550 
551 /**
552  * Calculate 1-dimensional gaussian coefficients.
553  *
554  * @param t relative coordinate
555  * @param coeffs coefficients
556  */
557 static void calculate_gaussian_coeffs(float t, float *coeffs)
558 {
559  float sum = 0.f;
560 
561  for (int i = 0; i < 4; i++) {
562  const float x = t - (i - 1);
563  if (x == 0.f) {
564  coeffs[i] = 1.f;
565  } else {
566  coeffs[i] = expf(-2.f * x * x) * expf(-x * x / 2.f);
567  }
568  sum += coeffs[i];
569  }
570 
571  for (int i = 0; i < 4; i++) {
572  coeffs[i] /= sum;
573  }
574 }
575 
576 /**
577  * Calculate kernel for gaussian interpolation.
578  *
579  * @param du horizontal relative coordinate
580  * @param dv vertical relative coordinate
581  * @param rmap calculated 4x4 window
582  * @param u u remap data
583  * @param v v remap data
584  * @param ker ker remap data
585  */
586 static void gaussian_kernel(float du, float dv, const XYRemap *rmap,
587  int16_t *u, int16_t *v, int16_t *ker)
588 {
589  float du_coeffs[4];
590  float dv_coeffs[4];
591 
592  calculate_gaussian_coeffs(du, du_coeffs);
593  calculate_gaussian_coeffs(dv, dv_coeffs);
594 
595  for (int i = 0; i < 4; i++) {
596  for (int j = 0; j < 4; j++) {
597  u[i * 4 + j] = rmap->u[i][j];
598  v[i * 4 + j] = rmap->v[i][j];
599  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
600  }
601  }
602 }
603 
604 /**
605  * Modulo operation with only positive remainders.
606  *
607  * @param a dividend
608  * @param b divisor
609  *
610  * @return positive remainder of (a / b)
611  */
612 static inline int mod(int a, int b)
613 {
614  const int res = a % b;
615  if (res < 0) {
616  return res + b;
617  } else {
618  return res;
619  }
620 }
621 
622 /**
623  * Convert char to corresponding direction.
624  * Used for cubemap options.
625  */
626 static int get_direction(char c)
627 {
628  switch (c) {
629  case 'r':
630  return RIGHT;
631  case 'l':
632  return LEFT;
633  case 'u':
634  return UP;
635  case 'd':
636  return DOWN;
637  case 'f':
638  return FRONT;
639  case 'b':
640  return BACK;
641  default:
642  return -1;
643  }
644 }
645 
646 /**
647  * Convert char to corresponding rotation angle.
648  * Used for cubemap options.
649  */
650 static int get_rotation(char c)
651 {
652  switch (c) {
653  case '0':
654  return ROT_0;
655  case '1':
656  return ROT_90;
657  case '2':
658  return ROT_180;
659  case '3':
660  return ROT_270;
661  default:
662  return -1;
663  }
664 }
665 
666 /**
667  * Convert char to corresponding rotation order.
668  */
669 static int get_rorder(char c)
670 {
671  switch (c) {
672  case 'Y':
673  case 'y':
674  return YAW;
675  case 'P':
676  case 'p':
677  return PITCH;
678  case 'R':
679  case 'r':
680  return ROLL;
681  default:
682  return -1;
683  }
684 }
685 
686 /**
687  * Prepare data for processing cubemap input format.
688  *
689  * @param ctx filter context
690  *
691  * @return error code
692  */
694 {
695  V360Context *s = ctx->priv;
696 
697  for (int face = 0; face < NB_FACES; face++) {
698  const char c = s->in_forder[face];
699  int direction;
700 
701  if (c == '\0') {
702  av_log(ctx, AV_LOG_ERROR,
703  "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
704  return AVERROR(EINVAL);
705  }
706 
707  direction = get_direction(c);
708  if (direction == -1) {
709  av_log(ctx, AV_LOG_ERROR,
710  "Incorrect direction symbol '%c' in in_forder option.\n", c);
711  return AVERROR(EINVAL);
712  }
713 
714  s->in_cubemap_face_order[direction] = face;
715  }
716 
717  for (int face = 0; face < NB_FACES; face++) {
718  const char c = s->in_frot[face];
719  int rotation;
720 
721  if (c == '\0') {
722  av_log(ctx, AV_LOG_ERROR,
723  "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
724  return AVERROR(EINVAL);
725  }
726 
727  rotation = get_rotation(c);
728  if (rotation == -1) {
729  av_log(ctx, AV_LOG_ERROR,
730  "Incorrect rotation symbol '%c' in in_frot option.\n", c);
731  return AVERROR(EINVAL);
732  }
733 
734  s->in_cubemap_face_rotation[face] = rotation;
735  }
736 
737  return 0;
738 }
739 
740 /**
741  * Prepare data for processing cubemap output format.
742  *
743  * @param ctx filter context
744  *
745  * @return error code
746  */
748 {
749  V360Context *s = ctx->priv;
750 
751  for (int face = 0; face < NB_FACES; face++) {
752  const char c = s->out_forder[face];
753  int direction;
754 
755  if (c == '\0') {
756  av_log(ctx, AV_LOG_ERROR,
757  "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
758  return AVERROR(EINVAL);
759  }
760 
761  direction = get_direction(c);
762  if (direction == -1) {
763  av_log(ctx, AV_LOG_ERROR,
764  "Incorrect direction symbol '%c' in out_forder option.\n", c);
765  return AVERROR(EINVAL);
766  }
767 
768  s->out_cubemap_direction_order[face] = direction;
769  }
770 
771  for (int face = 0; face < NB_FACES; face++) {
772  const char c = s->out_frot[face];
773  int rotation;
774 
775  if (c == '\0') {
776  av_log(ctx, AV_LOG_ERROR,
777  "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
778  return AVERROR(EINVAL);
779  }
780 
781  rotation = get_rotation(c);
782  if (rotation == -1) {
783  av_log(ctx, AV_LOG_ERROR,
784  "Incorrect rotation symbol '%c' in out_frot option.\n", c);
785  return AVERROR(EINVAL);
786  }
787 
788  s->out_cubemap_face_rotation[face] = rotation;
789  }
790 
791  return 0;
792 }
793 
794 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
795 {
796  float tmp;
797 
798  switch (rotation) {
799  case ROT_0:
800  break;
801  case ROT_90:
802  tmp = *uf;
803  *uf = -*vf;
804  *vf = tmp;
805  break;
806  case ROT_180:
807  *uf = -*uf;
808  *vf = -*vf;
809  break;
810  case ROT_270:
811  tmp = -*uf;
812  *uf = *vf;
813  *vf = tmp;
814  break;
815  default:
816  av_assert0(0);
817  }
818 }
819 
820 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
821 {
822  float tmp;
823 
824  switch (rotation) {
825  case ROT_0:
826  break;
827  case ROT_90:
828  tmp = -*uf;
829  *uf = *vf;
830  *vf = tmp;
831  break;
832  case ROT_180:
833  *uf = -*uf;
834  *vf = -*vf;
835  break;
836  case ROT_270:
837  tmp = *uf;
838  *uf = -*vf;
839  *vf = tmp;
840  break;
841  default:
842  av_assert0(0);
843  }
844 }
845 
846 /**
847  * Normalize vector.
848  *
849  * @param vec vector
850  */
851 static void normalize_vector(float *vec)
852 {
853  const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
854 
855  vec[0] /= norm;
856  vec[1] /= norm;
857  vec[2] /= norm;
858 }
859 
860 /**
861  * Calculate 3D coordinates on sphere for corresponding cubemap position.
862  * Common operation for every cubemap.
863  *
864  * @param s filter private context
865  * @param uf horizontal cubemap coordinate [0, 1)
866  * @param vf vertical cubemap coordinate [0, 1)
867  * @param face face of cubemap
868  * @param vec coordinates on sphere
869  * @param scalew scale for uf
870  * @param scaleh scale for vf
871  */
872 static void cube_to_xyz(const V360Context *s,
873  float uf, float vf, int face,
874  float *vec, float scalew, float scaleh)
875 {
876  const int direction = s->out_cubemap_direction_order[face];
877  float l_x, l_y, l_z;
878 
879  uf /= scalew;
880  vf /= scaleh;
881 
883 
884  switch (direction) {
885  case RIGHT:
886  l_x = 1.f;
887  l_y = -vf;
888  l_z = uf;
889  break;
890  case LEFT:
891  l_x = -1.f;
892  l_y = -vf;
893  l_z = -uf;
894  break;
895  case UP:
896  l_x = uf;
897  l_y = 1.f;
898  l_z = -vf;
899  break;
900  case DOWN:
901  l_x = uf;
902  l_y = -1.f;
903  l_z = vf;
904  break;
905  case FRONT:
906  l_x = uf;
907  l_y = -vf;
908  l_z = -1.f;
909  break;
910  case BACK:
911  l_x = -uf;
912  l_y = -vf;
913  l_z = 1.f;
914  break;
915  default:
916  av_assert0(0);
917  }
918 
919  vec[0] = l_x;
920  vec[1] = l_y;
921  vec[2] = l_z;
922 
923  normalize_vector(vec);
924 }
925 
926 /**
927  * Calculate cubemap position for corresponding 3D coordinates on sphere.
928  * Common operation for every cubemap.
929  *
930  * @param s filter private context
931  * @param vec coordinated on sphere
932  * @param uf horizontal cubemap coordinate [0, 1)
933  * @param vf vertical cubemap coordinate [0, 1)
934  * @param direction direction of view
935  */
936 static void xyz_to_cube(const V360Context *s,
937  const float *vec,
938  float *uf, float *vf, int *direction)
939 {
940  const float phi = atan2f(vec[0], -vec[2]);
941  const float theta = asinf(-vec[1]);
942  float phi_norm, theta_threshold;
943  int face;
944 
945  if (phi >= -M_PI_4 && phi < M_PI_4) {
946  *direction = FRONT;
947  phi_norm = phi;
948  } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
949  *direction = LEFT;
950  phi_norm = phi + M_PI_2;
951  } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
952  *direction = RIGHT;
953  phi_norm = phi - M_PI_2;
954  } else {
955  *direction = BACK;
956  phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
957  }
958 
959  theta_threshold = atanf(cosf(phi_norm));
960  if (theta > theta_threshold) {
961  *direction = DOWN;
962  } else if (theta < -theta_threshold) {
963  *direction = UP;
964  }
965 
966  switch (*direction) {
967  case RIGHT:
968  *uf = vec[2] / vec[0];
969  *vf = -vec[1] / vec[0];
970  break;
971  case LEFT:
972  *uf = vec[2] / vec[0];
973  *vf = vec[1] / vec[0];
974  break;
975  case UP:
976  *uf = vec[0] / vec[1];
977  *vf = -vec[2] / vec[1];
978  break;
979  case DOWN:
980  *uf = -vec[0] / vec[1];
981  *vf = -vec[2] / vec[1];
982  break;
983  case FRONT:
984  *uf = -vec[0] / vec[2];
985  *vf = vec[1] / vec[2];
986  break;
987  case BACK:
988  *uf = -vec[0] / vec[2];
989  *vf = -vec[1] / vec[2];
990  break;
991  default:
992  av_assert0(0);
993  }
994 
995  face = s->in_cubemap_face_order[*direction];
996  rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
997 
998  (*uf) *= s->input_mirror_modifier[0];
999  (*vf) *= s->input_mirror_modifier[1];
1000 }
1001 
1002 /**
1003  * Find position on another cube face in case of overflow/underflow.
1004  * Used for calculation of interpolation window.
1005  *
1006  * @param s filter private context
1007  * @param uf horizontal cubemap coordinate
1008  * @param vf vertical cubemap coordinate
1009  * @param direction direction of view
1010  * @param new_uf new horizontal cubemap coordinate
1011  * @param new_vf new vertical cubemap coordinate
1012  * @param face face position on cubemap
1013  */
1015  float uf, float vf, int direction,
1016  float *new_uf, float *new_vf, int *face)
1017 {
1018  /*
1019  * Cubemap orientation
1020  *
1021  * width
1022  * <------->
1023  * +-------+
1024  * | | U
1025  * | up | h ------->
1026  * +-------+-------+-------+-------+ ^ e |
1027  * | | | | | | i V |
1028  * | left | front | right | back | | g |
1029  * +-------+-------+-------+-------+ v h v
1030  * | | t
1031  * | down |
1032  * +-------+
1033  */
1034 
1035  *face = s->in_cubemap_face_order[direction];
1037 
1038  if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
1039  // There are no pixels to use in this case
1040  *new_uf = uf;
1041  *new_vf = vf;
1042  } else if (uf < -1.f) {
1043  uf += 2.f;
1044  switch (direction) {
1045  case RIGHT:
1046  direction = FRONT;
1047  *new_uf = uf;
1048  *new_vf = vf;
1049  break;
1050  case LEFT:
1051  direction = BACK;
1052  *new_uf = uf;
1053  *new_vf = vf;
1054  break;
1055  case UP:
1056  direction = LEFT;
1057  *new_uf = vf;
1058  *new_vf = -uf;
1059  break;
1060  case DOWN:
1061  direction = LEFT;
1062  *new_uf = -vf;
1063  *new_vf = uf;
1064  break;
1065  case FRONT:
1066  direction = LEFT;
1067  *new_uf = uf;
1068  *new_vf = vf;
1069  break;
1070  case BACK:
1071  direction = RIGHT;
1072  *new_uf = uf;
1073  *new_vf = vf;
1074  break;
1075  default:
1076  av_assert0(0);
1077  }
1078  } else if (uf >= 1.f) {
1079  uf -= 2.f;
1080  switch (direction) {
1081  case RIGHT:
1082  direction = BACK;
1083  *new_uf = uf;
1084  *new_vf = vf;
1085  break;
1086  case LEFT:
1087  direction = FRONT;
1088  *new_uf = uf;
1089  *new_vf = vf;
1090  break;
1091  case UP:
1092  direction = RIGHT;
1093  *new_uf = -vf;
1094  *new_vf = uf;
1095  break;
1096  case DOWN:
1097  direction = RIGHT;
1098  *new_uf = vf;
1099  *new_vf = -uf;
1100  break;
1101  case FRONT:
1102  direction = RIGHT;
1103  *new_uf = uf;
1104  *new_vf = vf;
1105  break;
1106  case BACK:
1107  direction = LEFT;
1108  *new_uf = uf;
1109  *new_vf = vf;
1110  break;
1111  default:
1112  av_assert0(0);
1113  }
1114  } else if (vf < -1.f) {
1115  vf += 2.f;
1116  switch (direction) {
1117  case RIGHT:
1118  direction = UP;
1119  *new_uf = vf;
1120  *new_vf = -uf;
1121  break;
1122  case LEFT:
1123  direction = UP;
1124  *new_uf = -vf;
1125  *new_vf = uf;
1126  break;
1127  case UP:
1128  direction = BACK;
1129  *new_uf = -uf;
1130  *new_vf = -vf;
1131  break;
1132  case DOWN:
1133  direction = FRONT;
1134  *new_uf = uf;
1135  *new_vf = vf;
1136  break;
1137  case FRONT:
1138  direction = UP;
1139  *new_uf = uf;
1140  *new_vf = vf;
1141  break;
1142  case BACK:
1143  direction = UP;
1144  *new_uf = -uf;
1145  *new_vf = -vf;
1146  break;
1147  default:
1148  av_assert0(0);
1149  }
1150  } else if (vf >= 1.f) {
1151  vf -= 2.f;
1152  switch (direction) {
1153  case RIGHT:
1154  direction = DOWN;
1155  *new_uf = -vf;
1156  *new_vf = uf;
1157  break;
1158  case LEFT:
1159  direction = DOWN;
1160  *new_uf = vf;
1161  *new_vf = -uf;
1162  break;
1163  case UP:
1164  direction = FRONT;
1165  *new_uf = uf;
1166  *new_vf = vf;
1167  break;
1168  case DOWN:
1169  direction = BACK;
1170  *new_uf = -uf;
1171  *new_vf = -vf;
1172  break;
1173  case FRONT:
1174  direction = DOWN;
1175  *new_uf = uf;
1176  *new_vf = vf;
1177  break;
1178  case BACK:
1179  direction = DOWN;
1180  *new_uf = -uf;
1181  *new_vf = -vf;
1182  break;
1183  default:
1184  av_assert0(0);
1185  }
1186  } else {
1187  // Inside cube face
1188  *new_uf = uf;
1189  *new_vf = vf;
1190  }
1191 
1192  *face = s->in_cubemap_face_order[direction];
1193  rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1194 }
1195 
1196 /**
1197  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1198  *
1199  * @param s filter private context
1200  * @param i horizontal position on frame [0, width)
1201  * @param j vertical position on frame [0, height)
1202  * @param width frame width
1203  * @param height frame height
1204  * @param vec coordinates on sphere
1205  */
1206 static int cube3x2_to_xyz(const V360Context *s,
1207  int i, int j, int width, int height,
1208  float *vec)
1209 {
1210  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 3.f) : 1.f - s->out_pad;
1211  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 2.f) : 1.f - s->out_pad;
1212 
1213  const float ew = width / 3.f;
1214  const float eh = height / 2.f;
1215 
1216  const int u_face = floorf(i / ew);
1217  const int v_face = floorf(j / eh);
1218  const int face = u_face + 3 * v_face;
1219 
1220  const int u_shift = ceilf(ew * u_face);
1221  const int v_shift = ceilf(eh * v_face);
1222  const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1223  const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1224 
1225  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1226  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1227 
1228  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1229 
1230  return 1;
1231 }
1232 
1233 /**
1234  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1235  *
1236  * @param s filter private context
1237  * @param vec coordinates on sphere
1238  * @param width frame width
1239  * @param height frame height
1240  * @param us horizontal coordinates for interpolation window
1241  * @param vs vertical coordinates for interpolation window
1242  * @param du horizontal relative coordinate
1243  * @param dv vertical relative coordinate
1244  */
1245 static int xyz_to_cube3x2(const V360Context *s,
1246  const float *vec, int width, int height,
1247  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1248 {
1249  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 3.f) : 1.f - s->in_pad;
1250  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 2.f) : 1.f - s->in_pad;
1251  const float ew = width / 3.f;
1252  const float eh = height / 2.f;
1253  float uf, vf;
1254  int ui, vi;
1255  int ewi, ehi;
1256  int direction, face;
1257  int u_face, v_face;
1258 
1259  xyz_to_cube(s, vec, &uf, &vf, &direction);
1260 
1261  uf *= scalew;
1262  vf *= scaleh;
1263 
1264  face = s->in_cubemap_face_order[direction];
1265  u_face = face % 3;
1266  v_face = face / 3;
1267  ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1268  ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1269 
1270  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1271  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1272 
1273  ui = floorf(uf);
1274  vi = floorf(vf);
1275 
1276  *du = uf - ui;
1277  *dv = vf - vi;
1278 
1279  for (int i = -1; i < 3; i++) {
1280  for (int j = -1; j < 3; j++) {
1281  int new_ui = ui + j;
1282  int new_vi = vi + i;
1283  int u_shift, v_shift;
1284  int new_ewi, new_ehi;
1285 
1286  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1287  face = s->in_cubemap_face_order[direction];
1288 
1289  u_face = face % 3;
1290  v_face = face / 3;
1291  u_shift = ceilf(ew * u_face);
1292  v_shift = ceilf(eh * v_face);
1293  } else {
1294  uf = 2.f * new_ui / ewi - 1.f;
1295  vf = 2.f * new_vi / ehi - 1.f;
1296 
1297  uf /= scalew;
1298  vf /= scaleh;
1299 
1300  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1301 
1302  uf *= scalew;
1303  vf *= scaleh;
1304 
1305  u_face = face % 3;
1306  v_face = face / 3;
1307  u_shift = ceilf(ew * u_face);
1308  v_shift = ceilf(eh * v_face);
1309  new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1310  new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1311 
1312  new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1313  new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1314  }
1315 
1316  us[i + 1][j + 1] = u_shift + new_ui;
1317  vs[i + 1][j + 1] = v_shift + new_vi;
1318  }
1319  }
1320 
1321  return 1;
1322 }
1323 
1324 /**
1325  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1326  *
1327  * @param s filter private context
1328  * @param i horizontal position on frame [0, width)
1329  * @param j vertical position on frame [0, height)
1330  * @param width frame width
1331  * @param height frame height
1332  * @param vec coordinates on sphere
1333  */
1334 static int cube1x6_to_xyz(const V360Context *s,
1335  int i, int j, int width, int height,
1336  float *vec)
1337 {
1338  const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_width : 1.f - s->out_pad;
1339  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 6.f) : 1.f - s->out_pad;
1340 
1341  const float ew = width;
1342  const float eh = height / 6.f;
1343 
1344  const int face = floorf(j / eh);
1345 
1346  const int v_shift = ceilf(eh * face);
1347  const int ehi = ceilf(eh * (face + 1)) - v_shift;
1348 
1349  const float uf = 2.f * (i + 0.5f) / ew - 1.f;
1350  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1351 
1352  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1353 
1354  return 1;
1355 }
1356 
1357 /**
1358  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1359  *
1360  * @param s filter private context
1361  * @param i horizontal position on frame [0, width)
1362  * @param j vertical position on frame [0, height)
1363  * @param width frame width
1364  * @param height frame height
1365  * @param vec coordinates on sphere
1366  */
1367 static int cube6x1_to_xyz(const V360Context *s,
1368  int i, int j, int width, int height,
1369  float *vec)
1370 {
1371  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 6.f) : 1.f - s->out_pad;
1372  const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_height : 1.f - s->out_pad;
1373 
1374  const float ew = width / 6.f;
1375  const float eh = height;
1376 
1377  const int face = floorf(i / ew);
1378 
1379  const int u_shift = ceilf(ew * face);
1380  const int ewi = ceilf(ew * (face + 1)) - u_shift;
1381 
1382  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1383  const float vf = 2.f * (j + 0.5f) / eh - 1.f;
1384 
1385  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1386 
1387  return 1;
1388 }
1389 
1390 /**
1391  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1392  *
1393  * @param s filter private context
1394  * @param vec coordinates on sphere
1395  * @param width frame width
1396  * @param height frame height
1397  * @param us horizontal coordinates for interpolation window
1398  * @param vs vertical coordinates for interpolation window
1399  * @param du horizontal relative coordinate
1400  * @param dv vertical relative coordinate
1401  */
1402 static int xyz_to_cube1x6(const V360Context *s,
1403  const float *vec, int width, int height,
1404  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1405 {
1406  const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_width : 1.f - s->in_pad;
1407  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 6.f) : 1.f - s->in_pad;
1408  const float eh = height / 6.f;
1409  const int ewi = width;
1410  float uf, vf;
1411  int ui, vi;
1412  int ehi;
1413  int direction, face;
1414 
1415  xyz_to_cube(s, vec, &uf, &vf, &direction);
1416 
1417  uf *= scalew;
1418  vf *= scaleh;
1419 
1420  face = s->in_cubemap_face_order[direction];
1421  ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1422 
1423  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1424  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1425 
1426  ui = floorf(uf);
1427  vi = floorf(vf);
1428 
1429  *du = uf - ui;
1430  *dv = vf - vi;
1431 
1432  for (int i = -1; i < 3; i++) {
1433  for (int j = -1; j < 3; j++) {
1434  int new_ui = ui + j;
1435  int new_vi = vi + i;
1436  int v_shift;
1437  int new_ehi;
1438 
1439  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1440  face = s->in_cubemap_face_order[direction];
1441 
1442  v_shift = ceilf(eh * face);
1443  } else {
1444  uf = 2.f * new_ui / ewi - 1.f;
1445  vf = 2.f * new_vi / ehi - 1.f;
1446 
1447  uf /= scalew;
1448  vf /= scaleh;
1449 
1450  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1451 
1452  uf *= scalew;
1453  vf *= scaleh;
1454 
1455  v_shift = ceilf(eh * face);
1456  new_ehi = ceilf(eh * (face + 1)) - v_shift;
1457 
1458  new_ui = av_clip(lrintf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1);
1459  new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1460  }
1461 
1462  us[i + 1][j + 1] = new_ui;
1463  vs[i + 1][j + 1] = v_shift + new_vi;
1464  }
1465  }
1466 
1467  return 1;
1468 }
1469 
1470 /**
1471  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1472  *
1473  * @param s filter private context
1474  * @param vec coordinates on sphere
1475  * @param width frame width
1476  * @param height frame height
1477  * @param us horizontal coordinates for interpolation window
1478  * @param vs vertical coordinates for interpolation window
1479  * @param du horizontal relative coordinate
1480  * @param dv vertical relative coordinate
1481  */
1482 static int xyz_to_cube6x1(const V360Context *s,
1483  const float *vec, int width, int height,
1484  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1485 {
1486  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 6.f) : 1.f - s->in_pad;
1487  const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_height : 1.f - s->in_pad;
1488  const float ew = width / 6.f;
1489  const int ehi = height;
1490  float uf, vf;
1491  int ui, vi;
1492  int ewi;
1493  int direction, face;
1494 
1495  xyz_to_cube(s, vec, &uf, &vf, &direction);
1496 
1497  uf *= scalew;
1498  vf *= scaleh;
1499 
1500  face = s->in_cubemap_face_order[direction];
1501  ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1502 
1503  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1504  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1505 
1506  ui = floorf(uf);
1507  vi = floorf(vf);
1508 
1509  *du = uf - ui;
1510  *dv = vf - vi;
1511 
1512  for (int i = -1; i < 3; i++) {
1513  for (int j = -1; j < 3; j++) {
1514  int new_ui = ui + j;
1515  int new_vi = vi + i;
1516  int u_shift;
1517  int new_ewi;
1518 
1519  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1520  face = s->in_cubemap_face_order[direction];
1521 
1522  u_shift = ceilf(ew * face);
1523  } else {
1524  uf = 2.f * new_ui / ewi - 1.f;
1525  vf = 2.f * new_vi / ehi - 1.f;
1526 
1527  uf /= scalew;
1528  vf /= scaleh;
1529 
1530  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1531 
1532  uf *= scalew;
1533  vf *= scaleh;
1534 
1535  u_shift = ceilf(ew * face);
1536  new_ewi = ceilf(ew * (face + 1)) - u_shift;
1537 
1538  new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1539  new_vi = av_clip(lrintf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1);
1540  }
1541 
1542  us[i + 1][j + 1] = u_shift + new_ui;
1543  vs[i + 1][j + 1] = new_vi;
1544  }
1545  }
1546 
1547  return 1;
1548 }
1549 
1550 /**
1551  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1552  *
1553  * @param s filter private context
1554  * @param i horizontal position on frame [0, width)
1555  * @param j vertical position on frame [0, height)
1556  * @param width frame width
1557  * @param height frame height
1558  * @param vec coordinates on sphere
1559  */
1560 static int equirect_to_xyz(const V360Context *s,
1561  int i, int j, int width, int height,
1562  float *vec)
1563 {
1564  const float phi = ((2.f * i) / width - 1.f) * M_PI;
1565  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1566 
1567  const float sin_phi = sinf(phi);
1568  const float cos_phi = cosf(phi);
1569  const float sin_theta = sinf(theta);
1570  const float cos_theta = cosf(theta);
1571 
1572  vec[0] = cos_theta * sin_phi;
1573  vec[1] = -sin_theta;
1574  vec[2] = -cos_theta * cos_phi;
1575 
1576  return 1;
1577 }
1578 
1579 /**
1580  * Prepare data for processing stereographic output format.
1581  *
1582  * @param ctx filter context
1583  *
1584  * @return error code
1585  */
1587 {
1588  V360Context *s = ctx->priv;
1589 
1590  s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
1591  s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
1592 
1593  return 0;
1594 }
1595 
1596 /**
1597  * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
1598  *
1599  * @param s filter private context
1600  * @param i horizontal position on frame [0, width)
1601  * @param j vertical position on frame [0, height)
1602  * @param width frame width
1603  * @param height frame height
1604  * @param vec coordinates on sphere
1605  */
1607  int i, int j, int width, int height,
1608  float *vec)
1609 {
1610  const float x = ((2.f * i) / width - 1.f) * s->flat_range[0];
1611  const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
1612  const float xy = x * x + y * y;
1613 
1614  vec[0] = 2.f * x / (1.f + xy);
1615  vec[1] = (-1.f + xy) / (1.f + xy);
1616  vec[2] = 2.f * y / (1.f + xy);
1617 
1618  normalize_vector(vec);
1619 
1620  return 1;
1621 }
1622 
1623 /**
1624  * Prepare data for processing stereographic input format.
1625  *
1626  * @param ctx filter context
1627  *
1628  * @return error code
1629  */
1631 {
1632  V360Context *s = ctx->priv;
1633 
1634  s->iflat_range[0] = tanf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f);
1635  s->iflat_range[1] = tanf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f);
1636 
1637  return 0;
1638 }
1639 
1640 /**
1641  * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
1642  *
1643  * @param s filter private context
1644  * @param vec coordinates on sphere
1645  * @param width frame width
1646  * @param height frame height
1647  * @param us horizontal coordinates for interpolation window
1648  * @param vs vertical coordinates for interpolation window
1649  * @param du horizontal relative coordinate
1650  * @param dv vertical relative coordinate
1651  */
1653  const float *vec, int width, int height,
1654  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1655 {
1656  const float x = vec[0] / (1.f - vec[1]) / s->iflat_range[0] * s->input_mirror_modifier[0];
1657  const float y = vec[2] / (1.f - vec[1]) / s->iflat_range[1] * s->input_mirror_modifier[1];
1658  float uf, vf;
1659  int visible, ui, vi;
1660 
1661  uf = (x + 1.f) * width / 2.f;
1662  vf = (y + 1.f) * height / 2.f;
1663  ui = floorf(uf);
1664  vi = floorf(vf);
1665 
1666  visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width;
1667 
1668  *du = visible ? uf - ui : 0.f;
1669  *dv = visible ? vf - vi : 0.f;
1670 
1671  for (int i = -1; i < 3; i++) {
1672  for (int j = -1; j < 3; j++) {
1673  us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
1674  vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
1675  }
1676  }
1677 
1678  return visible;
1679 }
1680 
1681 /**
1682  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
1683  *
1684  * @param s filter private context
1685  * @param vec coordinates on sphere
1686  * @param width frame width
1687  * @param height frame height
1688  * @param us horizontal coordinates for interpolation window
1689  * @param vs vertical coordinates for interpolation window
1690  * @param du horizontal relative coordinate
1691  * @param dv vertical relative coordinate
1692  */
1693 static int xyz_to_equirect(const V360Context *s,
1694  const float *vec, int width, int height,
1695  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1696 {
1697  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1698  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1699  float uf, vf;
1700  int ui, vi;
1701 
1702  uf = (phi / M_PI + 1.f) * width / 2.f;
1703  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1704  ui = floorf(uf);
1705  vi = floorf(vf);
1706 
1707  *du = uf - ui;
1708  *dv = vf - vi;
1709 
1710  for (int i = -1; i < 3; i++) {
1711  for (int j = -1; j < 3; j++) {
1712  us[i + 1][j + 1] = mod(ui + j, width);
1713  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1714  }
1715  }
1716 
1717  return 1;
1718 }
1719 
1720 /**
1721  * Prepare data for processing flat input format.
1722  *
1723  * @param ctx filter context
1724  *
1725  * @return error code
1726  */
1728 {
1729  V360Context *s = ctx->priv;
1730 
1731  s->iflat_range[0] = tanf(0.5f * s->ih_fov * M_PI / 180.f);
1732  s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
1733 
1734  return 0;
1735 }
1736 
1737 /**
1738  * Calculate frame position in flat format for corresponding 3D coordinates on sphere.
1739  *
1740  * @param s filter private context
1741  * @param vec coordinates on sphere
1742  * @param width frame width
1743  * @param height frame height
1744  * @param us horizontal coordinates for interpolation window
1745  * @param vs vertical coordinates for interpolation window
1746  * @param du horizontal relative coordinate
1747  * @param dv vertical relative coordinate
1748  */
1749 static int xyz_to_flat(const V360Context *s,
1750  const float *vec, int width, int height,
1751  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1752 {
1753  const float theta = acosf(vec[2]);
1754  const float r = tanf(theta);
1755  const float rr = fabsf(r) < 1e+6f ? r : hypotf(width, height);
1756  const float zf = -vec[2];
1757  const float h = hypotf(vec[0], vec[1]);
1758  const float c = h <= 1e-6f ? 1.f : rr / h;
1759  float uf = -vec[0] * c / s->iflat_range[0] * s->input_mirror_modifier[0];
1760  float vf = vec[1] * c / s->iflat_range[1] * s->input_mirror_modifier[1];
1761  int visible, ui, vi;
1762 
1763  uf = zf >= 0.f ? (uf + 1.f) * width / 2.f : 0.f;
1764  vf = zf >= 0.f ? (vf + 1.f) * height / 2.f : 0.f;
1765 
1766  ui = floorf(uf);
1767  vi = floorf(vf);
1768 
1769  visible = vi >= 0 && vi < height && ui >= 0 && ui < width && zf >= 0.f;
1770 
1771  *du = uf - ui;
1772  *dv = vf - vi;
1773 
1774  for (int i = -1; i < 3; i++) {
1775  for (int j = -1; j < 3; j++) {
1776  us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
1777  vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
1778  }
1779  }
1780 
1781  return visible;
1782 }
1783 
1784 /**
1785  * Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
1786  *
1787  * @param s filter private context
1788  * @param vec coordinates on sphere
1789  * @param width frame width
1790  * @param height frame height
1791  * @param us horizontal coordinates for interpolation window
1792  * @param vs vertical coordinates for interpolation window
1793  * @param du horizontal relative coordinate
1794  * @param dv vertical relative coordinate
1795  */
1796 static int xyz_to_mercator(const V360Context *s,
1797  const float *vec, int width, int height,
1798  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1799 {
1800  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1801  const float theta = -vec[1] * s->input_mirror_modifier[1];
1802  float uf, vf;
1803  int ui, vi;
1804 
1805  uf = (phi / M_PI + 1.f) * width / 2.f;
1806  vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f;
1807  ui = floorf(uf);
1808  vi = floorf(vf);
1809 
1810  *du = uf - ui;
1811  *dv = vf - vi;
1812 
1813  for (int i = -1; i < 3; i++) {
1814  for (int j = -1; j < 3; j++) {
1815  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1816  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1817  }
1818  }
1819 
1820  return 1;
1821 }
1822 
1823 /**
1824  * Calculate 3D coordinates on sphere for corresponding frame position in mercator format.
1825  *
1826  * @param s filter private context
1827  * @param i horizontal position on frame [0, width)
1828  * @param j vertical position on frame [0, height)
1829  * @param width frame width
1830  * @param height frame height
1831  * @param vec coordinates on sphere
1832  */
1833 static int mercator_to_xyz(const V360Context *s,
1834  int i, int j, int width, int height,
1835  float *vec)
1836 {
1837  const float phi = ((2.f * i) / width - 1.f) * M_PI + M_PI_2;
1838  const float y = ((2.f * j) / height - 1.f) * M_PI;
1839  const float div = expf(2.f * y) + 1.f;
1840 
1841  const float sin_phi = sinf(phi);
1842  const float cos_phi = cosf(phi);
1843  const float sin_theta = -2.f * expf(y) / div;
1844  const float cos_theta = -(expf(2.f * y) - 1.f) / div;
1845 
1846  vec[0] = sin_theta * cos_phi;
1847  vec[1] = cos_theta;
1848  vec[2] = sin_theta * sin_phi;
1849 
1850  return 1;
1851 }
1852 
1853 /**
1854  * Calculate frame position in ball format for corresponding 3D coordinates on sphere.
1855  *
1856  * @param s filter private context
1857  * @param vec coordinates on sphere
1858  * @param width frame width
1859  * @param height frame height
1860  * @param us horizontal coordinates for interpolation window
1861  * @param vs vertical coordinates for interpolation window
1862  * @param du horizontal relative coordinate
1863  * @param dv vertical relative coordinate
1864  */
1865 static int xyz_to_ball(const V360Context *s,
1866  const float *vec, int width, int height,
1867  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1868 {
1869  const float l = hypotf(vec[0], vec[1]);
1870  const float r = sqrtf(1.f + vec[2]) / M_SQRT2;
1871  float uf, vf;
1872  int ui, vi;
1873 
1874  uf = (1.f + r * vec[0] * s->input_mirror_modifier[0] / (l > 0.f ? l : 1.f)) * width * 0.5f;
1875  vf = (1.f - r * vec[1] * s->input_mirror_modifier[1] / (l > 0.f ? l : 1.f)) * height * 0.5f;
1876 
1877  ui = floorf(uf);
1878  vi = floorf(vf);
1879 
1880  *du = uf - ui;
1881  *dv = vf - vi;
1882 
1883  for (int i = -1; i < 3; i++) {
1884  for (int j = -1; j < 3; j++) {
1885  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1886  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1887  }
1888  }
1889 
1890  return 1;
1891 }
1892 
1893 /**
1894  * Calculate 3D coordinates on sphere for corresponding frame position in ball format.
1895  *
1896  * @param s filter private context
1897  * @param i horizontal position on frame [0, width)
1898  * @param j vertical position on frame [0, height)
1899  * @param width frame width
1900  * @param height frame height
1901  * @param vec coordinates on sphere
1902  */
1903 static int ball_to_xyz(const V360Context *s,
1904  int i, int j, int width, int height,
1905  float *vec)
1906 {
1907  const float x = (2.f * i) / width - 1.f;
1908  const float y = (2.f * j) / height - 1.f;
1909  const float l = hypotf(x, y);
1910 
1911  if (l <= 1.f) {
1912  const float z = 2.f * l * sqrtf(1.f - l * l);
1913 
1914  vec[0] = z * x / (l > 0.f ? l : 1.f);
1915  vec[1] = -z * y / (l > 0.f ? l : 1.f);
1916  vec[2] = -1.f + 2.f * l * l;
1917  } else {
1918  vec[0] = 0.f;
1919  vec[1] = -1.f;
1920  vec[2] = 0.f;
1921  return 0;
1922  }
1923 
1924  return 1;
1925 }
1926 
1927 /**
1928  * Calculate 3D coordinates on sphere for corresponding frame position in hammer format.
1929  *
1930  * @param s filter private context
1931  * @param i horizontal position on frame [0, width)
1932  * @param j vertical position on frame [0, height)
1933  * @param width frame width
1934  * @param height frame height
1935  * @param vec coordinates on sphere
1936  */
1937 static int hammer_to_xyz(const V360Context *s,
1938  int i, int j, int width, int height,
1939  float *vec)
1940 {
1941  const float x = ((2.f * i) / width - 1.f);
1942  const float y = ((2.f * j) / height - 1.f);
1943 
1944  const float xx = x * x;
1945  const float yy = y * y;
1946 
1947  const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f);
1948 
1949  const float a = M_SQRT2 * x * z;
1950  const float b = 2.f * z * z - 1.f;
1951 
1952  const float aa = a * a;
1953  const float bb = b * b;
1954 
1955  const float w = sqrtf(1.f - 2.f * yy * z * z);
1956 
1957  vec[0] = w * 2.f * a * b / (aa + bb);
1958  vec[1] = -M_SQRT2 * y * z;
1959  vec[2] = -w * (bb - aa) / (aa + bb);
1960 
1961  normalize_vector(vec);
1962 
1963  return 1;
1964 }
1965 
1966 /**
1967  * Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
1968  *
1969  * @param s filter private context
1970  * @param vec coordinates on sphere
1971  * @param width frame width
1972  * @param height frame height
1973  * @param us horizontal coordinates for interpolation window
1974  * @param vs vertical coordinates for interpolation window
1975  * @param du horizontal relative coordinate
1976  * @param dv vertical relative coordinate
1977  */
1978 static int xyz_to_hammer(const V360Context *s,
1979  const float *vec, int width, int height,
1980  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1981 {
1982  const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1983 
1984  const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f));
1985  const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z;
1986  const float y = -vec[1] / z * s->input_mirror_modifier[1];
1987  float uf, vf;
1988  int ui, vi;
1989 
1990  uf = (x + 1.f) * width / 2.f;
1991  vf = (y + 1.f) * height / 2.f;
1992  ui = floorf(uf);
1993  vi = floorf(vf);
1994 
1995  *du = uf - ui;
1996  *dv = vf - vi;
1997 
1998  for (int i = -1; i < 3; i++) {
1999  for (int j = -1; j < 3; j++) {
2000  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
2001  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2002  }
2003  }
2004 
2005  return 1;
2006 }
2007 
2008 /**
2009  * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format.
2010  *
2011  * @param s filter private context
2012  * @param i horizontal position on frame [0, width)
2013  * @param j vertical position on frame [0, height)
2014  * @param width frame width
2015  * @param height frame height
2016  * @param vec coordinates on sphere
2017  */
2018 static int sinusoidal_to_xyz(const V360Context *s,
2019  int i, int j, int width, int height,
2020  float *vec)
2021 {
2022  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
2023  const float phi = ((2.f * i) / width - 1.f) * M_PI / cosf(theta);
2024 
2025  const float sin_phi = sinf(phi);
2026  const float cos_phi = cosf(phi);
2027  const float sin_theta = sinf(theta);
2028  const float cos_theta = cosf(theta);
2029 
2030  vec[0] = cos_theta * sin_phi;
2031  vec[1] = -sin_theta;
2032  vec[2] = -cos_theta * cos_phi;
2033 
2034  normalize_vector(vec);
2035 
2036  return 1;
2037 }
2038 
2039 /**
2040  * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere.
2041  *
2042  * @param s filter private context
2043  * @param vec coordinates on sphere
2044  * @param width frame width
2045  * @param height frame height
2046  * @param us horizontal coordinates for interpolation window
2047  * @param vs vertical coordinates for interpolation window
2048  * @param du horizontal relative coordinate
2049  * @param dv vertical relative coordinate
2050  */
2051 static int xyz_to_sinusoidal(const V360Context *s,
2052  const float *vec, int width, int height,
2053  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2054 {
2055  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
2056  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] * cosf(theta);
2057  float uf, vf;
2058  int ui, vi;
2059 
2060  uf = (phi / M_PI + 1.f) * width / 2.f;
2061  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
2062  ui = floorf(uf);
2063  vi = floorf(vf);
2064 
2065  *du = uf - ui;
2066  *dv = vf - vi;
2067 
2068  for (int i = -1; i < 3; i++) {
2069  for (int j = -1; j < 3; j++) {
2070  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
2071  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2072  }
2073  }
2074 
2075  return 1;
2076 }
2077 
2078 /**
2079  * Prepare data for processing equi-angular cubemap input format.
2080  *
2081  * @param ctx filter context
2082  *
2083  * @return error code
2084  */
2086 {
2087  V360Context *s = ctx->priv;
2088 
2089  if (s->ih_flip && s->iv_flip) {
2096  } else if (s->ih_flip) {
2103  } else if (s->iv_flip) {
2110  } else {
2117  }
2118 
2119  if (s->iv_flip) {
2126  } else {
2133  }
2134 
2135  return 0;
2136 }
2137 
2138 /**
2139  * Prepare data for processing equi-angular cubemap output format.
2140  *
2141  * @param ctx filter context
2142  *
2143  * @return error code
2144  */
2146 {
2147  V360Context *s = ctx->priv;
2148 
2155 
2162 
2163  return 0;
2164 }
2165 
2166 /**
2167  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
2168  *
2169  * @param s filter private context
2170  * @param i horizontal position on frame [0, width)
2171  * @param j vertical position on frame [0, height)
2172  * @param width frame width
2173  * @param height frame height
2174  * @param vec coordinates on sphere
2175  */
2176 static int eac_to_xyz(const V360Context *s,
2177  int i, int j, int width, int height,
2178  float *vec)
2179 {
2180  const float pixel_pad = 2;
2181  const float u_pad = pixel_pad / width;
2182  const float v_pad = pixel_pad / height;
2183 
2184  int u_face, v_face, face;
2185 
2186  float l_x, l_y, l_z;
2187 
2188  float uf = (i + 0.5f) / width;
2189  float vf = (j + 0.5f) / height;
2190 
2191  // EAC has 2-pixel padding on faces except between faces on the same row
2192  // Padding pixels seems not to be stretched with tangent as regular pixels
2193  // Formulas below approximate original padding as close as I could get experimentally
2194 
2195  // Horizontal padding
2196  uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
2197  if (uf < 0.f) {
2198  u_face = 0;
2199  uf -= 0.5f;
2200  } else if (uf >= 3.f) {
2201  u_face = 2;
2202  uf -= 2.5f;
2203  } else {
2204  u_face = floorf(uf);
2205  uf = fmodf(uf, 1.f) - 0.5f;
2206  }
2207 
2208  // Vertical padding
2209  v_face = floorf(vf * 2.f);
2210  vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
2211 
2212  if (uf >= -0.5f && uf < 0.5f) {
2213  uf = tanf(M_PI_2 * uf);
2214  } else {
2215  uf = 2.f * uf;
2216  }
2217  if (vf >= -0.5f && vf < 0.5f) {
2218  vf = tanf(M_PI_2 * vf);
2219  } else {
2220  vf = 2.f * vf;
2221  }
2222 
2223  face = u_face + 3 * v_face;
2224 
2225  switch (face) {
2226  case TOP_LEFT:
2227  l_x = -1.f;
2228  l_y = -vf;
2229  l_z = -uf;
2230  break;
2231  case TOP_MIDDLE:
2232  l_x = uf;
2233  l_y = -vf;
2234  l_z = -1.f;
2235  break;
2236  case TOP_RIGHT:
2237  l_x = 1.f;
2238  l_y = -vf;
2239  l_z = uf;
2240  break;
2241  case BOTTOM_LEFT:
2242  l_x = -vf;
2243  l_y = -1.f;
2244  l_z = uf;
2245  break;
2246  case BOTTOM_MIDDLE:
2247  l_x = -vf;
2248  l_y = uf;
2249  l_z = 1.f;
2250  break;
2251  case BOTTOM_RIGHT:
2252  l_x = -vf;
2253  l_y = 1.f;
2254  l_z = -uf;
2255  break;
2256  default:
2257  av_assert0(0);
2258  }
2259 
2260  vec[0] = l_x;
2261  vec[1] = l_y;
2262  vec[2] = l_z;
2263 
2264  normalize_vector(vec);
2265 
2266  return 1;
2267 }
2268 
2269 /**
2270  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
2271  *
2272  * @param s filter private context
2273  * @param vec coordinates on sphere
2274  * @param width frame width
2275  * @param height frame height
2276  * @param us horizontal coordinates for interpolation window
2277  * @param vs vertical coordinates for interpolation window
2278  * @param du horizontal relative coordinate
2279  * @param dv vertical relative coordinate
2280  */
2281 static int xyz_to_eac(const V360Context *s,
2282  const float *vec, int width, int height,
2283  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2284 {
2285  const float pixel_pad = 2;
2286  const float u_pad = pixel_pad / width;
2287  const float v_pad = pixel_pad / height;
2288 
2289  float uf, vf;
2290  int ui, vi;
2291  int direction, face;
2292  int u_face, v_face;
2293 
2294  xyz_to_cube(s, vec, &uf, &vf, &direction);
2295 
2296  face = s->in_cubemap_face_order[direction];
2297  u_face = face % 3;
2298  v_face = face / 3;
2299 
2300  uf = M_2_PI * atanf(uf) + 0.5f;
2301  vf = M_2_PI * atanf(vf) + 0.5f;
2302 
2303  // These formulas are inversed from eac_to_xyz ones
2304  uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
2305  vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
2306 
2307  uf *= width;
2308  vf *= height;
2309 
2310  uf -= 0.5f;
2311  vf -= 0.5f;
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(ui + j, 0, width - 1);
2322  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2323  }
2324  }
2325 
2326  return 1;
2327 }
2328 
2329 /**
2330  * Prepare data for processing flat output format.
2331  *
2332  * @param ctx filter context
2333  *
2334  * @return error code
2335  */
2337 {
2338  V360Context *s = ctx->priv;
2339 
2340  s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
2341  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2342 
2343  return 0;
2344 }
2345 
2346 /**
2347  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
2348  *
2349  * @param s filter private context
2350  * @param i horizontal position on frame [0, width)
2351  * @param j vertical position on frame [0, height)
2352  * @param width frame width
2353  * @param height frame height
2354  * @param vec coordinates on sphere
2355  */
2356 static int flat_to_xyz(const V360Context *s,
2357  int i, int j, int width, int height,
2358  float *vec)
2359 {
2360  const float l_x = s->flat_range[0] * (2.f * i / width - 1.f);
2361  const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
2362 
2363  vec[0] = l_x;
2364  vec[1] = l_y;
2365  vec[2] = -1.f;
2366 
2367  normalize_vector(vec);
2368 
2369  return 1;
2370 }
2371 
2372 /**
2373  * Prepare data for processing fisheye output format.
2374  *
2375  * @param ctx filter context
2376  *
2377  * @return error code
2378  */
2380 {
2381  V360Context *s = ctx->priv;
2382 
2383  s->flat_range[0] = s->h_fov / 180.f;
2384  s->flat_range[1] = s->v_fov / 180.f;
2385 
2386  return 0;
2387 }
2388 
2389 /**
2390  * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format.
2391  *
2392  * @param s filter private context
2393  * @param i horizontal position on frame [0, width)
2394  * @param j vertical position on frame [0, height)
2395  * @param width frame width
2396  * @param height frame height
2397  * @param vec coordinates on sphere
2398  */
2399 static int fisheye_to_xyz(const V360Context *s,
2400  int i, int j, int width, int height,
2401  float *vec)
2402 {
2403  const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
2404  const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
2405 
2406  const float phi = -atan2f(vf, uf);
2407  const float theta = -M_PI_2 * (1.f - hypotf(uf, vf));
2408 
2409  vec[0] = cosf(theta) * cosf(phi);
2410  vec[1] = cosf(theta) * sinf(phi);
2411  vec[2] = sinf(theta);
2412 
2413  normalize_vector(vec);
2414 
2415  return 1;
2416 }
2417 
2418 /**
2419  * Prepare data for processing fisheye input format.
2420  *
2421  * @param ctx filter context
2422  *
2423  * @return error code
2424  */
2426 {
2427  V360Context *s = ctx->priv;
2428 
2429  s->iflat_range[0] = s->ih_fov / 180.f;
2430  s->iflat_range[1] = s->iv_fov / 180.f;
2431 
2432  return 0;
2433 }
2434 
2435 /**
2436  * Calculate frame position in fisheye format for corresponding 3D coordinates on sphere.
2437  *
2438  * @param s filter private context
2439  * @param vec coordinates on sphere
2440  * @param width frame width
2441  * @param height frame height
2442  * @param us horizontal coordinates for interpolation window
2443  * @param vs vertical coordinates for interpolation window
2444  * @param du horizontal relative coordinate
2445  * @param dv vertical relative coordinate
2446  */
2447 static int xyz_to_fisheye(const V360Context *s,
2448  const float *vec, int width, int height,
2449  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2450 {
2451  const float phi = -atan2f(hypotf(vec[0], vec[1]), -vec[2]) / M_PI;
2452  const float theta = -atan2f(vec[0], vec[1]);
2453 
2454  float uf = sinf(theta) * phi * s->input_mirror_modifier[0] / s->iflat_range[0];
2455  float vf = cosf(theta) * phi * s->input_mirror_modifier[1] / s->iflat_range[1];
2456 
2457  const int visible = hypotf(uf, vf) <= 0.5f;
2458  int ui, vi;
2459 
2460  uf = (uf + 0.5f) * width;
2461  vf = (vf + 0.5f) * height;
2462 
2463  ui = floorf(uf);
2464  vi = floorf(vf);
2465 
2466  *du = visible ? uf - ui : 0.f;
2467  *dv = visible ? vf - vi : 0.f;
2468 
2469  for (int i = -1; i < 3; i++) {
2470  for (int j = -1; j < 3; j++) {
2471  us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
2472  vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
2473  }
2474  }
2475 
2476  return visible;
2477 }
2478 
2479 /**
2480  * Calculate 3D coordinates on sphere for corresponding frame position in pannini format.
2481  *
2482  * @param s filter private context
2483  * @param i horizontal position on frame [0, width)
2484  * @param j vertical position on frame [0, height)
2485  * @param width frame width
2486  * @param height frame height
2487  * @param vec coordinates on sphere
2488  */
2489 static int pannini_to_xyz(const V360Context *s,
2490  int i, int j, int width, int height,
2491  float *vec)
2492 {
2493  const float uf = ((2.f * i) / width - 1.f);
2494  const float vf = ((2.f * j) / height - 1.f);
2495 
2496  const float d = s->h_fov;
2497  const float k = uf * uf / ((d + 1.f) * (d + 1.f));
2498  const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f);
2499  const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
2500  const float S = (d + 1.f) / (d + clon);
2501  const float lon = -(M_PI + atan2f(uf, S * clon));
2502  const float lat = -atan2f(vf, S);
2503 
2504  vec[0] = sinf(lon) * cosf(lat);
2505  vec[1] = sinf(lat);
2506  vec[2] = cosf(lon) * cosf(lat);
2507 
2508  normalize_vector(vec);
2509 
2510  return 1;
2511 }
2512 
2513 /**
2514  * Prepare data for processing cylindrical output format.
2515  *
2516  * @param ctx filter context
2517  *
2518  * @return error code
2519  */
2521 {
2522  V360Context *s = ctx->priv;
2523 
2524  s->flat_range[0] = M_PI * s->h_fov / 360.f;
2525  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2526 
2527  return 0;
2528 }
2529 
2530 /**
2531  * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format.
2532  *
2533  * @param s filter private context
2534  * @param i horizontal position on frame [0, width)
2535  * @param j vertical position on frame [0, height)
2536  * @param width frame width
2537  * @param height frame height
2538  * @param vec coordinates on sphere
2539  */
2541  int i, int j, int width, int height,
2542  float *vec)
2543 {
2544  const float uf = s->flat_range[0] * ((2.f * i) / width - 1.f);
2545  const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
2546 
2547  const float phi = uf;
2548  const float theta = atanf(vf);
2549 
2550  const float sin_phi = sinf(phi);
2551  const float cos_phi = cosf(phi);
2552  const float sin_theta = sinf(theta);
2553  const float cos_theta = cosf(theta);
2554 
2555  vec[0] = cos_theta * sin_phi;
2556  vec[1] = -sin_theta;
2557  vec[2] = -cos_theta * cos_phi;
2558 
2559  normalize_vector(vec);
2560 
2561  return 1;
2562 }
2563 
2564 /**
2565  * Prepare data for processing cylindrical input format.
2566  *
2567  * @param ctx filter context
2568  *
2569  * @return error code
2570  */
2572 {
2573  V360Context *s = ctx->priv;
2574 
2575  s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
2576  s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
2577 
2578  return 0;
2579 }
2580 
2581 /**
2582  * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere.
2583  *
2584  * @param s filter private context
2585  * @param vec coordinates on sphere
2586  * @param width frame width
2587  * @param height frame height
2588  * @param us horizontal coordinates for interpolation window
2589  * @param vs vertical coordinates for interpolation window
2590  * @param du horizontal relative coordinate
2591  * @param dv vertical relative coordinate
2592  */
2594  const float *vec, int width, int height,
2595  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2596 {
2597  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0];
2598  const float theta = atan2f(-vec[1], hypotf(vec[0], vec[2])) * s->input_mirror_modifier[1] / s->iflat_range[1];
2599  int visible, ui, vi;
2600  float uf, vf;
2601 
2602  uf = (phi + 1.f) * (width - 1) / 2.f;
2603  vf = (tanf(theta) + 1.f) * height / 2.f;
2604  ui = floorf(uf);
2605  vi = floorf(vf);
2606 
2607  visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
2608  theta <= M_PI * s->iv_fov / 180.f &&
2609  theta >= -M_PI * s->iv_fov / 180.f;
2610 
2611  *du = uf - ui;
2612  *dv = vf - vi;
2613 
2614  for (int i = -1; i < 3; i++) {
2615  for (int j = -1; j < 3; j++) {
2616  us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width - 1) : 0;
2617  vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
2618  }
2619  }
2620 
2621  return visible;
2622 }
2623 
2624 /**
2625  * Calculate 3D coordinates on sphere for corresponding frame position in perspective format.
2626  *
2627  * @param s filter private context
2628  * @param i horizontal position on frame [0, width)
2629  * @param j vertical position on frame [0, height)
2630  * @param width frame width
2631  * @param height frame height
2632  * @param vec coordinates on sphere
2633  */
2635  int i, int j, int width, int height,
2636  float *vec)
2637 {
2638  const float uf = ((2.f * i) / width - 1.f);
2639  const float vf = ((2.f * j) / height - 1.f);
2640  const float rh = hypotf(uf, vf);
2641  const float sinzz = 1.f - rh * rh;
2642  const float h = 1.f + s->v_fov;
2643  const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h);
2644  const float sinz2 = sinz * sinz;
2645 
2646  if (sinz2 <= 1.f) {
2647  const float cosz = sqrtf(1.f - sinz2);
2648 
2649  const float theta = asinf(cosz);
2650  const float phi = atan2f(uf, vf);
2651 
2652  const float sin_phi = sinf(phi);
2653  const float cos_phi = cosf(phi);
2654  const float sin_theta = sinf(theta);
2655  const float cos_theta = cosf(theta);
2656 
2657  vec[0] = cos_theta * sin_phi;
2658  vec[1] = sin_theta;
2659  vec[2] = -cos_theta * cos_phi;
2660  } else {
2661  vec[0] = 0.f;
2662  vec[1] = -1.f;
2663  vec[2] = 0.f;
2664  return 0;
2665  }
2666 
2667  normalize_vector(vec);
2668  return 1;
2669 }
2670 
2671 /**
2672  * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format.
2673  *
2674  * @param s filter private context
2675  * @param i horizontal position on frame [0, width)
2676  * @param j vertical position on frame [0, height)
2677  * @param width frame width
2678  * @param height frame height
2679  * @param vec coordinates on sphere
2680  */
2682  int i, int j, int width, int height,
2683  float *vec)
2684 {
2685  const float uf = (float)i / width;
2686  const float vf = (float)j / height;
2687 
2688  vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
2689  vec[1] = 1.f - vf * 2.f;
2690  vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
2691 
2692  normalize_vector(vec);
2693 
2694  return 1;
2695 }
2696 
2697 /**
2698  * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere.
2699  *
2700  * @param s filter private context
2701  * @param vec coordinates on sphere
2702  * @param width frame width
2703  * @param height frame height
2704  * @param us horizontal coordinates for interpolation window
2705  * @param vs vertical coordinates for interpolation window
2706  * @param du horizontal relative coordinate
2707  * @param dv vertical relative coordinate
2708  */
2710  const float *vec, int width, int height,
2711  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2712 {
2713  float d = 0.5f * (vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
2714 
2715  const float d0 = (vec[0] * 0.5f + vec[1] * 0.5f + vec[2] *-0.5f) / d;
2716  const float d1 = (vec[0] *-0.5f + vec[1] *-0.5f + vec[2] *-0.5f) / d;
2717  const float d2 = (vec[0] * 0.5f + vec[1] *-0.5f + vec[2] * 0.5f) / d;
2718  const float d3 = (vec[0] *-0.5f + vec[1] * 0.5f + vec[2] * 0.5f) / d;
2719 
2720  float uf, vf, x, y, z;
2721  int ui, vi;
2722 
2723  d = FFMAX(d0, FFMAX3(d1, d2, d3));
2724 
2725  x = vec[0] / d;
2726  y = vec[1] / d;
2727  z = -vec[2] / d;
2728 
2729  vf = 0.5f - y * 0.5f * s->input_mirror_modifier[1];
2730 
2731  if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) ||
2732  (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) {
2733  uf = 0.25f * x * s->input_mirror_modifier[0] + 0.25f;
2734  } else {
2735  uf = 0.75f - 0.25f * x * s->input_mirror_modifier[0];
2736  }
2737 
2738  uf *= width;
2739  vf *= height;
2740 
2741  ui = floorf(uf);
2742  vi = floorf(vf);
2743 
2744  *du = uf - ui;
2745  *dv = vf - vi;
2746 
2747  for (int i = -1; i < 3; i++) {
2748  for (int j = -1; j < 3; j++) {
2749  us[i + 1][j + 1] = mod(ui + j, width);
2750  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2751  }
2752  }
2753 
2754  return 1;
2755 }
2756 
2757 /**
2758  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
2759  *
2760  * @param s filter private context
2761  * @param i horizontal position on frame [0, width)
2762  * @param j vertical position on frame [0, height)
2763  * @param width frame width
2764  * @param height frame height
2765  * @param vec coordinates on sphere
2766  */
2767 static int dfisheye_to_xyz(const V360Context *s,
2768  int i, int j, int width, int height,
2769  float *vec)
2770 {
2771  const float scale = 1.f + s->out_pad;
2772 
2773  const float ew = width / 2.f;
2774  const float eh = height;
2775 
2776  const int ei = i >= ew ? i - ew : i;
2777  const float m = i >= ew ? -1.f : 1.f;
2778 
2779  const float uf = ((2.f * ei) / ew - 1.f) * scale;
2780  const float vf = ((2.f * j) / eh - 1.f) * scale;
2781 
2782  const float h = hypotf(uf, vf);
2783  const float lh = h > 0.f ? h : 1.f;
2784  const float theta = m * M_PI_2 * (1.f - h);
2785 
2786  const float sin_theta = sinf(theta);
2787  const float cos_theta = cosf(theta);
2788 
2789  vec[0] = cos_theta * m * -uf / lh;
2790  vec[1] = cos_theta * -vf / lh;
2791  vec[2] = sin_theta;
2792 
2793  normalize_vector(vec);
2794 
2795  return 1;
2796 }
2797 
2798 /**
2799  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
2800  *
2801  * @param s filter private context
2802  * @param vec coordinates on sphere
2803  * @param width frame width
2804  * @param height frame height
2805  * @param us horizontal coordinates for interpolation window
2806  * @param vs vertical coordinates for interpolation window
2807  * @param du horizontal relative coordinate
2808  * @param dv vertical relative coordinate
2809  */
2810 static int xyz_to_dfisheye(const V360Context *s,
2811  const float *vec, int width, int height,
2812  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2813 {
2814  const float scale = 1.f - s->in_pad;
2815 
2816  const float ew = width / 2.f;
2817  const float eh = height;
2818 
2819  const float h = hypotf(vec[0], vec[1]);
2820  const float lh = h > 0.f ? h : 1.f;
2821  const float theta = acosf(fabsf(vec[2])) / M_PI;
2822 
2823  float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
2824  float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
2825 
2826  int ui, vi;
2827  int u_shift;
2828 
2829  if (vec[2] >= 0.f) {
2830  u_shift = 0;
2831  } else {
2832  u_shift = ceilf(ew);
2833  uf = ew - uf;
2834  }
2835 
2836  ui = floorf(uf);
2837  vi = floorf(vf);
2838 
2839  *du = uf - ui;
2840  *dv = vf - vi;
2841 
2842  for (int i = -1; i < 3; i++) {
2843  for (int j = -1; j < 3; j++) {
2844  us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width - 1);
2845  vs[i + 1][j + 1] = av_clip( vi + i, 0, height - 1);
2846  }
2847  }
2848 
2849  return 1;
2850 }
2851 
2852 /**
2853  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
2854  *
2855  * @param s filter private context
2856  * @param i horizontal position on frame [0, width)
2857  * @param j vertical position on frame [0, height)
2858  * @param width frame width
2859  * @param height frame height
2860  * @param vec coordinates on sphere
2861  */
2862 static int barrel_to_xyz(const V360Context *s,
2863  int i, int j, int width, int height,
2864  float *vec)
2865 {
2866  const float scale = 0.99f;
2867  float l_x, l_y, l_z;
2868 
2869  if (i < 4 * width / 5) {
2870  const float theta_range = M_PI_4;
2871 
2872  const int ew = 4 * width / 5;
2873  const int eh = height;
2874 
2875  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
2876  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
2877 
2878  const float sin_phi = sinf(phi);
2879  const float cos_phi = cosf(phi);
2880  const float sin_theta = sinf(theta);
2881  const float cos_theta = cosf(theta);
2882 
2883  l_x = cos_theta * sin_phi;
2884  l_y = -sin_theta;
2885  l_z = -cos_theta * cos_phi;
2886  } else {
2887  const int ew = width / 5;
2888  const int eh = height / 2;
2889 
2890  float uf, vf;
2891 
2892  if (j < eh) { // UP
2893  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2894  vf = 2.f * (j ) / eh - 1.f;
2895 
2896  uf /= scale;
2897  vf /= scale;
2898 
2899  l_x = uf;
2900  l_y = 1.f;
2901  l_z = -vf;
2902  } else { // DOWN
2903  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2904  vf = 2.f * (j - eh) / eh - 1.f;
2905 
2906  uf /= scale;
2907  vf /= scale;
2908 
2909  l_x = uf;
2910  l_y = -1.f;
2911  l_z = vf;
2912  }
2913  }
2914 
2915  vec[0] = l_x;
2916  vec[1] = l_y;
2917  vec[2] = l_z;
2918 
2919  normalize_vector(vec);
2920 
2921  return 1;
2922 }
2923 
2924 /**
2925  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
2926  *
2927  * @param s filter private context
2928  * @param vec coordinates on sphere
2929  * @param width frame width
2930  * @param height frame height
2931  * @param us horizontal coordinates for interpolation window
2932  * @param vs vertical coordinates for interpolation window
2933  * @param du horizontal relative coordinate
2934  * @param dv vertical relative coordinate
2935  */
2936 static int xyz_to_barrel(const V360Context *s,
2937  const float *vec, int width, int height,
2938  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2939 {
2940  const float scale = 0.99f;
2941 
2942  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
2943  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
2944  const float theta_range = M_PI_4;
2945 
2946  int ew, eh;
2947  int u_shift, v_shift;
2948  float uf, vf;
2949  int ui, vi;
2950 
2951  if (theta > -theta_range && theta < theta_range) {
2952  ew = 4 * width / 5;
2953  eh = height;
2954 
2955  u_shift = s->ih_flip ? width / 5 : 0;
2956  v_shift = 0;
2957 
2958  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
2959  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
2960  } else {
2961  ew = width / 5;
2962  eh = height / 2;
2963 
2964  u_shift = s->ih_flip ? 0 : 4 * ew;
2965 
2966  if (theta < 0.f) { // UP
2967  uf = vec[0] / vec[1];
2968  vf = -vec[2] / vec[1];
2969  v_shift = 0;
2970  } else { // DOWN
2971  uf = -vec[0] / vec[1];
2972  vf = -vec[2] / vec[1];
2973  v_shift = eh;
2974  }
2975 
2976  uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
2977  vf *= s->input_mirror_modifier[1];
2978 
2979  uf = 0.5f * ew * (uf * scale + 1.f);
2980  vf = 0.5f * eh * (vf * scale + 1.f);
2981  }
2982 
2983  ui = floorf(uf);
2984  vi = floorf(vf);
2985 
2986  *du = uf - ui;
2987  *dv = vf - vi;
2988 
2989  for (int i = -1; i < 3; i++) {
2990  for (int j = -1; j < 3; j++) {
2991  us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
2992  vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
2993  }
2994  }
2995 
2996  return 1;
2997 }
2998 
2999 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
3000 {
3001  for (int i = 0; i < 3; i++) {
3002  for (int j = 0; j < 3; j++) {
3003  float sum = 0.f;
3004 
3005  for (int k = 0; k < 3; k++)
3006  sum += a[i][k] * b[k][j];
3007 
3008  c[i][j] = sum;
3009  }
3010  }
3011 }
3012 
3013 /**
3014  * Calculate rotation matrix for yaw/pitch/roll angles.
3015  */
3016 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
3017  float rot_mat[3][3],
3018  const int rotation_order[3])
3019 {
3020  const float yaw_rad = yaw * M_PI / 180.f;
3021  const float pitch_rad = pitch * M_PI / 180.f;
3022  const float roll_rad = roll * M_PI / 180.f;
3023 
3024  const float sin_yaw = sinf(-yaw_rad);
3025  const float cos_yaw = cosf(-yaw_rad);
3026  const float sin_pitch = sinf(pitch_rad);
3027  const float cos_pitch = cosf(pitch_rad);
3028  const float sin_roll = sinf(roll_rad);
3029  const float cos_roll = cosf(roll_rad);
3030 
3031  float m[3][3][3];
3032  float temp[3][3];
3033 
3034  m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw;
3035  m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0;
3036  m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw;
3037 
3038  m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0;
3039  m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch;
3040  m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch;
3041 
3042  m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0;
3043  m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0;
3044  m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1;
3045 
3046  multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
3047  multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
3048 }
3049 
3050 /**
3051  * Rotate vector with given rotation matrix.
3052  *
3053  * @param rot_mat rotation matrix
3054  * @param vec vector
3055  */
3056 static inline void rotate(const float rot_mat[3][3],
3057  float *vec)
3058 {
3059  const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
3060  const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
3061  const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
3062 
3063  vec[0] = x_tmp;
3064  vec[1] = y_tmp;
3065  vec[2] = z_tmp;
3066 }
3067 
3068 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3069  float *modifier)
3070 {
3071  modifier[0] = h_flip ? -1.f : 1.f;
3072  modifier[1] = v_flip ? -1.f : 1.f;
3073  modifier[2] = d_flip ? -1.f : 1.f;
3074 }
3075 
3076 static inline void mirror(const float *modifier, float *vec)
3077 {
3078  vec[0] *= modifier[0];
3079  vec[1] *= modifier[1];
3080  vec[2] *= modifier[2];
3081 }
3082 
3083 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3084 {
3085  s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3086  s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3087  if (!s->u[p] || !s->v[p])
3088  return AVERROR(ENOMEM);
3089  if (sizeof_ker) {
3090  s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
3091  if (!s->ker[p])
3092  return AVERROR(ENOMEM);
3093  }
3094 
3095  if (sizeof_mask && !p) {
3096  s->mask = av_calloc(s->pr_width[p] * s->pr_height[p], sizeof_mask);
3097  if (!s->mask)
3098  return AVERROR(ENOMEM);
3099  }
3100 
3101  return 0;
3102 }
3103 
3104 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
3105 {
3106  switch (format) {
3107  case FISHEYE:
3108  {
3109  const float d = 0.5f * hypotf(w, h);
3110 
3111  *h_fov = d / h * d_fov;
3112  *v_fov = d / w * d_fov;
3113  }
3114  break;
3115  case FLAT:
3116  default:
3117  {
3118  const float da = tanf(0.5 * FFMIN(d_fov, 359.f) * M_PI / 180.f);
3119  const float d = hypotf(w, h);
3120 
3121  *h_fov = atan2f(da * w, d) * 360.f / M_PI;
3122  *v_fov = atan2f(da * h, d) * 360.f / M_PI;
3123 
3124  if (*h_fov < 0.f)
3125  *h_fov += 360.f;
3126  if (*v_fov < 0.f)
3127  *v_fov += 360.f;
3128  }
3129  break;
3130  }
3131 }
3132 
3133 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
3134 {
3135  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
3136  outw[0] = outw[3] = w;
3137  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
3138  outh[0] = outh[3] = h;
3139 }
3140 
3141 // Calculate remap data
3142 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
3143 {
3144  V360Context *s = ctx->priv;
3145 
3146  for (int p = 0; p < s->nb_allocated; p++) {
3147  const int max_value = s->max_value;
3148  const int width = s->pr_width[p];
3149  const int uv_linesize = s->uv_linesize[p];
3150  const int height = s->pr_height[p];
3151  const int in_width = s->inplanewidth[p];
3152  const int in_height = s->inplaneheight[p];
3153  const int slice_start = (height * jobnr ) / nb_jobs;
3154  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
3155  float du, dv;
3156  float vec[3];
3157  XYRemap rmap;
3158 
3159  for (int j = slice_start; j < slice_end; j++) {
3160  for (int i = 0; i < width; i++) {
3161  int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
3162  int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
3163  int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
3164  uint8_t *mask8 = p ? NULL : s->mask + (j * s->pr_width[0] + i);
3165  uint16_t *mask16 = p ? NULL : (uint16_t *)s->mask + (j * s->pr_width[0] + i);
3166  int in_mask, out_mask;
3167 
3168  if (s->out_transpose)
3169  out_mask = s->out_transform(s, j, i, height, width, vec);
3170  else
3171  out_mask = s->out_transform(s, i, j, width, height, vec);
3172  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3173  rotate(s->rot_mat, vec);
3174  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3175  normalize_vector(vec);
3176  mirror(s->output_mirror_modifier, vec);
3177  if (s->in_transpose)
3178  in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
3179  else
3180  in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
3181  av_assert1(!isnan(du) && !isnan(dv));
3182  s->calculate_kernel(du, dv, &rmap, u, v, ker);
3183 
3184  if (!p && s->mask) {
3185  if (s->mask_size == 1) {
3186  mask8[0] = 255 * (out_mask & in_mask);
3187  } else {
3188  mask16[0] = max_value * (out_mask & in_mask);
3189  }
3190  }
3191  }
3192  }
3193  }
3194 
3195  return 0;
3196 }
3197 
3198 static int config_output(AVFilterLink *outlink)
3199 {
3200  AVFilterContext *ctx = outlink->src;
3201  AVFilterLink *inlink = ctx->inputs[0];
3202  V360Context *s = ctx->priv;
3204  const int depth = desc->comp[0].depth;
3205  const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
3206  int sizeof_uv;
3207  int sizeof_ker;
3208  int err;
3209  int h, w;
3210  int in_offset_h, in_offset_w;
3211  int out_offset_h, out_offset_w;
3212  float hf, wf;
3213  int (*prepare_out)(AVFilterContext *ctx);
3214  int have_alpha;
3215 
3216  s->max_value = (1 << depth) - 1;
3217  s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
3218  s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
3219 
3220  switch (s->interp) {
3221  case NEAREST:
3223  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
3224  s->elements = 1;
3225  sizeof_uv = sizeof(int16_t) * s->elements;
3226  sizeof_ker = 0;
3227  break;
3228  case BILINEAR:
3230  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
3231  s->elements = 2 * 2;
3232  sizeof_uv = sizeof(int16_t) * s->elements;
3233  sizeof_ker = sizeof(int16_t) * s->elements;
3234  break;
3235  case BICUBIC:
3237  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3238  s->elements = 4 * 4;
3239  sizeof_uv = sizeof(int16_t) * s->elements;
3240  sizeof_ker = sizeof(int16_t) * s->elements;
3241  break;
3242  case LANCZOS:
3244  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3245  s->elements = 4 * 4;
3246  sizeof_uv = sizeof(int16_t) * s->elements;
3247  sizeof_ker = sizeof(int16_t) * s->elements;
3248  break;
3249  case SPLINE16:
3251  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3252  s->elements = 4 * 4;
3253  sizeof_uv = sizeof(int16_t) * s->elements;
3254  sizeof_ker = sizeof(int16_t) * s->elements;
3255  break;
3256  case GAUSSIAN:
3258  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3259  s->elements = 4 * 4;
3260  sizeof_uv = sizeof(int16_t) * s->elements;
3261  sizeof_ker = sizeof(int16_t) * s->elements;
3262  break;
3263  default:
3264  av_assert0(0);
3265  }
3266 
3267  ff_v360_init(s, depth);
3268 
3269  for (int order = 0; order < NB_RORDERS; order++) {
3270  const char c = s->rorder[order];
3271  int rorder;
3272 
3273  if (c == '\0') {
3274  av_log(ctx, AV_LOG_ERROR,
3275  "Incomplete rorder option. Direction for all 3 rotation orders should be specified.\n");
3276  return AVERROR(EINVAL);
3277  }
3278 
3279  rorder = get_rorder(c);
3280  if (rorder == -1) {
3281  av_log(ctx, AV_LOG_ERROR,
3282  "Incorrect rotation order symbol '%c' in rorder option.\n", c);
3283  return AVERROR(EINVAL);
3284  }
3285 
3286  s->rotation_order[order] = rorder;
3287  }
3288 
3289  switch (s->in_stereo) {
3290  case STEREO_2D:
3291  w = inlink->w;
3292  h = inlink->h;
3293  in_offset_w = in_offset_h = 0;
3294  break;
3295  case STEREO_SBS:
3296  w = inlink->w / 2;
3297  h = inlink->h;
3298  in_offset_w = w;
3299  in_offset_h = 0;
3300  break;
3301  case STEREO_TB:
3302  w = inlink->w;
3303  h = inlink->h / 2;
3304  in_offset_w = 0;
3305  in_offset_h = h;
3306  break;
3307  default:
3308  av_assert0(0);
3309  }
3310 
3311  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
3312  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
3313 
3314  s->in_width = s->inplanewidth[0];
3315  s->in_height = s->inplaneheight[0];
3316 
3317  if (s->id_fov > 0.f)
3318  fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
3319 
3320  if (s->in_transpose)
3321  FFSWAP(int, s->in_width, s->in_height);
3322 
3323  switch (s->in) {
3324  case EQUIRECTANGULAR:
3326  err = 0;
3327  wf = w;
3328  hf = h;
3329  break;
3330  case CUBEMAP_3_2:
3332  err = prepare_cube_in(ctx);
3333  wf = w / 3.f * 4.f;
3334  hf = h;
3335  break;
3336  case CUBEMAP_1_6:
3338  err = prepare_cube_in(ctx);
3339  wf = w * 4.f;
3340  hf = h / 3.f;
3341  break;
3342  case CUBEMAP_6_1:
3344  err = prepare_cube_in(ctx);
3345  wf = w / 3.f * 2.f;
3346  hf = h * 2.f;
3347  break;
3348  case EQUIANGULAR:
3349  s->in_transform = xyz_to_eac;
3350  err = prepare_eac_in(ctx);
3351  wf = w;
3352  hf = h / 9.f * 8.f;
3353  break;
3354  case FLAT:
3356  err = prepare_flat_in(ctx);
3357  wf = w;
3358  hf = h;
3359  break;
3360  case PERSPECTIVE:
3361  case PANNINI:
3362  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
3363  return AVERROR(EINVAL);
3364  case DUAL_FISHEYE:
3366  err = 0;
3367  wf = w;
3368  hf = h;
3369  break;
3370  case BARREL:
3372  err = 0;
3373  wf = w / 5.f * 4.f;
3374  hf = h;
3375  break;
3376  case STEREOGRAPHIC:
3378  err = prepare_stereographic_in(ctx);
3379  wf = w;
3380  hf = h / 2.f;
3381  break;
3382  case MERCATOR:
3384  err = 0;
3385  wf = w;
3386  hf = h / 2.f;
3387  break;
3388  case BALL:
3390  err = 0;
3391  wf = w;
3392  hf = h / 2.f;
3393  break;
3394  case HAMMER:
3396  err = 0;
3397  wf = w;
3398  hf = h;
3399  break;
3400  case SINUSOIDAL:
3402  err = 0;
3403  wf = w;
3404  hf = h;
3405  break;
3406  case FISHEYE:
3408  err = prepare_fisheye_in(ctx);
3409  wf = w * 2;
3410  hf = h;
3411  break;
3412  case CYLINDRICAL:
3414  err = prepare_cylindrical_in(ctx);
3415  wf = w;
3416  hf = h * 2.f;
3417  break;
3418  case TETRAHEDRON:
3420  err = 0;
3421  wf = w;
3422  hf = h;
3423  break;
3424  default:
3425  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
3426  return AVERROR_BUG;
3427  }
3428 
3429  if (err != 0) {
3430  return err;
3431  }
3432 
3433  switch (s->out) {
3434  case EQUIRECTANGULAR:
3436  prepare_out = NULL;
3437  w = lrintf(wf);
3438  h = lrintf(hf);
3439  break;
3440  case CUBEMAP_3_2:
3442  prepare_out = prepare_cube_out;
3443  w = lrintf(wf / 4.f * 3.f);
3444  h = lrintf(hf);
3445  break;
3446  case CUBEMAP_1_6:
3448  prepare_out = prepare_cube_out;
3449  w = lrintf(wf / 4.f);
3450  h = lrintf(hf * 3.f);
3451  break;
3452  case CUBEMAP_6_1:
3454  prepare_out = prepare_cube_out;
3455  w = lrintf(wf / 2.f * 3.f);
3456  h = lrintf(hf / 2.f);
3457  break;
3458  case EQUIANGULAR:
3460  prepare_out = prepare_eac_out;
3461  w = lrintf(wf);
3462  h = lrintf(hf / 8.f * 9.f);
3463  break;
3464  case FLAT:
3466  prepare_out = prepare_flat_out;
3467  w = lrintf(wf);
3468  h = lrintf(hf);
3469  break;
3470  case DUAL_FISHEYE:
3472  prepare_out = NULL;
3473  w = lrintf(wf);
3474  h = lrintf(hf);
3475  break;
3476  case BARREL:
3478  prepare_out = NULL;
3479  w = lrintf(wf / 4.f * 5.f);
3480  h = lrintf(hf);
3481  break;
3482  case STEREOGRAPHIC:
3484  prepare_out = prepare_stereographic_out;
3485  w = lrintf(wf);
3486  h = lrintf(hf * 2.f);
3487  break;
3488  case MERCATOR:
3490  prepare_out = NULL;
3491  w = lrintf(wf);
3492  h = lrintf(hf * 2.f);
3493  break;
3494  case BALL:
3496  prepare_out = NULL;
3497  w = lrintf(wf);
3498  h = lrintf(hf * 2.f);
3499  break;
3500  case HAMMER:
3502  prepare_out = NULL;
3503  w = lrintf(wf);
3504  h = lrintf(hf);
3505  break;
3506  case SINUSOIDAL:
3508  prepare_out = NULL;
3509  w = lrintf(wf);
3510  h = lrintf(hf);
3511  break;
3512  case FISHEYE:
3514  prepare_out = prepare_fisheye_out;
3515  w = lrintf(wf * 0.5f);
3516  h = lrintf(hf);
3517  break;
3518  case PANNINI:
3520  prepare_out = NULL;
3521  w = lrintf(wf);
3522  h = lrintf(hf);
3523  break;
3524  case CYLINDRICAL:
3526  prepare_out = prepare_cylindrical_out;
3527  w = lrintf(wf);
3528  h = lrintf(hf * 0.5f);
3529  break;
3530  case PERSPECTIVE:
3532  prepare_out = NULL;
3533  w = lrintf(wf / 2.f);
3534  h = lrintf(hf);
3535  break;
3536  case TETRAHEDRON:
3538  prepare_out = NULL;
3539  w = lrintf(wf);
3540  h = lrintf(hf);
3541  break;
3542  default:
3543  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
3544  return AVERROR_BUG;
3545  }
3546 
3547  // Override resolution with user values if specified
3548  if (s->width > 0 && s->height > 0) {
3549  w = s->width;
3550  h = s->height;
3551  } else if (s->width > 0 || s->height > 0) {
3552  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
3553  return AVERROR(EINVAL);
3554  } else {
3555  if (s->out_transpose)
3556  FFSWAP(int, w, h);
3557 
3558  if (s->in_transpose)
3559  FFSWAP(int, w, h);
3560  }
3561 
3562  if (s->d_fov > 0.f)
3563  fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
3564 
3565  if (prepare_out) {
3566  err = prepare_out(ctx);
3567  if (err != 0)
3568  return err;
3569  }
3570 
3571  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
3572 
3573  s->out_width = s->pr_width[0];
3574  s->out_height = s->pr_height[0];
3575 
3576  if (s->out_transpose)
3577  FFSWAP(int, s->out_width, s->out_height);
3578 
3579  switch (s->out_stereo) {
3580  case STEREO_2D:
3581  out_offset_w = out_offset_h = 0;
3582  break;
3583  case STEREO_SBS:
3584  out_offset_w = w;
3585  out_offset_h = 0;
3586  w *= 2;
3587  break;
3588  case STEREO_TB:
3589  out_offset_w = 0;
3590  out_offset_h = h;
3591  h *= 2;
3592  break;
3593  default:
3594  av_assert0(0);
3595  }
3596 
3597  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
3598  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
3599 
3600  for (int i = 0; i < 4; i++)
3601  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
3602 
3603  outlink->h = h;
3604  outlink->w = w;
3605 
3607  have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
3608 
3609  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
3610  s->nb_allocated = 1;
3611  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
3612  } else {
3613  s->nb_allocated = 2;
3614  s->map[0] = s->map[3] = 0;
3615  s->map[1] = s->map[2] = 1;
3616  }
3617 
3618  for (int i = 0; i < s->nb_allocated; i++)
3619  allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
3620 
3623 
3624  ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3625 
3626  return 0;
3627 }
3628 
3630 {
3631  AVFilterContext *ctx = inlink->dst;
3632  AVFilterLink *outlink = ctx->outputs[0];
3633  V360Context *s = ctx->priv;
3634  AVFrame *out;
3635  ThreadData td;
3636 
3637  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
3638  if (!out) {
3639  av_frame_free(&in);
3640  return AVERROR(ENOMEM);
3641  }
3642  av_frame_copy_props(out, in);
3643 
3644  td.in = in;
3645  td.out = out;
3646 
3647  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3648 
3649  av_frame_free(&in);
3650  return ff_filter_frame(outlink, out);
3651 }
3652 
3654 {
3655  V360Context *s = ctx->priv;
3656 
3657  for (int p = 0; p < s->nb_allocated; p++) {
3658  av_freep(&s->u[p]);
3659  av_freep(&s->v[p]);
3660  av_freep(&s->ker[p]);
3661  }
3662  av_freep(&s->mask);
3663 }
3664 
3665 static const AVFilterPad inputs[] = {
3666  {
3667  .name = "default",
3668  .type = AVMEDIA_TYPE_VIDEO,
3669  .filter_frame = filter_frame,
3670  },
3671  { NULL }
3672 };
3673 
3674 static const AVFilterPad outputs[] = {
3675  {
3676  .name = "default",
3677  .type = AVMEDIA_TYPE_VIDEO,
3678  .config_props = config_output,
3679  },
3680  { NULL }
3681 };
3682 
3684  .name = "v360",
3685  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
3686  .priv_size = sizeof(V360Context),
3687  .uninit = uninit,
3689  .inputs = inputs,
3690  .outputs = outputs,
3691  .priv_class = &v360_class,
3693 };
Definition: v360.h:42
Axis +Z.
Definition: v360.h:80
int(* in_transform)(const struct V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Definition: v360.h:166
static int xyz_to_ball(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in ball format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1865
#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:2999
static const AVOption v360_options[]
Definition: vf_v360.c:56
static int 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:1937
#define AV_PIX_FMT_YUVA422P16
Definition: pixfmt.h:437
float iflat_range[2]
Definition: v360.h:137
AVFrame * out
Definition: af_adeclick.c:494
static int xyz_to_sinusoidal(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:2051
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:396
#define AV_PIX_FMT_YUVA422P9
Definition: pixfmt.h:429
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2526
int nb_planes
Definition: v360.h:155
static int xyz_to_stereographic(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in stereographic format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1652
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
static int 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:2399
#define TOP_LEFT
Definition: movtextdec.c:47
int in_transpose
Definition: v360.h:132
AVOption.
Definition: opt.h:246
float input_mirror_modifier[2]
Definition: v360.h:141
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:431
static int 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:2489
int h_flip
Definition: v360.h:131
#define AV_PIX_FMT_YUV444P14
Definition: pixfmt.h:404
static void calculate_lanczos_coeffs(float t, float *coeffs)
Calculate 1-dimensional lanczos coefficients.
Definition: vf_v360.c:462
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:414
static int prepare_stereographic_out(AVFilterContext *ctx)
Prepare data for processing stereographic output format.
Definition: vf_v360.c:1586
#define AV_PIX_FMT_YUVA422P10
Definition: pixfmt.h:432
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
static void lanczos_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Calculate kernel for lanczos interpolation.
Definition: vf_v360.c:491
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2566
Main libavfilter public API header.
else temp
Definition: vf_mcdeint.c:256
int elements
Definition: v360.h:157
static int prepare_eac_out(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap output format.
Definition: vf_v360.c:2145
int alpha
Definition: v360.h:109
int out_height
Definition: v360.h:145
static void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
Definition: vf_v360.c:820
static int 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:2356
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
static int tetrahedron_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 tetrahedron format...
Definition: vf_v360.c:2681
static int 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:2018
static void rotate_cube_face(float *uf, float *vf, int rotation)
Definition: vf_v360.c:794
int in
Definition: v360.h:107
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:410
static int get_rorder(char c)
Convert char to corresponding rotation order.
Definition: vf_v360.c:669
#define us(width, name, range_min, range_max, subs,...)
Definition: cbs_h2645.c:266
static int xyz_to_cube1x6(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1402
static int 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:1367
Definition: v360.h:37
static int xyz_to_fisheye(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in fisheye format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:2447
#define AV_PIX_FMT_GRAY9
Definition: pixfmt.h:374
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:398
int inplanewidth[4]
Definition: v360.h:153
int out_offset_w[4]
Definition: v360.h:150
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 spline16_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Calculate kernel for spline16 interpolation.
Definition: vf_v360.c:533
static void set_mirror_modifier(int h_flip, int v_flip, int d_flip, float *modifier)
Definition: vf_v360.c:3068
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 format(the sample packing is implied by the sample format) and sample rate.The lists are not just lists
char * in_frot
Definition: v360.h:113
int pr_height[4]
Definition: v360.h:147
static int config_output(AVFilterLink *outlink)
Definition: vf_v360.c:3198
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:92
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:244
static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
Definition: vf_v360.c:3104
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:375
Axis +Y.
Definition: v360.h:77
Definition: v360.h:47
int16_t u[4][4]
Definition: v360.h:100
const char * name
Pad name.
Definition: internal.h:60
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
Definition: vf_v360.c:3083
#define AV_PIX_FMT_GRAY12
Definition: pixfmt.h:376
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:346
float pitch
Definition: v360.h:128
#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:128
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
static void calculate_gaussian_coeffs(float t, float *coeffs)
Calculate 1-dimensional gaussian coefficients.
Definition: vf_v360.c:557
uint8_t
float yaw
Definition: v360.h:128
#define av_cold
Definition: attributes.h:82
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition: pixdesc.h:177
AVOptions.
int out_transpose
Definition: v360.h:132
int planeheight[4]
Definition: v360.h:152
#define f(width, name)
Definition: cbs_vp9.c:255
Axis -Y.
Definition: v360.h:78
char * out_frot
Definition: v360.h:114
void(* calculate_kernel)(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Definition: v360.h:174
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:252
#define AV_PIX_FMT_YUVA420P9
Definition: pixfmt.h:428
float iv_fov
Definition: v360.h:135
Definition: v360.h:95
#define cosf(x)
Definition: libm.h:78
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:409
int height
Definition: vf_avgblur.c:61
static int prepare_cylindrical_out(AVFilterContext *ctx)
Prepare data for processing cylindrical output format.
Definition: vf_v360.c:2520
static int 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:2176
#define atanf(x)
Definition: libm.h:40
AVFilter ff_vf_v360
Definition: vf_v360.c:3683
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:110
static int xyz_to_equirect(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1693
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
float ih_fov
Definition: v360.h:135
#define lrintf(x)
Definition: libm_mips.h:70
int width
Definition: v360.h:110
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:407
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:872
#define AV_PIX_FMT_YUV422P12
Definition: pixfmt.h:399
#define AV_PIX_FMT_YUVA420P16
Definition: pixfmt.h:436
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:936
static int xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_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:2810
#define FFALIGN(x, a)
Definition: macros.h:48
int rotation_order[3]
Definition: v360.h:121
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
#define expf(x)
Definition: libm.h:283
static int 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:1560
int fin_pad
Definition: v360.h:126
static int 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:2767
int in_stereo
Definition: v360.h:123
void ff_v360_init(V360Context *s, int depth)
Definition: vf_v360.c:344
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition: pixfmt.h:176
int max_value
Definition: v360.h:159
const AVS_VideoInfo * vi
Definition: avisynth_c.h:887
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:569
#define td
Definition: regdef.h:70
Definition: v360.h:93
static int 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:2862
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:3076
#define S(s, c, i)
#define isfinite(x)
Definition: libm.h:359
static int prepare_fisheye_out(AVFilterContext *ctx)
Prepare data for processing fisheye output format.
Definition: vf_v360.c:2379
int out_offset_h[4]
Definition: v360.h:150
#define DEFINE_REMAP1_LINE(bits, div)
Definition: vf_v360.c:236
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:417
#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
const char * r
Definition: vf_curves.c:114
void * priv
private data for use by the filter
Definition: avfilter.h:353
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:116
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:438
const char * arg
Definition: jacosubdec.c:66
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:415
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:2085
static void nearest_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Save nearest pixel coordinates for remapping.
Definition: vf_v360.c:375
Definition: v360.h:59
Definition: v360.h:88
static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
Definition: vf_v360.c:3133
static int 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:2540
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:397
Definition: v360.h:55
int in_offset_w[4]
Definition: v360.h:149
#define FFMAX(a, b)
Definition: common.h:94
int16_t * v[2]
Definition: v360.h:161
static int 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:1206
int out
Definition: v360.h:107
int out_stereo
Definition: v360.h:123
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:416
uint8_t * mask
Definition: v360.h:163
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:70
Definition: v360.h:57
int out_cubemap_face_rotation[6]
Definition: v360.h:120
int16_t * ker[2]
Definition: v360.h:162
int in_offset_h[4]
Definition: v360.h:149
static void calculate_spline16_coeffs(float t, float *coeffs)
Calculate 1-dimensional spline16 coefficients.
Definition: vf_v360.c:515
uint64_t flags
Combination of AV_PIX_FMT_FLAG_...
Definition: pixdesc.h:106
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:392
unsigned map[4]
Definition: v360.h:164
int ih_flip
Definition: v360.h:130
int interp
Definition: v360.h:108
int in_height
Definition: v360.h:144
#define OFFSET(x)
Definition: vf_v360.c:53
Definition: v360.h:86
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:413
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:378
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:53
static int xyz_to_hammer(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1978
float in_pad
Definition: v360.h:125
#define FFMIN(a, b)
Definition: common.h:96
static int 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:1606
float out_pad
Definition: v360.h:125
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:3142
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:435
static int prepare_stereographic_in(AVFilterContext *ctx)
Prepare data for processing stereographic input format.
Definition: vf_v360.c:1630
int(* out_transform)(const struct V360Context *s, int i, int j, int width, int height, float *vec)
Definition: v360.h:170
Definition: v360.h:26
static int query_formats(AVFilterContext *ctx)
Definition: vf_v360.c:152
#define M_PI_2
Definition: mathematics.h:55
int inplaneheight[4]
Definition: v360.h:153
AVFrame * m
int mask_size
Definition: v360.h:158
Definition: v360.h:56
AVFormatContext * ctx
Definition: movenc.c:48
Definition: v360.h:58
AVFILTER_DEFINE_CLASS(v360)
int in_cubemap_face_order[6]
Definition: v360.h:117
#define AV_PIX_FMT_YUVA444P10
Definition: pixfmt.h:433
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:1014
static const AVFilterPad outputs[]
Definition: vf_v360.c:3674
int(* remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: v360.h:177
#define AV_PIX_FMT_YUV444P9
Definition: pixfmt.h:393
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:412
#define AV_PIX_FMT_YUV420P16
Definition: pixfmt.h:405
float v_fov
Definition: v360.h:134
float output_mirror_modifier[3]
Definition: v360.h:142
Definition: v360.h:94
#define sinf(x)
Definition: libm.h:419
#define DEFINE_REMAP_LINE(ws, bits, div)
Definition: vf_v360.c:312
#define TOP_RIGHT
Definition: movtextdec.c:49
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_v360.c:3653
#define AV_PIX_FMT_YUV420P14
Definition: pixfmt.h:402
static int prepare_flat_out(AVFilterContext *ctx)
Prepare data for processing flat output format.
Definition: vf_v360.c:2336
int16_t v[4][4]
Definition: v360.h:101
static void bilinear_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Calculate kernel for bilinear interpolation.
Definition: vf_v360.c:395
int planewidth[4]
Definition: v360.h:152
int out_width
Definition: v360.h:145
Used for passing data between threads.
Definition: dsddec.c:64
int in_cubemap_face_rotation[6]
Definition: v360.h:119
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 void gaussian_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Calculate kernel for gaussian interpolation.
Definition: vf_v360.c:586
static int mod(int a, int b)
Modulo operation with only positive remainders.
Definition: vf_v360.c:612
int nb_allocated
Definition: v360.h:156
float id_fov
Definition: v360.h:135
#define AV_PIX_FMT_GRAY14
Definition: pixfmt.h:377
static const int16_t alpha[]
Definition: ilbcdata.h:55
Definition: v360.h:85
static int prepare_cube_in(AVFilterContext *ctx)
Prepare data for processing cubemap input format.
Definition: vf_v360.c:693
static int xyz_to_eac(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_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:2281
static int xyz_to_cube3x2(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1245
int d_flip
Definition: v360.h:131
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_v360.c:3629
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
int out_cubemap_direction_order[6]
Definition: v360.h:118
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:394
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:72
static const AVFilterPad inputs[]
Definition: vf_v360.c:3665
Definition: v360.h:99
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:3016
static int xyz_to_tetrahedron(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:2709
#define isnan(x)
Definition: libm.h:340
AVFrame * b
int v_flip
Definition: v360.h:131
#define DEFINE_REMAP(ws, bits)
Generate remapping function with a given window size and pixel depth.
Definition: vf_v360.c:260
static int perspective_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 perspective format...
Definition: vf_v360.c:2634
const char * name
Filter name.
Definition: avfilter.h:148
#define AV_PIX_FMT_YUV440P12
Definition: pixfmt.h:400
#define AV_PIX_FMT_YUV420P9
Definition: pixfmt.h:391
char * in_forder
Definition: v360.h:111
static int prepare_cube_out(AVFilterContext *ctx)
Prepare data for processing cubemap output format.
Definition: vf_v360.c:747
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:350
int outw
Definition: vf_rotate.c:88
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:275
float flat_range[2]
Definition: v360.h:136
#define AV_PIX_FMT_YUV422P14
Definition: pixfmt.h:403
static int xyz_to_cylindrical(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:2593
int pr_width[4]
Definition: v360.h:147
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:411
#define flags(name, subs,...)
Definition: cbs_av1.c:564
AVFilterInternal * internal
An opaque struct for libavfilter internal use.
Definition: avfilter.h:378
#define AV_PIX_FMT_YUV422P10
Definition: pixfmt.h:395
float rot_mat[3][3]
Definition: v360.h:139
int uv_linesize[4]
Definition: v360.h:154
#define AV_PIX_FMT_YUV444P12
Definition: pixfmt.h:401
static int xyz_to_flat(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in flat format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1749
#define M_SQRT2
Definition: mathematics.h:61
static int prepare_flat_in(AVFilterContext *ctx)
Prepare data for processing flat input format.
Definition: vf_v360.c:1727
float h_fov
Definition: v360.h:134
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:851
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:3056
Definition: v360.h:87
int iv_flip
Definition: v360.h:130
static int xyz_to_cube6x1(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1482
int fout_pad
Definition: v360.h:126
static int get_rotation(char c)
Convert char to corresponding rotation angle.
Definition: vf_v360.c:650
planar GBRA 4:4:4:4 32bpp
Definition: pixfmt.h:215
static int xyz_to_barrel(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_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:2936
#define AV_PIX_FMT_YUVA444P9
Definition: pixfmt.h:430
#define ui(width, name)
Definition: cbs_mpeg2.c:43
Definition: v360.h:71
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:112
avfilter_execute_func * execute
Definition: internal.h:144
static int xyz_to_mercator(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1796
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2035
static int 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:1903
static int 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:1334
Axis -Z.
Definition: v360.h:79
static int get_direction(char c)
Convert char to corresponding direction.
Definition: vf_v360.c:626
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
An instance of a filter.
Definition: avfilter.h:338
#define RIGHT
Definition: cdgraphics.c:167
#define av_freep(p)
const void ** s
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 int 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:1833
#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
static int prepare_fisheye_in(AVFilterContext *ctx)
Prepare data for processing fisheye input format.
Definition: vf_v360.c:2425
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:115
int depth
Number of bits in the component.
Definition: pixdesc.h:58
AVFrame * a
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
static void bicubic_kernel(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Calculate kernel for bicubic interpolation.
Definition: vf_v360.c:438
float d_fov
Definition: v360.h:134
int in_width
Definition: v360.h:144
#define AV_PIX_FMT_YUV422P16
Definition: pixfmt.h:406
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:654
#define FFMAX3(a, b, c)
Definition: common.h:95
static enum AVPixelFormat alpha_pix_fmts[]
Definition: vf_overlay.c:155
static int prepare_cylindrical_in(AVFilterContext *ctx)
Prepare data for processing cylindrical input format.
Definition: vf_v360.c:2571
#define AV_PIX_FMT_YUVA422P12
Definition: pixfmt.h:434
int16_t * u[2]
Definition: v360.h:161
Definition: v360.h:60
static uint8_t tmp[11]
Definition: aes_ctr.c:26