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