FFmpeg  4.4.6
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  default:
3641  av_assert0(0);
3642  }
3643  }
3644 
3645  vec[0] = l_x;
3646  vec[1] = l_y;
3647  vec[2] = l_z;
3648 
3649  normalize_vector(vec);
3650 
3651  return 1;
3652 }
3653 
3654 /**
3655  * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format.
3656  *
3657  * @param s filter private context
3658  * @param i horizontal position on frame [0, width)
3659  * @param j vertical position on frame [0, height)
3660  * @param width frame width
3661  * @param height frame height
3662  * @param vec coordinates on sphere
3663  */
3664 static int tspyramid_to_xyz(const V360Context *s,
3665  int i, int j, int width, int height,
3666  float *vec)
3667 {
3668  const float x = (i + 0.5f) / width;
3669  const float y = (j + 0.5f) / height;
3670 
3671  if (x < 0.5f) {
3672  vec[0] = x * 4.f - 1.f;
3673  vec[1] = (y * 2.f - 1.f);
3674  vec[2] = 1.f;
3675  } else if (x >= 0.6875f && x < 0.8125f &&
3676  y >= 0.375f && y < 0.625f) {
3677  vec[0] = -(x - 0.6875f) * 16.f + 1.f;
3678  vec[1] = (y - 0.375f) * 8.f - 1.f;
3679  vec[2] = -1.f;
3680  } else if (0.5f <= x && x < 0.6875f &&
3681  ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) ||
3682  (0.375f <= y && y < 0.625f) ||
3683  (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) {
3684  vec[0] = 1.f;
3685  vec[1] = 2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f;
3686  vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f;
3687  } else if (0.8125f <= x && x < 1.f &&
3688  ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) ||
3689  (0.375f <= y && y < 0.625f) ||
3690  (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) {
3691  vec[0] = -1.f;
3692  vec[1] = 2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f;
3693  vec[2] = 2.f * (x - 0.8125f) / 0.1875f - 1.f;
3694  } else if (0.f <= y && y < 0.375f &&
3695  ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) ||
3696  (0.6875f <= x && x < 0.8125f) ||
3697  (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) {
3698  vec[0] = 2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f;
3699  vec[1] = -1.f;
3700  vec[2] = 2.f * (0.375f - y) / 0.375f - 1.f;
3701  } else {
3702  vec[0] = 2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f;
3703  vec[1] = 1.f;
3704  vec[2] = -2.f * (1.f - y) / 0.375f + 1.f;
3705  }
3706 
3707  normalize_vector(vec);
3708 
3709  return 1;
3710 }
3711 
3712 /**
3713  * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere.
3714  *
3715  * @param s filter private context
3716  * @param vec coordinates on sphere
3717  * @param width frame width
3718  * @param height frame height
3719  * @param us horizontal coordinates for interpolation window
3720  * @param vs vertical coordinates for interpolation window
3721  * @param du horizontal relative coordinate
3722  * @param dv vertical relative coordinate
3723  */
3724 static int xyz_to_tspyramid(const V360Context *s,
3725  const float *vec, int width, int height,
3726  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3727 {
3728  float uf, vf;
3729  int ui, vi;
3730  int face;
3731 
3732  xyz_to_cube(s, vec, &uf, &vf, &face);
3733 
3734  uf = (uf + 1.f) * 0.5f;
3735  vf = (vf + 1.f) * 0.5f;
3736 
3737  switch (face) {
3738  case UP:
3739  uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f;
3740  vf = 0.375f - 0.375f * vf;
3741  break;
3742  case FRONT:
3743  uf = 0.5f * uf;
3744  break;
3745  case DOWN:
3746  uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf;
3747  vf = 1.f - 0.375f * vf;
3748  break;
3749  case LEFT:
3750  vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f;
3751  uf = 0.1875f * uf + 0.8125f;
3752  break;
3753  case RIGHT:
3754  vf = 0.375f * uf - 0.75f * uf * vf + vf;
3755  uf = 0.1875f * uf + 0.5f;
3756  break;
3757  case BACK:
3758  uf = 0.125f * uf + 0.6875f;
3759  vf = 0.25f * vf + 0.375f;
3760  break;
3761  }
3762 
3763  uf *= width;
3764  vf *= height;
3765 
3766  ui = floorf(uf);
3767  vi = floorf(vf);
3768 
3769  *du = uf - ui;
3770  *dv = vf - vi;
3771 
3772  for (int i = 0; i < 4; i++) {
3773  for (int j = 0; j < 4; j++) {
3774  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3775  vs[i][j] = reflecty(vi + i - 1, height);
3776  }
3777  }
3778 
3779  return 1;
3780 }
3781 
3782 /**
3783  * Calculate 3D coordinates on sphere for corresponding frame position in octahedron format.
3784  *
3785  * @param s filter private context
3786  * @param i horizontal position on frame [0, width)
3787  * @param j vertical position on frame [0, height)
3788  * @param width frame width
3789  * @param height frame height
3790  * @param vec coordinates on sphere
3791  */
3792 static int octahedron_to_xyz(const V360Context *s,
3793  int i, int j, int width, int height,
3794  float *vec)
3795 {
3796  const float x = ((i + 0.5f) / width) * 2.f - 1.f;
3797  const float y = ((j + 0.5f) / height) * 2.f - 1.f;
3798  const float ax = fabsf(x);
3799  const float ay = fabsf(y);
3800 
3801  vec[2] = 1.f - (ax + ay);
3802  if (ax + ay > 1.f) {
3803  vec[0] = (1.f - ay) * FFSIGN(x);
3804  vec[1] = (1.f - ax) * FFSIGN(y);
3805  } else {
3806  vec[0] = x;
3807  vec[1] = y;
3808  }
3809 
3810  normalize_vector(vec);
3811 
3812  return 1;
3813 }
3814 
3815 /**
3816  * Calculate frame position in octahedron format for corresponding 3D coordinates on sphere.
3817  *
3818  * @param s filter private context
3819  * @param vec coordinates on sphere
3820  * @param width frame width
3821  * @param height frame height
3822  * @param us horizontal coordinates for interpolation window
3823  * @param vs vertical coordinates for interpolation window
3824  * @param du horizontal relative coordinate
3825  * @param dv vertical relative coordinate
3826  */
3827 static int xyz_to_octahedron(const V360Context *s,
3828  const float *vec, int width, int height,
3829  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3830 {
3831  float uf, vf, zf;
3832  int ui, vi;
3833  float div = fabsf(vec[0]) + fabsf(vec[1]) + fabsf(vec[2]);
3834 
3835  uf = vec[0] / div;
3836  vf = vec[1] / div;
3837  zf = vec[2];
3838 
3839  if (zf < 0.f) {
3840  zf = vf;
3841  vf = (1.f - fabsf(uf)) * FFSIGN(zf);
3842  uf = (1.f - fabsf(zf)) * FFSIGN(uf);
3843  }
3844 
3845  uf = uf * 0.5f + 0.5f;
3846  vf = vf * 0.5f + 0.5f;
3847 
3848  uf *= width;
3849  vf *= height;
3850 
3851  ui = floorf(uf);
3852  vi = floorf(vf);
3853 
3854  *du = uf - ui;
3855  *dv = vf - vi;
3856 
3857  for (int i = 0; i < 4; i++) {
3858  for (int j = 0; j < 4; j++) {
3859  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
3860  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
3861  }
3862  }
3863 
3864  return 1;
3865 }
3866 
3867 static void multiply_quaternion(float c[4], const float a[4], const float b[4])
3868 {
3869  c[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3];
3870  c[1] = a[1] * b[0] + a[0] * b[1] + a[2] * b[3] - a[3] * b[2];
3871  c[2] = a[2] * b[0] + a[0] * b[2] + a[3] * b[1] - a[1] * b[3];
3872  c[3] = a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1];
3873 }
3874 
3875 static void conjugate_quaternion(float d[4], const float q[4])
3876 {
3877  d[0] = q[0];
3878  d[1] = -q[1];
3879  d[2] = -q[2];
3880  d[3] = -q[3];
3881 }
3882 
3883 /**
3884  * Calculate rotation quaternion for yaw/pitch/roll angles.
3885  */
3886 static inline void calculate_rotation(float yaw, float pitch, float roll,
3887  float rot_quaternion[2][4],
3888  const int rotation_order[3])
3889 {
3890  const float yaw_rad = yaw * M_PI / 180.f;
3891  const float pitch_rad = pitch * M_PI / 180.f;
3892  const float roll_rad = roll * M_PI / 180.f;
3893 
3894  const float sin_yaw = sinf(yaw_rad * 0.5f);
3895  const float cos_yaw = cosf(yaw_rad * 0.5f);
3896  const float sin_pitch = sinf(pitch_rad * 0.5f);
3897  const float cos_pitch = cosf(pitch_rad * 0.5f);
3898  const float sin_roll = sinf(roll_rad * 0.5f);
3899  const float cos_roll = cosf(roll_rad * 0.5f);
3900 
3901  float m[3][4];
3902  float tmp[2][4];
3903 
3904  m[0][0] = cos_yaw; m[0][1] = 0.f; m[0][2] = sin_yaw; m[0][3] = 0.f;
3905  m[1][0] = cos_pitch; m[1][1] = sin_pitch; m[1][2] = 0.f; m[1][3] = 0.f;
3906  m[2][0] = cos_roll; m[2][1] = 0.f; m[2][2] = 0.f; m[2][3] = sin_roll;
3907 
3908  multiply_quaternion(tmp[0], rot_quaternion[0], m[rotation_order[0]]);
3909  multiply_quaternion(tmp[1], tmp[0], m[rotation_order[1]]);
3910  multiply_quaternion(rot_quaternion[0], tmp[1], m[rotation_order[2]]);
3911 
3912  conjugate_quaternion(rot_quaternion[1], rot_quaternion[0]);
3913 }
3914 
3915 /**
3916  * Rotate vector with given rotation quaternion.
3917  *
3918  * @param rot_quaternion rotation quaternion
3919  * @param vec vector
3920  */
3921 static inline void rotate(const float rot_quaternion[2][4],
3922  float *vec)
3923 {
3924  float qv[4], temp[4], rqv[4];
3925 
3926  qv[0] = 0.f;
3927  qv[1] = vec[0];
3928  qv[2] = vec[1];
3929  qv[3] = vec[2];
3930 
3931  multiply_quaternion(temp, rot_quaternion[0], qv);
3932  multiply_quaternion(rqv, temp, rot_quaternion[1]);
3933 
3934  vec[0] = rqv[1];
3935  vec[1] = rqv[2];
3936  vec[2] = rqv[3];
3937 }
3938 
3939 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3940  float *modifier)
3941 {
3942  modifier[0] = h_flip ? -1.f : 1.f;
3943  modifier[1] = v_flip ? -1.f : 1.f;
3944  modifier[2] = d_flip ? -1.f : 1.f;
3945 }
3946 
3947 static inline void mirror(const float *modifier, float *vec)
3948 {
3949  vec[0] *= modifier[0];
3950  vec[1] *= modifier[1];
3951  vec[2] *= modifier[2];
3952 }
3953 
3954 static inline void input_flip(int16_t u[4][4], int16_t v[4][4], int w, int h, int hflip, int vflip)
3955 {
3956  if (hflip) {
3957  for (int i = 0; i < 4; i++) {
3958  for (int j = 0; j < 4; j++)
3959  u[i][j] = w - 1 - u[i][j];
3960  }
3961  }
3962 
3963  if (vflip) {
3964  for (int i = 0; i < 4; i++) {
3965  for (int j = 0; j < 4; j++)
3966  v[i][j] = h - 1 - v[i][j];
3967  }
3968  }
3969 }
3970 
3971 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3972 {
3973  const int pr_height = s->pr_height[p];
3974 
3975  for (int n = 0; n < s->nb_threads; n++) {
3976  SliceXYRemap *r = &s->slice_remap[n];
3977  const int slice_start = (pr_height * n ) / s->nb_threads;
3978  const int slice_end = (pr_height * (n + 1)) / s->nb_threads;
3979  const int height = slice_end - slice_start;
3980 
3981  if (!r->u[p])
3982  r->u[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3983  if (!r->v[p])
3984  r->v[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3985  if (!r->u[p] || !r->v[p])
3986  return AVERROR(ENOMEM);
3987  if (sizeof_ker) {
3988  if (!r->ker[p])
3989  r->ker[p] = av_calloc(s->uv_linesize[p] * height, sizeof_ker);
3990  if (!r->ker[p])
3991  return AVERROR(ENOMEM);
3992  }
3993 
3994  if (sizeof_mask && !p) {
3995  if (!r->mask)
3996  r->mask = av_calloc(s->pr_width[p] * height, sizeof_mask);
3997  if (!r->mask)
3998  return AVERROR(ENOMEM);
3999  }
4000  }
4001 
4002  return 0;
4003 }
4004 
4005 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
4006 {
4007  switch (format) {
4008  case ORTHOGRAPHIC:
4009  {
4010  const float d = 0.5f * hypotf(w, h);
4011  const float l = sinf(d_fov * M_PI / 360.f) / d;
4012 
4013  *h_fov = asinf(w * 0.5 * l) * 360.f / M_PI;
4014  *v_fov = asinf(h * 0.5 * l) * 360.f / M_PI;
4015 
4016  if (d_fov > 180.f) {
4017  *h_fov = 180.f - *h_fov;
4018  *v_fov = 180.f - *v_fov;
4019  }
4020  }
4021  break;
4022  case EQUISOLID:
4023  {
4024  const float d = 0.5f * hypotf(w, h);
4025  const float l = d / (sinf(d_fov * M_PI / 720.f));
4026 
4027  *h_fov = 2.f * asinf(w * 0.5f / l) * 360.f / M_PI;
4028  *v_fov = 2.f * asinf(h * 0.5f / l) * 360.f / M_PI;
4029  }
4030  break;
4031  case STEREOGRAPHIC:
4032  {
4033  const float d = 0.5f * hypotf(w, h);
4034  const float l = d / (tanf(d_fov * M_PI / 720.f));
4035 
4036  *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI;
4037  *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI;
4038  }
4039  break;
4040  case DUAL_FISHEYE:
4041  {
4042  const float d = 0.5f * hypotf(w * 0.5f, h);
4043 
4044  *h_fov = d / w * 2.f * d_fov;
4045  *v_fov = d / h * d_fov;
4046  }
4047  break;
4048  case FISHEYE:
4049  {
4050  const float d = 0.5f * hypotf(w, h);
4051 
4052  *h_fov = d / w * d_fov;
4053  *v_fov = d / h * d_fov;
4054  }
4055  break;
4056  case FLAT:
4057  default:
4058  {
4059  const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f);
4060  const float d = hypotf(w, h);
4061 
4062  *h_fov = atan2f(da * w, d) * 360.f / M_PI;
4063  *v_fov = atan2f(da * h, d) * 360.f / M_PI;
4064 
4065  if (*h_fov < 0.f)
4066  *h_fov += 360.f;
4067  if (*v_fov < 0.f)
4068  *v_fov += 360.f;
4069  }
4070  break;
4071  }
4072 }
4073 
4074 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
4075 {
4076  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
4077  outw[0] = outw[3] = w;
4078  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
4079  outh[0] = outh[3] = h;
4080 }
4081 
4082 // Calculate remap data
4083 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
4084 {
4085  V360Context *s = ctx->priv;
4086  SliceXYRemap *r = &s->slice_remap[jobnr];
4087 
4088  for (int p = 0; p < s->nb_allocated; p++) {
4089  const int max_value = s->max_value;
4090  const int width = s->pr_width[p];
4091  const int uv_linesize = s->uv_linesize[p];
4092  const int height = s->pr_height[p];
4093  const int in_width = s->inplanewidth[p];
4094  const int in_height = s->inplaneheight[p];
4095  const int slice_start = (height * jobnr ) / nb_jobs;
4096  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
4097  const int elements = s->elements;
4098  float du, dv;
4099  float vec[3];
4100  XYRemap rmap;
4101 
4102  for (int j = slice_start; j < slice_end; j++) {
4103  for (int i = 0; i < width; i++) {
4104  int16_t *u = r->u[p] + ((j - slice_start) * uv_linesize + i) * elements;
4105  int16_t *v = r->v[p] + ((j - slice_start) * uv_linesize + i) * elements;
4106  int16_t *ker = r->ker[p] + ((j - slice_start) * uv_linesize + i) * elements;
4107  uint8_t *mask8 = (p || !r->mask) ? NULL : r->mask + ((j - slice_start) * s->pr_width[0] + i);
4108  uint16_t *mask16 = (p || !r->mask) ? NULL : (uint16_t *)r->mask + ((j - slice_start) * s->pr_width[0] + i);
4109  int in_mask, out_mask;
4110 
4111  if (s->out_transpose)
4112  out_mask = s->out_transform(s, j, i, height, width, vec);
4113  else
4114  out_mask = s->out_transform(s, i, j, width, height, vec);
4115  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4116  rotate(s->rot_quaternion, vec);
4117  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4118  normalize_vector(vec);
4119  mirror(s->output_mirror_modifier, vec);
4120  if (s->in_transpose)
4121  in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
4122  else
4123  in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
4124  input_flip(rmap.u, rmap.v, in_width, in_height, s->ih_flip, s->iv_flip);
4125  av_assert1(!isnan(du) && !isnan(dv));
4126  s->calculate_kernel(du, dv, &rmap, u, v, ker);
4127 
4128  if (!p && r->mask) {
4129  if (s->mask_size == 1) {
4130  mask8[0] = 255 * (out_mask & in_mask);
4131  } else {
4132  mask16[0] = max_value * (out_mask & in_mask);
4133  }
4134  }
4135  }
4136  }
4137  }
4138 
4139  return 0;
4140 }
4141 
4142 static int config_output(AVFilterLink *outlink)
4143 {
4144  AVFilterContext *ctx = outlink->src;
4145  AVFilterLink *inlink = ctx->inputs[0];
4146  V360Context *s = ctx->priv;
4148  const int depth = desc->comp[0].depth;
4149  const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
4150  int sizeof_uv;
4151  int sizeof_ker;
4152  int err;
4153  int h, w;
4154  int in_offset_h, in_offset_w;
4155  int out_offset_h, out_offset_w;
4156  float hf, wf;
4157  int (*prepare_out)(AVFilterContext *ctx);
4158  int have_alpha;
4159 
4160  s->max_value = (1 << depth) - 1;
4161 
4162  switch (s->interp) {
4163  case NEAREST:
4164  s->calculate_kernel = nearest_kernel;
4165  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
4166  s->elements = 1;
4167  sizeof_uv = sizeof(int16_t) * s->elements;
4168  sizeof_ker = 0;
4169  break;
4170  case BILINEAR:
4171  s->calculate_kernel = bilinear_kernel;
4172  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
4173  s->elements = 2 * 2;
4174  sizeof_uv = sizeof(int16_t) * s->elements;
4175  sizeof_ker = sizeof(int16_t) * s->elements;
4176  break;
4177  case LAGRANGE9:
4178  s->calculate_kernel = lagrange_kernel;
4179  s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice;
4180  s->elements = 3 * 3;
4181  sizeof_uv = sizeof(int16_t) * s->elements;
4182  sizeof_ker = sizeof(int16_t) * s->elements;
4183  break;
4184  case BICUBIC:
4185  s->calculate_kernel = bicubic_kernel;
4186  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4187  s->elements = 4 * 4;
4188  sizeof_uv = sizeof(int16_t) * s->elements;
4189  sizeof_ker = sizeof(int16_t) * s->elements;
4190  break;
4191  case LANCZOS:
4192  s->calculate_kernel = lanczos_kernel;
4193  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4194  s->elements = 4 * 4;
4195  sizeof_uv = sizeof(int16_t) * s->elements;
4196  sizeof_ker = sizeof(int16_t) * s->elements;
4197  break;
4198  case SPLINE16:
4199  s->calculate_kernel = spline16_kernel;
4200  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4201  s->elements = 4 * 4;
4202  sizeof_uv = sizeof(int16_t) * s->elements;
4203  sizeof_ker = sizeof(int16_t) * s->elements;
4204  break;
4205  case GAUSSIAN:
4206  s->calculate_kernel = gaussian_kernel;
4207  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4208  s->elements = 4 * 4;
4209  sizeof_uv = sizeof(int16_t) * s->elements;
4210  sizeof_ker = sizeof(int16_t) * s->elements;
4211  break;
4212  case MITCHELL:
4213  s->calculate_kernel = mitchell_kernel;
4214  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4215  s->elements = 4 * 4;
4216  sizeof_uv = sizeof(int16_t) * s->elements;
4217  sizeof_ker = sizeof(int16_t) * s->elements;
4218  break;
4219  default:
4220  av_assert0(0);
4221  }
4222 
4223  ff_v360_init(s, depth);
4224 
4225  for (int order = 0; order < NB_RORDERS; order++) {
4226  const char c = s->rorder[order];
4227  int rorder;
4228 
4229  if (c == '\0') {
4231  "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
4232  s->rotation_order[0] = YAW;
4233  s->rotation_order[1] = PITCH;
4234  s->rotation_order[2] = ROLL;
4235  break;
4236  }
4237 
4238  rorder = get_rorder(c);
4239  if (rorder == -1) {
4241  "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
4242  s->rotation_order[0] = YAW;
4243  s->rotation_order[1] = PITCH;
4244  s->rotation_order[2] = ROLL;
4245  break;
4246  }
4247 
4248  s->rotation_order[order] = rorder;
4249  }
4250 
4251  switch (s->in_stereo) {
4252  case STEREO_2D:
4253  w = inlink->w;
4254  h = inlink->h;
4255  in_offset_w = in_offset_h = 0;
4256  break;
4257  case STEREO_SBS:
4258  w = inlink->w / 2;
4259  h = inlink->h;
4260  in_offset_w = w;
4261  in_offset_h = 0;
4262  break;
4263  case STEREO_TB:
4264  w = inlink->w;
4265  h = inlink->h / 2;
4266  in_offset_w = 0;
4267  in_offset_h = h;
4268  break;
4269  default:
4270  av_assert0(0);
4271  }
4272 
4273  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
4274  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
4275 
4276  s->in_width = s->inplanewidth[0];
4277  s->in_height = s->inplaneheight[0];
4278 
4279  if (s->id_fov > 0.f)
4280  fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
4281 
4282  if (s->in_transpose)
4283  FFSWAP(int, s->in_width, s->in_height);
4284 
4285  switch (s->in) {
4286  case EQUIRECTANGULAR:
4287  s->in_transform = xyz_to_equirect;
4288  err = 0;
4289  wf = w;
4290  hf = h;
4291  break;
4292  case CUBEMAP_3_2:
4293  s->in_transform = xyz_to_cube3x2;
4294  err = prepare_cube_in(ctx);
4295  wf = w / 3.f * 4.f;
4296  hf = h;
4297  break;
4298  case CUBEMAP_1_6:
4299  s->in_transform = xyz_to_cube1x6;
4300  err = prepare_cube_in(ctx);
4301  wf = w * 4.f;
4302  hf = h / 3.f;
4303  break;
4304  case CUBEMAP_6_1:
4305  s->in_transform = xyz_to_cube6x1;
4306  err = prepare_cube_in(ctx);
4307  wf = w / 3.f * 2.f;
4308  hf = h * 2.f;
4309  break;
4310  case EQUIANGULAR:
4311  s->in_transform = xyz_to_eac;
4312  err = prepare_eac_in(ctx);
4313  wf = w;
4314  hf = h / 9.f * 8.f;
4315  break;
4316  case FLAT:
4317  s->in_transform = xyz_to_flat;
4318  err = prepare_flat_in(ctx);
4319  wf = w;
4320  hf = h;
4321  break;
4322  case PERSPECTIVE:
4323  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
4324  return AVERROR(EINVAL);
4325  case DUAL_FISHEYE:
4326  s->in_transform = xyz_to_dfisheye;
4327  err = prepare_fisheye_in(ctx);
4328  wf = w;
4329  hf = h;
4330  break;
4331  case BARREL:
4332  s->in_transform = xyz_to_barrel;
4333  err = 0;
4334  wf = w / 5.f * 4.f;
4335  hf = h;
4336  break;
4337  case STEREOGRAPHIC:
4338  s->in_transform = xyz_to_stereographic;
4340  wf = w;
4341  hf = h / 2.f;
4342  break;
4343  case MERCATOR:
4344  s->in_transform = xyz_to_mercator;
4345  err = 0;
4346  wf = w;
4347  hf = h / 2.f;
4348  break;
4349  case BALL:
4350  s->in_transform = xyz_to_ball;
4351  err = 0;
4352  wf = w;
4353  hf = h / 2.f;
4354  break;
4355  case HAMMER:
4356  s->in_transform = xyz_to_hammer;
4357  err = 0;
4358  wf = w;
4359  hf = h;
4360  break;
4361  case SINUSOIDAL:
4362  s->in_transform = xyz_to_sinusoidal;
4363  err = 0;
4364  wf = w;
4365  hf = h;
4366  break;
4367  case FISHEYE:
4368  s->in_transform = xyz_to_fisheye;
4369  err = prepare_fisheye_in(ctx);
4370  wf = w * 2;
4371  hf = h;
4372  break;
4373  case PANNINI:
4374  s->in_transform = xyz_to_pannini;
4375  err = 0;
4376  wf = w;
4377  hf = h;
4378  break;
4379  case CYLINDRICAL:
4380  s->in_transform = xyz_to_cylindrical;
4381  err = prepare_cylindrical_in(ctx);
4382  wf = w;
4383  hf = h * 2.f;
4384  break;
4385  case TETRAHEDRON:
4386  s->in_transform = xyz_to_tetrahedron;
4387  err = 0;
4388  wf = w;
4389  hf = h;
4390  break;
4391  case BARREL_SPLIT:
4392  s->in_transform = xyz_to_barrelsplit;
4393  err = 0;
4394  wf = w * 4.f / 3.f;
4395  hf = h;
4396  break;
4397  case TSPYRAMID:
4398  s->in_transform = xyz_to_tspyramid;
4399  err = 0;
4400  wf = w;
4401  hf = h;
4402  break;
4403  case HEQUIRECTANGULAR:
4404  s->in_transform = xyz_to_hequirect;
4405  err = 0;
4406  wf = w * 2.f;
4407  hf = h;
4408  break;
4409  case EQUISOLID:
4410  s->in_transform = xyz_to_equisolid;
4411  err = prepare_equisolid_in(ctx);
4412  wf = w;
4413  hf = h / 2.f;
4414  break;
4415  case ORTHOGRAPHIC:
4416  s->in_transform = xyz_to_orthographic;
4418  wf = w;
4419  hf = h / 2.f;
4420  break;
4421  case OCTAHEDRON:
4422  s->in_transform = xyz_to_octahedron;
4423  err = 0;
4424  wf = w;
4425  hf = h / 2.f;
4426  break;
4427  default:
4428  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
4429  return AVERROR_BUG;
4430  }
4431 
4432  if (err != 0) {
4433  return err;
4434  }
4435 
4436  switch (s->out) {
4437  case EQUIRECTANGULAR:
4438  s->out_transform = equirect_to_xyz;
4439  prepare_out = NULL;
4440  w = lrintf(wf);
4441  h = lrintf(hf);
4442  break;
4443  case CUBEMAP_3_2:
4444  s->out_transform = cube3x2_to_xyz;
4445  prepare_out = prepare_cube_out;
4446  w = lrintf(wf / 4.f * 3.f);
4447  h = lrintf(hf);
4448  break;
4449  case CUBEMAP_1_6:
4450  s->out_transform = cube1x6_to_xyz;
4451  prepare_out = prepare_cube_out;
4452  w = lrintf(wf / 4.f);
4453  h = lrintf(hf * 3.f);
4454  break;
4455  case CUBEMAP_6_1:
4456  s->out_transform = cube6x1_to_xyz;
4457  prepare_out = prepare_cube_out;
4458  w = lrintf(wf / 2.f * 3.f);
4459  h = lrintf(hf / 2.f);
4460  break;
4461  case EQUIANGULAR:
4462  s->out_transform = eac_to_xyz;
4463  prepare_out = prepare_eac_out;
4464  w = lrintf(wf);
4465  h = lrintf(hf / 8.f * 9.f);
4466  break;
4467  case FLAT:
4468  s->out_transform = flat_to_xyz;
4469  prepare_out = prepare_flat_out;
4470  w = lrintf(wf);
4471  h = lrintf(hf);
4472  break;
4473  case DUAL_FISHEYE:
4474  s->out_transform = dfisheye_to_xyz;
4475  prepare_out = prepare_fisheye_out;
4476  w = lrintf(wf);
4477  h = lrintf(hf);
4478  break;
4479  case BARREL:
4480  s->out_transform = barrel_to_xyz;
4481  prepare_out = NULL;
4482  w = lrintf(wf / 4.f * 5.f);
4483  h = lrintf(hf);
4484  break;
4485  case STEREOGRAPHIC:
4486  s->out_transform = stereographic_to_xyz;
4487  prepare_out = prepare_stereographic_out;
4488  w = lrintf(wf);
4489  h = lrintf(hf * 2.f);
4490  break;
4491  case MERCATOR:
4492  s->out_transform = mercator_to_xyz;
4493  prepare_out = NULL;
4494  w = lrintf(wf);
4495  h = lrintf(hf * 2.f);
4496  break;
4497  case BALL:
4498  s->out_transform = ball_to_xyz;
4499  prepare_out = NULL;
4500  w = lrintf(wf);
4501  h = lrintf(hf * 2.f);
4502  break;
4503  case HAMMER:
4504  s->out_transform = hammer_to_xyz;
4505  prepare_out = NULL;
4506  w = lrintf(wf);
4507  h = lrintf(hf);
4508  break;
4509  case SINUSOIDAL:
4510  s->out_transform = sinusoidal_to_xyz;
4511  prepare_out = NULL;
4512  w = lrintf(wf);
4513  h = lrintf(hf);
4514  break;
4515  case FISHEYE:
4516  s->out_transform = fisheye_to_xyz;
4517  prepare_out = prepare_fisheye_out;
4518  w = lrintf(wf * 0.5f);
4519  h = lrintf(hf);
4520  break;
4521  case PANNINI:
4522  s->out_transform = pannini_to_xyz;
4523  prepare_out = NULL;
4524  w = lrintf(wf);
4525  h = lrintf(hf);
4526  break;
4527  case CYLINDRICAL:
4528  s->out_transform = cylindrical_to_xyz;
4529  prepare_out = prepare_cylindrical_out;
4530  w = lrintf(wf);
4531  h = lrintf(hf * 0.5f);
4532  break;
4533  case PERSPECTIVE:
4534  s->out_transform = perspective_to_xyz;
4535  prepare_out = NULL;
4536  w = lrintf(wf / 2.f);
4537  h = lrintf(hf);
4538  break;
4539  case TETRAHEDRON:
4540  s->out_transform = tetrahedron_to_xyz;
4541  prepare_out = NULL;
4542  w = lrintf(wf);
4543  h = lrintf(hf);
4544  break;
4545  case BARREL_SPLIT:
4546  s->out_transform = barrelsplit_to_xyz;
4547  prepare_out = NULL;
4548  w = lrintf(wf / 4.f * 3.f);
4549  h = lrintf(hf);
4550  break;
4551  case TSPYRAMID:
4552  s->out_transform = tspyramid_to_xyz;
4553  prepare_out = NULL;
4554  w = lrintf(wf);
4555  h = lrintf(hf);
4556  break;
4557  case HEQUIRECTANGULAR:
4558  s->out_transform = hequirect_to_xyz;
4559  prepare_out = NULL;
4560  w = lrintf(wf / 2.f);
4561  h = lrintf(hf);
4562  break;
4563  case EQUISOLID:
4564  s->out_transform = equisolid_to_xyz;
4565  prepare_out = prepare_equisolid_out;
4566  w = lrintf(wf);
4567  h = lrintf(hf * 2.f);
4568  break;
4569  case ORTHOGRAPHIC:
4570  s->out_transform = orthographic_to_xyz;
4571  prepare_out = prepare_orthographic_out;
4572  w = lrintf(wf);
4573  h = lrintf(hf * 2.f);
4574  break;
4575  case OCTAHEDRON:
4576  s->out_transform = octahedron_to_xyz;
4577  prepare_out = NULL;
4578  w = lrintf(wf);
4579  h = lrintf(hf * 2.f);
4580  break;
4581  default:
4582  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
4583  return AVERROR_BUG;
4584  }
4585 
4586  // Override resolution with user values if specified
4587  if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4588  s->out == FLAT && s->d_fov == 0.f) {
4589  w = s->width;
4590  h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
4591  } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4592  s->out == FLAT && s->d_fov == 0.f) {
4593  h = s->height;
4594  w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
4595  } else if (s->width > 0 && s->height > 0) {
4596  w = s->width;
4597  h = s->height;
4598  } else if (s->width > 0 || s->height > 0) {
4599  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
4600  return AVERROR(EINVAL);
4601  } else {
4602  if (s->out_transpose)
4603  FFSWAP(int, w, h);
4604 
4605  if (s->in_transpose)
4606  FFSWAP(int, w, h);
4607  }
4608 
4609  s->width = w;
4610  s->height = h;
4611 
4612  if (s->d_fov > 0.f)
4613  fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
4614 
4615  if (prepare_out) {
4616  err = prepare_out(ctx);
4617  if (err != 0)
4618  return err;
4619  }
4620 
4621  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
4622 
4623  switch (s->out_stereo) {
4624  case STEREO_2D:
4625  out_offset_w = out_offset_h = 0;
4626  break;
4627  case STEREO_SBS:
4628  out_offset_w = w;
4629  out_offset_h = 0;
4630  w *= 2;
4631  break;
4632  case STEREO_TB:
4633  out_offset_w = 0;
4634  out_offset_h = h;
4635  h *= 2;
4636  break;
4637  default:
4638  av_assert0(0);
4639  }
4640 
4641  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
4642  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
4643 
4644  for (int i = 0; i < 4; i++)
4645  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
4646 
4647  outlink->h = h;
4648  outlink->w = w;
4649 
4650  s->nb_threads = FFMIN(outlink->h, ff_filter_get_nb_threads(ctx));
4651  s->nb_planes = av_pix_fmt_count_planes(inlink->format);
4652  have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
4653 
4654  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
4655  s->nb_allocated = 1;
4656  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
4657  } else {
4658  s->nb_allocated = 2;
4659  s->map[0] = s->map[3] = 0;
4660  s->map[1] = s->map[2] = 1;
4661  }
4662 
4663  if (!s->slice_remap)
4664  s->slice_remap = av_calloc(s->nb_threads, sizeof(*s->slice_remap));
4665  if (!s->slice_remap)
4666  return AVERROR(ENOMEM);
4667 
4668  for (int i = 0; i < s->nb_allocated; i++) {
4669  err = allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
4670  if (err < 0)
4671  return err;
4672  }
4673 
4674  calculate_rotation(s->yaw, s->pitch, s->roll,
4675  s->rot_quaternion, s->rotation_order);
4676 
4677  set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier);
4678 
4679  ctx->internal->execute(ctx, v360_slice, NULL, NULL, s->nb_threads);
4680 
4681  return 0;
4682 }
4683 
4684 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
4685 {
4686  AVFilterContext *ctx = inlink->dst;
4687  AVFilterLink *outlink = ctx->outputs[0];
4688  V360Context *s = ctx->priv;
4689  AVFrame *out;
4690  ThreadData td;
4691 
4692  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
4693  if (!out) {
4694  av_frame_free(&in);
4695  return AVERROR(ENOMEM);
4696  }
4698 
4699  td.in = in;
4700  td.out = out;
4701 
4702  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, s->nb_threads);
4703 
4704  av_frame_free(&in);
4705  return ff_filter_frame(outlink, out);
4706 }
4707 
4708 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
4709  char *res, int res_len, int flags)
4710 {
4711  V360Context *s = ctx->priv;
4712  int ret;
4713 
4714  s->yaw = s->pitch = s->roll = 0.f;
4715 
4716  ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
4717  if (ret < 0)
4718  return ret;
4719 
4720  return config_output(ctx->outputs[0]);
4721 }
4722 
4724 {
4725  V360Context *s = ctx->priv;
4726 
4727  s->rot_quaternion[0][0] = 1.f;
4728  s->rot_quaternion[0][1] = s->rot_quaternion[0][2] = s->rot_quaternion[0][3] = 0.f;
4729 
4730  return 0;
4731 }
4732 
4734 {
4735  V360Context *s = ctx->priv;
4736 
4737  for (int n = 0; n < s->nb_threads && s->slice_remap; n++) {
4738  SliceXYRemap *r = &s->slice_remap[n];
4739 
4740  for (int p = 0; p < s->nb_allocated; p++) {
4741  av_freep(&r->u[p]);
4742  av_freep(&r->v[p]);
4743  av_freep(&r->ker[p]);
4744  }
4745 
4746  av_freep(&r->mask);
4747  }
4748 
4749  av_freep(&s->slice_remap);
4750 }
4751 
4752 static const AVFilterPad inputs[] = {
4753  {
4754  .name = "default",
4755  .type = AVMEDIA_TYPE_VIDEO,
4756  .filter_frame = filter_frame,
4757  },
4758  { NULL }
4759 };
4760 
4761 static const AVFilterPad outputs[] = {
4762  {
4763  .name = "default",
4764  .type = AVMEDIA_TYPE_VIDEO,
4765  .config_props = config_output,
4766  },
4767  { NULL }
4768 };
4769 
4771  .name = "v360",
4772  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
4773  .priv_size = sizeof(V360Context),
4774  .init = init,
4775  .uninit = uninit,
4777  .inputs = inputs,
4778  .outputs = outputs,
4779  .priv_class = &v360_class,
4782 };
static const char *const format[]
Definition: af_aiir.c:456
#define av_always_inline
Definition: attributes.h:45
#define av_cold
Definition: attributes.h:88
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31)))) #define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac) { } void ff_audio_convert_free(AudioConvert **ac) { if(! *ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);} AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map) { AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method !=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2) { ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc) { av_free(ac);return NULL;} return ac;} in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar) { ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar ? ac->channels :1;} else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;} int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic=1;int len=in->nb_samples;int p;if(ac->dc) { av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
uint8_t
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:53
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
Generic processing of user supplied commands that are set in the same way as the filter options.
Definition: avfilter.c:882
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
Main libavfilter public API header.
#define flags(name, subs,...)
Definition: cbs_av1.c:572
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:264
#define us(width, name, range_min, range_max, subs,...)
Definition: cbs_h2645.c:278
#define ui(width, name)
Definition: cbs_mpeg2.c:43
#define s(width, name)
Definition: cbs_vp9.c:257
#define LEFT
Definition: cdgraphics.c:166
#define RIGHT
Definition: cdgraphics.c:167
#define FFMAX3(a, b, c)
Definition: common.h:104
#define FFSWAP(type, a, b)
Definition: common.h:108
#define FFMIN(a, b)
Definition: common.h:105
#define FF_CEIL_RSHIFT
Definition: common.h:61
#define av_clip
Definition: common.h:122
#define FFMAX(a, b)
Definition: common.h:103
#define av_clipf
Definition: common.h:170
#define FFSIGN(a)
Definition: common.h:73
#define ARCH_X86
Definition: config.h:39
#define NULL
Definition: coverity.c:32
static __device__ float fabsf(float a)
Definition: cuda_runtime.h:181
static __device__ float floorf(float a)
Definition: cuda_runtime.h:172
static __device__ float ceilf(float a)
Definition: cuda_runtime.h:175
int
#define S(s, c, i)
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:587
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:286
@ AV_OPT_TYPE_CONST
Definition: opt.h:234
@ AV_OPT_TYPE_INT
Definition: opt.h:225
@ AV_OPT_TYPE_FLOAT
Definition: opt.h:228
@ AV_OPT_TYPE_BOOL
Definition: opt.h:242
@ AV_OPT_TYPE_STRING
Definition: opt.h:229
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:658
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:245
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
static const int16_t alpha[]
Definition: ilbcdata.h:55
misc image utilities
int i
Definition: input.c:407
const char * arg
Definition: jacosubdec.c:66
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:309
#define isfinite(x)
Definition: libm.h:359
#define isnan(x)
Definition: libm.h:340
#define atanf(x)
Definition: libm.h:40
#define sinf(x)
Definition: libm.h:419
#define cosf(x)
Definition: libm.h:78
#define expf(x)
Definition: libm.h:283
#define atan2f(y, x)
Definition: libm.h:45
#define lrintf(x)
Definition: libm_mips.h:70
const char * desc
Definition: libsvtav1.c:79
uint8_t w
Definition: llviddspenc.c:39
#define FFALIGN(x, a)
Definition: macros.h:48
#define M_SQRT2
Definition: mathematics.h:61
#define M_PI_2
Definition: mathematics.h:55
#define M_PI
Definition: mathematics.h:52
#define TOP_LEFT
Definition: movtextdec.c:49
#define TOP_RIGHT
Definition: movtextdec.c:51
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2033
AVOptions.
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2613
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2573
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition: pixdesc.h:179
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:420
#define AV_PIX_FMT_YUV420P16
Definition: pixfmt.h:410
#define AV_PIX_FMT_YUV444P12
Definition: pixfmt.h:406
#define AV_PIX_FMT_YUV444P9
Definition: pixfmt.h:398
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:399
#define AV_PIX_FMT_YUV440P12
Definition: pixfmt.h:405
#define AV_PIX_FMT_GRAY9
Definition: pixfmt.h:379
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:421
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:414
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:397
#define AV_PIX_FMT_YUVA444P10
Definition: pixfmt.h:438
#define AV_PIX_FMT_YUVA420P16
Definition: pixfmt.h:441
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:403
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:436
#define AV_PIX_FMT_YUVA422P9
Definition: pixfmt.h:434
#define AV_PIX_FMT_YUV422P12
Definition: pixfmt.h:404
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:415
#define AV_PIX_FMT_YUV422P10
Definition: pixfmt.h:400
#define AV_PIX_FMT_GRAY12
Definition: pixfmt.h:381
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:416
#define AV_PIX_FMT_YUV420P9
Definition: pixfmt.h:396
#define AV_PIX_FMT_YUVA420P9
Definition: pixfmt.h:433
#define AV_PIX_FMT_YUVA422P10
Definition: pixfmt.h:437
#define AV_PIX_FMT_YUV420P14
Definition: pixfmt.h:407
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:65
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:66
@ AV_PIX_FMT_YUV440P
planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
Definition: pixfmt.h:99
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:70
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition: pixfmt.h:74
@ AV_PIX_FMT_YUVA420P
planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
Definition: pixfmt.h:101
@ AV_PIX_FMT_YUVJ440P
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
Definition: pixfmt.h:100
@ AV_PIX_FMT_YUV410P
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:72
@ AV_PIX_FMT_YUV411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
Definition: pixfmt.h:73
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:71
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition: pixfmt.h:177
@ AV_PIX_FMT_YUVJ411P
planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor ...
Definition: pixfmt.h:258
@ AV_PIX_FMT_GBRAP
planar GBRA 4:4:4:4 32bpp
Definition: pixfmt.h:215
@ AV_PIX_FMT_YUVJ422P
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition: pixfmt.h:79
@ AV_PIX_FMT_YUVA422P
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition: pixfmt.h:176
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
@ AV_PIX_FMT_YUVJ444P
planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting col...
Definition: pixfmt.h:80
@ AV_PIX_FMT_YUVJ420P
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:78
#define AV_PIX_FMT_YUVA422P12
Definition: pixfmt.h:439
#define AV_PIX_FMT_YUV422P14
Definition: pixfmt.h:408
#define AV_PIX_FMT_GRAY10
Definition: pixfmt.h:380
#define AV_PIX_FMT_GRAY14
Definition: pixfmt.h:382
#define AV_PIX_FMT_YUV422P16
Definition: pixfmt.h:411
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:401
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:383
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:419
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:443
#define AV_PIX_FMT_YUVA422P16
Definition: pixfmt.h:442
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:418
#define AV_PIX_FMT_YUV444P14
Definition: pixfmt.h:409
#define AV_PIX_FMT_YUVA444P9
Definition: pixfmt.h:435
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:417
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:440
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:412
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:402
#define td
Definition: regdef.h:70
static const ElemCat * elements[ELEMENT_COUNT]
Definition: signature.h:566
An instance of a filter.
Definition: avfilter.h:341
A list of supported formats for one end of a filter link.
Definition: formats.h:65
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
AVFormatInternal * internal
An opaque field for libavformat internal usage.
Definition: avformat.h:1699
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
AVOption.
Definition: opt.h:248
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:81
Used for passing data between threads.
Definition: dsddec.c:67
AVFrame * out
Definition: af_adeclick.c:502
AVFrame * in
Definition: af_adenorm.c:223
Definition: v360.h:107
int16_t u[4][4]
Definition: v360.h:108
int16_t v[4][4]
Definition: v360.h:109
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[11]
Definition: aes_ctr.c:27
FILE * out
Definition: movenc.c:54
AVFormatContext * ctx
Definition: movenc.c:48
#define height
#define width
static const uint8_t q1[256]
Definition: twofish.c:96
static const uint8_t q0[256]
Definition: twofish.c:77
@ NB_FACES
Definition: v360.h:79
@ TOP_MIDDLE
Definition: v360.h:74
@ BOTTOM_RIGHT
Definition: v360.h:78
@ BOTTOM_LEFT
Definition: v360.h:76
@ BOTTOM_MIDDLE
Definition: v360.h:77
@ FRONT
Axis -Z.
Definition: v360.h:87
@ DOWN
Axis -Y.
Definition: v360.h:86
@ NB_DIRECTIONS
Definition: v360.h:89
@ UP
Axis +Y.
Definition: v360.h:85
@ BACK
Axis +Z.
Definition: v360.h:88
@ ROT_90
Definition: v360.h:94
@ ROT_270
Definition: v360.h:96
@ ROT_0
Definition: v360.h:93
@ ROT_180
Definition: v360.h:95
@ SPLINE16
Definition: v360.h:66
@ NB_INTERP_METHODS
Definition: v360.h:69
@ LAGRANGE9
Definition: v360.h:63
@ MITCHELL
Definition: v360.h:68
@ BILINEAR
Definition: v360.h:62
@ BICUBIC
Definition: v360.h:64
@ LANCZOS
Definition: v360.h:65
@ GAUSSIAN
Definition: v360.h:67
void ff_v360_init_x86(V360Context *s, int depth)
Definition: vf_v360_init.c:44
@ EQUISOLID
Definition: v360.h:54
@ FLAT
Definition: v360.h:37
@ FISHEYE
Definition: v360.h:46
@ DUAL_FISHEYE
Definition: v360.h:38
@ EQUIRECTANGULAR
Definition: v360.h:33
@ PERSPECTIVE
Definition: v360.h:49
@ TSPYRAMID
Definition: v360.h:52
@ CUBEMAP_3_2
Definition: v360.h:34
@ CUBEMAP_6_1
Definition: v360.h:35
@ SINUSOIDAL
Definition: v360.h:45
@ HEQUIRECTANGULAR
Definition: v360.h:53
@ OCTAHEDRON
Definition: v360.h:56
@ HAMMER
Definition: v360.h:44
@ CUBEMAP_1_6
Definition: v360.h:40
@ STEREOGRAPHIC
Definition: v360.h:41
@ CYLINDRICAL
Definition: v360.h:48
@ BALL
Definition: v360.h:43
@ PANNINI
Definition: v360.h:47
@ BARREL_SPLIT
Definition: v360.h:51
@ EQUIANGULAR
Definition: v360.h:36
@ BARREL
Definition: v360.h:39
@ NB_PROJECTIONS
Definition: v360.h:57
@ TETRAHEDRON
Definition: v360.h:50
@ MERCATOR
Definition: v360.h:42
@ ORTHOGRAPHIC
Definition: v360.h:55
@ STEREO_SBS
Definition: v360.h:27
@ STEREO_TB
Definition: v360.h:28
@ STEREO_2D
Definition: v360.h:26
@ NB_STEREO_FMTS
Definition: v360.h:29
@ ROLL
Definition: v360.h:103
@ NB_RORDERS
Definition: v360.h:104
@ YAW
Definition: v360.h:101
@ PITCH
Definition: v360.h:102
const char * b
Definition: vf_curves.c:118
const char * r
Definition: vf_curves.c:116
#define NEAREST(type, name)
else temp
Definition: vf_mcdeint.c:259
if(ret< 0)
Definition: vf_mcdeint.c:282
static enum AVPixelFormat alpha_pix_fmts[]
Definition: vf_overlay.c:155
static int prepare_orthographic_in(AVFilterContext *ctx)
Prepare data for processing orthographic input format.
Definition: vf_v360.c: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:4074
static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_v360.c:4083
AVFilter ff_vf_v360
Definition: vf_v360.c:4770
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:3921
#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:3954
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:3827
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:3875
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:4752
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:3947
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:4761
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:3664
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:4684
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
Definition: vf_v360.c:3971
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:3939
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:4708
static av_cold int init(AVFilterContext *ctx)
Definition: vf_v360.c:4723
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:4733
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:4005
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:4142
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:3867
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:3886
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:3792
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:3724
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]