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