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 #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
56 
57 static const AVOption v360_options[] = {
58  { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" },
59  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
60  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
61  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "in" },
62  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" },
63  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" },
64  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
65  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
66  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
67  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
68  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
69  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
70  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" },
71  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
72  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" },
73  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" },
74  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" },
75  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" },
76  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" },
77  { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "in" },
78  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "in" },
79  {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "in" },
80  {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, "in" },
81  { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, "in" },
82  { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" },
83  { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" },
84  { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, "in" },
85  { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, "in" },
86  {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, "in" },
87  {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, "in" },
88  { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
89  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
90  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
91  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
92  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
93  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
94  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
95  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
96  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
97  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
98  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
99  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
100  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" },
101  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
102  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" },
103  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" },
104  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" },
105  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" },
106  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" },
107  { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "out" },
108  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "out" },
109  {"perspective", "perspective", 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE}, 0, 0, FLAGS, "out" },
110  {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "out" },
111  {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, "out" },
112  { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, "out" },
113  { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "out" },
114  { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "out" },
115  { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, "out" },
116  { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, "out" },
117  {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, "out" },
118  {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, "out" },
119  { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" },
120  { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
121  { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
122  { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
123  { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
124  { "lagrange9", "lagrange9 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LAGRANGE9}, 0, 0, FLAGS, "interp" },
125  { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
126  { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
127  { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
128  { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
129  { "sp16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" },
130  { "spline16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" },
131  { "gauss", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" },
132  { "gaussian", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" },
133  { "mitchell", "mitchell interpolation", 0, AV_OPT_TYPE_CONST, {.i64=MITCHELL}, 0, 0, FLAGS, "interp" },
134  { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
135  { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
136  { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
137  {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
138  { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
139  { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
140  { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
141  { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
142  {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
143  { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
144  { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"},
145  { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, "in_pad"},
146  { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, "out_pad"},
147  { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, "fin_pad"},
148  { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, "fout_pad"},
149  { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "yaw"},
150  { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "pitch"},
151  { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "roll"},
152  { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0,TFLAGS, "rorder"},
153  { "h_fov", "output horizontal field of view",OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "h_fov"},
154  { "v_fov", "output vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "v_fov"},
155  { "d_fov", "output diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "d_fov"},
156  { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "h_flip"},
157  { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "v_flip"},
158  { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "d_flip"},
159  { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "ih_flip"},
160  { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "iv_flip"},
161  { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"},
162  { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"},
163  { "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "ih_fov"},
164  { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "iv_fov"},
165  { "id_fov", "input diagonal field of view", OFFSET(id_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "id_fov"},
166  {"alpha_mask", "build mask in alpha plane", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "alpha"},
167  { NULL }
168 };
169 
171 
173 {
174  V360Context *s = ctx->priv;
175  static const enum AVPixelFormat pix_fmts[] = {
176  // YUVA444
180 
181  // YUVA422
185 
186  // YUVA420
189 
190  // YUVJ
194 
195  // YUV444
199 
200  // YUV440
203 
204  // YUV422
208 
209  // YUV420
213 
214  // YUV411
216 
217  // YUV410
219 
220  // GBR
224 
225  // GBRA
228 
229  // GRAY
233 
235  };
236  static const enum AVPixelFormat alpha_pix_fmts[] = {
248  };
249 
251 }
252 
253 #define DEFINE_REMAP1_LINE(bits, div) \
254 static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \
255  ptrdiff_t in_linesize, \
256  const int16_t *const u, const int16_t *const v, \
257  const int16_t *const ker) \
258 { \
259  const uint##bits##_t *const s = (const uint##bits##_t *const)src; \
260  uint##bits##_t *d = (uint##bits##_t *)dst; \
261  \
262  in_linesize /= div; \
263  \
264  for (int x = 0; x < width; x++) \
265  d[x] = s[v[x] * in_linesize + u[x]]; \
266 }
267 
268 DEFINE_REMAP1_LINE( 8, 1)
269 DEFINE_REMAP1_LINE(16, 2)
270 
271 /**
272  * Generate remapping function with a given window size and pixel depth.
273  *
274  * @param ws size of interpolation window
275  * @param bits number of bits per pixel
276  */
277 #define DEFINE_REMAP(ws, bits) \
278 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
279 { \
280  ThreadData *td = arg; \
281  const V360Context *s = ctx->priv; \
282  const SliceXYRemap *r = &s->slice_remap[jobnr]; \
283  const AVFrame *in = td->in; \
284  AVFrame *out = td->out; \
285  \
286  for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
287  for (int plane = 0; plane < s->nb_planes; plane++) { \
288  const unsigned map = s->map[plane]; \
289  const int in_linesize = in->linesize[plane]; \
290  const int out_linesize = out->linesize[plane]; \
291  const int uv_linesize = s->uv_linesize[plane]; \
292  const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
293  const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
294  const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
295  const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
296  const uint8_t *const src = in->data[plane] + \
297  in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
298  uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
299  const uint8_t *mask = plane == 3 ? r->mask : NULL; \
300  const int width = s->pr_width[plane]; \
301  const int height = s->pr_height[plane]; \
302  \
303  const int slice_start = (height * jobnr ) / nb_jobs; \
304  const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
305  \
306  for (int y = slice_start; y < slice_end && !mask; y++) { \
307  const int16_t *const u = r->u[map] + (y - slice_start) * uv_linesize * ws * ws; \
308  const int16_t *const v = r->v[map] + (y - slice_start) * uv_linesize * ws * ws; \
309  const int16_t *const ker = r->ker[map] + (y - slice_start) * uv_linesize * ws * ws; \
310  \
311  s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
312  } \
313  \
314  for (int y = slice_start; y < slice_end && mask; y++) { \
315  memcpy(dst + y * out_linesize, mask + \
316  (y - slice_start) * width * (bits >> 3), width * (bits >> 3)); \
317  } \
318  } \
319  } \
320  \
321  return 0; \
322 }
323 
324 DEFINE_REMAP(1, 8)
325 DEFINE_REMAP(2, 8)
326 DEFINE_REMAP(3, 8)
327 DEFINE_REMAP(4, 8)
328 DEFINE_REMAP(1, 16)
329 DEFINE_REMAP(2, 16)
330 DEFINE_REMAP(3, 16)
331 DEFINE_REMAP(4, 16)
332 
333 #define DEFINE_REMAP_LINE(ws, bits, div) \
334 static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *const src, \
335  ptrdiff_t in_linesize, \
336  const int16_t *const u, const int16_t *const v, \
337  const int16_t *const ker) \
338 { \
339  const uint##bits##_t *const s = (const uint##bits##_t *const)src; \
340  uint##bits##_t *d = (uint##bits##_t *)dst; \
341  \
342  in_linesize /= div; \
343  \
344  for (int x = 0; x < width; x++) { \
345  const int16_t *const uu = u + x * ws * ws; \
346  const int16_t *const vv = v + x * ws * ws; \
347  const int16_t *const kker = ker + x * ws * ws; \
348  int tmp = 0; \
349  \
350  for (int i = 0; i < ws; i++) { \
351  const int iws = i * ws; \
352  for (int j = 0; j < ws; j++) { \
353  tmp += kker[iws + j] * s[vv[iws + j] * in_linesize + uu[iws + j]]; \
354  } \
355  } \
356  \
357  d[x] = av_clip_uint##bits(tmp >> 14); \
358  } \
359 }
360 
361 DEFINE_REMAP_LINE(2, 8, 1)
362 DEFINE_REMAP_LINE(3, 8, 1)
363 DEFINE_REMAP_LINE(4, 8, 1)
364 DEFINE_REMAP_LINE(2, 16, 2)
365 DEFINE_REMAP_LINE(3, 16, 2)
366 DEFINE_REMAP_LINE(4, 16, 2)
367 
368 void ff_v360_init(V360Context *s, int depth)
369 {
370  switch (s->interp) {
371  case NEAREST:
372  s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c;
373  break;
374  case BILINEAR:
375  s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c;
376  break;
377  case LAGRANGE9:
378  s->remap_line = depth <= 8 ? remap3_8bit_line_c : remap3_16bit_line_c;
379  break;
380  case BICUBIC:
381  case LANCZOS:
382  case SPLINE16:
383  case GAUSSIAN:
384  case MITCHELL:
385  s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c;
386  break;
387  }
388 
389  if (ARCH_X86)
390  ff_v360_init_x86(s, depth);
391 }
392 
393 /**
394  * Save nearest pixel coordinates for remapping.
395  *
396  * @param du horizontal relative coordinate
397  * @param dv vertical relative coordinate
398  * @param rmap calculated 4x4 window
399  * @param u u remap data
400  * @param v v remap data
401  * @param ker ker remap data
402  */
403 static void nearest_kernel(float du, float dv, const XYRemap *rmap,
404  int16_t *u, int16_t *v, int16_t *ker)
405 {
406  const int i = lrintf(dv) + 1;
407  const int j = lrintf(du) + 1;
408 
409  u[0] = rmap->u[i][j];
410  v[0] = rmap->v[i][j];
411 }
412 
413 /**
414  * Calculate kernel for bilinear interpolation.
415  *
416  * @param du horizontal relative coordinate
417  * @param dv vertical relative coordinate
418  * @param rmap calculated 4x4 window
419  * @param u u remap data
420  * @param v v remap data
421  * @param ker ker remap data
422  */
423 static void bilinear_kernel(float du, float dv, const XYRemap *rmap,
424  int16_t *u, int16_t *v, int16_t *ker)
425 {
426  for (int i = 0; i < 2; i++) {
427  for (int j = 0; j < 2; j++) {
428  u[i * 2 + j] = rmap->u[i + 1][j + 1];
429  v[i * 2 + j] = rmap->v[i + 1][j + 1];
430  }
431  }
432 
433  ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f);
434  ker[1] = lrintf( du * (1.f - dv) * 16385.f);
435  ker[2] = lrintf((1.f - du) * dv * 16385.f);
436  ker[3] = lrintf( du * dv * 16385.f);
437 }
438 
439 /**
440  * Calculate 1-dimensional lagrange coefficients.
441  *
442  * @param t relative coordinate
443  * @param coeffs coefficients
444  */
445 static inline void calculate_lagrange_coeffs(float t, float *coeffs)
446 {
447  coeffs[0] = (t - 1.f) * (t - 2.f) * 0.5f;
448  coeffs[1] = -t * (t - 2.f);
449  coeffs[2] = t * (t - 1.f) * 0.5f;
450 }
451 
452 /**
453  * Calculate kernel for lagrange interpolation.
454  *
455  * @param du horizontal relative coordinate
456  * @param dv vertical relative coordinate
457  * @param rmap calculated 4x4 window
458  * @param u u remap data
459  * @param v v remap data
460  * @param ker ker remap data
461  */
462 static void lagrange_kernel(float du, float dv, const XYRemap *rmap,
463  int16_t *u, int16_t *v, int16_t *ker)
464 {
465  float du_coeffs[3];
466  float dv_coeffs[3];
467 
468  calculate_lagrange_coeffs(du, du_coeffs);
469  calculate_lagrange_coeffs(dv, dv_coeffs);
470 
471  for (int i = 0; i < 3; i++) {
472  for (int j = 0; j < 3; j++) {
473  u[i * 3 + j] = rmap->u[i + 1][j + 1];
474  v[i * 3 + j] = rmap->v[i + 1][j + 1];
475  ker[i * 3 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
476  }
477  }
478 }
479 
480 /**
481  * Calculate 1-dimensional cubic coefficients.
482  *
483  * @param t relative coordinate
484  * @param coeffs coefficients
485  */
486 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
487 {
488  const float tt = t * t;
489  const float ttt = t * t * t;
490 
491  coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f;
492  coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f;
493  coeffs[2] = t + tt / 2.f - ttt / 2.f;
494  coeffs[3] = - t / 6.f + ttt / 6.f;
495 }
496 
497 /**
498  * Calculate kernel for bicubic interpolation.
499  *
500  * @param du horizontal relative coordinate
501  * @param dv vertical relative coordinate
502  * @param rmap calculated 4x4 window
503  * @param u u remap data
504  * @param v v remap data
505  * @param ker ker remap data
506  */
507 static void bicubic_kernel(float du, float dv, const XYRemap *rmap,
508  int16_t *u, int16_t *v, int16_t *ker)
509 {
510  float du_coeffs[4];
511  float dv_coeffs[4];
512 
513  calculate_bicubic_coeffs(du, du_coeffs);
514  calculate_bicubic_coeffs(dv, dv_coeffs);
515 
516  for (int i = 0; i < 4; i++) {
517  for (int j = 0; j < 4; j++) {
518  u[i * 4 + j] = rmap->u[i][j];
519  v[i * 4 + j] = rmap->v[i][j];
520  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
521  }
522  }
523 }
524 
525 /**
526  * Calculate 1-dimensional lanczos coefficients.
527  *
528  * @param t relative coordinate
529  * @param coeffs coefficients
530  */
531 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
532 {
533  float sum = 0.f;
534 
535  for (int i = 0; i < 4; i++) {
536  const float x = M_PI * (t - i + 1);
537  if (x == 0.f) {
538  coeffs[i] = 1.f;
539  } else {
540  coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
541  }
542  sum += coeffs[i];
543  }
544 
545  for (int i = 0; i < 4; i++) {
546  coeffs[i] /= sum;
547  }
548 }
549 
550 /**
551  * Calculate kernel for lanczos interpolation.
552  *
553  * @param du horizontal relative coordinate
554  * @param dv vertical relative coordinate
555  * @param rmap calculated 4x4 window
556  * @param u u remap data
557  * @param v v remap data
558  * @param ker ker remap data
559  */
560 static void lanczos_kernel(float du, float dv, const XYRemap *rmap,
561  int16_t *u, int16_t *v, int16_t *ker)
562 {
563  float du_coeffs[4];
564  float dv_coeffs[4];
565 
566  calculate_lanczos_coeffs(du, du_coeffs);
567  calculate_lanczos_coeffs(dv, dv_coeffs);
568 
569  for (int i = 0; i < 4; i++) {
570  for (int j = 0; j < 4; j++) {
571  u[i * 4 + j] = rmap->u[i][j];
572  v[i * 4 + j] = rmap->v[i][j];
573  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
574  }
575  }
576 }
577 
578 /**
579  * Calculate 1-dimensional spline16 coefficients.
580  *
581  * @param t relative coordinate
582  * @param coeffs coefficients
583  */
584 static void calculate_spline16_coeffs(float t, float *coeffs)
585 {
586  coeffs[0] = ((-1.f / 3.f * t + 0.8f) * t - 7.f / 15.f) * t;
587  coeffs[1] = ((t - 9.f / 5.f) * t - 0.2f) * t + 1.f;
588  coeffs[2] = ((6.f / 5.f - t) * t + 0.8f) * t;
589  coeffs[3] = ((1.f / 3.f * t - 0.2f) * t - 2.f / 15.f) * t;
590 }
591 
592 /**
593  * Calculate kernel for spline16 interpolation.
594  *
595  * @param du horizontal relative coordinate
596  * @param dv vertical relative coordinate
597  * @param rmap calculated 4x4 window
598  * @param u u remap data
599  * @param v v remap data
600  * @param ker ker remap data
601  */
602 static void spline16_kernel(float du, float dv, const XYRemap *rmap,
603  int16_t *u, int16_t *v, int16_t *ker)
604 {
605  float du_coeffs[4];
606  float dv_coeffs[4];
607 
608  calculate_spline16_coeffs(du, du_coeffs);
609  calculate_spline16_coeffs(dv, dv_coeffs);
610 
611  for (int i = 0; i < 4; i++) {
612  for (int j = 0; j < 4; j++) {
613  u[i * 4 + j] = rmap->u[i][j];
614  v[i * 4 + j] = rmap->v[i][j];
615  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
616  }
617  }
618 }
619 
620 /**
621  * Calculate 1-dimensional gaussian coefficients.
622  *
623  * @param t relative coordinate
624  * @param coeffs coefficients
625  */
626 static void calculate_gaussian_coeffs(float t, float *coeffs)
627 {
628  float sum = 0.f;
629 
630  for (int i = 0; i < 4; i++) {
631  const float x = t - (i - 1);
632  if (x == 0.f) {
633  coeffs[i] = 1.f;
634  } else {
635  coeffs[i] = expf(-2.f * x * x) * expf(-x * x / 2.f);
636  }
637  sum += coeffs[i];
638  }
639 
640  for (int i = 0; i < 4; i++) {
641  coeffs[i] /= sum;
642  }
643 }
644 
645 /**
646  * Calculate kernel for gaussian interpolation.
647  *
648  * @param du horizontal relative coordinate
649  * @param dv vertical relative coordinate
650  * @param rmap calculated 4x4 window
651  * @param u u remap data
652  * @param v v remap data
653  * @param ker ker remap data
654  */
655 static void gaussian_kernel(float du, float dv, const XYRemap *rmap,
656  int16_t *u, int16_t *v, int16_t *ker)
657 {
658  float du_coeffs[4];
659  float dv_coeffs[4];
660 
661  calculate_gaussian_coeffs(du, du_coeffs);
662  calculate_gaussian_coeffs(dv, dv_coeffs);
663 
664  for (int i = 0; i < 4; i++) {
665  for (int j = 0; j < 4; j++) {
666  u[i * 4 + j] = rmap->u[i][j];
667  v[i * 4 + j] = rmap->v[i][j];
668  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
669  }
670  }
671 }
672 
673 /**
674  * Calculate 1-dimensional cubic_bc_spline coefficients.
675  *
676  * @param t relative coordinate
677  * @param coeffs coefficients
678  */
679 static void calculate_cubic_bc_coeffs(float t, float *coeffs,
680  float b, float c)
681 {
682  float sum = 0.f;
683  float p0 = (6.f - 2.f * b) / 6.f,
684  p2 = (-18.f + 12.f * b + 6.f * c) / 6.f,
685  p3 = (12.f - 9.f * b - 6.f * c) / 6.f,
686  q0 = (8.f * b + 24.f * c) / 6.f,
687  q1 = (-12.f * b - 48.f * c) / 6.f,
688  q2 = (6.f * b + 30.f * c) / 6.f,
689  q3 = (-b - 6.f * c) / 6.f;
690 
691  for (int i = 0; i < 4; i++) {
692  const float x = fabsf(t - i + 1.f);
693  if (x < 1.f) {
694  coeffs[i] = (p0 + x * x * (p2 + x * p3)) *
695  (p0 + x * x * (p2 + x * p3 / 2.f) / 4.f);
696  } else if (x < 2.f) {
697  coeffs[i] = (q0 + x * (q1 + x * (q2 + x * q3))) *
698  (q0 + x * (q1 + x * (q2 + x / 2.f * q3) / 2.f) / 2.f);
699  } else {
700  coeffs[i] = 0.f;
701  }
702  sum += coeffs[i];
703  }
704 
705  for (int i = 0; i < 4; i++) {
706  coeffs[i] /= sum;
707  }
708 }
709 
710 /**
711  * Calculate kernel for mitchell interpolation.
712  *
713  * @param du horizontal relative coordinate
714  * @param dv vertical relative coordinate
715  * @param rmap calculated 4x4 window
716  * @param u u remap data
717  * @param v v remap data
718  * @param ker ker remap data
719  */
720 static void mitchell_kernel(float du, float dv, const XYRemap *rmap,
721  int16_t *u, int16_t *v, int16_t *ker)
722 {
723  float du_coeffs[4];
724  float dv_coeffs[4];
725 
726  calculate_cubic_bc_coeffs(du, du_coeffs, 1.f / 3.f, 1.f / 3.f);
727  calculate_cubic_bc_coeffs(dv, dv_coeffs, 1.f / 3.f, 1.f / 3.f);
728 
729  for (int i = 0; i < 4; i++) {
730  for (int j = 0; j < 4; j++) {
731  u[i * 4 + j] = rmap->u[i][j];
732  v[i * 4 + j] = rmap->v[i][j];
733  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
734  }
735  }
736 }
737 
738 /**
739  * Modulo operation with only positive remainders.
740  *
741  * @param a dividend
742  * @param b divisor
743  *
744  * @return positive remainder of (a / b)
745  */
746 static inline int mod(int a, int b)
747 {
748  const int res = a % b;
749  if (res < 0) {
750  return res + b;
751  } else {
752  return res;
753  }
754 }
755 
756 /**
757  * Reflect y operation.
758  *
759  * @param y input vertical position
760  * @param h input height
761  */
762 static inline int reflecty(int y, int h)
763 {
764  if (y < 0) {
765  y = -y;
766  } else if (y >= h) {
767  y = 2 * h - 1 - y;
768  }
769 
770  return av_clip(y, 0, h - 1);
771 }
772 
773 /**
774  * Reflect x operation for equirect.
775  *
776  * @param x input horizontal position
777  * @param y input vertical position
778  * @param w input width
779  * @param h input height
780  */
781 static inline int ereflectx(int x, int y, int w, int h)
782 {
783  if (y < 0 || y >= h)
784  x += w / 2;
785 
786  return mod(x, w);
787 }
788 
789 /**
790  * Reflect x operation.
791  *
792  * @param x input horizontal position
793  * @param y input vertical position
794  * @param w input width
795  * @param h input height
796  */
797 static inline int reflectx(int x, int y, int w, int h)
798 {
799  if (y < 0 || y >= h)
800  return w - 1 - x;
801 
802  return mod(x, w);
803 }
804 
805 /**
806  * Convert char to corresponding direction.
807  * Used for cubemap options.
808  */
809 static int get_direction(char c)
810 {
811  switch (c) {
812  case 'r':
813  return RIGHT;
814  case 'l':
815  return LEFT;
816  case 'u':
817  return UP;
818  case 'd':
819  return DOWN;
820  case 'f':
821  return FRONT;
822  case 'b':
823  return BACK;
824  default:
825  return -1;
826  }
827 }
828 
829 /**
830  * Convert char to corresponding rotation angle.
831  * Used for cubemap options.
832  */
833 static int get_rotation(char c)
834 {
835  switch (c) {
836  case '0':
837  return ROT_0;
838  case '1':
839  return ROT_90;
840  case '2':
841  return ROT_180;
842  case '3':
843  return ROT_270;
844  default:
845  return -1;
846  }
847 }
848 
849 /**
850  * Convert char to corresponding rotation order.
851  */
852 static int get_rorder(char c)
853 {
854  switch (c) {
855  case 'Y':
856  case 'y':
857  return YAW;
858  case 'P':
859  case 'p':
860  return PITCH;
861  case 'R':
862  case 'r':
863  return ROLL;
864  default:
865  return -1;
866  }
867 }
868 
869 /**
870  * Prepare data for processing cubemap input format.
871  *
872  * @param ctx filter context
873  *
874  * @return error code
875  */
877 {
878  V360Context *s = ctx->priv;
879 
880  for (int face = 0; face < NB_FACES; face++) {
881  const char c = s->in_forder[face];
882  int direction;
883 
884  if (c == '\0') {
886  "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
887  return AVERROR(EINVAL);
888  }
889 
890  direction = get_direction(c);
891  if (direction == -1) {
893  "Incorrect direction symbol '%c' in in_forder option.\n", c);
894  return AVERROR(EINVAL);
895  }
896 
897  s->in_cubemap_face_order[direction] = face;
898  }
899 
900  for (int face = 0; face < NB_FACES; face++) {
901  const char c = s->in_frot[face];
902  int rotation;
903 
904  if (c == '\0') {
906  "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
907  return AVERROR(EINVAL);
908  }
909 
910  rotation = get_rotation(c);
911  if (rotation == -1) {
913  "Incorrect rotation symbol '%c' in in_frot option.\n", c);
914  return AVERROR(EINVAL);
915  }
916 
917  s->in_cubemap_face_rotation[face] = rotation;
918  }
919 
920  return 0;
921 }
922 
923 /**
924  * Prepare data for processing cubemap output format.
925  *
926  * @param ctx filter context
927  *
928  * @return error code
929  */
931 {
932  V360Context *s = ctx->priv;
933 
934  for (int face = 0; face < NB_FACES; face++) {
935  const char c = s->out_forder[face];
936  int direction;
937 
938  if (c == '\0') {
940  "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
941  return AVERROR(EINVAL);
942  }
943 
944  direction = get_direction(c);
945  if (direction == -1) {
947  "Incorrect direction symbol '%c' in out_forder option.\n", c);
948  return AVERROR(EINVAL);
949  }
950 
951  s->out_cubemap_direction_order[face] = direction;
952  }
953 
954  for (int face = 0; face < NB_FACES; face++) {
955  const char c = s->out_frot[face];
956  int rotation;
957 
958  if (c == '\0') {
960  "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
961  return AVERROR(EINVAL);
962  }
963 
964  rotation = get_rotation(c);
965  if (rotation == -1) {
967  "Incorrect rotation symbol '%c' in out_frot option.\n", c);
968  return AVERROR(EINVAL);
969  }
970 
971  s->out_cubemap_face_rotation[face] = rotation;
972  }
973 
974  return 0;
975 }
976 
977 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
978 {
979  float tmp;
980 
981  switch (rotation) {
982  case ROT_0:
983  break;
984  case ROT_90:
985  tmp = *uf;
986  *uf = -*vf;
987  *vf = tmp;
988  break;
989  case ROT_180:
990  *uf = -*uf;
991  *vf = -*vf;
992  break;
993  case ROT_270:
994  tmp = -*uf;
995  *uf = *vf;
996  *vf = tmp;
997  break;
998  default:
999  av_assert0(0);
1000  }
1001 }
1002 
1003 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
1004 {
1005  float tmp;
1006 
1007  switch (rotation) {
1008  case ROT_0:
1009  break;
1010  case ROT_90:
1011  tmp = -*uf;
1012  *uf = *vf;
1013  *vf = tmp;
1014  break;
1015  case ROT_180:
1016  *uf = -*uf;
1017  *vf = -*vf;
1018  break;
1019  case ROT_270:
1020  tmp = *uf;
1021  *uf = -*vf;
1022  *vf = tmp;
1023  break;
1024  default:
1025  av_assert0(0);
1026  }
1027 }
1028 
1029 /**
1030  * Normalize vector.
1031  *
1032  * @param vec vector
1033  */
1034 static void normalize_vector(float *vec)
1035 {
1036  const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
1037 
1038  vec[0] /= norm;
1039  vec[1] /= norm;
1040  vec[2] /= norm;
1041 }
1042 
1043 /**
1044  * Calculate 3D coordinates on sphere for corresponding cubemap position.
1045  * Common operation for every cubemap.
1046  *
1047  * @param s filter private context
1048  * @param uf horizontal cubemap coordinate [0, 1)
1049  * @param vf vertical cubemap coordinate [0, 1)
1050  * @param face face of cubemap
1051  * @param vec coordinates on sphere
1052  * @param scalew scale for uf
1053  * @param scaleh scale for vf
1054  */
1055 static void cube_to_xyz(const V360Context *s,
1056  float uf, float vf, int face,
1057  float *vec, float scalew, float scaleh)
1058 {
1059  const int direction = s->out_cubemap_direction_order[face];
1060  float l_x, l_y, l_z;
1061 
1062  uf /= scalew;
1063  vf /= scaleh;
1064 
1065  rotate_cube_face_inverse(&uf, &vf, s->out_cubemap_face_rotation[face]);
1066 
1067  switch (direction) {
1068  case RIGHT:
1069  l_x = 1.f;
1070  l_y = vf;
1071  l_z = -uf;
1072  break;
1073  case LEFT:
1074  l_x = -1.f;
1075  l_y = vf;
1076  l_z = uf;
1077  break;
1078  case UP:
1079  l_x = uf;
1080  l_y = -1.f;
1081  l_z = vf;
1082  break;
1083  case DOWN:
1084  l_x = uf;
1085  l_y = 1.f;
1086  l_z = -vf;
1087  break;
1088  case FRONT:
1089  l_x = uf;
1090  l_y = vf;
1091  l_z = 1.f;
1092  break;
1093  case BACK:
1094  l_x = -uf;
1095  l_y = vf;
1096  l_z = -1.f;
1097  break;
1098  default:
1099  av_assert0(0);
1100  }
1101 
1102  vec[0] = l_x;
1103  vec[1] = l_y;
1104  vec[2] = l_z;
1105 
1106  normalize_vector(vec);
1107 }
1108 
1109 /**
1110  * Calculate cubemap position for corresponding 3D coordinates on sphere.
1111  * Common operation for every cubemap.
1112  *
1113  * @param s filter private context
1114  * @param vec coordinated on sphere
1115  * @param uf horizontal cubemap coordinate [0, 1)
1116  * @param vf vertical cubemap coordinate [0, 1)
1117  * @param direction direction of view
1118  */
1119 static void xyz_to_cube(const V360Context *s,
1120  const float *vec,
1121  float *uf, float *vf, int *direction)
1122 {
1123  const float phi = atan2f(vec[0], vec[2]);
1124  const float theta = asinf(vec[1]);
1125  float phi_norm, theta_threshold;
1126  int face;
1127 
1128  if (phi >= -M_PI_4 && phi < M_PI_4) {
1129  *direction = FRONT;
1130  phi_norm = phi;
1131  } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
1132  *direction = LEFT;
1133  phi_norm = phi + M_PI_2;
1134  } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
1135  *direction = RIGHT;
1136  phi_norm = phi - M_PI_2;
1137  } else {
1138  *direction = BACK;
1139  phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
1140  }
1141 
1142  theta_threshold = atanf(cosf(phi_norm));
1143  if (theta > theta_threshold) {
1144  *direction = DOWN;
1145  } else if (theta < -theta_threshold) {
1146  *direction = UP;
1147  }
1148 
1149  switch (*direction) {
1150  case RIGHT:
1151  *uf = -vec[2] / vec[0];
1152  *vf = vec[1] / vec[0];
1153  break;
1154  case LEFT:
1155  *uf = -vec[2] / vec[0];
1156  *vf = -vec[1] / vec[0];
1157  break;
1158  case UP:
1159  *uf = -vec[0] / vec[1];
1160  *vf = -vec[2] / vec[1];
1161  break;
1162  case DOWN:
1163  *uf = vec[0] / vec[1];
1164  *vf = -vec[2] / vec[1];
1165  break;
1166  case FRONT:
1167  *uf = vec[0] / vec[2];
1168  *vf = vec[1] / vec[2];
1169  break;
1170  case BACK:
1171  *uf = vec[0] / vec[2];
1172  *vf = -vec[1] / vec[2];
1173  break;
1174  default:
1175  av_assert0(0);
1176  }
1177 
1178  face = s->in_cubemap_face_order[*direction];
1179  rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
1180 }
1181 
1182 /**
1183  * Find position on another cube face in case of overflow/underflow.
1184  * Used for calculation of interpolation window.
1185  *
1186  * @param s filter private context
1187  * @param uf horizontal cubemap coordinate
1188  * @param vf vertical cubemap coordinate
1189  * @param direction direction of view
1190  * @param new_uf new horizontal cubemap coordinate
1191  * @param new_vf new vertical cubemap coordinate
1192  * @param face face position on cubemap
1193  */
1195  float uf, float vf, int direction,
1196  float *new_uf, float *new_vf, int *face)
1197 {
1198  /*
1199  * Cubemap orientation
1200  *
1201  * width
1202  * <------->
1203  * +-------+
1204  * | | U
1205  * | up | h ------->
1206  * +-------+-------+-------+-------+ ^ e |
1207  * | | | | | | i V |
1208  * | left | front | right | back | | g |
1209  * +-------+-------+-------+-------+ v h v
1210  * | | t
1211  * | down |
1212  * +-------+
1213  */
1214 
1215  *face = s->in_cubemap_face_order[direction];
1216  rotate_cube_face_inverse(&uf, &vf, s->in_cubemap_face_rotation[*face]);
1217 
1218  if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
1219  // There are no pixels to use in this case
1220  *new_uf = uf;
1221  *new_vf = vf;
1222  } else if (uf < -1.f) {
1223  uf += 2.f;
1224  switch (direction) {
1225  case RIGHT:
1226  direction = FRONT;
1227  *new_uf = uf;
1228  *new_vf = vf;
1229  break;
1230  case LEFT:
1231  direction = BACK;
1232  *new_uf = uf;
1233  *new_vf = vf;
1234  break;
1235  case UP:
1236  direction = LEFT;
1237  *new_uf = vf;
1238  *new_vf = -uf;
1239  break;
1240  case DOWN:
1241  direction = LEFT;
1242  *new_uf = -vf;
1243  *new_vf = uf;
1244  break;
1245  case FRONT:
1246  direction = LEFT;
1247  *new_uf = uf;
1248  *new_vf = vf;
1249  break;
1250  case BACK:
1251  direction = RIGHT;
1252  *new_uf = uf;
1253  *new_vf = vf;
1254  break;
1255  default:
1256  av_assert0(0);
1257  }
1258  } else if (uf >= 1.f) {
1259  uf -= 2.f;
1260  switch (direction) {
1261  case RIGHT:
1262  direction = BACK;
1263  *new_uf = uf;
1264  *new_vf = vf;
1265  break;
1266  case LEFT:
1267  direction = FRONT;
1268  *new_uf = uf;
1269  *new_vf = vf;
1270  break;
1271  case UP:
1272  direction = RIGHT;
1273  *new_uf = -vf;
1274  *new_vf = uf;
1275  break;
1276  case DOWN:
1277  direction = RIGHT;
1278  *new_uf = vf;
1279  *new_vf = -uf;
1280  break;
1281  case FRONT:
1282  direction = RIGHT;
1283  *new_uf = uf;
1284  *new_vf = vf;
1285  break;
1286  case BACK:
1287  direction = LEFT;
1288  *new_uf = uf;
1289  *new_vf = vf;
1290  break;
1291  default:
1292  av_assert0(0);
1293  }
1294  } else if (vf < -1.f) {
1295  vf += 2.f;
1296  switch (direction) {
1297  case RIGHT:
1298  direction = UP;
1299  *new_uf = vf;
1300  *new_vf = -uf;
1301  break;
1302  case LEFT:
1303  direction = UP;
1304  *new_uf = -vf;
1305  *new_vf = uf;
1306  break;
1307  case UP:
1308  direction = BACK;
1309  *new_uf = -uf;
1310  *new_vf = -vf;
1311  break;
1312  case DOWN:
1313  direction = FRONT;
1314  *new_uf = uf;
1315  *new_vf = vf;
1316  break;
1317  case FRONT:
1318  direction = UP;
1319  *new_uf = uf;
1320  *new_vf = vf;
1321  break;
1322  case BACK:
1323  direction = UP;
1324  *new_uf = -uf;
1325  *new_vf = -vf;
1326  break;
1327  default:
1328  av_assert0(0);
1329  }
1330  } else if (vf >= 1.f) {
1331  vf -= 2.f;
1332  switch (direction) {
1333  case RIGHT:
1334  direction = DOWN;
1335  *new_uf = -vf;
1336  *new_vf = uf;
1337  break;
1338  case LEFT:
1339  direction = DOWN;
1340  *new_uf = vf;
1341  *new_vf = -uf;
1342  break;
1343  case UP:
1344  direction = FRONT;
1345  *new_uf = uf;
1346  *new_vf = vf;
1347  break;
1348  case DOWN:
1349  direction = BACK;
1350  *new_uf = -uf;
1351  *new_vf = -vf;
1352  break;
1353  case FRONT:
1354  direction = DOWN;
1355  *new_uf = uf;
1356  *new_vf = vf;
1357  break;
1358  case BACK:
1359  direction = DOWN;
1360  *new_uf = -uf;
1361  *new_vf = -vf;
1362  break;
1363  default:
1364  av_assert0(0);
1365  }
1366  } else {
1367  // Inside cube face
1368  *new_uf = uf;
1369  *new_vf = vf;
1370  }
1371 
1372  *face = s->in_cubemap_face_order[direction];
1373  rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1374 }
1375 
1376 static av_always_inline float scale(float x, float s)
1377 {
1378  return (0.5f * x + 0.5f) * (s - 1.f);
1379 }
1380 
1381 static av_always_inline float rescale(int x, float s)
1382 {
1383  return (2.f * x + 1.f) / s - 1.f;
1384 }
1385 
1386 /**
1387  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1388  *
1389  * @param s filter private context
1390  * @param i horizontal position on frame [0, width)
1391  * @param j vertical position on frame [0, height)
1392  * @param width frame width
1393  * @param height frame height
1394  * @param vec coordinates on sphere
1395  */
1396 static int cube3x2_to_xyz(const V360Context *s,
1397  int i, int j, int width, int height,
1398  float *vec)
1399 {
1400  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad;
1401  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
1402 
1403  const float ew = width / 3.f;
1404  const float eh = height / 2.f;
1405 
1406  const int u_face = floorf(i / ew);
1407  const int v_face = floorf(j / eh);
1408  const int face = u_face + 3 * v_face;
1409 
1410  const int u_shift = ceilf(ew * u_face);
1411  const int v_shift = ceilf(eh * v_face);
1412  const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1413  const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1414 
1415  const float uf = rescale(i - u_shift, ewi);
1416  const float vf = rescale(j - v_shift, ehi);
1417 
1418  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1419 
1420  return 1;
1421 }
1422 
1423 /**
1424  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1425  *
1426  * @param s filter private context
1427  * @param vec coordinates on sphere
1428  * @param width frame width
1429  * @param height frame height
1430  * @param us horizontal coordinates for interpolation window
1431  * @param vs vertical coordinates for interpolation window
1432  * @param du horizontal relative coordinate
1433  * @param dv vertical relative coordinate
1434  */
1435 static int xyz_to_cube3x2(const V360Context *s,
1436  const float *vec, int width, int height,
1437  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1438 {
1439  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad;
1440  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
1441  const float ew = width / 3.f;
1442  const float eh = height / 2.f;
1443  float uf, vf;
1444  int ui, vi;
1445  int ewi, ehi;
1446  int direction, face;
1447  int u_face, v_face;
1448 
1449  xyz_to_cube(s, vec, &uf, &vf, &direction);
1450 
1451  uf *= scalew;
1452  vf *= scaleh;
1453 
1454  face = s->in_cubemap_face_order[direction];
1455  u_face = face % 3;
1456  v_face = face / 3;
1457  ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1458  ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1459 
1460  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1461  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1462 
1463  ui = floorf(uf);
1464  vi = floorf(vf);
1465 
1466  *du = uf - ui;
1467  *dv = vf - vi;
1468 
1469  for (int i = 0; i < 4; i++) {
1470  for (int j = 0; j < 4; j++) {
1471  int new_ui = ui + j - 1;
1472  int new_vi = vi + i - 1;
1473  int u_shift, v_shift;
1474  int new_ewi, new_ehi;
1475 
1476  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1477  face = s->in_cubemap_face_order[direction];
1478 
1479  u_face = face % 3;
1480  v_face = face / 3;
1481  u_shift = ceilf(ew * u_face);
1482  v_shift = ceilf(eh * v_face);
1483  } else {
1484  uf = 2.f * new_ui / ewi - 1.f;
1485  vf = 2.f * new_vi / ehi - 1.f;
1486 
1487  uf /= scalew;
1488  vf /= scaleh;
1489 
1490  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1491 
1492  uf *= scalew;
1493  vf *= scaleh;
1494 
1495  u_face = face % 3;
1496  v_face = face / 3;
1497  u_shift = ceilf(ew * u_face);
1498  v_shift = ceilf(eh * v_face);
1499  new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1500  new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1501 
1502  new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1503  new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1504  }
1505 
1506  us[i][j] = u_shift + new_ui;
1507  vs[i][j] = v_shift + new_vi;
1508  }
1509  }
1510 
1511  return 1;
1512 }
1513 
1514 /**
1515  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1516  *
1517  * @param s filter private context
1518  * @param i horizontal position on frame [0, width)
1519  * @param j vertical position on frame [0, height)
1520  * @param width frame width
1521  * @param height frame height
1522  * @param vec coordinates on sphere
1523  */
1524 static int cube1x6_to_xyz(const V360Context *s,
1525  int i, int j, int width, int height,
1526  float *vec)
1527 {
1528  const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / width : 1.f - s->out_pad;
1529  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 6.f) : 1.f - s->out_pad;
1530 
1531  const float ew = width;
1532  const float eh = height / 6.f;
1533 
1534  const int face = floorf(j / eh);
1535 
1536  const int v_shift = ceilf(eh * face);
1537  const int ehi = ceilf(eh * (face + 1)) - v_shift;
1538 
1539  const float uf = rescale(i, ew);
1540  const float vf = rescale(j - v_shift, ehi);
1541 
1542  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1543 
1544  return 1;
1545 }
1546 
1547 /**
1548  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1549  *
1550  * @param s filter private context
1551  * @param i horizontal position on frame [0, width)
1552  * @param j vertical position on frame [0, height)
1553  * @param width frame width
1554  * @param height frame height
1555  * @param vec coordinates on sphere
1556  */
1557 static int cube6x1_to_xyz(const V360Context *s,
1558  int i, int j, int width, int height,
1559  float *vec)
1560 {
1561  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 6.f) : 1.f - s->out_pad;
1562  const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / height : 1.f - s->out_pad;
1563 
1564  const float ew = width / 6.f;
1565  const float eh = height;
1566 
1567  const int face = floorf(i / ew);
1568 
1569  const int u_shift = ceilf(ew * face);
1570  const int ewi = ceilf(ew * (face + 1)) - u_shift;
1571 
1572  const float uf = rescale(i - u_shift, ewi);
1573  const float vf = rescale(j, eh);
1574 
1575  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1576 
1577  return 1;
1578 }
1579 
1580 /**
1581  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1582  *
1583  * @param s filter private context
1584  * @param vec coordinates on sphere
1585  * @param width frame width
1586  * @param height frame height
1587  * @param us horizontal coordinates for interpolation window
1588  * @param vs vertical coordinates for interpolation window
1589  * @param du horizontal relative coordinate
1590  * @param dv vertical relative coordinate
1591  */
1592 static int xyz_to_cube1x6(const V360Context *s,
1593  const float *vec, int width, int height,
1594  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1595 {
1596  const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / width : 1.f - s->in_pad;
1597  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 6.f) : 1.f - s->in_pad;
1598  const float eh = height / 6.f;
1599  const int ewi = width;
1600  float uf, vf;
1601  int ui, vi;
1602  int ehi;
1603  int direction, face;
1604 
1605  xyz_to_cube(s, vec, &uf, &vf, &direction);
1606 
1607  uf *= scalew;
1608  vf *= scaleh;
1609 
1610  face = s->in_cubemap_face_order[direction];
1611  ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1612 
1613  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1614  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1615 
1616  ui = floorf(uf);
1617  vi = floorf(vf);
1618 
1619  *du = uf - ui;
1620  *dv = vf - vi;
1621 
1622  for (int i = 0; i < 4; i++) {
1623  for (int j = 0; j < 4; j++) {
1624  int new_ui = ui + j - 1;
1625  int new_vi = vi + i - 1;
1626  int v_shift;
1627  int new_ehi;
1628 
1629  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1630  face = s->in_cubemap_face_order[direction];
1631 
1632  v_shift = ceilf(eh * face);
1633  } else {
1634  uf = 2.f * new_ui / ewi - 1.f;
1635  vf = 2.f * new_vi / ehi - 1.f;
1636 
1637  uf /= scalew;
1638  vf /= scaleh;
1639 
1640  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1641 
1642  uf *= scalew;
1643  vf *= scaleh;
1644 
1645  v_shift = ceilf(eh * face);
1646  new_ehi = ceilf(eh * (face + 1)) - v_shift;
1647 
1648  new_ui = av_clip(lrintf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1);
1649  new_vi = av_clip(lrintf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1650  }
1651 
1652  us[i][j] = new_ui;
1653  vs[i][j] = v_shift + new_vi;
1654  }
1655  }
1656 
1657  return 1;
1658 }
1659 
1660 /**
1661  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1662  *
1663  * @param s filter private context
1664  * @param vec coordinates on sphere
1665  * @param width frame width
1666  * @param height frame height
1667  * @param us horizontal coordinates for interpolation window
1668  * @param vs vertical coordinates for interpolation window
1669  * @param du horizontal relative coordinate
1670  * @param dv vertical relative coordinate
1671  */
1672 static int xyz_to_cube6x1(const V360Context *s,
1673  const float *vec, int width, int height,
1674  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1675 {
1676  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 6.f) : 1.f - s->in_pad;
1677  const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / height : 1.f - s->in_pad;
1678  const float ew = width / 6.f;
1679  const int ehi = height;
1680  float uf, vf;
1681  int ui, vi;
1682  int ewi;
1683  int direction, face;
1684 
1685  xyz_to_cube(s, vec, &uf, &vf, &direction);
1686 
1687  uf *= scalew;
1688  vf *= scaleh;
1689 
1690  face = s->in_cubemap_face_order[direction];
1691  ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1692 
1693  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1694  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1695 
1696  ui = floorf(uf);
1697  vi = floorf(vf);
1698 
1699  *du = uf - ui;
1700  *dv = vf - vi;
1701 
1702  for (int i = 0; i < 4; i++) {
1703  for (int j = 0; j < 4; j++) {
1704  int new_ui = ui + j - 1;
1705  int new_vi = vi + i - 1;
1706  int u_shift;
1707  int new_ewi;
1708 
1709  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1710  face = s->in_cubemap_face_order[direction];
1711 
1712  u_shift = ceilf(ew * face);
1713  } else {
1714  uf = 2.f * new_ui / ewi - 1.f;
1715  vf = 2.f * new_vi / ehi - 1.f;
1716 
1717  uf /= scalew;
1718  vf /= scaleh;
1719 
1720  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1721 
1722  uf *= scalew;
1723  vf *= scaleh;
1724 
1725  u_shift = ceilf(ew * face);
1726  new_ewi = ceilf(ew * (face + 1)) - u_shift;
1727 
1728  new_ui = av_clip(lrintf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1729  new_vi = av_clip(lrintf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1);
1730  }
1731 
1732  us[i][j] = u_shift + new_ui;
1733  vs[i][j] = new_vi;
1734  }
1735  }
1736 
1737  return 1;
1738 }
1739 
1740 /**
1741  * Prepare data for processing equirectangular output format.
1742  *
1743  * @param ctx filter context
1744  *
1745  * @return error code
1746  */
1748 {
1749  V360Context *s = ctx->priv;
1750 
1751  s->flat_range[0] = s->h_fov * M_PI / 360.f;
1752  s->flat_range[1] = s->v_fov * M_PI / 360.f;
1753 
1754  return 0;
1755 }
1756 
1757 /**
1758  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1759  *
1760  * @param s filter private context
1761  * @param i horizontal position on frame [0, width)
1762  * @param j vertical position on frame [0, height)
1763  * @param width frame width
1764  * @param height frame height
1765  * @param vec coordinates on sphere
1766  */
1767 static int equirect_to_xyz(const V360Context *s,
1768  int i, int j, int width, int height,
1769  float *vec)
1770 {
1771  const float phi = rescale(i, width) * s->flat_range[0];
1772  const float theta = rescale(j, height) * s->flat_range[1];
1773 
1774  const float sin_phi = sinf(phi);
1775  const float cos_phi = cosf(phi);
1776  const float sin_theta = sinf(theta);
1777  const float cos_theta = cosf(theta);
1778 
1779  vec[0] = cos_theta * sin_phi;
1780  vec[1] = sin_theta;
1781  vec[2] = cos_theta * cos_phi;
1782 
1783  return 1;
1784 }
1785 
1786 /**
1787  * Calculate 3D coordinates on sphere for corresponding frame position in half equirectangular format.
1788  *
1789  * @param s filter private context
1790  * @param i horizontal position on frame [0, width)
1791  * @param j vertical position on frame [0, height)
1792  * @param width frame width
1793  * @param height frame height
1794  * @param vec coordinates on sphere
1795  */
1796 static int hequirect_to_xyz(const V360Context *s,
1797  int i, int j, int width, int height,
1798  float *vec)
1799 {
1800  const float phi = rescale(i, width) * M_PI_2;
1801  const float theta = rescale(j, height) * M_PI_2;
1802 
1803  const float sin_phi = sinf(phi);
1804  const float cos_phi = cosf(phi);
1805  const float sin_theta = sinf(theta);
1806  const float cos_theta = cosf(theta);
1807 
1808  vec[0] = cos_theta * sin_phi;
1809  vec[1] = sin_theta;
1810  vec[2] = cos_theta * cos_phi;
1811 
1812  return 1;
1813 }
1814 
1815 /**
1816  * Prepare data for processing stereographic output format.
1817  *
1818  * @param ctx filter context
1819  *
1820  * @return error code
1821  */
1823 {
1824  V360Context *s = ctx->priv;
1825 
1826  s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
1827  s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
1828 
1829  return 0;
1830 }
1831 
1832 /**
1833  * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
1834  *
1835  * @param s filter private context
1836  * @param i horizontal position on frame [0, width)
1837  * @param j vertical position on frame [0, height)
1838  * @param width frame width
1839  * @param height frame height
1840  * @param vec coordinates on sphere
1841  */
1843  int i, int j, int width, int height,
1844  float *vec)
1845 {
1846  const float x = rescale(i, width) * s->flat_range[0];
1847  const float y = rescale(j, height) * s->flat_range[1];
1848  const float r = hypotf(x, y);
1849  const float theta = atanf(r) * 2.f;
1850  const float sin_theta = sinf(theta);
1851 
1852  vec[0] = x / r * sin_theta;
1853  vec[1] = y / r * sin_theta;
1854  vec[2] = cosf(theta);
1855 
1856  normalize_vector(vec);
1857 
1858  return 1;
1859 }
1860 
1861 /**
1862  * Prepare data for processing stereographic input format.
1863  *
1864  * @param ctx filter context
1865  *
1866  * @return error code
1867  */
1869 {
1870  V360Context *s = ctx->priv;
1871 
1872  s->iflat_range[0] = tanf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f);
1873  s->iflat_range[1] = tanf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f);
1874 
1875  return 0;
1876 }
1877 
1878 /**
1879  * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
1880  *
1881  * @param s filter private context
1882  * @param vec coordinates on sphere
1883  * @param width frame width
1884  * @param height frame height
1885  * @param us horizontal coordinates for interpolation window
1886  * @param vs vertical coordinates for interpolation window
1887  * @param du horizontal relative coordinate
1888  * @param dv vertical relative coordinate
1889  */
1891  const float *vec, int width, int height,
1892  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1893 {
1894  const float theta = acosf(vec[2]);
1895  const float r = tanf(theta * 0.5f);
1896  const float c = r / hypotf(vec[0], vec[1]);
1897  const float x = vec[0] * c / s->iflat_range[0];
1898  const float y = vec[1] * c / s->iflat_range[1];
1899 
1900  const float uf = scale(x, width);
1901  const float vf = scale(y, height);
1902 
1903  const int ui = floorf(uf);
1904  const int vi = floorf(vf);
1905 
1906  const int visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width;
1907 
1908  *du = visible ? uf - ui : 0.f;
1909  *dv = visible ? vf - vi : 0.f;
1910 
1911  for (int i = 0; i < 4; i++) {
1912  for (int j = 0; j < 4; j++) {
1913  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
1914  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
1915  }
1916  }
1917 
1918  return visible;
1919 }
1920 
1921 /**
1922  * Prepare data for processing equisolid output format.
1923  *
1924  * @param ctx filter context
1925  *
1926  * @return error code
1927  */
1929 {
1930  V360Context *s = ctx->priv;
1931 
1932  s->flat_range[0] = sinf(s->h_fov * M_PI / 720.f);
1933  s->flat_range[1] = sinf(s->v_fov * M_PI / 720.f);
1934 
1935  return 0;
1936 }
1937 
1938 /**
1939  * Calculate 3D coordinates on sphere for corresponding frame position in equisolid format.
1940  *
1941  * @param s filter private context
1942  * @param i horizontal position on frame [0, width)
1943  * @param j vertical position on frame [0, height)
1944  * @param width frame width
1945  * @param height frame height
1946  * @param vec coordinates on sphere
1947  */
1948 static int equisolid_to_xyz(const V360Context *s,
1949  int i, int j, int width, int height,
1950  float *vec)
1951 {
1952  const float x = rescale(i, width) * s->flat_range[0];
1953  const float y = rescale(j, height) * s->flat_range[1];
1954  const float r = hypotf(x, y);
1955  const float theta = asinf(r) * 2.f;
1956  const float sin_theta = sinf(theta);
1957 
1958  vec[0] = x / r * sin_theta;
1959  vec[1] = y / r * sin_theta;
1960  vec[2] = cosf(theta);
1961 
1962  normalize_vector(vec);
1963 
1964  return 1;
1965 }
1966 
1967 /**
1968  * Prepare data for processing equisolid input format.
1969  *
1970  * @param ctx filter context
1971  *
1972  * @return error code
1973  */
1975 {
1976  V360Context *s = ctx->priv;
1977 
1978  s->iflat_range[0] = sinf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f);
1979  s->iflat_range[1] = sinf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f);
1980 
1981  return 0;
1982 }
1983 
1984 /**
1985  * Calculate frame position in equisolid format for corresponding 3D coordinates on sphere.
1986  *
1987  * @param s filter private context
1988  * @param vec coordinates on sphere
1989  * @param width frame width
1990  * @param height frame height
1991  * @param us horizontal coordinates for interpolation window
1992  * @param vs vertical coordinates for interpolation window
1993  * @param du horizontal relative coordinate
1994  * @param dv vertical relative coordinate
1995  */
1996 static int xyz_to_equisolid(const V360Context *s,
1997  const float *vec, int width, int height,
1998  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
1999 {
2000  const float theta = acosf(vec[2]);
2001  const float r = sinf(theta * 0.5f);
2002  const float c = r / hypotf(vec[0], vec[1]);
2003  const float x = vec[0] * c / s->iflat_range[0];
2004  const float y = vec[1] * c / s->iflat_range[1];
2005 
2006  const float uf = scale(x, width);
2007  const float vf = scale(y, height);
2008 
2009  const int ui = floorf(uf);
2010  const int vi = floorf(vf);
2011 
2012  const int visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width;
2013 
2014  *du = visible ? uf - ui : 0.f;
2015  *dv = visible ? vf - vi : 0.f;
2016 
2017  for (int i = 0; i < 4; i++) {
2018  for (int j = 0; j < 4; j++) {
2019  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
2020  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2021  }
2022  }
2023 
2024  return visible;
2025 }
2026 
2027 /**
2028  * Prepare data for processing orthographic output format.
2029  *
2030  * @param ctx filter context
2031  *
2032  * @return error code
2033  */
2035 {
2036  V360Context *s = ctx->priv;
2037 
2038  s->flat_range[0] = sinf(FFMIN(s->h_fov, 180.f) * M_PI / 360.f);
2039  s->flat_range[1] = sinf(FFMIN(s->v_fov, 180.f) * M_PI / 360.f);
2040 
2041  return 0;
2042 }
2043 
2044 /**
2045  * Calculate 3D coordinates on sphere for corresponding frame position in orthographic format.
2046  *
2047  * @param s filter private context
2048  * @param i horizontal position on frame [0, width)
2049  * @param j vertical position on frame [0, height)
2050  * @param width frame width
2051  * @param height frame height
2052  * @param vec coordinates on sphere
2053  */
2055  int i, int j, int width, int height,
2056  float *vec)
2057 {
2058  const float x = rescale(i, width) * s->flat_range[0];
2059  const float y = rescale(j, height) * s->flat_range[1];
2060  const float r = hypotf(x, y);
2061  const float theta = asinf(r);
2062 
2063  vec[2] = cosf(theta);
2064  if (vec[2] > 0) {
2065  vec[0] = x;
2066  vec[1] = y;
2067 
2068  normalize_vector(vec);
2069  return 1;
2070  } else {
2071  vec[0] = 0;
2072  vec[1] = 0;
2073  vec[2] = 1;
2074 
2075  return 0;
2076  }
2077 }
2078 
2079 /**
2080  * Prepare data for processing orthographic input format.
2081  *
2082  * @param ctx filter context
2083  *
2084  * @return error code
2085  */
2087 {
2088  V360Context *s = ctx->priv;
2089 
2090  s->iflat_range[0] = sinf(FFMIN(s->ih_fov, 180.f) * M_PI / 360.f);
2091  s->iflat_range[1] = sinf(FFMIN(s->iv_fov, 180.f) * M_PI / 360.f);
2092 
2093  return 0;
2094 }
2095 
2096 /**
2097  * Calculate frame position in orthographic format for corresponding 3D coordinates on sphere.
2098  *
2099  * @param s filter private context
2100  * @param vec coordinates on sphere
2101  * @param width frame width
2102  * @param height frame height
2103  * @param us horizontal coordinates for interpolation window
2104  * @param vs vertical coordinates for interpolation window
2105  * @param du horizontal relative coordinate
2106  * @param dv vertical relative coordinate
2107  */
2109  const float *vec, int width, int height,
2110  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2111 {
2112  const float theta = acosf(vec[2]);
2113  const float r = sinf(theta);
2114  const float c = r / hypotf(vec[0], vec[1]);
2115  const float x = vec[0] * c / s->iflat_range[0];
2116  const float y = vec[1] * c / s->iflat_range[1];
2117 
2118  const float uf = scale(x, width);
2119  const float vf = scale(y, height);
2120 
2121  const int ui = floorf(uf);
2122  const int vi = floorf(vf);
2123 
2124  const int visible = vec[2] >= 0.f && isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width;
2125 
2126  *du = visible ? uf - ui : 0.f;
2127  *dv = visible ? vf - vi : 0.f;
2128 
2129  for (int i = 0; i < 4; i++) {
2130  for (int j = 0; j < 4; j++) {
2131  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
2132  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2133  }
2134  }
2135 
2136  return visible;
2137 }
2138 
2139 /**
2140  * Prepare data for processing equirectangular input format.
2141  *
2142  * @param ctx filter context
2143  *
2144  * @return error code
2145  */
2147 {
2148  V360Context *s = ctx->priv;
2149 
2150  s->iflat_range[0] = s->ih_fov * M_PI / 360.f;
2151  s->iflat_range[1] = s->iv_fov * M_PI / 360.f;
2152 
2153  return 0;
2154 }
2155 
2156 /**
2157  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
2158  *
2159  * @param s filter private context
2160  * @param vec coordinates on sphere
2161  * @param width frame width
2162  * @param height frame height
2163  * @param us horizontal coordinates for interpolation window
2164  * @param vs vertical coordinates for interpolation window
2165  * @param du horizontal relative coordinate
2166  * @param dv vertical relative coordinate
2167  */
2168 static int xyz_to_equirect(const V360Context *s,
2169  const float *vec, int width, int height,
2170  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2171 {
2172  const float phi = atan2f(vec[0], vec[2]) / s->iflat_range[0];
2173  const float theta = asinf(vec[1]) / s->iflat_range[1];
2174 
2175  const float uf = scale(phi, width);
2176  const float vf = scale(theta, height);
2177 
2178  const int ui = floorf(uf);
2179  const int vi = floorf(vf);
2180  int visible;
2181 
2182  *du = uf - ui;
2183  *dv = vf - vi;
2184 
2185  visible = vi >= 0 && vi < height && ui >= 0 && ui < width;
2186 
2187  for (int i = 0; i < 4; i++) {
2188  for (int j = 0; j < 4; j++) {
2189  us[i][j] = ereflectx(ui + j - 1, vi + i - 1, width, height);
2190  vs[i][j] = reflecty(vi + i - 1, height);
2191  }
2192  }
2193 
2194  return visible;
2195 }
2196 
2197 /**
2198  * Calculate frame position in half equirectangular format for corresponding 3D coordinates on sphere.
2199  *
2200  * @param s filter private context
2201  * @param vec coordinates on sphere
2202  * @param width frame width
2203  * @param height frame height
2204  * @param us horizontal coordinates for interpolation window
2205  * @param vs vertical coordinates for interpolation window
2206  * @param du horizontal relative coordinate
2207  * @param dv vertical relative coordinate
2208  */
2209 static int xyz_to_hequirect(const V360Context *s,
2210  const float *vec, int width, int height,
2211  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2212 {
2213  const float phi = atan2f(vec[0], vec[2]) / M_PI_2;
2214  const float theta = asinf(vec[1]) / M_PI_2;
2215 
2216  const float uf = scale(phi, width);
2217  const float vf = scale(theta, height);
2218 
2219  const int ui = floorf(uf);
2220  const int vi = floorf(vf);
2221 
2222  const int visible = phi >= -M_PI_2 && phi <= M_PI_2;
2223 
2224  *du = uf - ui;
2225  *dv = vf - vi;
2226 
2227  for (int i = 0; i < 4; i++) {
2228  for (int j = 0; j < 4; j++) {
2229  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2230  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2231  }
2232  }
2233 
2234  return visible;
2235 }
2236 
2237 /**
2238  * Prepare data for processing flat input format.
2239  *
2240  * @param ctx filter context
2241  *
2242  * @return error code
2243  */
2245 {
2246  V360Context *s = ctx->priv;
2247 
2248  s->iflat_range[0] = tanf(0.5f * s->ih_fov * M_PI / 180.f);
2249  s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
2250 
2251  return 0;
2252 }
2253 
2254 /**
2255  * Calculate frame position in flat format for corresponding 3D coordinates on sphere.
2256  *
2257  * @param s filter private context
2258  * @param vec coordinates on sphere
2259  * @param width frame width
2260  * @param height frame height
2261  * @param us horizontal coordinates for interpolation window
2262  * @param vs vertical coordinates for interpolation window
2263  * @param du horizontal relative coordinate
2264  * @param dv vertical relative coordinate
2265  */
2266 static int xyz_to_flat(const V360Context *s,
2267  const float *vec, int width, int height,
2268  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2269 {
2270  const float theta = acosf(vec[2]);
2271  const float r = tanf(theta);
2272  const float rr = fabsf(r) < 1e+6f ? r : hypotf(width, height);
2273  const float zf = vec[2];
2274  const float h = hypotf(vec[0], vec[1]);
2275  const float c = h <= 1e-6f ? 1.f : rr / h;
2276  float uf = vec[0] * c / s->iflat_range[0];
2277  float vf = vec[1] * c / s->iflat_range[1];
2278  int visible, ui, vi;
2279 
2280  uf = zf >= 0.f ? scale(uf, width) : 0.f;
2281  vf = zf >= 0.f ? scale(vf, height) : 0.f;
2282 
2283  ui = floorf(uf);
2284  vi = floorf(vf);
2285 
2286  visible = vi >= 0 && vi < height && ui >= 0 && ui < width && zf >= 0.f;
2287 
2288  *du = uf - ui;
2289  *dv = vf - vi;
2290 
2291  for (int i = 0; i < 4; i++) {
2292  for (int j = 0; j < 4; j++) {
2293  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
2294  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2295  }
2296  }
2297 
2298  return visible;
2299 }
2300 
2301 /**
2302  * Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
2303  *
2304  * @param s filter private context
2305  * @param vec coordinates on sphere
2306  * @param width frame width
2307  * @param height frame height
2308  * @param us horizontal coordinates for interpolation window
2309  * @param vs vertical coordinates for interpolation window
2310  * @param du horizontal relative coordinate
2311  * @param dv vertical relative coordinate
2312  */
2313 static int xyz_to_mercator(const V360Context *s,
2314  const float *vec, int width, int height,
2315  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2316 {
2317  const float phi = atan2f(vec[0], vec[2]) / M_PI;
2318  const float theta = av_clipf(logf((1.f + vec[1]) / (1.f - vec[1])) / (2.f * M_PI), -1.f, 1.f);
2319 
2320  const float uf = scale(phi, width);
2321  const float vf = scale(theta, height);
2322 
2323  const int ui = floorf(uf);
2324  const int vi = floorf(vf);
2325 
2326  *du = uf - ui;
2327  *dv = vf - vi;
2328 
2329  for (int i = 0; i < 4; i++) {
2330  for (int j = 0; j < 4; j++) {
2331  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2332  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2333  }
2334  }
2335 
2336  return 1;
2337 }
2338 
2339 /**
2340  * Calculate 3D coordinates on sphere for corresponding frame position in mercator format.
2341  *
2342  * @param s filter private context
2343  * @param i horizontal position on frame [0, width)
2344  * @param j vertical position on frame [0, height)
2345  * @param width frame width
2346  * @param height frame height
2347  * @param vec coordinates on sphere
2348  */
2349 static int mercator_to_xyz(const V360Context *s,
2350  int i, int j, int width, int height,
2351  float *vec)
2352 {
2353  const float phi = rescale(i, width) * M_PI + M_PI_2;
2354  const float y = rescale(j, height) * M_PI;
2355  const float div = expf(2.f * y) + 1.f;
2356 
2357  const float sin_phi = sinf(phi);
2358  const float cos_phi = cosf(phi);
2359  const float sin_theta = 2.f * expf(y) / div;
2360  const float cos_theta = (expf(2.f * y) - 1.f) / div;
2361 
2362  vec[0] = -sin_theta * cos_phi;
2363  vec[1] = cos_theta;
2364  vec[2] = sin_theta * sin_phi;
2365 
2366  return 1;
2367 }
2368 
2369 /**
2370  * Calculate frame position in ball format for corresponding 3D coordinates on sphere.
2371  *
2372  * @param s filter private context
2373  * @param vec coordinates on sphere
2374  * @param width frame width
2375  * @param height frame height
2376  * @param us horizontal coordinates for interpolation window
2377  * @param vs vertical coordinates for interpolation window
2378  * @param du horizontal relative coordinate
2379  * @param dv vertical relative coordinate
2380  */
2381 static int xyz_to_ball(const V360Context *s,
2382  const float *vec, int width, int height,
2383  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2384 {
2385  const float l = hypotf(vec[0], vec[1]);
2386  const float r = sqrtf(1.f - vec[2]) / M_SQRT2;
2387  const float d = l > 0.f ? l : 1.f;
2388 
2389  const float uf = scale(r * vec[0] / d, width);
2390  const float vf = scale(r * vec[1] / d, height);
2391 
2392  const int ui = floorf(uf);
2393  const int vi = floorf(vf);
2394 
2395  *du = uf - ui;
2396  *dv = vf - vi;
2397 
2398  for (int i = 0; i < 4; i++) {
2399  for (int j = 0; j < 4; j++) {
2400  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2401  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2402  }
2403  }
2404 
2405  return 1;
2406 }
2407 
2408 /**
2409  * Calculate 3D coordinates on sphere for corresponding frame position in ball format.
2410  *
2411  * @param s filter private context
2412  * @param i horizontal position on frame [0, width)
2413  * @param j vertical position on frame [0, height)
2414  * @param width frame width
2415  * @param height frame height
2416  * @param vec coordinates on sphere
2417  */
2418 static int ball_to_xyz(const V360Context *s,
2419  int i, int j, int width, int height,
2420  float *vec)
2421 {
2422  const float x = rescale(i, width);
2423  const float y = rescale(j, height);
2424  const float l = hypotf(x, y);
2425 
2426  if (l <= 1.f) {
2427  const float z = 2.f * l * sqrtf(1.f - l * l);
2428 
2429  vec[0] = z * x / (l > 0.f ? l : 1.f);
2430  vec[1] = z * y / (l > 0.f ? l : 1.f);
2431  vec[2] = 1.f - 2.f * l * l;
2432  } else {
2433  vec[0] = 0.f;
2434  vec[1] = 1.f;
2435  vec[2] = 0.f;
2436  return 0;
2437  }
2438 
2439  return 1;
2440 }
2441 
2442 /**
2443  * Calculate 3D coordinates on sphere for corresponding frame position in hammer format.
2444  *
2445  * @param s filter private context
2446  * @param i horizontal position on frame [0, width)
2447  * @param j vertical position on frame [0, height)
2448  * @param width frame width
2449  * @param height frame height
2450  * @param vec coordinates on sphere
2451  */
2452 static int hammer_to_xyz(const V360Context *s,
2453  int i, int j, int width, int height,
2454  float *vec)
2455 {
2456  const float x = rescale(i, width);
2457  const float y = rescale(j, height);
2458 
2459  const float xx = x * x;
2460  const float yy = y * y;
2461 
2462  const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f);
2463 
2464  const float a = M_SQRT2 * x * z;
2465  const float b = 2.f * z * z - 1.f;
2466 
2467  const float aa = a * a;
2468  const float bb = b * b;
2469 
2470  const float w = sqrtf(1.f - 2.f * yy * z * z);
2471 
2472  vec[0] = w * 2.f * a * b / (aa + bb);
2473  vec[1] = M_SQRT2 * y * z;
2474  vec[2] = w * (bb - aa) / (aa + bb);
2475 
2476  normalize_vector(vec);
2477 
2478  return 1;
2479 }
2480 
2481 /**
2482  * Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
2483  *
2484  * @param s filter private context
2485  * @param vec coordinates on sphere
2486  * @param width frame width
2487  * @param height frame height
2488  * @param us horizontal coordinates for interpolation window
2489  * @param vs vertical coordinates for interpolation window
2490  * @param du horizontal relative coordinate
2491  * @param dv vertical relative coordinate
2492  */
2493 static int xyz_to_hammer(const V360Context *s,
2494  const float *vec, int width, int height,
2495  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2496 {
2497  const float theta = atan2f(vec[0], vec[2]);
2498 
2499  const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f));
2500  const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z;
2501  const float y = vec[1] / z;
2502 
2503  const float uf = (x + 1.f) * width / 2.f;
2504  const float vf = (y + 1.f) * height / 2.f;
2505 
2506  const int ui = floorf(uf);
2507  const int vi = floorf(vf);
2508 
2509  *du = uf - ui;
2510  *dv = vf - vi;
2511 
2512  for (int i = 0; i < 4; i++) {
2513  for (int j = 0; j < 4; j++) {
2514  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2515  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2516  }
2517  }
2518 
2519  return 1;
2520 }
2521 
2522 /**
2523  * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format.
2524  *
2525  * @param s filter private context
2526  * @param i horizontal position on frame [0, width)
2527  * @param j vertical position on frame [0, height)
2528  * @param width frame width
2529  * @param height frame height
2530  * @param vec coordinates on sphere
2531  */
2532 static int sinusoidal_to_xyz(const V360Context *s,
2533  int i, int j, int width, int height,
2534  float *vec)
2535 {
2536  const float theta = rescale(j, height) * M_PI_2;
2537  const float phi = rescale(i, width) * M_PI / cosf(theta);
2538 
2539  const float sin_phi = sinf(phi);
2540  const float cos_phi = cosf(phi);
2541  const float sin_theta = sinf(theta);
2542  const float cos_theta = cosf(theta);
2543 
2544  vec[0] = cos_theta * sin_phi;
2545  vec[1] = sin_theta;
2546  vec[2] = cos_theta * cos_phi;
2547 
2548  normalize_vector(vec);
2549 
2550  return 1;
2551 }
2552 
2553 /**
2554  * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere.
2555  *
2556  * @param s filter private context
2557  * @param vec coordinates on sphere
2558  * @param width frame width
2559  * @param height frame height
2560  * @param us horizontal coordinates for interpolation window
2561  * @param vs vertical coordinates for interpolation window
2562  * @param du horizontal relative coordinate
2563  * @param dv vertical relative coordinate
2564  */
2565 static int xyz_to_sinusoidal(const V360Context *s,
2566  const float *vec, int width, int height,
2567  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2568 {
2569  const float theta = asinf(vec[1]);
2570  const float phi = atan2f(vec[0], vec[2]) * cosf(theta);
2571 
2572  const float uf = scale(phi / M_PI, width);
2573  const float vf = scale(theta / M_PI_2, height);
2574 
2575  const int ui = floorf(uf);
2576  const int vi = floorf(vf);
2577 
2578  *du = uf - ui;
2579  *dv = vf - vi;
2580 
2581  for (int i = 0; i < 4; i++) {
2582  for (int j = 0; j < 4; j++) {
2583  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2584  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2585  }
2586  }
2587 
2588  return 1;
2589 }
2590 
2591 /**
2592  * Prepare data for processing equi-angular cubemap input format.
2593  *
2594  * @param ctx filter context
2595  *
2596  * @return error code
2597  */
2599 {
2600  V360Context *s = ctx->priv;
2601 
2602  s->in_cubemap_face_order[RIGHT] = TOP_RIGHT;
2603  s->in_cubemap_face_order[LEFT] = TOP_LEFT;
2604  s->in_cubemap_face_order[UP] = BOTTOM_RIGHT;
2605  s->in_cubemap_face_order[DOWN] = BOTTOM_LEFT;
2606  s->in_cubemap_face_order[FRONT] = TOP_MIDDLE;
2607  s->in_cubemap_face_order[BACK] = BOTTOM_MIDDLE;
2608 
2609  s->in_cubemap_face_rotation[TOP_LEFT] = ROT_0;
2610  s->in_cubemap_face_rotation[TOP_MIDDLE] = ROT_0;
2611  s->in_cubemap_face_rotation[TOP_RIGHT] = ROT_0;
2612  s->in_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270;
2613  s->in_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
2614  s->in_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270;
2615 
2616  return 0;
2617 }
2618 
2619 /**
2620  * Prepare data for processing equi-angular cubemap output format.
2621  *
2622  * @param ctx filter context
2623  *
2624  * @return error code
2625  */
2627 {
2628  V360Context *s = ctx->priv;
2629 
2630  s->out_cubemap_direction_order[TOP_LEFT] = LEFT;
2631  s->out_cubemap_direction_order[TOP_MIDDLE] = FRONT;
2632  s->out_cubemap_direction_order[TOP_RIGHT] = RIGHT;
2633  s->out_cubemap_direction_order[BOTTOM_LEFT] = DOWN;
2634  s->out_cubemap_direction_order[BOTTOM_MIDDLE] = BACK;
2635  s->out_cubemap_direction_order[BOTTOM_RIGHT] = UP;
2636 
2637  s->out_cubemap_face_rotation[TOP_LEFT] = ROT_0;
2638  s->out_cubemap_face_rotation[TOP_MIDDLE] = ROT_0;
2639  s->out_cubemap_face_rotation[TOP_RIGHT] = ROT_0;
2640  s->out_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270;
2641  s->out_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
2642  s->out_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270;
2643 
2644  return 0;
2645 }
2646 
2647 /**
2648  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
2649  *
2650  * @param s filter private context
2651  * @param i horizontal position on frame [0, width)
2652  * @param j vertical position on frame [0, height)
2653  * @param width frame width
2654  * @param height frame height
2655  * @param vec coordinates on sphere
2656  */
2657 static int eac_to_xyz(const V360Context *s,
2658  int i, int j, int width, int height,
2659  float *vec)
2660 {
2661  const float pixel_pad = 2;
2662  const float u_pad = pixel_pad / width;
2663  const float v_pad = pixel_pad / height;
2664 
2665  int u_face, v_face, face;
2666 
2667  float l_x, l_y, l_z;
2668 
2669  float uf = (i + 0.5f) / width;
2670  float vf = (j + 0.5f) / height;
2671 
2672  // EAC has 2-pixel padding on faces except between faces on the same row
2673  // Padding pixels seems not to be stretched with tangent as regular pixels
2674  // Formulas below approximate original padding as close as I could get experimentally
2675 
2676  // Horizontal padding
2677  uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
2678  if (uf < 0.f) {
2679  u_face = 0;
2680  uf -= 0.5f;
2681  } else if (uf >= 3.f) {
2682  u_face = 2;
2683  uf -= 2.5f;
2684  } else {
2685  u_face = floorf(uf);
2686  uf = fmodf(uf, 1.f) - 0.5f;
2687  }
2688 
2689  // Vertical padding
2690  v_face = floorf(vf * 2.f);
2691  vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
2692 
2693  if (uf >= -0.5f && uf < 0.5f) {
2694  uf = tanf(M_PI_2 * uf);
2695  } else {
2696  uf = 2.f * uf;
2697  }
2698  if (vf >= -0.5f && vf < 0.5f) {
2699  vf = tanf(M_PI_2 * vf);
2700  } else {
2701  vf = 2.f * vf;
2702  }
2703 
2704  face = u_face + 3 * v_face;
2705 
2706  switch (face) {
2707  case TOP_LEFT:
2708  l_x = -1.f;
2709  l_y = vf;
2710  l_z = uf;
2711  break;
2712  case TOP_MIDDLE:
2713  l_x = uf;
2714  l_y = vf;
2715  l_z = 1.f;
2716  break;
2717  case TOP_RIGHT:
2718  l_x = 1.f;
2719  l_y = vf;
2720  l_z = -uf;
2721  break;
2722  case BOTTOM_LEFT:
2723  l_x = -vf;
2724  l_y = 1.f;
2725  l_z = -uf;
2726  break;
2727  case BOTTOM_MIDDLE:
2728  l_x = -vf;
2729  l_y = -uf;
2730  l_z = -1.f;
2731  break;
2732  case BOTTOM_RIGHT:
2733  l_x = -vf;
2734  l_y = -1.f;
2735  l_z = uf;
2736  break;
2737  default:
2738  av_assert0(0);
2739  }
2740 
2741  vec[0] = l_x;
2742  vec[1] = l_y;
2743  vec[2] = l_z;
2744 
2745  normalize_vector(vec);
2746 
2747  return 1;
2748 }
2749 
2750 /**
2751  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
2752  *
2753  * @param s filter private context
2754  * @param vec coordinates on sphere
2755  * @param width frame width
2756  * @param height frame height
2757  * @param us horizontal coordinates for interpolation window
2758  * @param vs vertical coordinates for interpolation window
2759  * @param du horizontal relative coordinate
2760  * @param dv vertical relative coordinate
2761  */
2762 static int xyz_to_eac(const V360Context *s,
2763  const float *vec, int width, int height,
2764  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2765 {
2766  const float pixel_pad = 2;
2767  const float u_pad = pixel_pad / width;
2768  const float v_pad = pixel_pad / height;
2769 
2770  float uf, vf;
2771  int ui, vi;
2772  int direction, face;
2773  int u_face, v_face;
2774 
2775  xyz_to_cube(s, vec, &uf, &vf, &direction);
2776 
2777  face = s->in_cubemap_face_order[direction];
2778  u_face = face % 3;
2779  v_face = face / 3;
2780 
2781  uf = M_2_PI * atanf(uf) + 0.5f;
2782  vf = M_2_PI * atanf(vf) + 0.5f;
2783 
2784  // These formulas are inversed from eac_to_xyz ones
2785  uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
2786  vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
2787 
2788  uf *= width;
2789  vf *= height;
2790 
2791  uf -= 0.5f;
2792  vf -= 0.5f;
2793 
2794  ui = floorf(uf);
2795  vi = floorf(vf);
2796 
2797  *du = uf - ui;
2798  *dv = vf - vi;
2799 
2800  for (int i = 0; i < 4; i++) {
2801  for (int j = 0; j < 4; j++) {
2802  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
2803  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
2804  }
2805  }
2806 
2807  return 1;
2808 }
2809 
2810 /**
2811  * Prepare data for processing flat output format.
2812  *
2813  * @param ctx filter context
2814  *
2815  * @return error code
2816  */
2818 {
2819  V360Context *s = ctx->priv;
2820 
2821  s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
2822  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2823 
2824  return 0;
2825 }
2826 
2827 /**
2828  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
2829  *
2830  * @param s filter private context
2831  * @param i horizontal position on frame [0, width)
2832  * @param j vertical position on frame [0, height)
2833  * @param width frame width
2834  * @param height frame height
2835  * @param vec coordinates on sphere
2836  */
2837 static int flat_to_xyz(const V360Context *s,
2838  int i, int j, int width, int height,
2839  float *vec)
2840 {
2841  const float l_x = s->flat_range[0] * rescale(i, width);
2842  const float l_y = s->flat_range[1] * rescale(j, height);
2843 
2844  vec[0] = l_x;
2845  vec[1] = l_y;
2846  vec[2] = 1.f;
2847 
2848  normalize_vector(vec);
2849 
2850  return 1;
2851 }
2852 
2853 /**
2854  * Prepare data for processing fisheye output format.
2855  *
2856  * @param ctx filter context
2857  *
2858  * @return error code
2859  */
2861 {
2862  V360Context *s = ctx->priv;
2863 
2864  s->flat_range[0] = s->h_fov / 180.f;
2865  s->flat_range[1] = s->v_fov / 180.f;
2866 
2867  return 0;
2868 }
2869 
2870 /**
2871  * Calculate 3D coordinates on sphere for corresponding frame position in fisheye format.
2872  *
2873  * @param s filter private context
2874  * @param i horizontal position on frame [0, width)
2875  * @param j vertical position on frame [0, height)
2876  * @param width frame width
2877  * @param height frame height
2878  * @param vec coordinates on sphere
2879  */
2880 static int fisheye_to_xyz(const V360Context *s,
2881  int i, int j, int width, int height,
2882  float *vec)
2883 {
2884  const float uf = s->flat_range[0] * rescale(i, width);
2885  const float vf = s->flat_range[1] * rescale(j, height);
2886 
2887  const float phi = atan2f(vf, uf);
2888  const float theta = M_PI_2 * (1.f - hypotf(uf, vf));
2889 
2890  const float sin_phi = sinf(phi);
2891  const float cos_phi = cosf(phi);
2892  const float sin_theta = sinf(theta);
2893  const float cos_theta = cosf(theta);
2894 
2895  vec[0] = cos_theta * cos_phi;
2896  vec[1] = cos_theta * sin_phi;
2897  vec[2] = sin_theta;
2898 
2899  normalize_vector(vec);
2900 
2901  return 1;
2902 }
2903 
2904 /**
2905  * Prepare data for processing fisheye input format.
2906  *
2907  * @param ctx filter context
2908  *
2909  * @return error code
2910  */
2912 {
2913  V360Context *s = ctx->priv;
2914 
2915  s->iflat_range[0] = s->ih_fov / 180.f;
2916  s->iflat_range[1] = s->iv_fov / 180.f;
2917 
2918  return 0;
2919 }
2920 
2921 /**
2922  * Calculate frame position in fisheye format for corresponding 3D coordinates on sphere.
2923  *
2924  * @param s filter private context
2925  * @param vec coordinates on sphere
2926  * @param width frame width
2927  * @param height frame height
2928  * @param us horizontal coordinates for interpolation window
2929  * @param vs vertical coordinates for interpolation window
2930  * @param du horizontal relative coordinate
2931  * @param dv vertical relative coordinate
2932  */
2933 static int xyz_to_fisheye(const V360Context *s,
2934  const float *vec, int width, int height,
2935  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2936 {
2937  const float h = hypotf(vec[0], vec[1]);
2938  const float lh = h > 0.f ? h : 1.f;
2939  const float phi = atan2f(h, vec[2]) / M_PI;
2940 
2941  float uf = vec[0] / lh * phi / s->iflat_range[0];
2942  float vf = vec[1] / lh * phi / s->iflat_range[1];
2943 
2944  const int visible = -0.5f < uf && uf < 0.5f && -0.5f < vf && vf < 0.5f;
2945  int ui, vi;
2946 
2947  uf = (uf + 0.5f) * width;
2948  vf = (vf + 0.5f) * height;
2949 
2950  ui = floorf(uf);
2951  vi = floorf(vf);
2952 
2953  *du = visible ? uf - ui : 0.f;
2954  *dv = visible ? vf - vi : 0.f;
2955 
2956  for (int i = 0; i < 4; i++) {
2957  for (int j = 0; j < 4; j++) {
2958  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
2959  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2960  }
2961  }
2962 
2963  return visible;
2964 }
2965 
2966 /**
2967  * Calculate 3D coordinates on sphere for corresponding frame position in pannini format.
2968  *
2969  * @param s filter private context
2970  * @param i horizontal position on frame [0, width)
2971  * @param j vertical position on frame [0, height)
2972  * @param width frame width
2973  * @param height frame height
2974  * @param vec coordinates on sphere
2975  */
2976 static int pannini_to_xyz(const V360Context *s,
2977  int i, int j, int width, int height,
2978  float *vec)
2979 {
2980  const float uf = rescale(i, width);
2981  const float vf = rescale(j, height);
2982 
2983  const float d = s->h_fov;
2984  const float k = uf * uf / ((d + 1.f) * (d + 1.f));
2985  const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f);
2986  const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
2987  const float S = (d + 1.f) / (d + clon);
2988  const float lon = atan2f(uf, S * clon);
2989  const float lat = atan2f(vf, S);
2990 
2991  vec[0] = sinf(lon) * cosf(lat);
2992  vec[1] = sinf(lat);
2993  vec[2] = cosf(lon) * cosf(lat);
2994 
2995  normalize_vector(vec);
2996 
2997  return 1;
2998 }
2999 
3000 /**
3001  * Calculate frame position in pannini format for corresponding 3D coordinates on sphere.
3002  *
3003  * @param s filter private context
3004  * @param vec coordinates on sphere
3005  * @param width frame width
3006  * @param height frame height
3007  * @param us horizontal coordinates for interpolation window
3008  * @param vs vertical coordinates for interpolation window
3009  * @param du horizontal relative coordinate
3010  * @param dv vertical relative coordinate
3011  */
3012 static int xyz_to_pannini(const V360Context *s,
3013  const float *vec, int width, int height,
3014  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3015 {
3016  const float phi = atan2f(vec[0], vec[2]);
3017  const float theta = asinf(vec[1]);
3018 
3019  const float d = s->ih_fov;
3020  const float S = (d + 1.f) / (d + cosf(phi));
3021 
3022  const float x = S * sinf(phi);
3023  const float y = S * tanf(theta);
3024 
3025  const float uf = scale(x, width);
3026  const float vf = scale(y, height);
3027 
3028  const int ui = floorf(uf);
3029  const int vi = floorf(vf);
3030 
3031  const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width && vec[2] >= 0.f;
3032 
3033  *du = uf - ui;
3034  *dv = vf - vi;
3035 
3036  for (int i = 0; i < 4; i++) {
3037  for (int j = 0; j < 4; j++) {
3038  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
3039  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
3040  }
3041  }
3042 
3043  return visible;
3044 }
3045 
3046 /**
3047  * Prepare data for processing cylindrical output format.
3048  *
3049  * @param ctx filter context
3050  *
3051  * @return error code
3052  */
3054 {
3055  V360Context *s = ctx->priv;
3056 
3057  s->flat_range[0] = M_PI * s->h_fov / 360.f;
3058  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
3059 
3060  return 0;
3061 }
3062 
3063 /**
3064  * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format.
3065  *
3066  * @param s filter private context
3067  * @param i horizontal position on frame [0, width)
3068  * @param j vertical position on frame [0, height)
3069  * @param width frame width
3070  * @param height frame height
3071  * @param vec coordinates on sphere
3072  */
3074  int i, int j, int width, int height,
3075  float *vec)
3076 {
3077  const float uf = s->flat_range[0] * rescale(i, width);
3078  const float vf = s->flat_range[1] * rescale(j, height);
3079 
3080  const float phi = uf;
3081  const float theta = atanf(vf);
3082 
3083  const float sin_phi = sinf(phi);
3084  const float cos_phi = cosf(phi);
3085  const float sin_theta = sinf(theta);
3086  const float cos_theta = cosf(theta);
3087 
3088  vec[0] = cos_theta * sin_phi;
3089  vec[1] = sin_theta;
3090  vec[2] = cos_theta * cos_phi;
3091 
3092  normalize_vector(vec);
3093 
3094  return 1;
3095 }
3096 
3097 /**
3098  * Prepare data for processing cylindrical input format.
3099  *
3100  * @param ctx filter context
3101  *
3102  * @return error code
3103  */
3105 {
3106  V360Context *s = ctx->priv;
3107 
3108  s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
3109  s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
3110 
3111  return 0;
3112 }
3113 
3114 /**
3115  * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere.
3116  *
3117  * @param s filter private context
3118  * @param vec coordinates on sphere
3119  * @param width frame width
3120  * @param height frame height
3121  * @param us horizontal coordinates for interpolation window
3122  * @param vs vertical coordinates for interpolation window
3123  * @param du horizontal relative coordinate
3124  * @param dv vertical relative coordinate
3125  */
3127  const float *vec, int width, int height,
3128  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3129 {
3130  const float phi = atan2f(vec[0], vec[2]) / s->iflat_range[0];
3131  const float theta = asinf(vec[1]);
3132 
3133  const float uf = scale(phi, width);
3134  const float vf = scale(tanf(theta) / s->iflat_range[1], height);
3135 
3136  const int ui = floorf(uf);
3137  const int vi = floorf(vf);
3138 
3139  const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
3140  theta <= M_PI * s->iv_fov / 180.f &&
3141  theta >= -M_PI * s->iv_fov / 180.f;
3142 
3143  *du = uf - ui;
3144  *dv = vf - vi;
3145 
3146  for (int i = 0; i < 4; i++) {
3147  for (int j = 0; j < 4; j++) {
3148  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
3149  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
3150  }
3151  }
3152 
3153  return visible;
3154 }
3155 
3156 /**
3157  * Prepare data for processing cylindrical equal area output format.
3158  *
3159  * @param ctx filter context
3160  *
3161  * @return error code
3162  */
3164 {
3165  V360Context *s = ctx->priv;
3166 
3167  s->flat_range[0] = s->h_fov * M_PI / 360.f;
3168  s->flat_range[1] = s->v_fov / 180.f;
3169 
3170  return 0;
3171 }
3172 
3173 /**
3174  * Prepare data for processing cylindrical equal area input format.
3175  *
3176  * @param ctx filter context
3177  *
3178  * @return error code
3179  */
3181 {
3182  V360Context *s = ctx->priv;
3183 
3184  s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
3185  s->iflat_range[1] = s->iv_fov / 180.f;
3186 
3187  return 0;
3188 }
3189 
3190 /**
3191  * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical equal area format.
3192  *
3193  * @param s filter private context
3194  * @param i horizontal position on frame [0, width)
3195  * @param j vertical position on frame [0, height)
3196  * @param width frame width
3197  * @param height frame height
3198  * @param vec coordinates on sphere
3199  */
3201  int i, int j, int width, int height,
3202  float *vec)
3203 {
3204  const float uf = s->flat_range[0] * rescale(i, width);
3205  const float vf = s->flat_range[1] * rescale(j, height);
3206 
3207  const float phi = uf;
3208  const float theta = asinf(vf);
3209 
3210  const float sin_phi = sinf(phi);
3211  const float cos_phi = cosf(phi);
3212  const float sin_theta = sinf(theta);
3213  const float cos_theta = cosf(theta);
3214 
3215  vec[0] = cos_theta * sin_phi;
3216  vec[1] = sin_theta;
3217  vec[2] = cos_theta * cos_phi;
3218 
3219  normalize_vector(vec);
3220 
3221  return 1;
3222 }
3223 
3224 /**
3225  * Calculate frame position in cylindrical equal area format for corresponding 3D coordinates on sphere.
3226  *
3227  * @param s filter private context
3228  * @param vec coordinates on sphere
3229  * @param width frame width
3230  * @param height frame height
3231  * @param us horizontal coordinates for interpolation window
3232  * @param vs vertical coordinates for interpolation window
3233  * @param du horizontal relative coordinate
3234  * @param dv vertical relative coordinate
3235  */
3237  const float *vec, int width, int height,
3238  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3239 {
3240  const float phi = atan2f(vec[0], vec[2]) / s->iflat_range[0];
3241  const float theta = asinf(vec[1]);
3242 
3243  const float uf = scale(phi, width);
3244  const float vf = scale(sinf(theta) / s->iflat_range[1], height);
3245 
3246  const int ui = floorf(uf);
3247  const int vi = floorf(vf);
3248 
3249  const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
3250  theta <= M_PI * s->iv_fov / 180.f &&
3251  theta >= -M_PI * s->iv_fov / 180.f;
3252 
3253  *du = uf - ui;
3254  *dv = vf - vi;
3255 
3256  for (int i = 0; i < 4; i++) {
3257  for (int j = 0; j < 4; j++) {
3258  us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
3259  vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
3260  }
3261  }
3262 
3263  return visible;
3264 }
3265 
3266 /**
3267  * Calculate 3D coordinates on sphere for corresponding frame position in perspective format.
3268  *
3269  * @param s filter private context
3270  * @param i horizontal position on frame [0, width)
3271  * @param j vertical position on frame [0, height)
3272  * @param width frame width
3273  * @param height frame height
3274  * @param vec coordinates on sphere
3275  */
3277  int i, int j, int width, int height,
3278  float *vec)
3279 {
3280  const float uf = rescale(i, width);
3281  const float vf = rescale(j, height);
3282  const float rh = hypotf(uf, vf);
3283  const float sinzz = 1.f - rh * rh;
3284  const float h = 1.f + s->v_fov;
3285  const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h);
3286  const float sinz2 = sinz * sinz;
3287 
3288  if (sinz2 <= 1.f) {
3289  const float cosz = sqrtf(1.f - sinz2);
3290 
3291  const float theta = asinf(cosz);
3292  const float phi = atan2f(uf, vf);
3293 
3294  const float sin_phi = sinf(phi);
3295  const float cos_phi = cosf(phi);
3296  const float sin_theta = sinf(theta);
3297  const float cos_theta = cosf(theta);
3298 
3299  vec[0] = cos_theta * sin_phi;
3300  vec[1] = cos_theta * cos_phi;
3301  vec[2] = sin_theta;
3302  } else {
3303  vec[0] = 0.f;
3304  vec[1] = 1.f;
3305  vec[2] = 0.f;
3306  return 0;
3307  }
3308 
3309  return 1;
3310 }
3311 
3312 /**
3313  * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format.
3314  *
3315  * @param s filter private context
3316  * @param i horizontal position on frame [0, width)
3317  * @param j vertical position on frame [0, height)
3318  * @param width frame width
3319  * @param height frame height
3320  * @param vec coordinates on sphere
3321  */
3323  int i, int j, int width, int height,
3324  float *vec)
3325 {
3326  const float uf = ((float)i + 0.5f) / width;
3327  const float vf = ((float)j + 0.5f) / height;
3328 
3329  vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
3330  vec[1] = 1.f - vf * 2.f;
3331  vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
3332 
3333  normalize_vector(vec);
3334 
3335  return 1;
3336 }
3337 
3338 /**
3339  * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere.
3340  *
3341  * @param s filter private context
3342  * @param vec coordinates on sphere
3343  * @param width frame width
3344  * @param height frame height
3345  * @param us horizontal coordinates for interpolation window
3346  * @param vs vertical coordinates for interpolation window
3347  * @param du horizontal relative coordinate
3348  * @param dv vertical relative coordinate
3349  */
3351  const float *vec, int width, int height,
3352  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3353 {
3354  const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f;
3355  const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f;
3356  const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f;
3357  const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f;
3358  const float d = FFMAX(d0, FFMAX3(d1, d2, d3));
3359 
3360  float uf, vf, x, y, z;
3361  int ui, vi;
3362 
3363  x = vec[0] / d;
3364  y = vec[1] / d;
3365  z = -vec[2] / d;
3366 
3367  vf = 0.5f - y * 0.5f;
3368 
3369  if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) ||
3370  (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) {
3371  uf = 0.25f * x + 0.25f;
3372  } else {
3373  uf = 0.75f - 0.25f * x;
3374  }
3375 
3376  uf *= width;
3377  vf *= height;
3378 
3379  ui = floorf(uf);
3380  vi = floorf(vf);
3381 
3382  *du = uf - ui;
3383  *dv = vf - vi;
3384 
3385  for (int i = 0; i < 4; i++) {
3386  for (int j = 0; j < 4; j++) {
3387  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3388  vs[i][j] = reflecty(vi + i - 1, height);
3389  }
3390  }
3391 
3392  return 1;
3393 }
3394 
3395 /**
3396  * Prepare data for processing double fisheye input format.
3397  *
3398  * @param ctx filter context
3399  *
3400  * @return error code
3401  */
3403 {
3404  V360Context *s = ctx->priv;
3405 
3406  s->iflat_range[0] = s->ih_fov / 360.f;
3407  s->iflat_range[1] = s->iv_fov / 360.f;
3408 
3409  return 0;
3410 }
3411 
3412 /**
3413  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
3414  *
3415  * @param s filter private context
3416  * @param i horizontal position on frame [0, width)
3417  * @param j vertical position on frame [0, height)
3418  * @param width frame width
3419  * @param height frame height
3420  * @param vec coordinates on sphere
3421  */
3422 static int dfisheye_to_xyz(const V360Context *s,
3423  int i, int j, int width, int height,
3424  float *vec)
3425 {
3426  const float ew = width * 0.5f;
3427  const float eh = height;
3428 
3429  const int ei = i >= ew ? i - ew : i;
3430  const float m = i >= ew ? 1.f : -1.f;
3431 
3432  const float uf = s->flat_range[0] * rescale(ei, ew);
3433  const float vf = s->flat_range[1] * rescale(j, eh);
3434 
3435  const float h = hypotf(uf, vf);
3436  const float lh = h > 0.f ? h : 1.f;
3437  const float theta = m * M_PI_2 * (1.f - h);
3438 
3439  const float sin_theta = sinf(theta);
3440  const float cos_theta = cosf(theta);
3441 
3442  vec[0] = cos_theta * m * uf / lh;
3443  vec[1] = cos_theta * vf / lh;
3444  vec[2] = sin_theta;
3445 
3446  normalize_vector(vec);
3447 
3448  return 1;
3449 }
3450 
3451 /**
3452  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
3453  *
3454  * @param s filter private context
3455  * @param vec coordinates on sphere
3456  * @param width frame width
3457  * @param height frame height
3458  * @param us horizontal coordinates for interpolation window
3459  * @param vs vertical coordinates for interpolation window
3460  * @param du horizontal relative coordinate
3461  * @param dv vertical relative coordinate
3462  */
3463 static int xyz_to_dfisheye(const V360Context *s,
3464  const float *vec, int width, int height,
3465  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3466 {
3467  const float ew = (width - 1) * 0.5f;
3468  const float eh = height;
3469 
3470  const float h = hypotf(vec[0], vec[1]);
3471  const float lh = h > 0.f ? h : 1.f;
3472  const float theta = acosf(fabsf(vec[2])) / M_PI;
3473 
3474  float uf = scale(theta * (vec[0] / lh) / s->iflat_range[0], ew);
3475  float vf = scale(theta * (vec[1] / lh) / s->iflat_range[1], eh);
3476 
3477  int ui, vi;
3478  int u_shift;
3479 
3480  if (vec[2] >= 0.f) {
3481  u_shift = ceilf(ew);
3482  } else {
3483  u_shift = 0;
3484  uf = ew - uf;
3485  }
3486 
3487  ui = floorf(uf);
3488  vi = floorf(vf);
3489 
3490  *du = uf - ui;
3491  *dv = vf - vi;
3492 
3493  for (int i = 0; i < 4; i++) {
3494  for (int j = 0; j < 4; j++) {
3495  us[i][j] = av_clip(u_shift + ui + j - 1, 0, width - 1);
3496  vs[i][j] = av_clip( vi + i - 1, 0, height - 1);
3497  }
3498  }
3499 
3500  return 1;
3501 }
3502 
3503 /**
3504  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
3505  *
3506  * @param s filter private context
3507  * @param i horizontal position on frame [0, width)
3508  * @param j vertical position on frame [0, height)
3509  * @param width frame width
3510  * @param height frame height
3511  * @param vec coordinates on sphere
3512  */
3513 static int barrel_to_xyz(const V360Context *s,
3514  int i, int j, int width, int height,
3515  float *vec)
3516 {
3517  const float scale = 0.99f;
3518  float l_x, l_y, l_z;
3519 
3520  if (i < 4 * width / 5) {
3521  const float theta_range = M_PI_4;
3522 
3523  const int ew = 4 * width / 5;
3524  const int eh = height;
3525 
3526  const float phi = rescale(i, ew) * M_PI / scale;
3527  const float theta = rescale(j, eh) * theta_range / scale;
3528 
3529  const float sin_phi = sinf(phi);
3530  const float cos_phi = cosf(phi);
3531  const float sin_theta = sinf(theta);
3532  const float cos_theta = cosf(theta);
3533 
3534  l_x = cos_theta * sin_phi;
3535  l_y = sin_theta;
3536  l_z = cos_theta * cos_phi;
3537  } else {
3538  const int ew = width / 5;
3539  const int eh = height / 2;
3540 
3541  float uf, vf;
3542 
3543  if (j < eh) { // UP
3544  uf = rescale(i - 4 * ew, ew);
3545  vf = rescale(j, eh);
3546 
3547  uf /= scale;
3548  vf /= scale;
3549 
3550  l_x = uf;
3551  l_y = -1.f;
3552  l_z = vf;
3553  } else { // DOWN
3554  uf = rescale(i - 4 * ew, ew);
3555  vf = rescale(j - eh, eh);
3556 
3557  uf /= scale;
3558  vf /= scale;
3559 
3560  l_x = uf;
3561  l_y = 1.f;
3562  l_z = -vf;
3563  }
3564  }
3565 
3566  vec[0] = l_x;
3567  vec[1] = l_y;
3568  vec[2] = l_z;
3569 
3570  normalize_vector(vec);
3571 
3572  return 1;
3573 }
3574 
3575 /**
3576  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
3577  *
3578  * @param s filter private context
3579  * @param vec coordinates on sphere
3580  * @param width frame width
3581  * @param height frame height
3582  * @param us horizontal coordinates for interpolation window
3583  * @param vs vertical coordinates for interpolation window
3584  * @param du horizontal relative coordinate
3585  * @param dv vertical relative coordinate
3586  */
3587 static int xyz_to_barrel(const V360Context *s,
3588  const float *vec, int width, int height,
3589  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3590 {
3591  const float scale = 0.99f;
3592 
3593  const float phi = atan2f(vec[0], vec[2]);
3594  const float theta = asinf(vec[1]);
3595  const float theta_range = M_PI_4;
3596 
3597  int ew, eh;
3598  int u_shift, v_shift;
3599  float uf, vf;
3600  int ui, vi;
3601 
3602  if (theta > -theta_range && theta < theta_range) {
3603  ew = 4 * width / 5;
3604  eh = height;
3605 
3606  u_shift = 0;
3607  v_shift = 0;
3608 
3609  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
3610  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
3611  } else {
3612  ew = width / 5;
3613  eh = height / 2;
3614 
3615  u_shift = 4 * ew;
3616 
3617  if (theta < 0.f) { // UP
3618  uf = -vec[0] / vec[1];
3619  vf = -vec[2] / vec[1];
3620  v_shift = 0;
3621  } else { // DOWN
3622  uf = vec[0] / vec[1];
3623  vf = -vec[2] / vec[1];
3624  v_shift = eh;
3625  }
3626 
3627  uf = 0.5f * ew * (uf * scale + 1.f);
3628  vf = 0.5f * eh * (vf * scale + 1.f);
3629  }
3630 
3631  ui = floorf(uf);
3632  vi = floorf(vf);
3633 
3634  *du = uf - ui;
3635  *dv = vf - vi;
3636 
3637  for (int i = 0; i < 4; i++) {
3638  for (int j = 0; j < 4; j++) {
3639  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3640  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3641  }
3642  }
3643 
3644  return 1;
3645 }
3646 
3647 /**
3648  * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere.
3649  *
3650  * @param s filter private context
3651  * @param vec coordinates on sphere
3652  * @param width frame width
3653  * @param height frame height
3654  * @param us horizontal coordinates for interpolation window
3655  * @param vs vertical coordinates for interpolation window
3656  * @param du horizontal relative coordinate
3657  * @param dv vertical relative coordinate
3658  */
3660  const float *vec, int width, int height,
3661  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3662 {
3663  const float phi = atan2f(vec[0], vec[2]);
3664  const float theta = asinf(vec[1]);
3665 
3666  const float theta_range = M_PI_4;
3667 
3668  int ew, eh;
3669  int u_shift, v_shift;
3670  float uf, vf;
3671  int ui, vi;
3672 
3673  if (theta >= -theta_range && theta <= theta_range) {
3674  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad;
3675  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
3676 
3677  ew = width / 3 * 2;
3678  eh = height / 2;
3679 
3680  u_shift = 0;
3681  v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0;
3682 
3683  uf = fmodf(phi, M_PI_2) / M_PI_2;
3684  vf = theta / M_PI_4;
3685 
3686  if (v_shift)
3687  uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f);
3688 
3689  uf = (uf * scalew + 1.f) * width / 3.f;
3690  vf = (vf * scaleh + 1.f) * height / 4.f;
3691  } else {
3692  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad;
3693  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad;
3694  int v_offset = 0;
3695 
3696  ew = width / 3;
3697  eh = height / 4;
3698 
3699  u_shift = 2 * ew;
3700 
3701  if (theta <= 0.f && theta >= -M_PI_2 &&
3702  phi <= M_PI_2 && phi >= -M_PI_2) {
3703  uf = -vec[0] / vec[1];
3704  vf = -vec[2] / vec[1];
3705  v_shift = 0;
3706  v_offset = -eh;
3707  } else if (theta >= 0.f && theta <= M_PI_2 &&
3708  phi <= M_PI_2 && phi >= -M_PI_2) {
3709  uf = vec[0] / vec[1];
3710  vf = -vec[2] / vec[1];
3711  v_shift = height * 0.25f;
3712  } else if (theta <= 0.f && theta >= -M_PI_2) {
3713  uf = vec[0] / vec[1];
3714  vf = vec[2] / vec[1];
3715  v_shift = height * 0.5f;
3716  v_offset = -eh;
3717  } else {
3718  uf = -vec[0] / vec[1];
3719  vf = vec[2] / vec[1];
3720  v_shift = height * 0.75f;
3721  }
3722 
3723  uf = 0.5f * width / 3.f * (uf * scalew + 1.f);
3724  vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset;
3725  }
3726 
3727  ui = floorf(uf);
3728  vi = floorf(vf);
3729 
3730  *du = uf - ui;
3731  *dv = vf - vi;
3732 
3733  for (int i = 0; i < 4; i++) {
3734  for (int j = 0; j < 4; j++) {
3735  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3736  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3737  }
3738  }
3739 
3740  return 1;
3741 }
3742 
3743 /**
3744  * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format.
3745  *
3746  * @param s filter private context
3747  * @param i horizontal position on frame [0, width)
3748  * @param j vertical position on frame [0, height)
3749  * @param width frame width
3750  * @param height frame height
3751  * @param vec coordinates on sphere
3752  */
3754  int i, int j, int width, int height,
3755  float *vec)
3756 {
3757  const float x = (i + 0.5f) / width;
3758  const float y = (j + 0.5f) / height;
3759  float l_x, l_y, l_z;
3760 
3761  if (x < 2.f / 3.f) {
3762  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad;
3763  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
3764 
3765  const float back = floorf(y * 2.f);
3766 
3767  const float phi = ((3.f / 2.f * x - 0.5f) / scalew - back) * M_PI;
3768  const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI;
3769 
3770  const float sin_phi = sinf(phi);
3771  const float cos_phi = cosf(phi);
3772  const float sin_theta = sinf(theta);
3773  const float cos_theta = cosf(theta);
3774 
3775  l_x = cos_theta * sin_phi;
3776  l_y = sin_theta;
3777  l_z = cos_theta * cos_phi;
3778  } else {
3779  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad;
3780  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad;
3781 
3782  const int face = floorf(y * 4.f);
3783  float uf, vf;
3784 
3785  uf = x * 3.f - 2.f;
3786 
3787  switch (face) {
3788  case 0:
3789  vf = y * 2.f;
3790  uf = 1.f - uf;
3791  vf = 0.5f - vf;
3792 
3793  l_x = (0.5f - uf) / scalew;
3794  l_y = -0.5f;
3795  l_z = (0.5f - vf) / scaleh;
3796  break;
3797  case 1:
3798  vf = y * 2.f;
3799  uf = 1.f - uf;
3800  vf = 1.f - (vf - 0.5f);
3801 
3802  l_x = (0.5f - uf) / scalew;
3803  l_y = 0.5f;
3804  l_z = (-0.5f + vf) / scaleh;
3805  break;
3806  case 2:
3807  vf = y * 2.f - 0.5f;
3808  vf = 1.f - (1.f - vf);
3809 
3810  l_x = (0.5f - uf) / scalew;
3811  l_y = -0.5f;
3812  l_z = (0.5f - vf) / scaleh;
3813  break;
3814  case 3:
3815  vf = y * 2.f - 1.5f;
3816 
3817  l_x = (0.5f - uf) / scalew;
3818  l_y = 0.5f;
3819  l_z = (-0.5f + vf) / scaleh;
3820  break;
3821  }
3822  }
3823 
3824  vec[0] = l_x;
3825  vec[1] = l_y;
3826  vec[2] = l_z;
3827 
3828  normalize_vector(vec);
3829 
3830  return 1;
3831 }
3832 
3833 /**
3834  * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format.
3835  *
3836  * @param s filter private context
3837  * @param i horizontal position on frame [0, width)
3838  * @param j vertical position on frame [0, height)
3839  * @param width frame width
3840  * @param height frame height
3841  * @param vec coordinates on sphere
3842  */
3843 static int tspyramid_to_xyz(const V360Context *s,
3844  int i, int j, int width, int height,
3845  float *vec)
3846 {
3847  const float x = (i + 0.5f) / width;
3848  const float y = (j + 0.5f) / height;
3849 
3850  if (x < 0.5f) {
3851  vec[0] = x * 4.f - 1.f;
3852  vec[1] = (y * 2.f - 1.f);
3853  vec[2] = 1.f;
3854  } else if (x >= 0.6875f && x < 0.8125f &&
3855  y >= 0.375f && y < 0.625f) {
3856  vec[0] = -(x - 0.6875f) * 16.f + 1.f;
3857  vec[1] = (y - 0.375f) * 8.f - 1.f;
3858  vec[2] = -1.f;
3859  } else if (0.5f <= x && x < 0.6875f &&
3860  ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) ||
3861  (0.375f <= y && y < 0.625f) ||
3862  (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) {
3863  vec[0] = 1.f;
3864  vec[1] = 2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f;
3865  vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f;
3866  } else if (0.8125f <= x && x < 1.f &&
3867  ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) ||
3868  (0.375f <= y && y < 0.625f) ||
3869  (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) {
3870  vec[0] = -1.f;
3871  vec[1] = 2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f;
3872  vec[2] = 2.f * (x - 0.8125f) / 0.1875f - 1.f;
3873  } else if (0.f <= y && y < 0.375f &&
3874  ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) ||
3875  (0.6875f <= x && x < 0.8125f) ||
3876  (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) {
3877  vec[0] = 2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f;
3878  vec[1] = -1.f;
3879  vec[2] = 2.f * (0.375f - y) / 0.375f - 1.f;
3880  } else {
3881  vec[0] = 2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f;
3882  vec[1] = 1.f;
3883  vec[2] = -2.f * (1.f - y) / 0.375f + 1.f;
3884  }
3885 
3886  normalize_vector(vec);
3887 
3888  return 1;
3889 }
3890 
3891 /**
3892  * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere.
3893  *
3894  * @param s filter private context
3895  * @param vec coordinates on sphere
3896  * @param width frame width
3897  * @param height frame height
3898  * @param us horizontal coordinates for interpolation window
3899  * @param vs vertical coordinates for interpolation window
3900  * @param du horizontal relative coordinate
3901  * @param dv vertical relative coordinate
3902  */
3903 static int xyz_to_tspyramid(const V360Context *s,
3904  const float *vec, int width, int height,
3905  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3906 {
3907  float uf, vf;
3908  int ui, vi;
3909  int face;
3910 
3911  xyz_to_cube(s, vec, &uf, &vf, &face);
3912 
3913  uf = (uf + 1.f) * 0.5f;
3914  vf = (vf + 1.f) * 0.5f;
3915 
3916  switch (face) {
3917  case UP:
3918  uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f;
3919  vf = 0.375f - 0.375f * vf;
3920  break;
3921  case FRONT:
3922  uf = 0.5f * uf;
3923  break;
3924  case DOWN:
3925  uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf;
3926  vf = 1.f - 0.375f * vf;
3927  break;
3928  case LEFT:
3929  vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f;
3930  uf = 0.1875f * uf + 0.8125f;
3931  break;
3932  case RIGHT:
3933  vf = 0.375f * uf - 0.75f * uf * vf + vf;
3934  uf = 0.1875f * uf + 0.5f;
3935  break;
3936  case BACK:
3937  uf = 0.125f * uf + 0.6875f;
3938  vf = 0.25f * vf + 0.375f;
3939  break;
3940  }
3941 
3942  uf *= width;
3943  vf *= height;
3944 
3945  ui = floorf(uf);
3946  vi = floorf(vf);
3947 
3948  *du = uf - ui;
3949  *dv = vf - vi;
3950 
3951  for (int i = 0; i < 4; i++) {
3952  for (int j = 0; j < 4; j++) {
3953  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3954  vs[i][j] = reflecty(vi + i - 1, height);
3955  }
3956  }
3957 
3958  return 1;
3959 }
3960 
3961 /**
3962  * Calculate 3D coordinates on sphere for corresponding frame position in octahedron format.
3963  *
3964  * @param s filter private context
3965  * @param i horizontal position on frame [0, width)
3966  * @param j vertical position on frame [0, height)
3967  * @param width frame width
3968  * @param height frame height
3969  * @param vec coordinates on sphere
3970  */
3971 static int octahedron_to_xyz(const V360Context *s,
3972  int i, int j, int width, int height,
3973  float *vec)
3974 {
3975  const float x = rescale(i, width);
3976  const float y = rescale(j, height);
3977  const float ax = fabsf(x);
3978  const float ay = fabsf(y);
3979 
3980  vec[2] = 1.f - (ax + ay);
3981  if (ax + ay > 1.f) {
3982  vec[0] = (1.f - ay) * FFSIGN(x);
3983  vec[1] = (1.f - ax) * FFSIGN(y);
3984  } else {
3985  vec[0] = x;
3986  vec[1] = y;
3987  }
3988 
3989  normalize_vector(vec);
3990 
3991  return 1;
3992 }
3993 
3994 /**
3995  * Calculate frame position in octahedron format for corresponding 3D coordinates on sphere.
3996  *
3997  * @param s filter private context
3998  * @param vec coordinates on sphere
3999  * @param width frame width
4000  * @param height frame height
4001  * @param us horizontal coordinates for interpolation window
4002  * @param vs vertical coordinates for interpolation window
4003  * @param du horizontal relative coordinate
4004  * @param dv vertical relative coordinate
4005  */
4006 static int xyz_to_octahedron(const V360Context *s,
4007  const float *vec, int width, int height,
4008  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
4009 {
4010  float uf, vf, zf;
4011  int ui, vi;
4012  float div = fabsf(vec[0]) + fabsf(vec[1]) + fabsf(vec[2]);
4013 
4014  uf = vec[0] / div;
4015  vf = vec[1] / div;
4016  zf = vec[2];
4017 
4018  if (zf < 0.f) {
4019  zf = vf;
4020  vf = (1.f - fabsf(uf)) * FFSIGN(zf);
4021  uf = (1.f - fabsf(zf)) * FFSIGN(uf);
4022  }
4023 
4024  uf = scale(uf, width);
4025  vf = scale(vf, height);
4026 
4027  ui = floorf(uf);
4028  vi = floorf(vf);
4029 
4030  *du = uf - ui;
4031  *dv = vf - vi;
4032 
4033  for (int i = 0; i < 4; i++) {
4034  for (int j = 0; j < 4; j++) {
4035  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
4036  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
4037  }
4038  }
4039 
4040  return 1;
4041 }
4042 
4043 static void multiply_quaternion(float c[4], const float a[4], const float b[4])
4044 {
4045  c[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3];
4046  c[1] = a[1] * b[0] + a[0] * b[1] + a[2] * b[3] - a[3] * b[2];
4047  c[2] = a[2] * b[0] + a[0] * b[2] + a[3] * b[1] - a[1] * b[3];
4048  c[3] = a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1];
4049 }
4050 
4051 static void conjugate_quaternion(float d[4], const float q[4])
4052 {
4053  d[0] = q[0];
4054  d[1] = -q[1];
4055  d[2] = -q[2];
4056  d[3] = -q[3];
4057 }
4058 
4059 /**
4060  * Calculate rotation quaternion for yaw/pitch/roll angles.
4061  */
4062 static inline void calculate_rotation(float yaw, float pitch, float roll,
4063  float rot_quaternion[2][4],
4064  const int rotation_order[3])
4065 {
4066  const float yaw_rad = yaw * M_PI / 180.f;
4067  const float pitch_rad = pitch * M_PI / 180.f;
4068  const float roll_rad = roll * M_PI / 180.f;
4069 
4070  const float sin_yaw = sinf(yaw_rad * 0.5f);
4071  const float cos_yaw = cosf(yaw_rad * 0.5f);
4072  const float sin_pitch = sinf(pitch_rad * 0.5f);
4073  const float cos_pitch = cosf(pitch_rad * 0.5f);
4074  const float sin_roll = sinf(roll_rad * 0.5f);
4075  const float cos_roll = cosf(roll_rad * 0.5f);
4076 
4077  float m[3][4];
4078  float tmp[2][4];
4079 
4080  m[0][0] = cos_yaw; m[0][1] = 0.f; m[0][2] = sin_yaw; m[0][3] = 0.f;
4081  m[1][0] = cos_pitch; m[1][1] = sin_pitch; m[1][2] = 0.f; m[1][3] = 0.f;
4082  m[2][0] = cos_roll; m[2][1] = 0.f; m[2][2] = 0.f; m[2][3] = sin_roll;
4083 
4084  multiply_quaternion(tmp[0], rot_quaternion[0], m[rotation_order[0]]);
4085  multiply_quaternion(tmp[1], tmp[0], m[rotation_order[1]]);
4086  multiply_quaternion(rot_quaternion[0], tmp[1], m[rotation_order[2]]);
4087 
4088  conjugate_quaternion(rot_quaternion[1], rot_quaternion[0]);
4089 }
4090 
4091 /**
4092  * Rotate vector with given rotation quaternion.
4093  *
4094  * @param rot_quaternion rotation quaternion
4095  * @param vec vector
4096  */
4097 static inline void rotate(const float rot_quaternion[2][4],
4098  float *vec)
4099 {
4100  float qv[4], temp[4], rqv[4];
4101 
4102  qv[0] = 0.f;
4103  qv[1] = vec[0];
4104  qv[2] = vec[1];
4105  qv[3] = vec[2];
4106 
4107  multiply_quaternion(temp, rot_quaternion[0], qv);
4108  multiply_quaternion(rqv, temp, rot_quaternion[1]);
4109 
4110  vec[0] = rqv[1];
4111  vec[1] = rqv[2];
4112  vec[2] = rqv[3];
4113 }
4114 
4115 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
4116  float *modifier)
4117 {
4118  modifier[0] = h_flip ? -1.f : 1.f;
4119  modifier[1] = v_flip ? -1.f : 1.f;
4120  modifier[2] = d_flip ? -1.f : 1.f;
4121 }
4122 
4123 static inline void mirror(const float *modifier, float *vec)
4124 {
4125  vec[0] *= modifier[0];
4126  vec[1] *= modifier[1];
4127  vec[2] *= modifier[2];
4128 }
4129 
4130 static inline void input_flip(int16_t u[4][4], int16_t v[4][4], int w, int h, int hflip, int vflip)
4131 {
4132  if (hflip) {
4133  for (int i = 0; i < 4; i++) {
4134  for (int j = 0; j < 4; j++)
4135  u[i][j] = w - 1 - u[i][j];
4136  }
4137  }
4138 
4139  if (vflip) {
4140  for (int i = 0; i < 4; i++) {
4141  for (int j = 0; j < 4; j++)
4142  v[i][j] = h - 1 - v[i][j];
4143  }
4144  }
4145 }
4146 
4147 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
4148 {
4149  const int pr_height = s->pr_height[p];
4150 
4151  for (int n = 0; n < s->nb_threads; n++) {
4152  SliceXYRemap *r = &s->slice_remap[n];
4153  const int slice_start = (pr_height * n ) / s->nb_threads;
4154  const int slice_end = (pr_height * (n + 1)) / s->nb_threads;
4155  const int height = slice_end - slice_start;
4156 
4157  if (!r->u[p])
4158  r->u[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
4159  if (!r->v[p])
4160  r->v[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
4161  if (!r->u[p] || !r->v[p])
4162  return AVERROR(ENOMEM);
4163  if (sizeof_ker) {
4164  if (!r->ker[p])
4165  r->ker[p] = av_calloc(s->uv_linesize[p] * height, sizeof_ker);
4166  if (!r->ker[p])
4167  return AVERROR(ENOMEM);
4168  }
4169 
4170  if (sizeof_mask && !p) {
4171  if (!r->mask)
4172  r->mask = av_calloc(s->pr_width[p] * height, sizeof_mask);
4173  if (!r->mask)
4174  return AVERROR(ENOMEM);
4175  }
4176  }
4177 
4178  return 0;
4179 }
4180 
4181 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
4182 {
4183  switch (format) {
4184  case EQUIRECTANGULAR:
4185  *h_fov = d_fov;
4186  *v_fov = d_fov * 0.5f;
4187  break;
4188  case ORTHOGRAPHIC:
4189  {
4190  const float d = 0.5f * hypotf(w, h);
4191  const float l = sinf(d_fov * M_PI / 360.f) / d;
4192 
4193  *h_fov = asinf(w * 0.5f * l) * 360.f / M_PI;
4194  *v_fov = asinf(h * 0.5f * l) * 360.f / M_PI;
4195 
4196  if (d_fov > 180.f) {
4197  *h_fov = 180.f - *h_fov;
4198  *v_fov = 180.f - *v_fov;
4199  }
4200  }
4201  break;
4202  case EQUISOLID:
4203  {
4204  const float d = 0.5f * hypotf(w, h);
4205  const float l = d / (sinf(d_fov * M_PI / 720.f));
4206 
4207  *h_fov = 2.f * asinf(w * 0.5f / l) * 360.f / M_PI;
4208  *v_fov = 2.f * asinf(h * 0.5f / l) * 360.f / M_PI;
4209  }
4210  break;
4211  case STEREOGRAPHIC:
4212  {
4213  const float d = 0.5f * hypotf(w, h);
4214  const float l = d / (tanf(d_fov * M_PI / 720.f));
4215 
4216  *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI;
4217  *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI;
4218  }
4219  break;
4220  case DUAL_FISHEYE:
4221  {
4222  const float d = hypotf(w * 0.5f, h);
4223 
4224  *h_fov = 0.5f * w / d * d_fov;
4225  *v_fov = h / d * d_fov;
4226  }
4227  break;
4228  case FISHEYE:
4229  {
4230  const float d = hypotf(w, h);
4231 
4232  *h_fov = w / d * d_fov;
4233  *v_fov = h / d * d_fov;
4234  }
4235  break;
4236  case FLAT:
4237  default:
4238  {
4239  const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f);
4240  const float d = hypotf(w, h);
4241 
4242  *h_fov = atan2f(da * w, d) * 360.f / M_PI;
4243  *v_fov = atan2f(da * h, d) * 360.f / M_PI;
4244 
4245  if (*h_fov < 0.f)
4246  *h_fov += 360.f;
4247  if (*v_fov < 0.f)
4248  *v_fov += 360.f;
4249  }
4250  break;
4251  }
4252 }
4253 
4254 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
4255 {
4256  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
4257  outw[0] = outw[3] = w;
4258  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
4259  outh[0] = outh[3] = h;
4260 }
4261 
4262 // Calculate remap data
4263 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
4264 {
4265  V360Context *s = ctx->priv;
4266  SliceXYRemap *r = &s->slice_remap[jobnr];
4267 
4268  for (int p = 0; p < s->nb_allocated; p++) {
4269  const int max_value = s->max_value;
4270  const int width = s->pr_width[p];
4271  const int uv_linesize = s->uv_linesize[p];
4272  const int height = s->pr_height[p];
4273  const int in_width = s->inplanewidth[p];
4274  const int in_height = s->inplaneheight[p];
4275  const int slice_start = (height * jobnr ) / nb_jobs;
4276  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
4277  const int elements = s->elements;
4278  float du, dv;
4279  float vec[3];
4280  XYRemap rmap;
4281 
4282  for (int j = slice_start; j < slice_end; j++) {
4283  for (int i = 0; i < width; i++) {
4284  int16_t *u = r->u[p] + ((j - slice_start) * uv_linesize + i) * elements;
4285  int16_t *v = r->v[p] + ((j - slice_start) * uv_linesize + i) * elements;
4286  int16_t *ker = r->ker[p] + ((j - slice_start) * uv_linesize + i) * elements;
4287  uint8_t *mask8 = p ? NULL : r->mask + ((j - slice_start) * s->pr_width[0] + i);
4288  uint16_t *mask16 = p ? NULL : (uint16_t *)r->mask + ((j - slice_start) * s->pr_width[0] + i);
4289  int in_mask, out_mask;
4290 
4291  if (s->out_transpose)
4292  out_mask = s->out_transform(s, j, i, height, width, vec);
4293  else
4294  out_mask = s->out_transform(s, i, j, width, height, vec);
4295  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4296  rotate(s->rot_quaternion, vec);
4297  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4298  normalize_vector(vec);
4299  mirror(s->output_mirror_modifier, vec);
4300  if (s->in_transpose)
4301  in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
4302  else
4303  in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
4304  input_flip(rmap.u, rmap.v, in_width, in_height, s->ih_flip, s->iv_flip);
4305  av_assert1(!isnan(du) && !isnan(dv));
4306  s->calculate_kernel(du, dv, &rmap, u, v, ker);
4307 
4308  if (!p && r->mask) {
4309  if (s->mask_size == 1) {
4310  mask8[0] = 255 * (out_mask & in_mask);
4311  } else {
4312  mask16[0] = max_value * (out_mask & in_mask);
4313  }
4314  }
4315  }
4316  }
4317  }
4318 
4319  return 0;
4320 }
4321 
4322 static int config_output(AVFilterLink *outlink)
4323 {
4324  AVFilterContext *ctx = outlink->src;
4325  AVFilterLink *inlink = ctx->inputs[0];
4326  V360Context *s = ctx->priv;
4328  const int depth = desc->comp[0].depth;
4329  const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
4330  float default_h_fov = 360.f;
4331  float default_v_fov = 180.f;
4332  float default_ih_fov = 360.f;
4333  float default_iv_fov = 180.f;
4334  int sizeof_uv;
4335  int sizeof_ker;
4336  int err;
4337  int h, w;
4338  int in_offset_h, in_offset_w;
4339  int out_offset_h, out_offset_w;
4340  float hf, wf;
4341  int (*prepare_out)(AVFilterContext *ctx);
4342  int have_alpha;
4343 
4344  s->max_value = (1 << depth) - 1;
4345 
4346  switch (s->interp) {
4347  case NEAREST:
4348  s->calculate_kernel = nearest_kernel;
4349  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
4350  s->elements = 1;
4351  sizeof_uv = sizeof(int16_t) * s->elements;
4352  sizeof_ker = 0;
4353  break;
4354  case BILINEAR:
4355  s->calculate_kernel = bilinear_kernel;
4356  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
4357  s->elements = 2 * 2;
4358  sizeof_uv = sizeof(int16_t) * s->elements;
4359  sizeof_ker = sizeof(int16_t) * s->elements;
4360  break;
4361  case LAGRANGE9:
4362  s->calculate_kernel = lagrange_kernel;
4363  s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice;
4364  s->elements = 3 * 3;
4365  sizeof_uv = sizeof(int16_t) * s->elements;
4366  sizeof_ker = sizeof(int16_t) * s->elements;
4367  break;
4368  case BICUBIC:
4369  s->calculate_kernel = bicubic_kernel;
4370  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4371  s->elements = 4 * 4;
4372  sizeof_uv = sizeof(int16_t) * s->elements;
4373  sizeof_ker = sizeof(int16_t) * s->elements;
4374  break;
4375  case LANCZOS:
4376  s->calculate_kernel = lanczos_kernel;
4377  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4378  s->elements = 4 * 4;
4379  sizeof_uv = sizeof(int16_t) * s->elements;
4380  sizeof_ker = sizeof(int16_t) * s->elements;
4381  break;
4382  case SPLINE16:
4383  s->calculate_kernel = spline16_kernel;
4384  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4385  s->elements = 4 * 4;
4386  sizeof_uv = sizeof(int16_t) * s->elements;
4387  sizeof_ker = sizeof(int16_t) * s->elements;
4388  break;
4389  case GAUSSIAN:
4390  s->calculate_kernel = gaussian_kernel;
4391  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4392  s->elements = 4 * 4;
4393  sizeof_uv = sizeof(int16_t) * s->elements;
4394  sizeof_ker = sizeof(int16_t) * s->elements;
4395  break;
4396  case MITCHELL:
4397  s->calculate_kernel = mitchell_kernel;
4398  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4399  s->elements = 4 * 4;
4400  sizeof_uv = sizeof(int16_t) * s->elements;
4401  sizeof_ker = sizeof(int16_t) * s->elements;
4402  break;
4403  default:
4404  av_assert0(0);
4405  }
4406 
4407  ff_v360_init(s, depth);
4408 
4409  for (int order = 0; order < NB_RORDERS; order++) {
4410  const char c = s->rorder[order];
4411  int rorder;
4412 
4413  if (c == '\0') {
4415  "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
4416  s->rotation_order[0] = YAW;
4417  s->rotation_order[1] = PITCH;
4418  s->rotation_order[2] = ROLL;
4419  break;
4420  }
4421 
4422  rorder = get_rorder(c);
4423  if (rorder == -1) {
4425  "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
4426  s->rotation_order[0] = YAW;
4427  s->rotation_order[1] = PITCH;
4428  s->rotation_order[2] = ROLL;
4429  break;
4430  }
4431 
4432  s->rotation_order[order] = rorder;
4433  }
4434 
4435  switch (s->in_stereo) {
4436  case STEREO_2D:
4437  w = inlink->w;
4438  h = inlink->h;
4439  in_offset_w = in_offset_h = 0;
4440  break;
4441  case STEREO_SBS:
4442  w = inlink->w / 2;
4443  h = inlink->h;
4444  in_offset_w = w;
4445  in_offset_h = 0;
4446  break;
4447  case STEREO_TB:
4448  w = inlink->w;
4449  h = inlink->h / 2;
4450  in_offset_w = 0;
4451  in_offset_h = h;
4452  break;
4453  default:
4454  av_assert0(0);
4455  }
4456 
4457  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
4458  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
4459 
4460  s->in_width = s->inplanewidth[0];
4461  s->in_height = s->inplaneheight[0];
4462 
4463  switch (s->in) {
4464  case CYLINDRICAL:
4465  case FLAT:
4466  default_ih_fov = 90.f;
4467  default_iv_fov = 45.f;
4468  break;
4469  case EQUISOLID:
4470  case ORTHOGRAPHIC:
4471  case STEREOGRAPHIC:
4472  case DUAL_FISHEYE:
4473  case FISHEYE:
4474  default_ih_fov = 180.f;
4475  default_iv_fov = 180.f;
4476  default:
4477  break;
4478  }
4479 
4480  if (s->ih_fov == 0.f)
4481  s->ih_fov = default_ih_fov;
4482 
4483  if (s->iv_fov == 0.f)
4484  s->iv_fov = default_iv_fov;
4485 
4486  if (s->id_fov > 0.f)
4487  fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
4488 
4489  if (s->in_transpose)
4490  FFSWAP(int, s->in_width, s->in_height);
4491 
4492  switch (s->in) {
4493  case EQUIRECTANGULAR:
4494  s->in_transform = xyz_to_equirect;
4495  err = prepare_equirect_in(ctx);
4496  wf = w;
4497  hf = h;
4498  break;
4499  case CUBEMAP_3_2:
4500  s->in_transform = xyz_to_cube3x2;
4501  err = prepare_cube_in(ctx);
4502  wf = w / 3.f * 4.f;
4503  hf = h;
4504  break;
4505  case CUBEMAP_1_6:
4506  s->in_transform = xyz_to_cube1x6;
4507  err = prepare_cube_in(ctx);
4508  wf = w * 4.f;
4509  hf = h / 3.f;
4510  break;
4511  case CUBEMAP_6_1:
4512  s->in_transform = xyz_to_cube6x1;
4513  err = prepare_cube_in(ctx);
4514  wf = w / 3.f * 2.f;
4515  hf = h * 2.f;
4516  break;
4517  case EQUIANGULAR:
4518  s->in_transform = xyz_to_eac;
4519  err = prepare_eac_in(ctx);
4520  wf = w;
4521  hf = h / 9.f * 8.f;
4522  break;
4523  case FLAT:
4524  s->in_transform = xyz_to_flat;
4525  err = prepare_flat_in(ctx);
4526  wf = w;
4527  hf = h;
4528  break;
4529  case PERSPECTIVE:
4530  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
4531  return AVERROR(EINVAL);
4532  case DUAL_FISHEYE:
4533  s->in_transform = xyz_to_dfisheye;
4534  err = prepare_dfisheye_in(ctx);
4535  wf = w;
4536  hf = h;
4537  break;
4538  case BARREL:
4539  s->in_transform = xyz_to_barrel;
4540  err = 0;
4541  wf = w / 5.f * 4.f;
4542  hf = h;
4543  break;
4544  case STEREOGRAPHIC:
4545  s->in_transform = xyz_to_stereographic;
4547  wf = w;
4548  hf = h / 2.f;
4549  break;
4550  case MERCATOR:
4551  s->in_transform = xyz_to_mercator;
4552  err = 0;
4553  wf = w;
4554  hf = h / 2.f;
4555  break;
4556  case BALL:
4557  s->in_transform = xyz_to_ball;
4558  err = 0;
4559  wf = w;
4560  hf = h / 2.f;
4561  break;
4562  case HAMMER:
4563  s->in_transform = xyz_to_hammer;
4564  err = 0;
4565  wf = w;
4566  hf = h;
4567  break;
4568  case SINUSOIDAL:
4569  s->in_transform = xyz_to_sinusoidal;
4570  err = 0;
4571  wf = w;
4572  hf = h;
4573  break;
4574  case FISHEYE:
4575  s->in_transform = xyz_to_fisheye;
4576  err = prepare_fisheye_in(ctx);
4577  wf = w * 2;
4578  hf = h;
4579  break;
4580  case PANNINI:
4581  s->in_transform = xyz_to_pannini;
4582  err = 0;
4583  wf = w;
4584  hf = h;
4585  break;
4586  case CYLINDRICAL:
4587  s->in_transform = xyz_to_cylindrical;
4588  err = prepare_cylindrical_in(ctx);
4589  wf = w;
4590  hf = h * 2.f;
4591  break;
4592  case CYLINDRICALEA:
4593  s->in_transform = xyz_to_cylindricalea;
4595  wf = w;
4596  hf = h;
4597  break;
4598  case TETRAHEDRON:
4599  s->in_transform = xyz_to_tetrahedron;
4600  err = 0;
4601  wf = w;
4602  hf = h;
4603  break;
4604  case BARREL_SPLIT:
4605  s->in_transform = xyz_to_barrelsplit;
4606  err = 0;
4607  wf = w * 4.f / 3.f;
4608  hf = h;
4609  break;
4610  case TSPYRAMID:
4611  s->in_transform = xyz_to_tspyramid;
4612  err = 0;
4613  wf = w;
4614  hf = h;
4615  break;
4616  case HEQUIRECTANGULAR:
4617  s->in_transform = xyz_to_hequirect;
4618  err = 0;
4619  wf = w * 2.f;
4620  hf = h;
4621  break;
4622  case EQUISOLID:
4623  s->in_transform = xyz_to_equisolid;
4624  err = prepare_equisolid_in(ctx);
4625  wf = w;
4626  hf = h / 2.f;
4627  break;
4628  case ORTHOGRAPHIC:
4629  s->in_transform = xyz_to_orthographic;
4631  wf = w;
4632  hf = h / 2.f;
4633  break;
4634  case OCTAHEDRON:
4635  s->in_transform = xyz_to_octahedron;
4636  err = 0;
4637  wf = w;
4638  hf = h / 2.f;
4639  break;
4640  default:
4641  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
4642  return AVERROR_BUG;
4643  }
4644 
4645  if (err != 0) {
4646  return err;
4647  }
4648 
4649  switch (s->out) {
4650  case EQUIRECTANGULAR:
4651  s->out_transform = equirect_to_xyz;
4652  prepare_out = prepare_equirect_out;
4653  w = lrintf(wf);
4654  h = lrintf(hf);
4655  break;
4656  case CUBEMAP_3_2:
4657  s->out_transform = cube3x2_to_xyz;
4658  prepare_out = prepare_cube_out;
4659  w = lrintf(wf / 4.f * 3.f);
4660  h = lrintf(hf);
4661  break;
4662  case CUBEMAP_1_6:
4663  s->out_transform = cube1x6_to_xyz;
4664  prepare_out = prepare_cube_out;
4665  w = lrintf(wf / 4.f);
4666  h = lrintf(hf * 3.f);
4667  break;
4668  case CUBEMAP_6_1:
4669  s->out_transform = cube6x1_to_xyz;
4670  prepare_out = prepare_cube_out;
4671  w = lrintf(wf / 2.f * 3.f);
4672  h = lrintf(hf / 2.f);
4673  break;
4674  case EQUIANGULAR:
4675  s->out_transform = eac_to_xyz;
4676  prepare_out = prepare_eac_out;
4677  w = lrintf(wf);
4678  h = lrintf(hf / 8.f * 9.f);
4679  break;
4680  case FLAT:
4681  s->out_transform = flat_to_xyz;
4682  prepare_out = prepare_flat_out;
4683  w = lrintf(wf);
4684  h = lrintf(hf);
4685  break;
4686  case DUAL_FISHEYE:
4687  s->out_transform = dfisheye_to_xyz;
4688  prepare_out = prepare_fisheye_out;
4689  w = lrintf(wf);
4690  h = lrintf(hf);
4691  break;
4692  case BARREL:
4693  s->out_transform = barrel_to_xyz;
4694  prepare_out = NULL;
4695  w = lrintf(wf / 4.f * 5.f);
4696  h = lrintf(hf);
4697  break;
4698  case STEREOGRAPHIC:
4699  s->out_transform = stereographic_to_xyz;
4700  prepare_out = prepare_stereographic_out;
4701  w = lrintf(wf);
4702  h = lrintf(hf * 2.f);
4703  break;
4704  case MERCATOR:
4705  s->out_transform = mercator_to_xyz;
4706  prepare_out = NULL;
4707  w = lrintf(wf);
4708  h = lrintf(hf * 2.f);
4709  break;
4710  case BALL:
4711  s->out_transform = ball_to_xyz;
4712  prepare_out = NULL;
4713  w = lrintf(wf);
4714  h = lrintf(hf * 2.f);
4715  break;
4716  case HAMMER:
4717  s->out_transform = hammer_to_xyz;
4718  prepare_out = NULL;
4719  w = lrintf(wf);
4720  h = lrintf(hf);
4721  break;
4722  case SINUSOIDAL:
4723  s->out_transform = sinusoidal_to_xyz;
4724  prepare_out = NULL;
4725  w = lrintf(wf);
4726  h = lrintf(hf);
4727  break;
4728  case FISHEYE:
4729  s->out_transform = fisheye_to_xyz;
4730  prepare_out = prepare_fisheye_out;
4731  w = lrintf(wf * 0.5f);
4732  h = lrintf(hf);
4733  break;
4734  case PANNINI:
4735  s->out_transform = pannini_to_xyz;
4736  prepare_out = NULL;
4737  w = lrintf(wf);
4738  h = lrintf(hf);
4739  break;
4740  case CYLINDRICAL:
4741  s->out_transform = cylindrical_to_xyz;
4742  prepare_out = prepare_cylindrical_out;
4743  w = lrintf(wf);
4744  h = lrintf(hf * 0.5f);
4745  break;
4746  case CYLINDRICALEA:
4747  s->out_transform = cylindricalea_to_xyz;
4748  prepare_out = prepare_cylindricalea_out;
4749  w = lrintf(wf);
4750  h = lrintf(hf);
4751  break;
4752  case PERSPECTIVE:
4753  s->out_transform = perspective_to_xyz;
4754  prepare_out = NULL;
4755  w = lrintf(wf / 2.f);
4756  h = lrintf(hf);
4757  break;
4758  case TETRAHEDRON:
4759  s->out_transform = tetrahedron_to_xyz;
4760  prepare_out = NULL;
4761  w = lrintf(wf);
4762  h = lrintf(hf);
4763  break;
4764  case BARREL_SPLIT:
4765  s->out_transform = barrelsplit_to_xyz;
4766  prepare_out = NULL;
4767  w = lrintf(wf / 4.f * 3.f);
4768  h = lrintf(hf);
4769  break;
4770  case TSPYRAMID:
4771  s->out_transform = tspyramid_to_xyz;
4772  prepare_out = NULL;
4773  w = lrintf(wf);
4774  h = lrintf(hf);
4775  break;
4776  case HEQUIRECTANGULAR:
4777  s->out_transform = hequirect_to_xyz;
4778  prepare_out = NULL;
4779  w = lrintf(wf / 2.f);
4780  h = lrintf(hf);
4781  break;
4782  case EQUISOLID:
4783  s->out_transform = equisolid_to_xyz;
4784  prepare_out = prepare_equisolid_out;
4785  w = lrintf(wf);
4786  h = lrintf(hf * 2.f);
4787  break;
4788  case ORTHOGRAPHIC:
4789  s->out_transform = orthographic_to_xyz;
4790  prepare_out = prepare_orthographic_out;
4791  w = lrintf(wf);
4792  h = lrintf(hf * 2.f);
4793  break;
4794  case OCTAHEDRON:
4795  s->out_transform = octahedron_to_xyz;
4796  prepare_out = NULL;
4797  w = lrintf(wf);
4798  h = lrintf(hf * 2.f);
4799  break;
4800  default:
4801  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
4802  return AVERROR_BUG;
4803  }
4804 
4805  // Override resolution with user values if specified
4806  if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4807  s->out == FLAT && s->d_fov == 0.f) {
4808  w = s->width;
4809  h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
4810  } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4811  s->out == FLAT && s->d_fov == 0.f) {
4812  h = s->height;
4813  w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
4814  } else if (s->width > 0 && s->height > 0) {
4815  w = s->width;
4816  h = s->height;
4817  } else if (s->width > 0 || s->height > 0) {
4818  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
4819  return AVERROR(EINVAL);
4820  } else {
4821  if (s->out_transpose)
4822  FFSWAP(int, w, h);
4823 
4824  if (s->in_transpose)
4825  FFSWAP(int, w, h);
4826  }
4827 
4828  s->width = w;
4829  s->height = h;
4830 
4831  switch (s->out) {
4832  case CYLINDRICAL:
4833  case FLAT:
4834  default_h_fov = 90.f;
4835  default_v_fov = 45.f;
4836  break;
4837  case EQUISOLID:
4838  case ORTHOGRAPHIC:
4839  case STEREOGRAPHIC:
4840  case DUAL_FISHEYE:
4841  case FISHEYE:
4842  default_h_fov = 180.f;
4843  default_v_fov = 180.f;
4844  break;
4845  default:
4846  break;
4847  }
4848 
4849  if (s->h_fov == 0.f)
4850  s->h_fov = default_h_fov;
4851 
4852  if (s->v_fov == 0.f)
4853  s->v_fov = default_v_fov;
4854 
4855  if (s->d_fov > 0.f)
4856  fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
4857 
4858  if (prepare_out) {
4859  err = prepare_out(ctx);
4860  if (err != 0)
4861  return err;
4862  }
4863 
4864  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
4865 
4866  switch (s->out_stereo) {
4867  case STEREO_2D:
4868  out_offset_w = out_offset_h = 0;
4869  break;
4870  case STEREO_SBS:
4871  out_offset_w = w;
4872  out_offset_h = 0;
4873  w *= 2;
4874  break;
4875  case STEREO_TB:
4876  out_offset_w = 0;
4877  out_offset_h = h;
4878  h *= 2;
4879  break;
4880  default:
4881  av_assert0(0);
4882  }
4883 
4884  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
4885  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
4886 
4887  for (int i = 0; i < 4; i++)
4888  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
4889 
4890  outlink->h = h;
4891  outlink->w = w;
4892 
4893  s->nb_threads = FFMIN(outlink->h, ff_filter_get_nb_threads(ctx));
4894  s->nb_planes = av_pix_fmt_count_planes(inlink->format);
4895  have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
4896 
4897  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
4898  s->nb_allocated = 1;
4899  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
4900  } else {
4901  s->nb_allocated = 2;
4902  s->map[0] = s->map[3] = 0;
4903  s->map[1] = s->map[2] = 1;
4904  }
4905 
4906  if (!s->slice_remap)
4907  s->slice_remap = av_calloc(s->nb_threads, sizeof(*s->slice_remap));
4908  if (!s->slice_remap)
4909  return AVERROR(ENOMEM);
4910 
4911  for (int i = 0; i < s->nb_allocated; i++) {
4912  err = allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
4913  if (err < 0)
4914  return err;
4915  }
4916 
4917  calculate_rotation(s->yaw, s->pitch, s->roll,
4918  s->rot_quaternion, s->rotation_order);
4919 
4920  set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier);
4921 
4922  ff_filter_execute(ctx, v360_slice, NULL, NULL, s->nb_threads);
4923 
4924  return 0;
4925 }
4926 
4928 {
4929  AVFilterContext *ctx = inlink->dst;
4930  AVFilterLink *outlink = ctx->outputs[0];
4931  V360Context *s = ctx->priv;
4932  AVFrame *out;
4933  ThreadData td;
4934 
4935  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
4936  if (!out) {
4937  av_frame_free(&in);
4938  return AVERROR(ENOMEM);
4939  }
4940  av_frame_copy_props(out, in);
4941 
4942  td.in = in;
4943  td.out = out;
4944 
4945  ff_filter_execute(ctx, s->remap_slice, &td, NULL, s->nb_threads);
4946 
4947  av_frame_free(&in);
4948  return ff_filter_frame(outlink, out);
4949 }
4950 
4951 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
4952  char *res, int res_len, int flags)
4953 {
4954  V360Context *s = ctx->priv;
4955  int ret;
4956 
4957  s->yaw = s->pitch = s->roll = 0.f;
4958 
4959  ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
4960  if (ret < 0)
4961  return ret;
4962 
4963  return config_output(ctx->outputs[0]);
4964 }
4965 
4967 {
4968  V360Context *s = ctx->priv;
4969 
4970  s->rot_quaternion[0][0] = 1.f;
4971  s->rot_quaternion[0][1] = s->rot_quaternion[0][2] = s->rot_quaternion[0][3] = 0.f;
4972 
4973  return 0;
4974 }
4975 
4977 {
4978  V360Context *s = ctx<