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