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