FFmpeg
vf_v360.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 Eugene Lyapustin
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * 360 video conversion filter.
24  * Principle of operation:
25  *
26  * (for each pixel in output frame)
27  * 1) Calculate OpenGL-like coordinates (x, y, z) for pixel position (i, j)
28  * 2) Apply 360 operations (rotation, mirror) to (x, y, z)
29  * 3) Calculate pixel position (u, v) in input frame
30  * 4) Calculate interpolation window and weight for each pixel
31  *
32  * (for each frame)
33  * 5) Remap input frame to output frame using precalculated data
34  */
35 
36 #include <math.h>
37 
38 #include "libavutil/avassert.h"
39 #include "libavutil/imgutils.h"
40 #include "libavutil/pixdesc.h"
41 #include "libavutil/opt.h"
42 #include "avfilter.h"
43 #include "formats.h"
44 #include "internal.h"
45 #include "video.h"
46 #include "v360.h"
47 
48 typedef struct ThreadData {
49  AVFrame *in;
50  AVFrame *out;
51 } ThreadData;
52 
53 #define OFFSET(x) offsetof(V360Context, x)
54 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
55 #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
56 
57 static const AVOption v360_options[] = {
58  { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" },
59  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
60  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
61  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "in" },
62  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" },
63  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" },
64  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
65  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
66  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
67  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" },
68  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
69  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
70  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" },
71  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
72  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" },
73  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" },
74  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" },
75  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" },
76  { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" },
77  { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "in" },
78  {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "in" },
79  {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "in" },
80  {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, "in" },
81  { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, "in" },
82  { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" },
83  { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" },
84  { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, "in" },
85  { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, "in" },
86  {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, "in" },
87  { "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[] = {
245  AV_PIX_FMT_NONE
246  };
247 
248  AVFilterFormats *fmts_list = ff_make_format_list(s->alpha ? alpha_pix_fmts : pix_fmts);
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') {
886  av_log(ctx, AV_LOG_ERROR,
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) {
893  av_log(ctx, AV_LOG_ERROR,
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') {
906  av_log(ctx, AV_LOG_ERROR,
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) {
913  av_log(ctx, AV_LOG_ERROR,
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') {
940  av_log(ctx, AV_LOG_ERROR,
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) {
947  av_log(ctx, AV_LOG_ERROR,
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') {
960  av_log(ctx, AV_LOG_ERROR,
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) {
967  av_log(ctx, AV_LOG_ERROR,
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 
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];
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 
2554 
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 
2582 
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] = sin_theta;
3137  vec[2] = cos_theta * cos_phi;
3138  } else {
3139  vec[0] = 0.f;
3140  vec[1] = 1.f;
3141  vec[2] = 0.f;
3142  return 0;
3143  }
3144 
3145  normalize_vector(vec);
3146  return 1;
3147 }
3148 
3149 /**
3150  * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format.
3151  *
3152  * @param s filter private context
3153  * @param i horizontal position on frame [0, width)
3154  * @param j vertical position on frame [0, height)
3155  * @param width frame width
3156  * @param height frame height
3157  * @param vec coordinates on sphere
3158  */
3160  int i, int j, int width, int height,
3161  float *vec)
3162 {
3163  const float uf = (float)i / width;
3164  const float vf = (float)j / height;
3165 
3166  vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
3167  vec[1] = 1.f - vf * 2.f;
3168  vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
3169 
3170  normalize_vector(vec);
3171 
3172  return 1;
3173 }
3174 
3175 /**
3176  * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere.
3177  *
3178  * @param s filter private context
3179  * @param vec coordinates on sphere
3180  * @param width frame width
3181  * @param height frame height
3182  * @param us horizontal coordinates for interpolation window
3183  * @param vs vertical coordinates for interpolation window
3184  * @param du horizontal relative coordinate
3185  * @param dv vertical relative coordinate
3186  */
3188  const float *vec, int width, int height,
3189  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3190 {
3191  const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f;
3192  const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f;
3193  const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f;
3194  const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f;
3195  const float d = FFMAX(d0, FFMAX3(d1, d2, d3));
3196 
3197  float uf, vf, x, y, z;
3198  int ui, vi;
3199 
3200  x = vec[0] / d;
3201  y = vec[1] / d;
3202  z = -vec[2] / d;
3203 
3204  vf = 0.5f - y * 0.5f;
3205 
3206  if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) ||
3207  (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) {
3208  uf = 0.25f * x + 0.25f;
3209  } else {
3210  uf = 0.75f - 0.25f * x;
3211  }
3212 
3213  uf *= width;
3214  vf *= height;
3215 
3216  ui = floorf(uf);
3217  vi = floorf(vf);
3218 
3219  *du = uf - ui;
3220  *dv = vf - vi;
3221 
3222  for (int i = 0; i < 4; i++) {
3223  for (int j = 0; j < 4; j++) {
3224  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3225  vs[i][j] = reflecty(vi + i - 1, height);
3226  }
3227  }
3228 
3229  return 1;
3230 }
3231 
3232 /**
3233  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
3234  *
3235  * @param s filter private context
3236  * @param i horizontal position on frame [0, width)
3237  * @param j vertical position on frame [0, height)
3238  * @param width frame width
3239  * @param height frame height
3240  * @param vec coordinates on sphere
3241  */
3242 static int dfisheye_to_xyz(const V360Context *s,
3243  int i, int j, int width, int height,
3244  float *vec)
3245 {
3246  const float ew = width / 2.f;
3247  const float eh = height;
3248 
3249  const int ei = i >= ew ? i - ew : i;
3250  const float m = i >= ew ? 1.f : -1.f;
3251 
3252  const float uf = s->flat_range[0] * ((2.f * ei) / ew - 1.f);
3253  const float vf = s->flat_range[1] * ((2.f * j + 1.f) / eh - 1.f);
3254 
3255  const float h = hypotf(uf, vf);
3256  const float lh = h > 0.f ? h : 1.f;
3257  const float theta = m * M_PI_2 * (1.f - h);
3258 
3259  const float sin_theta = sinf(theta);
3260  const float cos_theta = cosf(theta);
3261 
3262  vec[0] = cos_theta * m * uf / lh;
3263  vec[1] = cos_theta * vf / lh;
3264  vec[2] = sin_theta;
3265 
3266  normalize_vector(vec);
3267 
3268  return 1;
3269 }
3270 
3271 /**
3272  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
3273  *
3274  * @param s filter private context
3275  * @param vec coordinates on sphere
3276  * @param width frame width
3277  * @param height frame height
3278  * @param us horizontal coordinates for interpolation window
3279  * @param vs vertical coordinates for interpolation window
3280  * @param du horizontal relative coordinate
3281  * @param dv vertical relative coordinate
3282  */
3283 static int xyz_to_dfisheye(const V360Context *s,
3284  const float *vec, int width, int height,
3285  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3286 {
3287  const float ew = width / 2.f;
3288  const float eh = height;
3289 
3290  const float h = hypotf(vec[0], vec[1]);
3291  const float lh = h > 0.f ? h : 1.f;
3292  const float theta = acosf(fabsf(vec[2])) / M_PI;
3293 
3294  float uf = (theta * (vec[0] / lh) / s->iflat_range[0] + 0.5f) * ew;
3295  float vf = (theta * (vec[1] / lh) / s->iflat_range[1] + 0.5f) * eh;
3296 
3297  int ui, vi;
3298  int u_shift;
3299 
3300  if (vec[2] >= 0.f) {
3301  u_shift = ceilf(ew);
3302  } else {
3303  u_shift = 0;
3304  uf = ew - uf;
3305  }
3306 
3307  ui = floorf(uf);
3308  vi = floorf(vf);
3309 
3310  *du = uf - ui;
3311  *dv = vf - vi;
3312 
3313  for (int i = 0; i < 4; i++) {
3314  for (int j = 0; j < 4; j++) {
3315  us[i][j] = av_clip(u_shift + ui + j - 1, 0, width - 1);
3316  vs[i][j] = av_clip( vi + i - 1, 0, height - 1);
3317  }
3318  }
3319 
3320  return 1;
3321 }
3322 
3323 /**
3324  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
3325  *
3326  * @param s filter private context
3327  * @param i horizontal position on frame [0, width)
3328  * @param j vertical position on frame [0, height)
3329  * @param width frame width
3330  * @param height frame height
3331  * @param vec coordinates on sphere
3332  */
3333 static int barrel_to_xyz(const V360Context *s,
3334  int i, int j, int width, int height,
3335  float *vec)
3336 {
3337  const float scale = 0.99f;
3338  float l_x, l_y, l_z;
3339 
3340  if (i < 4 * width / 5) {
3341  const float theta_range = M_PI_4;
3342 
3343  const int ew = 4 * width / 5;
3344  const int eh = height;
3345 
3346  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
3347  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
3348 
3349  const float sin_phi = sinf(phi);
3350  const float cos_phi = cosf(phi);
3351  const float sin_theta = sinf(theta);
3352  const float cos_theta = cosf(theta);
3353 
3354  l_x = cos_theta * sin_phi;
3355  l_y = sin_theta;
3356  l_z = cos_theta * cos_phi;
3357  } else {
3358  const int ew = width / 5;
3359  const int eh = height / 2;
3360 
3361  float uf, vf;
3362 
3363  if (j < eh) { // UP
3364  uf = 2.f * (i - 4 * ew) / ew - 1.f;
3365  vf = 2.f * (j ) / eh - 1.f;
3366 
3367  uf /= scale;
3368  vf /= scale;
3369 
3370  l_x = uf;
3371  l_y = -1.f;
3372  l_z = vf;
3373  } else { // DOWN
3374  uf = 2.f * (i - 4 * ew) / ew - 1.f;
3375  vf = 2.f * (j - eh) / eh - 1.f;
3376 
3377  uf /= scale;
3378  vf /= scale;
3379 
3380  l_x = uf;
3381  l_y = 1.f;
3382  l_z = -vf;
3383  }
3384  }
3385 
3386  vec[0] = l_x;
3387  vec[1] = l_y;
3388  vec[2] = l_z;
3389 
3390  normalize_vector(vec);
3391 
3392  return 1;
3393 }
3394 
3395 /**
3396  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
3397  *
3398  * @param s filter private context
3399  * @param vec coordinates on sphere
3400  * @param width frame width
3401  * @param height frame height
3402  * @param us horizontal coordinates for interpolation window
3403  * @param vs vertical coordinates for interpolation window
3404  * @param du horizontal relative coordinate
3405  * @param dv vertical relative coordinate
3406  */
3407 static int xyz_to_barrel(const V360Context *s,
3408  const float *vec, int width, int height,
3409  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3410 {
3411  const float scale = 0.99f;
3412 
3413  const float phi = atan2f(vec[0], vec[2]);
3414  const float theta = asinf(vec[1]);
3415  const float theta_range = M_PI_4;
3416 
3417  int ew, eh;
3418  int u_shift, v_shift;
3419  float uf, vf;
3420  int ui, vi;
3421 
3422  if (theta > -theta_range && theta < theta_range) {
3423  ew = 4 * width / 5;
3424  eh = height;
3425 
3426  u_shift = 0;
3427  v_shift = 0;
3428 
3429  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
3430  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
3431  } else {
3432  ew = width / 5;
3433  eh = height / 2;
3434 
3435  u_shift = 4 * ew;
3436 
3437  if (theta < 0.f) { // UP
3438  uf = -vec[0] / vec[1];
3439  vf = -vec[2] / vec[1];
3440  v_shift = 0;
3441  } else { // DOWN
3442  uf = vec[0] / vec[1];
3443  vf = -vec[2] / vec[1];
3444  v_shift = eh;
3445  }
3446 
3447  uf = 0.5f * ew * (uf * scale + 1.f);
3448  vf = 0.5f * eh * (vf * scale + 1.f);
3449  }
3450 
3451  ui = floorf(uf);
3452  vi = floorf(vf);
3453 
3454  *du = uf - ui;
3455  *dv = vf - vi;
3456 
3457  for (int i = 0; i < 4; i++) {
3458  for (int j = 0; j < 4; j++) {
3459  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3460  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3461  }
3462  }
3463 
3464  return 1;
3465 }
3466 
3467 /**
3468  * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere.
3469  *
3470  * @param s filter private context
3471  * @param vec coordinates on sphere
3472  * @param width frame width
3473  * @param height frame height
3474  * @param us horizontal coordinates for interpolation window
3475  * @param vs vertical coordinates for interpolation window
3476  * @param du horizontal relative coordinate
3477  * @param dv vertical relative coordinate
3478  */
3480  const float *vec, int width, int height,
3481  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3482 {
3483  const float phi = atan2f(vec[0], vec[2]);
3484  const float theta = asinf(vec[1]);
3485 
3486  const float theta_range = M_PI_4;
3487 
3488  int ew, eh;
3489  int u_shift, v_shift;
3490  float uf, vf;
3491  int ui, vi;
3492 
3493  if (theta >= -theta_range && theta <= theta_range) {
3494  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad;
3495  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
3496 
3497  ew = width / 3 * 2;
3498  eh = height / 2;
3499 
3500  u_shift = 0;
3501  v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0;
3502 
3503  uf = fmodf(phi, M_PI_2) / M_PI_2;
3504  vf = theta / M_PI_4;
3505 
3506  if (v_shift)
3507  uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f);
3508 
3509  uf = (uf * scalew + 1.f) * width / 3.f;
3510  vf = (vf * scaleh + 1.f) * height / 4.f;
3511  } else {
3512  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad;
3513  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad;
3514  int v_offset = 0;
3515 
3516  ew = width / 3;
3517  eh = height / 4;
3518 
3519  u_shift = 2 * ew;
3520 
3521  if (theta <= 0.f && theta >= -M_PI_2 &&
3522  phi <= M_PI_2 && phi >= -M_PI_2) {
3523  uf = -vec[0] / vec[1];
3524  vf = -vec[2] / vec[1];
3525  v_shift = 0;
3526  v_offset = -eh;
3527  } else if (theta >= 0.f && theta <= M_PI_2 &&
3528  phi <= M_PI_2 && phi >= -M_PI_2) {
3529  uf = vec[0] / vec[1];
3530  vf = -vec[2] / vec[1];
3531  v_shift = height * 0.25f;
3532  } else if (theta <= 0.f && theta >= -M_PI_2) {
3533  uf = vec[0] / vec[1];
3534  vf = vec[2] / vec[1];
3535  v_shift = height * 0.5f;
3536  v_offset = -eh;
3537  } else {
3538  uf = -vec[0] / vec[1];
3539  vf = vec[2] / vec[1];
3540  v_shift = height * 0.75f;
3541  }
3542 
3543  uf = 0.5f * width / 3.f * (uf * scalew + 1.f);
3544  vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset;
3545  }
3546 
3547  ui = floorf(uf);
3548  vi = floorf(vf);
3549 
3550  *du = uf - ui;
3551  *dv = vf - vi;
3552 
3553  for (int i = 0; i < 4; i++) {
3554  for (int j = 0; j < 4; j++) {
3555  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3556  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3557  }
3558  }
3559 
3560  return 1;
3561 }
3562 
3563 /**
3564  * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format.
3565  *
3566  * @param s filter private context
3567  * @param i horizontal position on frame [0, width)
3568  * @param j vertical position on frame [0, height)
3569  * @param width frame width
3570  * @param height frame height
3571  * @param vec coordinates on sphere
3572  */
3574  int i, int j, int width, int height,
3575  float *vec)
3576 {
3577  const float x = (i + 0.5f) / width;
3578  const float y = (j + 0.5f) / height;
3579  float l_x, l_y, l_z;
3580 
3581  if (x < 2.f / 3.f) {
3582  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad;
3583  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
3584 
3585  const float back = floorf(y * 2.f);
3586 
3587  const float phi = ((3.f / 2.f * x - 0.5f) / scalew - back) * M_PI;
3588  const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI;
3589 
3590  const float sin_phi = sinf(phi);
3591  const float cos_phi = cosf(phi);
3592  const float sin_theta = sinf(theta);
3593  const float cos_theta = cosf(theta);
3594 
3595  l_x = cos_theta * sin_phi;
3596  l_y = sin_theta;
3597  l_z = cos_theta * cos_phi;
3598  } else {
3599  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad;
3600  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad;
3601 
3602  const int face = floorf(y * 4.f);
3603  float uf, vf;
3604 
3605  uf = x * 3.f - 2.f;
3606 
3607  switch (face) {
3608  case 0:
3609  vf = y * 2.f;
3610  uf = 1.f - uf;
3611  vf = 0.5f - vf;
3612 
3613  l_x = (0.5f - uf) / scalew;
3614  l_y = -0.5f;
3615  l_z = (0.5f - vf) / scaleh;
3616  break;
3617  case 1:
3618  vf = y * 2.f;
3619  uf = 1.f - uf;
3620  vf = 1.f - (vf - 0.5f);
3621 
3622  l_x = (0.5f - uf) / scalew;
3623  l_y = 0.5f;
3624  l_z = (-0.5f + vf) / scaleh;
3625  break;
3626  case 2:
3627  vf = y * 2.f - 0.5f;
3628  vf = 1.f - (1.f - vf);
3629 
3630  l_x = (0.5f - uf) / scalew;
3631  l_y = -0.5f;
3632  l_z = (0.5f - vf) / scaleh;
3633  break;
3634  case 3:
3635  vf = y * 2.f - 1.5f;
3636 
3637  l_x = (0.5f - uf) / scalew;
3638  l_y = 0.5f;
3639  l_z = (-0.5f + vf) / scaleh;
3640  break;
3641  }
3642  }
3643 
3644  vec[0] = l_x;
3645  vec[1] = l_y;
3646  vec[2] = l_z;
3647 
3648  normalize_vector(vec);
3649 
3650  return 1;
3651 }
3652 
3653 /**
3654  * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format.
3655  *
3656  * @param s filter private context
3657  * @param i horizontal position on frame [0, width)
3658  * @param j vertical position on frame [0, height)
3659  * @param width frame width
3660  * @param height frame height
3661  * @param vec coordinates on sphere
3662  */
3663 static int tspyramid_to_xyz(const V360Context *s,
3664  int i, int j, int width, int height,
3665  float *vec)
3666 {
3667  const float x = (i + 0.5f) / width;
3668  const float y = (j + 0.5f) / height;
3669 
3670  if (x < 0.5f) {
3671  vec[0] = x * 4.f - 1.f;
3672  vec[1] = (y * 2.f - 1.f);
3673  vec[2] = 1.f;
3674  } else if (x >= 0.6875f && x < 0.8125f &&
3675  y >= 0.375f && y < 0.625f) {
3676  vec[0] = -(x - 0.6875f) * 16.f + 1.f;
3677  vec[1] = (y - 0.375f) * 8.f - 1.f;
3678  vec[2] = -1.f;
3679  } else if (0.5f <= x && x < 0.6875f &&
3680  ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) ||
3681  (0.375f <= y && y < 0.625f) ||
3682  (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) {
3683  vec[0] = 1.f;
3684  vec[1] = 2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f;
3685  vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f;
3686  } else if (0.8125f <= x && x < 1.f &&
3687  ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) ||
3688  (0.375f <= y && y < 0.625f) ||
3689  (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) {
3690  vec[0] = -1.f;
3691  vec[1] = 2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f;
3692  vec[2] = 2.f * (x - 0.8125f) / 0.1875f - 1.f;
3693  } else if (0.f <= y && y < 0.375f &&
3694  ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) ||
3695  (0.6875f <= x && x < 0.8125f) ||
3696  (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) {
3697  vec[0] = 2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f;
3698  vec[1] = -1.f;
3699  vec[2] = 2.f * (0.375f - y) / 0.375f - 1.f;
3700  } else {
3701  vec[0] = 2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f;
3702  vec[1] = 1.f;
3703  vec[2] = -2.f * (1.f - y) / 0.375f + 1.f;
3704  }
3705 
3706  normalize_vector(vec);
3707 
3708  return 1;
3709 }
3710 
3711 /**
3712  * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere.
3713  *
3714  * @param s filter private context
3715  * @param vec coordinates on sphere
3716  * @param width frame width
3717  * @param height frame height
3718  * @param us horizontal coordinates for interpolation window
3719  * @param vs vertical coordinates for interpolation window
3720  * @param du horizontal relative coordinate
3721  * @param dv vertical relative coordinate
3722  */
3723 static int xyz_to_tspyramid(const V360Context *s,
3724  const float *vec, int width, int height,
3725  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3726 {
3727  float uf, vf;
3728  int ui, vi;
3729  int face;
3730 
3731  xyz_to_cube(s, vec, &uf, &vf, &face);
3732 
3733  uf = (uf + 1.f) * 0.5f;
3734  vf = (vf + 1.f) * 0.5f;
3735 
3736  switch (face) {
3737  case UP:
3738  uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f;
3739  vf = 0.375f - 0.375f * vf;
3740  break;
3741  case FRONT:
3742  uf = 0.5f * uf;
3743  break;
3744  case DOWN:
3745  uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf;
3746  vf = 1.f - 0.375f * vf;
3747  break;
3748  case LEFT:
3749  vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f;
3750  uf = 0.1875f * uf + 0.8125f;
3751  break;
3752  case RIGHT:
3753  vf = 0.375f * uf - 0.75f * uf * vf + vf;
3754  uf = 0.1875f * uf + 0.5f;
3755  break;
3756  case BACK:
3757  uf = 0.125f * uf + 0.6875f;
3758  vf = 0.25f * vf + 0.375f;
3759  break;
3760  }
3761 
3762  uf *= width;
3763  vf *= height;
3764 
3765  ui = floorf(uf);
3766  vi = floorf(vf);
3767 
3768  *du = uf - ui;
3769  *dv = vf - vi;
3770 
3771  for (int i = 0; i < 4; i++) {
3772  for (int j = 0; j < 4; j++) {
3773  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3774  vs[i][j] = reflecty(vi + i - 1, height);
3775  }
3776  }
3777 
3778  return 1;
3779 }
3780 
3781 /**
3782  * Calculate 3D coordinates on sphere for corresponding frame position in octahedron format.
3783  *
3784  * @param s filter private context
3785  * @param i horizontal position on frame [0, width)
3786  * @param j vertical position on frame [0, height)
3787  * @param width frame width
3788  * @param height frame height
3789  * @param vec coordinates on sphere
3790  */
3791 static int octahedron_to_xyz(const V360Context *s,
3792  int i, int j, int width, int height,
3793  float *vec)
3794 {
3795  const float x = ((i + 0.5f) / width) * 2.f - 1.f;
3796  const float y = ((j + 0.5f) / height) * 2.f - 1.f;
3797  const float ax = fabsf(x);
3798  const float ay = fabsf(y);
3799 
3800  vec[2] = 1.f - (ax + ay);
3801  if (ax + ay > 1.f) {
3802  vec[0] = (1.f - ay) * FFSIGN(x);
3803  vec[1] = (1.f - ax) * FFSIGN(y);
3804  } else {
3805  vec[0] = x;
3806  vec[1] = y;
3807  }
3808 
3809  normalize_vector(vec);
3810 
3811  return 1;
3812 }
3813 
3814 /**
3815  * Calculate frame position in octahedron format for corresponding 3D coordinates on sphere.
3816  *
3817  * @param s filter private context
3818  * @param vec coordinates on sphere
3819  * @param width frame width
3820  * @param height frame height
3821  * @param us horizontal coordinates for interpolation window
3822  * @param vs vertical coordinates for interpolation window
3823  * @param du horizontal relative coordinate
3824  * @param dv vertical relative coordinate
3825  */
3826 static int xyz_to_octahedron(const V360Context *s,
3827  const float *vec, int width, int height,
3828  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3829 {
3830  float uf, vf, zf;
3831  int ui, vi;
3832  float div = fabsf(vec[0]) + fabsf(vec[1]) + fabsf(vec[2]);
3833 
3834  uf = vec[0] / div;
3835  vf = vec[1] / div;
3836  zf = vec[2];
3837 
3838  if (zf < 0.f) {
3839  zf = vf;
3840  vf = (1.f - fabsf(uf)) * FFSIGN(zf);
3841  uf = (1.f - fabsf(zf)) * FFSIGN(uf);
3842  }
3843 
3844  uf = uf * 0.5f + 0.5f;
3845  vf = vf * 0.5f + 0.5f;
3846 
3847  uf *= width;
3848  vf *= height;
3849 
3850  ui = floorf(uf);
3851  vi = floorf(vf);
3852 
3853  *du = uf - ui;
3854  *dv = vf - vi;
3855 
3856  for (int i = 0; i < 4; i++) {
3857  for (int j = 0; j < 4; j++) {
3858  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
3859  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
3860  }
3861  }
3862 
3863  return 1;
3864 }
3865 
3866 static void multiply_quaternion(float c[4], const float a[4], const float b[4])
3867 {
3868  c[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3];
3869  c[1] = a[1] * b[0] + a[0] * b[1] + a[2] * b[3] - a[3] * b[2];
3870  c[2] = a[2] * b[0] + a[0] * b[2] + a[3] * b[1] - a[1] * b[3];
3871  c[3] = a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1];
3872 }
3873 
3874 static void conjugate_quaternion(float d[4], const float q[4])
3875 {
3876  d[0] = q[0];
3877  d[1] = -q[1];
3878  d[2] = -q[2];
3879  d[3] = -q[3];
3880 }
3881 
3882 /**
3883  * Calculate rotation quaternion for yaw/pitch/roll angles.
3884  */
3885 static inline void calculate_rotation(float yaw, float pitch, float roll,
3886  float rot_quaternion[2][4],
3887  const int rotation_order[3])
3888 {
3889  const float yaw_rad = yaw * M_PI / 180.f;
3890  const float pitch_rad = pitch * M_PI / 180.f;
3891  const float roll_rad = roll * M_PI / 180.f;
3892 
3893  const float sin_yaw = sinf(yaw_rad * 0.5f);
3894  const float cos_yaw = cosf(yaw_rad * 0.5f);
3895  const float sin_pitch = sinf(pitch_rad * 0.5f);
3896  const float cos_pitch = cosf(pitch_rad * 0.5f);
3897  const float sin_roll = sinf(roll_rad * 0.5f);
3898  const float cos_roll = cosf(roll_rad * 0.5f);
3899 
3900  float m[3][4];
3901  float tmp[2][4];
3902 
3903  m[0][0] = cos_yaw; m[0][1] = 0.f; m[0][2] = sin_yaw; m[0][3] = 0.f;
3904  m[1][0] = cos_pitch; m[1][1] = sin_pitch; m[1][2] = 0.f; m[1][3] = 0.f;
3905  m[2][0] = cos_roll; m[2][1] = 0.f; m[2][2] = 0.f; m[2][3] = sin_roll;
3906 
3907  multiply_quaternion(tmp[0], rot_quaternion[0], m[rotation_order[0]]);
3908  multiply_quaternion(tmp[1], tmp[0], m[rotation_order[1]]);
3909  multiply_quaternion(rot_quaternion[0], tmp[1], m[rotation_order[2]]);
3910 
3911  conjugate_quaternion(rot_quaternion[1], rot_quaternion[0]);
3912 }
3913 
3914 /**
3915  * Rotate vector with given rotation quaternion.
3916  *
3917  * @param rot_quaternion rotation quaternion
3918  * @param vec vector
3919  */
3920 static inline void rotate(const float rot_quaternion[2][4],
3921  float *vec)
3922 {
3923  float qv[4], temp[4], rqv[4];
3924 
3925  qv[0] = 0;
3926  qv[1] = vec[0];
3927  qv[2] = vec[1];
3928  qv[3] = vec[2];
3929 
3930  multiply_quaternion(temp, rot_quaternion[0], qv);
3931  multiply_quaternion(rqv, temp, rot_quaternion[1]);
3932 
3933  vec[0] = rqv[1];
3934  vec[1] = rqv[2];
3935  vec[2] = rqv[3];
3936 }
3937 
3938 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3939  float *modifier)
3940 {
3941  modifier[0] = h_flip ? -1.f : 1.f;
3942  modifier[1] = v_flip ? -1.f : 1.f;
3943  modifier[2] = d_flip ? -1.f : 1.f;
3944 }
3945 
3946 static inline void mirror(const float *modifier, float *vec)
3947 {
3948  vec[0] *= modifier[0];
3949  vec[1] *= modifier[1];
3950  vec[2] *= modifier[2];
3951 }
3952 
3953 static inline void input_flip(int16_t u[4][4], int16_t v[4][4], int w, int h, int hflip, int vflip)
3954 {
3955  if (hflip) {
3956  for (int i = 0; i < 4; i++) {
3957  for (int j = 0; j < 4; j++)
3958  u[i][j] = w - 1 - u[i][j];
3959  }
3960  }
3961 
3962  if (vflip) {
3963  for (int i = 0; i < 4; i++) {
3964  for (int j = 0; j < 4; j++)
3965  v[i][j] = h - 1 - v[i][j];
3966  }
3967  }
3968 }
3969 
3970 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3971 {
3972  const int pr_height = s->pr_height[p];
3973 
3974  for (int n = 0; n < s->nb_threads; n++) {
3975  SliceXYRemap *r = &s->slice_remap[n];
3976  const int slice_start = (pr_height * n ) / s->nb_threads;
3977  const int slice_end = (pr_height * (n + 1)) / s->nb_threads;
3978  const int height = slice_end - slice_start;
3979 
3980  if (!r->u[p])
3981  r->u[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3982  if (!r->v[p])
3983  r->v[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3984  if (!r->u[p] || !r->v[p])
3985  return AVERROR(ENOMEM);
3986  if (sizeof_ker) {
3987  if (!r->ker[p])
3988  r->ker[p] = av_calloc(s->uv_linesize[p] * height, sizeof_ker);
3989  if (!r->ker[p])
3990  return AVERROR(ENOMEM);
3991  }
3992 
3993  if (sizeof_mask && !p) {
3994  if (!r->mask)
3995  r->mask = av_calloc(s->pr_width[p] * height, sizeof_mask);
3996  if (!r->mask)
3997  return AVERROR(ENOMEM);
3998  }
3999  }
4000 
4001  return 0;
4002 }
4003 
4004 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
4005 {
4006  switch (format) {
4007  case ORTHOGRAPHIC:
4008  {
4009  const float d = 0.5f * hypotf(w, h);
4010  const float l = sinf(d_fov * M_PI / 360.f) / d;
4011 
4012  *h_fov = asinf(w * 0.5 * l) * 360.f / M_PI;
4013  *v_fov = asinf(h * 0.5 * l) * 360.f / M_PI;
4014 
4015  if (d_fov > 180.f) {
4016  *h_fov = 180.f - *h_fov;
4017  *v_fov = 180.f - *v_fov;
4018  }
4019  }
4020  break;
4021  case EQUISOLID:
4022  {
4023  const float d = 0.5f * hypotf(w, h);
4024  const float l = d / (sinf(d_fov * M_PI / 720.f));
4025 
4026  *h_fov = 2.f * asinf(w * 0.5f / l) * 360.f / M_PI;
4027  *v_fov = 2.f * asinf(h * 0.5f / l) * 360.f / M_PI;
4028  }
4029  break;
4030  case STEREOGRAPHIC:
4031  {
4032  const float d = 0.5f * hypotf(w, h);
4033  const float l = d / (tanf(d_fov * M_PI / 720.f));
4034 
4035  *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI;
4036  *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI;
4037  }
4038  break;
4039  case DUAL_FISHEYE:
4040  {
4041  const float d = 0.5f * hypotf(w * 0.5f, h);
4042 
4043  *h_fov = d / w * 2.f * d_fov;
4044  *v_fov = d / h * d_fov;
4045  }
4046  break;
4047  case FISHEYE:
4048  {
4049  const float d = 0.5f * hypotf(w, h);
4050 
4051  *h_fov = d / w * d_fov;
4052  *v_fov = d / h * d_fov;
4053  }
4054  break;
4055  case FLAT:
4056  default:
4057  {
4058  const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f);
4059  const float d = hypotf(w, h);
4060 
4061  *h_fov = atan2f(da * w, d) * 360.f / M_PI;
4062  *v_fov = atan2f(da * h, d) * 360.f / M_PI;
4063 
4064  if (*h_fov < 0.f)
4065  *h_fov += 360.f;
4066  if (*v_fov < 0.f)
4067  *v_fov += 360.f;
4068  }
4069  break;
4070  }
4071 }
4072 
4073 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
4074 {
4075  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
4076  outw[0] = outw[3] = w;
4077  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
4078  outh[0] = outh[3] = h;
4079 }
4080 
4081 // Calculate remap data
4082 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
4083 {
4084  V360Context *s = ctx->priv;
4085  SliceXYRemap *r = &s->slice_remap[jobnr];
4086 
4087  for (int p = 0; p < s->nb_allocated; p++) {
4088  const int max_value = s->max_value;
4089  const int width = s->pr_width[p];
4090  const int uv_linesize = s->uv_linesize[p];
4091  const int height = s->pr_height[p];
4092  const int in_width = s->inplanewidth[p];
4093  const int in_height = s->inplaneheight[p];
4094  const int slice_start = (height * jobnr ) / nb_jobs;
4095  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
4096  const int elements = s->elements;
4097  float du, dv;
4098  float vec[3];
4099  XYRemap rmap;
4100 
4101  for (int j = slice_start; j < slice_end; j++) {
4102  for (int i = 0; i < width; i++) {
4103  int16_t *u = r->u[p] + ((j - slice_start) * uv_linesize + i) * elements;
4104  int16_t *v = r->v[p] + ((j - slice_start) * uv_linesize + i) * elements;
4105  int16_t *ker = r->ker[p] + ((j - slice_start) * uv_linesize + i) * elements;
4106  uint8_t *mask8 = p ? NULL : r->mask + ((j - slice_start) * s->pr_width[0] + i);
4107  uint16_t *mask16 = p ? NULL : (uint16_t *)r->mask + ((j - slice_start) * s->pr_width[0] + i);
4108  int in_mask, out_mask;
4109 
4110  if (s->out_transpose)
4111  out_mask = s->out_transform(s, j, i, height, width, vec);
4112  else
4113  out_mask = s->out_transform(s, i, j, width, height, vec);
4114  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4115  rotate(s->rot_quaternion, vec);
4116  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4117  normalize_vector(vec);
4118  mirror(s->output_mirror_modifier, vec);
4119  if (s->in_transpose)
4120  in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
4121  else
4122  in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
4123  input_flip(rmap.u, rmap.v, in_width, in_height, s->ih_flip, s->iv_flip);
4124  av_assert1(!isnan(du) && !isnan(dv));
4125  s->calculate_kernel(du, dv, &rmap, u, v, ker);
4126 
4127  if (!p && r->mask) {
4128  if (s->mask_size == 1) {
4129  mask8[0] = 255 * (out_mask & in_mask);
4130  } else {
4131  mask16[0] = max_value * (out_mask & in_mask);
4132  }
4133  }
4134  }
4135  }
4136  }
4137 
4138  return 0;
4139 }
4140 
4141 static int config_output(AVFilterLink *outlink)
4142 {
4143  AVFilterContext *ctx = outlink->src;
4144  AVFilterLink *inlink = ctx->inputs[0];
4145  V360Context *s = ctx->priv;
4147  const int depth = desc->comp[0].depth;
4148  const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
4149  int sizeof_uv;
4150  int sizeof_ker;
4151  int err;
4152  int h, w;
4153  int in_offset_h, in_offset_w;
4154  int out_offset_h, out_offset_w;
4155  float hf, wf;
4156  int (*prepare_out)(AVFilterContext *ctx);
4157  int have_alpha;
4158 
4159  s->max_value = (1 << depth) - 1;
4160 
4161  switch (s->interp) {
4162  case NEAREST:
4164  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
4165  s->elements = 1;
4166  sizeof_uv = sizeof(int16_t) * s->elements;
4167  sizeof_ker = 0;
4168  break;
4169  case BILINEAR:
4171  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
4172  s->elements = 2 * 2;
4173  sizeof_uv = sizeof(int16_t) * s->elements;
4174  sizeof_ker = sizeof(int16_t) * s->elements;
4175  break;
4176  case LAGRANGE9:
4178  s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice;
4179  s->elements = 3 * 3;
4180  sizeof_uv = sizeof(int16_t) * s->elements;
4181  sizeof_ker = sizeof(int16_t) * s->elements;
4182  break;
4183  case BICUBIC:
4185  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4186  s->elements = 4 * 4;
4187  sizeof_uv = sizeof(int16_t) * s->elements;
4188  sizeof_ker = sizeof(int16_t) * s->elements;
4189  break;
4190  case LANCZOS:
4192  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4193  s->elements = 4 * 4;
4194  sizeof_uv = sizeof(int16_t) * s->elements;
4195  sizeof_ker = sizeof(int16_t) * s->elements;
4196  break;
4197  case SPLINE16:
4199  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4200  s->elements = 4 * 4;
4201  sizeof_uv = sizeof(int16_t) * s->elements;
4202  sizeof_ker = sizeof(int16_t) * s->elements;
4203  break;
4204  case GAUSSIAN:
4206  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4207  s->elements = 4 * 4;
4208  sizeof_uv = sizeof(int16_t) * s->elements;
4209  sizeof_ker = sizeof(int16_t) * s->elements;
4210  break;
4211  case MITCHELL:
4213  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4214  s->elements = 4 * 4;
4215  sizeof_uv = sizeof(int16_t) * s->elements;
4216  sizeof_ker = sizeof(int16_t) * s->elements;
4217  break;
4218  default:
4219  av_assert0(0);
4220  }
4221 
4222  ff_v360_init(s, depth);
4223 
4224  for (int order = 0; order < NB_RORDERS; order++) {
4225  const char c = s->rorder[order];
4226  int rorder;
4227 
4228  if (c == '\0') {
4229  av_log(ctx, AV_LOG_WARNING,
4230  "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
4231  s->rotation_order[0] = YAW;
4232  s->rotation_order[1] = PITCH;
4233  s->rotation_order[2] = ROLL;
4234  break;
4235  }
4236 
4237  rorder = get_rorder(c);
4238  if (rorder == -1) {
4239  av_log(ctx, AV_LOG_WARNING,
4240  "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
4241  s->rotation_order[0] = YAW;
4242  s->rotation_order[1] = PITCH;
4243  s->rotation_order[2] = ROLL;
4244  break;
4245  }
4246 
4247  s->rotation_order[order] = rorder;
4248  }
4249 
4250  switch (s->in_stereo) {
4251  case STEREO_2D:
4252  w = inlink->w;
4253  h = inlink->h;
4254  in_offset_w = in_offset_h = 0;
4255  break;
4256  case STEREO_SBS:
4257  w = inlink->w / 2;
4258  h = inlink->h;
4259  in_offset_w = w;
4260  in_offset_h = 0;
4261  break;
4262  case STEREO_TB:
4263  w = inlink->w;
4264  h = inlink->h / 2;
4265  in_offset_w = 0;
4266  in_offset_h = h;
4267  break;
4268  default:
4269  av_assert0(0);
4270  }
4271 
4272  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
4273  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
4274 
4275  s->in_width = s->inplanewidth[0];
4276  s->in_height = s->inplaneheight[0];
4277 
4278  if (s->id_fov > 0.f)
4279  fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
4280 
4281  if (s->in_transpose)
4282  FFSWAP(int, s->in_width, s->in_height);
4283 
4284  switch (s->in) {
4285  case EQUIRECTANGULAR:
4287  err = 0;
4288  wf = w;
4289  hf = h;
4290  break;
4291  case CUBEMAP_3_2:
4293  err = prepare_cube_in(ctx);
4294  wf = w / 3.f * 4.f;
4295  hf = h;
4296  break;
4297  case CUBEMAP_1_6:
4299  err = prepare_cube_in(ctx);
4300  wf = w * 4.f;
4301  hf = h / 3.f;
4302  break;
4303  case CUBEMAP_6_1:
4305  err = prepare_cube_in(ctx);
4306  wf = w / 3.f * 2.f;
4307  hf = h * 2.f;
4308  break;
4309  case EQUIANGULAR:
4310  s->in_transform = xyz_to_eac;
4311  err = prepare_eac_in(ctx);
4312  wf = w;
4313  hf = h / 9.f * 8.f;
4314  break;
4315  case FLAT:
4317  err = prepare_flat_in(ctx);
4318  wf = w;
4319  hf = h;
4320  break;
4321  case PERSPECTIVE:
4322  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
4323  return AVERROR(EINVAL);
4324  case DUAL_FISHEYE:
4326  err = prepare_fisheye_in(ctx);
4327  wf = w;
4328  hf = h;
4329  break;
4330  case BARREL:
4332  err = 0;
4333  wf = w / 5.f * 4.f;
4334  hf = h;
4335  break;
4336  case STEREOGRAPHIC:
4338  err = prepare_stereographic_in(ctx);
4339  wf = w;
4340  hf = h / 2.f;
4341  break;
4342  case MERCATOR:
4344  err = 0;
4345  wf = w;
4346  hf = h / 2.f;
4347  break;
4348  case BALL:
4350  err = 0;
4351  wf = w;
4352  hf = h / 2.f;
4353  break;
4354  case HAMMER:
4356  err = 0;
4357  wf = w;
4358  hf = h;
4359  break;
4360  case SINUSOIDAL:
4362  err = 0;
4363  wf = w;
4364  hf = h;
4365  break;
4366  case FISHEYE:
4368  err = prepare_fisheye_in(ctx);
4369  wf = w * 2;
4370  hf = h;
4371  break;
4372  case PANNINI:
4374  err = 0;
4375  wf = w;
4376  hf = h;
4377  break;
4378  case CYLINDRICAL:
4380  err = prepare_cylindrical_in(ctx);
4381  wf = w;
4382  hf = h * 2.f;
4383  break;
4384  case TETRAHEDRON:
4386  err = 0;
4387  wf = w;
4388  hf = h;
4389  break;
4390  case BARREL_SPLIT:
4392  err = 0;
4393  wf = w * 4.f / 3.f;
4394  hf = h;
4395  break;
4396  case TSPYRAMID:
4398  err = 0;
4399  wf = w;
4400  hf = h;
4401  break;
4402  case HEQUIRECTANGULAR:
4404  err = 0;
4405  wf = w * 2.f;
4406  hf = h;
4407  break;
4408  case EQUISOLID:
4410  err = prepare_equisolid_in(ctx);
4411  wf = w;
4412  hf = h / 2.f;
4413  break;
4414  case ORTHOGRAPHIC:
4416  err = prepare_orthographic_in(ctx);
4417  wf = w;
4418  hf = h / 2.f;
4419  break;
4420  case OCTAHEDRON:
4422  err = 0;
4423  wf = w;
4424  hf = h / 2.f;
4425  break;
4426  default:
4427  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
4428  return AVERROR_BUG;
4429  }
4430 
4431  if (err != 0) {
4432  return err;
4433  }
4434 
4435  switch (s->out) {
4436  case EQUIRECTANGULAR:
4438  prepare_out = NULL;
4439  w = lrintf(wf);
4440  h = lrintf(hf);
4441  break;
4442  case CUBEMAP_3_2:
4444  prepare_out = prepare_cube_out;
4445  w = lrintf(wf / 4.f * 3.f);
4446  h = lrintf(hf);
4447  break;
4448  case CUBEMAP_1_6:
4450  prepare_out = prepare_cube_out;
4451  w = lrintf(wf / 4.f);
4452  h = lrintf(hf * 3.f);
4453  break;
4454  case CUBEMAP_6_1:
4456  prepare_out = prepare_cube_out;
4457  w = lrintf(wf / 2.f * 3.f);
4458  h = lrintf(hf / 2.f);
4459  break;
4460  case EQUIANGULAR:
4462  prepare_out = prepare_eac_out;
4463  w = lrintf(wf);
4464  h = lrintf(hf / 8.f * 9.f);
4465  break;
4466  case FLAT:
4468  prepare_out = prepare_flat_out;
4469  w = lrintf(wf);
4470  h = lrintf(hf);
4471  break;
4472  case DUAL_FISHEYE:
4474  prepare_out = prepare_fisheye_out;
4475  w = lrintf(wf);
4476  h = lrintf(hf);
4477  break;
4478  case BARREL:
4480  prepare_out = NULL;
4481  w = lrintf(wf / 4.f * 5.f);
4482  h = lrintf(hf);
4483  break;
4484  case STEREOGRAPHIC:
4486  prepare_out = prepare_stereographic_out;
4487  w = lrintf(wf);
4488  h = lrintf(hf * 2.f);
4489  break;
4490  case MERCATOR:
4492  prepare_out = NULL;
4493  w = lrintf(wf);
4494  h = lrintf(hf * 2.f);
4495  break;
4496  case BALL:
4498  prepare_out = NULL;
4499  w = lrintf(wf);
4500  h = lrintf(hf * 2.f);
4501  break;
4502  case HAMMER:
4504  prepare_out = NULL;
4505  w = lrintf(wf);
4506  h = lrintf(hf);
4507  break;
4508  case SINUSOIDAL:
4510  prepare_out = NULL;
4511  w = lrintf(wf);
4512  h = lrintf(hf);
4513  break;
4514  case FISHEYE:
4516  prepare_out = prepare_fisheye_out;
4517  w = lrintf(wf * 0.5f);
4518  h = lrintf(hf);
4519  break;
4520  case PANNINI:
4522  prepare_out = NULL;
4523  w = lrintf(wf);
4524  h = lrintf(hf);
4525  break;
4526  case CYLINDRICAL:
4528  prepare_out = prepare_cylindrical_out;
4529  w = lrintf(wf);
4530  h = lrintf(hf * 0.5f);
4531  break;
4532  case PERSPECTIVE:
4534  prepare_out = NULL;
4535  w = lrintf(wf / 2.f);
4536  h = lrintf(hf);
4537  break;
4538  case TETRAHEDRON:
4540  prepare_out = NULL;
4541  w = lrintf(wf);
4542  h = lrintf(hf);
4543  break;
4544  case BARREL_SPLIT:
4546  prepare_out = NULL;
4547  w = lrintf(wf / 4.f * 3.f);
4548  h = lrintf(hf);
4549  break;
4550  case TSPYRAMID:
4552  prepare_out = NULL;
4553  w = lrintf(wf);
4554  h = lrintf(hf);
4555  break;
4556  case HEQUIRECTANGULAR:
4558  prepare_out = NULL;
4559  w = lrintf(wf / 2.f);
4560  h = lrintf(hf);
4561  break;
4562  case EQUISOLID:
4564  prepare_out = prepare_equisolid_out;
4565  w = lrintf(wf);
4566  h = lrintf(hf * 2.f);
4567  break;
4568  case ORTHOGRAPHIC:
4570  prepare_out = prepare_orthographic_out;
4571  w = lrintf(wf);
4572  h = lrintf(hf * 2.f);
4573  break;
4574  case OCTAHEDRON:
4576  prepare_out = NULL;
4577  w = lrintf(wf);
4578  h = lrintf(hf * 2.f);
4579  break;
4580  default:
4581  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
4582  return AVERROR_BUG;
4583  }
4584 
4585  // Override resolution with user values if specified
4586  if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4587  s->out == FLAT && s->d_fov == 0.f) {
4588  w = s->width;
4589  h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
4590  } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4591  s->out == FLAT && s->d_fov == 0.f) {
4592  h = s->height;
4593  w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
4594  } else if (s->width > 0 && s->height > 0) {
4595  w = s->width;
4596  h = s->height;
4597  } else if (s->width > 0 || s->height > 0) {
4598  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
4599  return AVERROR(EINVAL);
4600  } else {
4601  if (s->out_transpose)
4602  FFSWAP(int, w, h);
4603 
4604  if (s->in_transpose)
4605  FFSWAP(int, w, h);
4606  }
4607 
4608  s->width = w;
4609  s->height = h;
4610 
4611  if (s->d_fov > 0.f)
4612  fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
4613 
4614  if (prepare_out) {
4615  err = prepare_out(ctx);
4616  if (err != 0)
4617  return err;
4618  }
4619 
4620  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
4621 
4622  switch (s->out_stereo) {
4623  case STEREO_2D:
4624  out_offset_w = out_offset_h = 0;
4625  break;
4626  case STEREO_SBS:
4627  out_offset_w = w;
4628  out_offset_h = 0;
4629  w *= 2;
4630  break;
4631  case STEREO_TB:
4632  out_offset_w = 0;
4633  out_offset_h = h;
4634  h *= 2;
4635  break;
4636  default:
4637  av_assert0(0);
4638  }
4639 
4640  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
4641  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
4642 
4643  for (int i = 0; i < 4; i++)
4644  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
4645 
4646  outlink->h = h;
4647  outlink->w = w;
4648 
4649  s->nb_threads = FFMIN(outlink->h, ff_filter_get_nb_threads(ctx));
4651  have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
4652 
4653  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
4654  s->nb_allocated = 1;
4655  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
4656  } else {
4657  s->nb_allocated = 2;
4658  s->map[0] = s->map[3] = 0;
4659  s->map[1] = s->map[2] = 1;
4660  }
4661 
4662  if (!s->slice_remap)
4663  s->slice_remap = av_calloc(s->nb_threads, sizeof(*s->slice_remap));
4664  if (!s->slice_remap)
4665  return AVERROR(ENOMEM);
4666 
4667  for (int i = 0; i < s->nb_allocated; i++) {
4668  err = allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
4669  if (err < 0)
4670  return err;
4671  }
4672 
4673  s->rot_quaternion[0][0] = 1.f;
4674  s->rot_quaternion[0][1] = s->rot_quaternion[0][2] = s->rot_quaternion[0][3] = 0.f;
4675 
4676  for (int i = 0; i < 4; i++) {
4677  calculate_rotation(s->yaw * 0.25f, s->pitch * 0.25f, s->roll * 0.25f,
4679  }
4680 
4682 
4683  ctx->internal->execute(ctx, v360_slice, NULL, NULL, s->nb_threads);
4684 
4685  return 0;
4686 }
4687 
4689 {
4690  AVFilterContext *ctx = inlink->dst;
4691  AVFilterLink *outlink = ctx->outputs[0];
4692  V360Context *s = ctx->priv;
4693  AVFrame *out;
4694  ThreadData td;
4695 
4696  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
4697  if (!out) {
4698  av_frame_free(&in);
4699  return AVERROR(ENOMEM);
4700  }
4701  av_frame_copy_props(out, in);
4702 
4703  td.in = in;
4704  td.out = out;
4705 
4706  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, s->nb_threads);
4707 
4708  av_frame_free(&in);
4709  return ff_filter_frame(outlink, out);
4710 }
4711 
4712 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
4713  char *res, int res_len, int flags)
4714 {
4715  int ret;
4716 
4717  ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
4718  if (ret < 0)
4719  return ret;
4720 
4721  return config_output(ctx->outputs[0]);
4722 }
4723 
4725 {
4726  V360Context *s = ctx->priv;
4727 
4728  for (int n = 0; n < s->nb_threads && s->slice_remap; n++) {
4729  SliceXYRemap *r = &s->slice_remap[n];
4730 
4731  for (int p = 0; p < s->nb_allocated; p++) {
4732  av_freep(&r->u[p]);
4733  av_freep(&r->v[p]);
4734  av_freep(&r->ker[p]);
4735  }
4736 
4737  av_freep(&r->mask);
4738  }
4739 
4740  av_freep(&s->slice_remap);
4741 }
4742 
4743 static const AVFilterPad inputs[] = {
4744  {
4745  .name = "default",
4746  .type = AVMEDIA_TYPE_VIDEO,
4747  .filter_frame = filter_frame,
4748  },
4749  { NULL }
4750 };
4751 
4752 static const AVFilterPad outputs[] = {
4753  {
4754  .name = "default",
4755  .type = AVMEDIA_TYPE_VIDEO,
4756  .config_props = config_output,
4757  },
4758  { NULL }
4759 };
4760 
4762  .name = "v360",
4763  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
4764  .priv_size = sizeof(V360Context),
4765  .uninit = uninit,
4767  .inputs = inputs,
4768  .outputs = outputs,
4769  .priv_class = &v360_class,
4772 };
Definition: v360.h:63
Definition: v360.h:42
Axis +Z.
Definition: v360.h:88
int(* in_transform)(const struct V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Definition: v360.h:178
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
#define NULL
Definition: coverity.c:32
static const AVOption v360_options[]
Definition: vf_v360.c:57
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
#define AV_PIX_FMT_YUVA422P16
Definition: pixfmt.h:442
float iflat_range[2]
Definition: v360.h:151
AVFrame * out
Definition: af_adeclick.c:494
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
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:401
#define AV_PIX_FMT_YUVA422P9
Definition: pixfmt.h:434
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2573
int nb_planes
Definition: v360.h:168
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
This structure describes decoded (raw) audio or video data.
Definition: frame.h:308
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
int nb_threads
Definition: v360.h:173
#define TOP_LEFT
Definition: movtextdec.c:49
int in_transpose
Definition: v360.h:146
AVOption.
Definition: opt.h:248
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&#39;s format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:3479
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:436
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
int h_flip
Definition: v360.h:145
#define AV_PIX_FMT_YUV444P14
Definition: pixfmt.h:409
static void calculate_lanczos_coeffs(float t, float *coeffs)
Calculate 1-dimensional lanczos coefficients.
Definition: vf_v360.c:532
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:419
static int prepare_stereographic_out(AVFilterContext *ctx)
Prepare data for processing stereographic output format.
Definition: vf_v360.c:1796
#define AV_PIX_FMT_YUVA422P10
Definition: pixfmt.h:437
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:71
Definition: v360.h:28
misc image utilities
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
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
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2613
Main libavfilter public API header.
else temp
Definition: vf_mcdeint.c:256
int elements
Definition: v360.h:170
static int prepare_eac_out(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap output format.
Definition: vf_v360.c:2572
int alpha
Definition: v360.h:123
static void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
Definition: vf_v360.c:1004
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:3791
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 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
SliceXYRemap * slice_remap
Definition: v360.h:175
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
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
static int tetrahedron_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format...
Definition: vf_v360.c:3159
static int 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 void rotate_cube_face(float *uf, float *vf, int rotation)
Definition: vf_v360.c:978
int in
Definition: v360.h:121
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:415
static int get_rorder(char c)
Convert char to corresponding rotation order.
Definition: vf_v360.c:853
#define us(width, name, range_min, range_max, subs,...)
Definition: cbs_h2645.c:276
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
uint8_t * mask
Definition: v360.h:116
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
Definition: v360.h:52
Definition: v360.h:37
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
#define AV_PIX_FMT_GRAY9
Definition: pixfmt.h:379
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
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:403
int inplanewidth[4]
Definition: v360.h:166
int out_offset_w[4]
Definition: v360.h:163
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:99
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 const uint8_t q1[256]
Definition: twofish.c:96
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:3826
static void set_mirror_modifier(int h_flip, int v_flip, int d_flip, float *modifier)
Definition: vf_v360.c:3938
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample format(the sample packing is implied by the sample format) and sample rate.The lists are not just lists
char * in_frot
Definition: v360.h:127
int pr_height[4]
Definition: v360.h:160
static int config_output(AVFilterLink *outlink)
Definition: vf_v360.c:4141
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:92
static int reflectx(int x, int y, int w, int h)
Reflect x operation.
Definition: vf_v360.c:798
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:245
static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
Definition: vf_v360.c:4004
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:287
#define AV_PIX_FMT_GRAY10
Definition: pixfmt.h:380
Axis +Y.
Definition: v360.h:85
Definition: v360.h:47
int16_t u[4][4]
Definition: v360.h:108
const char * name
Pad name.
Definition: internal.h:60
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
Definition: vf_v360.c:3970
#define AV_PIX_FMT_GRAY12
Definition: pixfmt.h:381
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:349
float pitch
Definition: v360.h:142
#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:1091
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 void rotate(const float rot_quaternion[2][4], float *vec)
Rotate vector with given rotation quaternion.
Definition: vf_v360.c:3920
float roll
Definition: v360.h:142
planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
Definition: pixfmt.h:101
AVComponentDescriptor comp[4]
Parameters that describe how pixels are packed.
Definition: pixdesc.h:117
static void calculate_gaussian_coeffs(float t, float *coeffs)
Calculate 1-dimensional gaussian coefficients.
Definition: vf_v360.c:627
uint8_t
float yaw
Definition: v360.h:142
#define av_cold
Definition: attributes.h:88
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition: pixdesc.h:179
AVOptions.
int out_transpose
Definition: v360.h:146
int planeheight[4]
Definition: v360.h:165
#define f(width, name)
Definition: cbs_vp9.c:255
Axis -Y.
Definition: v360.h:86
float rot_quaternion[2][4]
Definition: v360.h:153
char * out_frot
Definition: v360.h:128
void(* calculate_kernel)(float du, float dv, const XYRemap *rmap, int16_t *u, int16_t *v, int16_t *ker)
Definition: v360.h:186
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:262
#define AV_PIX_FMT_YUVA420P9
Definition: pixfmt.h:433
float iv_fov
Definition: v360.h:149
Definition: v360.h:103
#define cosf(x)
Definition: libm.h:78
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:414
int height
Definition: vf_avgblur.c:61
static int prepare_cylindrical_out(AVFilterContext *ctx)
Prepare data for processing cylindrical output format.
Definition: vf_v360.c:2999
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
#define atanf(x)
Definition: libm.h:40
AVFilter ff_vf_v360
Definition: vf_v360.c:4761
planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range...
Definition: pixfmt.h:100
int height
Definition: v360.h:124
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
planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting col...
Definition: pixfmt.h:79
#define atan2f(y, x)
Definition: libm.h:45
float ih_fov
Definition: v360.h:149
#define lrintf(x)
Definition: libm_mips.h:70
int width
Definition: v360.h:124
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:412
static int prepare_orthographic_out(AVFilterContext *ctx)
Prepare data for processing orthographic output format.
Definition: vf_v360.c:2008
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
#define AV_PIX_FMT_YUV422P12
Definition: pixfmt.h:404
#define AV_PIX_FMT_YUVA420P16
Definition: pixfmt.h:441
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 xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:3283
#define FFALIGN(x, a)
Definition: macros.h:48
int rotation_order[3]
Definition: v360.h:135
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
#define expf(x)
Definition: libm.h:283
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
int fin_pad
Definition: v360.h:140
static int dfisheye_to_xyz(const V360Context *s, int i, int j, int width, int height, float *vec)
Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format...
Definition: vf_v360.c:3242
int in_stereo
Definition: v360.h:137
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&#39;s format...
Definition: vf_v360.c:3573
void ff_v360_init(V360Context *s, int depth)
Definition: vf_v360.c:369
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition: pixfmt.h:176
int max_value
Definition: v360.h:172
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
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:588
#define td
Definition: regdef.h:70
Definition: v360.h:101
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&#39;s format...
Definition: vf_v360.c:3333
uint8_t log2_chroma_h
Amount to shift the luma height right to find the chroma height.
Definition: pixdesc.h:101
Definition: v360.h:44
Definition: v360.h:46
static void mirror(const float *modifier, float *vec)
Definition: vf_v360.c:3946
#define S(s, c, i)
#define isfinite(x)
Definition: libm.h:359
static int prepare_fisheye_out(AVFilterContext *ctx)
Prepare data for processing fisheye output format.
Definition: vf_v360.c:2806
int out_offset_h[4]
Definition: v360.h:163
#define DEFINE_REMAP1_LINE(bits, div)
Definition: vf_v360.c:254
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
#define FF_CEIL_RSHIFT
Definition: common.h:61
static void calculate_bicubic_coeffs(float t, float *coeffs)
Calculate 1-dimensional cubic coefficients.
Definition: vf_v360.c:487
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:153
#define FLAGS
Definition: vf_v360.c:54
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:885
const char * r
Definition: vf_curves.c:114
void * priv
private data for use by the filter
Definition: avfilter.h:356
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
int16_t * v[2]
Definition: v360.h:114
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:443
const char * arg
Definition: jacosubdec.c:66
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:420
simple assert() macros that are a bit more flexible than ISO C assert().
static int prepare_eac_in(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap input format.
Definition: vf_v360.c:2544
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
Definition: v360.h:66
Definition: v360.h:96
static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
Definition: vf_v360.c:4073
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
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:402
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:3885
Definition: v360.h:68
Definition: v360.h:61
int in_offset_w[4]
Definition: v360.h:162
#define FFMAX(a, b)
Definition: common.h:94
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
int out
Definition: v360.h:121
int out_stereo
Definition: v360.h:137
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:421
static const uint8_t q0[256]
Definition: twofish.c:77
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
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:70
Definition: v360.h:64
int out_cubemap_face_rotation[6]
Definition: v360.h:134
int in_offset_h[4]
Definition: v360.h:162
static void calculate_spline16_coeffs(float t, float *coeffs)
Calculate 1-dimensional spline16 coefficients.
Definition: vf_v360.c:585
uint64_t flags
Combination of AV_PIX_FMT_FLAG_...
Definition: pixdesc.h:106
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:397
unsigned map[4]
Definition: v360.h:176
int ih_flip
Definition: v360.h:144
int interp
Definition: v360.h:122
int in_height
Definition: v360.h:157
#define OFFSET(x)
Definition: vf_v360.c:53
Definition: v360.h:94
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:418
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:800
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:383
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:53
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
float in_pad
Definition: v360.h:139
#define FFMIN(a, b)
Definition: common.h:96
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
float out_pad
Definition: v360.h:139
planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting col...
Definition: pixfmt.h:78
static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_v360.c:4082
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:440
static int prepare_stereographic_in(AVFilterContext *ctx)
Prepare data for processing stereographic input format.
Definition: vf_v360.c:1842
#define FFSIGN(a)
Definition: common.h:73
int(* out_transform)(const struct V360Context *s, int i, int j, int width, int height, float *vec)
Definition: v360.h:182
Definition: v360.h:26
static const ElemCat * elements[ELEMENT_COUNT]
Definition: signature.h:566
static int query_formats(AVFilterContext *ctx)
Definition: vf_v360.c:170
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:3953
#define M_PI_2
Definition: mathematics.h:55
int inplaneheight[4]
Definition: v360.h:166
AVFrame * m
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
int mask_size
Definition: v360.h:171
Definition: v360.h:62
AVFormatContext * ctx
Definition: movenc.c:48
Definition: v360.h:65
static int ereflectx(int x, int y, int w, int h)
Reflect x operation for equirect.
Definition: vf_v360.c:782
AVFILTER_DEFINE_CLASS(v360)