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