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