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