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