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