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