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