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] = cos_theta * cos_phi;
3137  vec[2] = sin_theta;
3138  } else {
3139  vec[0] = 0.f;
3140  vec[1] = 1.f;
3141  vec[2] = 0.f;
3142  return 0;
3143  }
3144 
3145  return 1;
3146 }
3147 
3148 /**
3149  * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format.
3150  *
3151  * @param s filter private context
3152  * @param i horizontal position on frame [0, width)
3153  * @param j vertical position on frame [0, height)
3154  * @param width frame width
3155  * @param height frame height
3156  * @param vec coordinates on sphere
3157  */
3159  int i, int j, int width, int height,
3160  float *vec)
3161 {
3162  const float uf = (float)i / width;
3163  const float vf = (float)j / height;
3164 
3165  vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
3166  vec[1] = 1.f - vf * 2.f;
3167  vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
3168 
3169  normalize_vector(vec);
3170 
3171  return 1;
3172 }
3173 
3174 /**
3175  * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere.
3176  *
3177  * @param s filter private context
3178  * @param vec coordinates on sphere
3179  * @param width frame width
3180  * @param height frame height
3181  * @param us horizontal coordinates for interpolation window
3182  * @param vs vertical coordinates for interpolation window
3183  * @param du horizontal relative coordinate
3184  * @param dv vertical relative coordinate
3185  */
3187  const float *vec, int width, int height,
3188  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3189 {
3190  const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f;
3191  const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f;
3192  const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f;
3193  const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f;
3194  const float d = FFMAX(d0, FFMAX3(d1, d2, d3));
3195 
3196  float uf, vf, x, y, z;
3197  int ui, vi;
3198 
3199  x = vec[0] / d;
3200  y = vec[1] / d;
3201  z = -vec[2] / d;
3202 
3203  vf = 0.5f - y * 0.5f;
3204 
3205  if ((x + y >= 0.f && y + z >= 0.f && -z - x <= 0.f) ||
3206  (x + y <= 0.f && -y + z >= 0.f && z - x >= 0.f)) {
3207  uf = 0.25f * x + 0.25f;
3208  } else {
3209  uf = 0.75f - 0.25f * x;
3210  }
3211 
3212  uf *= width;
3213  vf *= height;
3214 
3215  ui = floorf(uf);
3216  vi = floorf(vf);
3217 
3218  *du = uf - ui;
3219  *dv = vf - vi;
3220 
3221  for (int i = 0; i < 4; i++) {
3222  for (int j = 0; j < 4; j++) {
3223  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3224  vs[i][j] = reflecty(vi + i - 1, height);
3225  }
3226  }
3227 
3228  return 1;
3229 }
3230 
3231 /**
3232  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
3233  *
3234  * @param s filter private context
3235  * @param i horizontal position on frame [0, width)
3236  * @param j vertical position on frame [0, height)
3237  * @param width frame width
3238  * @param height frame height
3239  * @param vec coordinates on sphere
3240  */
3241 static int dfisheye_to_xyz(const V360Context *s,
3242  int i, int j, int width, int height,
3243  float *vec)
3244 {
3245  const float ew = width / 2.f;
3246  const float eh = height;
3247 
3248  const int ei = i >= ew ? i - ew : i;
3249  const float m = i >= ew ? 1.f : -1.f;
3250 
3251  const float uf = s->flat_range[0] * ((2.f * ei) / ew - 1.f);
3252  const float vf = s->flat_range[1] * ((2.f * j + 1.f) / eh - 1.f);
3253 
3254  const float h = hypotf(uf, vf);
3255  const float lh = h > 0.f ? h : 1.f;
3256  const float theta = m * M_PI_2 * (1.f - h);
3257 
3258  const float sin_theta = sinf(theta);
3259  const float cos_theta = cosf(theta);
3260 
3261  vec[0] = cos_theta * m * uf / lh;
3262  vec[1] = cos_theta * vf / lh;
3263  vec[2] = sin_theta;
3264 
3265  normalize_vector(vec);
3266 
3267  return 1;
3268 }
3269 
3270 /**
3271  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
3272  *
3273  * @param s filter private context
3274  * @param vec coordinates on sphere
3275  * @param width frame width
3276  * @param height frame height
3277  * @param us horizontal coordinates for interpolation window
3278  * @param vs vertical coordinates for interpolation window
3279  * @param du horizontal relative coordinate
3280  * @param dv vertical relative coordinate
3281  */
3282 static int xyz_to_dfisheye(const V360Context *s,
3283  const float *vec, int width, int height,
3284  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3285 {
3286  const float ew = width / 2.f;
3287  const float eh = height;
3288 
3289  const float h = hypotf(vec[0], vec[1]);
3290  const float lh = h > 0.f ? h : 1.f;
3291  const float theta = acosf(fabsf(vec[2])) / M_PI;
3292 
3293  float uf = (theta * (vec[0] / lh) / s->iflat_range[0] + 0.5f) * ew;
3294  float vf = (theta * (vec[1] / lh) / s->iflat_range[1] + 0.5f) * eh;
3295 
3296  int ui, vi;
3297  int u_shift;
3298 
3299  if (vec[2] >= 0.f) {
3300  u_shift = ceilf(ew);
3301  } else {
3302  u_shift = 0;
3303  uf = ew - uf;
3304  }
3305 
3306  ui = floorf(uf);
3307  vi = floorf(vf);
3308 
3309  *du = uf - ui;
3310  *dv = vf - vi;
3311 
3312  for (int i = 0; i < 4; i++) {
3313  for (int j = 0; j < 4; j++) {
3314  us[i][j] = av_clip(u_shift + ui + j - 1, 0, width - 1);
3315  vs[i][j] = av_clip( vi + i - 1, 0, height - 1);
3316  }
3317  }
3318 
3319  return 1;
3320 }
3321 
3322 /**
3323  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
3324  *
3325  * @param s filter private context
3326  * @param i horizontal position on frame [0, width)
3327  * @param j vertical position on frame [0, height)
3328  * @param width frame width
3329  * @param height frame height
3330  * @param vec coordinates on sphere
3331  */
3332 static int barrel_to_xyz(const V360Context *s,
3333  int i, int j, int width, int height,
3334  float *vec)
3335 {
3336  const float scale = 0.99f;
3337  float l_x, l_y, l_z;
3338 
3339  if (i < 4 * width / 5) {
3340  const float theta_range = M_PI_4;
3341 
3342  const int ew = 4 * width / 5;
3343  const int eh = height;
3344 
3345  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
3346  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
3347 
3348  const float sin_phi = sinf(phi);
3349  const float cos_phi = cosf(phi);
3350  const float sin_theta = sinf(theta);
3351  const float cos_theta = cosf(theta);
3352 
3353  l_x = cos_theta * sin_phi;
3354  l_y = sin_theta;
3355  l_z = cos_theta * cos_phi;
3356  } else {
3357  const int ew = width / 5;
3358  const int eh = height / 2;
3359 
3360  float uf, vf;
3361 
3362  if (j < eh) { // UP
3363  uf = 2.f * (i - 4 * ew) / ew - 1.f;
3364  vf = 2.f * (j ) / eh - 1.f;
3365 
3366  uf /= scale;
3367  vf /= scale;
3368 
3369  l_x = uf;
3370  l_y = -1.f;
3371  l_z = vf;
3372  } else { // DOWN
3373  uf = 2.f * (i - 4 * ew) / ew - 1.f;
3374  vf = 2.f * (j - eh) / eh - 1.f;
3375 
3376  uf /= scale;
3377  vf /= scale;
3378 
3379  l_x = uf;
3380  l_y = 1.f;
3381  l_z = -vf;
3382  }
3383  }
3384 
3385  vec[0] = l_x;
3386  vec[1] = l_y;
3387  vec[2] = l_z;
3388 
3389  normalize_vector(vec);
3390 
3391  return 1;
3392 }
3393 
3394 /**
3395  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
3396  *
3397  * @param s filter private context
3398  * @param vec coordinates on sphere
3399  * @param width frame width
3400  * @param height frame height
3401  * @param us horizontal coordinates for interpolation window
3402  * @param vs vertical coordinates for interpolation window
3403  * @param du horizontal relative coordinate
3404  * @param dv vertical relative coordinate
3405  */
3406 static int xyz_to_barrel(const V360Context *s,
3407  const float *vec, int width, int height,
3408  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3409 {
3410  const float scale = 0.99f;
3411 
3412  const float phi = atan2f(vec[0], vec[2]);
3413  const float theta = asinf(vec[1]);
3414  const float theta_range = M_PI_4;
3415 
3416  int ew, eh;
3417  int u_shift, v_shift;
3418  float uf, vf;
3419  int ui, vi;
3420 
3421  if (theta > -theta_range && theta < theta_range) {
3422  ew = 4 * width / 5;
3423  eh = height;
3424 
3425  u_shift = 0;
3426  v_shift = 0;
3427 
3428  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
3429  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
3430  } else {
3431  ew = width / 5;
3432  eh = height / 2;
3433 
3434  u_shift = 4 * ew;
3435 
3436  if (theta < 0.f) { // UP
3437  uf = -vec[0] / vec[1];
3438  vf = -vec[2] / vec[1];
3439  v_shift = 0;
3440  } else { // DOWN
3441  uf = vec[0] / vec[1];
3442  vf = -vec[2] / vec[1];
3443  v_shift = eh;
3444  }
3445 
3446  uf = 0.5f * ew * (uf * scale + 1.f);
3447  vf = 0.5f * eh * (vf * scale + 1.f);
3448  }
3449 
3450  ui = floorf(uf);
3451  vi = floorf(vf);
3452 
3453  *du = uf - ui;
3454  *dv = vf - vi;
3455 
3456  for (int i = 0; i < 4; i++) {
3457  for (int j = 0; j < 4; j++) {
3458  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3459  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3460  }
3461  }
3462 
3463  return 1;
3464 }
3465 
3466 /**
3467  * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere.
3468  *
3469  * @param s filter private context
3470  * @param vec coordinates on sphere
3471  * @param width frame width
3472  * @param height frame height
3473  * @param us horizontal coordinates for interpolation window
3474  * @param vs vertical coordinates for interpolation window
3475  * @param du horizontal relative coordinate
3476  * @param dv vertical relative coordinate
3477  */
3479  const float *vec, int width, int height,
3480  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3481 {
3482  const float phi = atan2f(vec[0], vec[2]);
3483  const float theta = asinf(vec[1]);
3484 
3485  const float theta_range = M_PI_4;
3486 
3487  int ew, eh;
3488  int u_shift, v_shift;
3489  float uf, vf;
3490  int ui, vi;
3491 
3492  if (theta >= -theta_range && theta <= theta_range) {
3493  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad;
3494  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
3495 
3496  ew = width / 3 * 2;
3497  eh = height / 2;
3498 
3499  u_shift = 0;
3500  v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0;
3501 
3502  uf = fmodf(phi, M_PI_2) / M_PI_2;
3503  vf = theta / M_PI_4;
3504 
3505  if (v_shift)
3506  uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f);
3507 
3508  uf = (uf * scalew + 1.f) * width / 3.f;
3509  vf = (vf * scaleh + 1.f) * height / 4.f;
3510  } else {
3511  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width / 3.f) : 1.f - s->in_pad;
3512  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad;
3513  int v_offset = 0;
3514 
3515  ew = width / 3;
3516  eh = height / 4;
3517 
3518  u_shift = 2 * ew;
3519 
3520  if (theta <= 0.f && theta >= -M_PI_2 &&
3521  phi <= M_PI_2 && phi >= -M_PI_2) {
3522  uf = -vec[0] / vec[1];
3523  vf = -vec[2] / vec[1];
3524  v_shift = 0;
3525  v_offset = -eh;
3526  } else if (theta >= 0.f && theta <= M_PI_2 &&
3527  phi <= M_PI_2 && phi >= -M_PI_2) {
3528  uf = vec[0] / vec[1];
3529  vf = -vec[2] / vec[1];
3530  v_shift = height * 0.25f;
3531  } else if (theta <= 0.f && theta >= -M_PI_2) {
3532  uf = vec[0] / vec[1];
3533  vf = vec[2] / vec[1];
3534  v_shift = height * 0.5f;
3535  v_offset = -eh;
3536  } else {
3537  uf = -vec[0] / vec[1];
3538  vf = vec[2] / vec[1];
3539  v_shift = height * 0.75f;
3540  }
3541 
3542  uf = 0.5f * width / 3.f * (uf * scalew + 1.f);
3543  vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset;
3544  }
3545 
3546  ui = floorf(uf);
3547  vi = floorf(vf);
3548 
3549  *du = uf - ui;
3550  *dv = vf - vi;
3551 
3552  for (int i = 0; i < 4; i++) {
3553  for (int j = 0; j < 4; j++) {
3554  us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3555  vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3556  }
3557  }
3558 
3559  return 1;
3560 }
3561 
3562 /**
3563  * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format.
3564  *
3565  * @param s filter private context
3566  * @param i horizontal position on frame [0, width)
3567  * @param j vertical position on frame [0, height)
3568  * @param width frame width
3569  * @param height frame height
3570  * @param vec coordinates on sphere
3571  */
3573  int i, int j, int width, int height,
3574  float *vec)
3575 {
3576  const float x = (i + 0.5f) / width;
3577  const float y = (j + 0.5f) / height;
3578  float l_x, l_y, l_z;
3579 
3580  if (x < 2.f / 3.f) {
3581  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad;
3582  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
3583 
3584  const float back = floorf(y * 2.f);
3585 
3586  const float phi = ((3.f / 2.f * x - 0.5f) / scalew - back) * M_PI;
3587  const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI;
3588 
3589  const float sin_phi = sinf(phi);
3590  const float cos_phi = cosf(phi);
3591  const float sin_theta = sinf(theta);
3592  const float cos_theta = cosf(theta);
3593 
3594  l_x = cos_theta * sin_phi;
3595  l_y = sin_theta;
3596  l_z = cos_theta * cos_phi;
3597  } else {
3598  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width / 3.f) : 1.f - s->out_pad;
3599  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad;
3600 
3601  const int face = floorf(y * 4.f);
3602  float uf, vf;
3603 
3604  uf = x * 3.f - 2.f;
3605 
3606  switch (face) {
3607  case 0:
3608  vf = y * 2.f;
3609  uf = 1.f - uf;
3610  vf = 0.5f - vf;
3611 
3612  l_x = (0.5f - uf) / scalew;
3613  l_y = -0.5f;
3614  l_z = (0.5f - vf) / scaleh;
3615  break;
3616  case 1:
3617  vf = y * 2.f;
3618  uf = 1.f - uf;
3619  vf = 1.f - (vf - 0.5f);
3620 
3621  l_x = (0.5f - uf) / scalew;
3622  l_y = 0.5f;
3623  l_z = (-0.5f + vf) / scaleh;
3624  break;
3625  case 2:
3626  vf = y * 2.f - 0.5f;
3627  vf = 1.f - (1.f - vf);
3628 
3629  l_x = (0.5f - uf) / scalew;
3630  l_y = -0.5f;
3631  l_z = (0.5f - vf) / scaleh;
3632  break;
3633  case 3:
3634  vf = y * 2.f - 1.5f;
3635 
3636  l_x = (0.5f - uf) / scalew;
3637  l_y = 0.5f;
3638  l_z = (-0.5f + vf) / scaleh;
3639  break;
3640  }
3641  }
3642 
3643  vec[0] = l_x;
3644  vec[1] = l_y;
3645  vec[2] = l_z;
3646 
3647  normalize_vector(vec);
3648 
3649  return 1;
3650 }
3651 
3652 /**
3653  * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format.
3654  *
3655  * @param s filter private context
3656  * @param i horizontal position on frame [0, width)
3657  * @param j vertical position on frame [0, height)
3658  * @param width frame width
3659  * @param height frame height
3660  * @param vec coordinates on sphere
3661  */
3662 static int tspyramid_to_xyz(const V360Context *s,
3663  int i, int j, int width, int height,
3664  float *vec)
3665 {
3666  const float x = (i + 0.5f) / width;
3667  const float y = (j + 0.5f) / height;
3668 
3669  if (x < 0.5f) {
3670  vec[0] = x * 4.f - 1.f;
3671  vec[1] = (y * 2.f - 1.f);
3672  vec[2] = 1.f;
3673  } else if (x >= 0.6875f && x < 0.8125f &&
3674  y >= 0.375f && y < 0.625f) {
3675  vec[0] = -(x - 0.6875f) * 16.f + 1.f;
3676  vec[1] = (y - 0.375f) * 8.f - 1.f;
3677  vec[2] = -1.f;
3678  } else if (0.5f <= x && x < 0.6875f &&
3679  ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) ||
3680  (0.375f <= y && y < 0.625f) ||
3681  (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) {
3682  vec[0] = 1.f;
3683  vec[1] = 2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f;
3684  vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f;
3685  } else if (0.8125f <= x && x < 1.f &&
3686  ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) ||
3687  (0.375f <= y && y < 0.625f) ||
3688  (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) {
3689  vec[0] = -1.f;
3690  vec[1] = 2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f;
3691  vec[2] = 2.f * (x - 0.8125f) / 0.1875f - 1.f;
3692  } else if (0.f <= y && y < 0.375f &&
3693  ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) ||
3694  (0.6875f <= x && x < 0.8125f) ||
3695  (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) {
3696  vec[0] = 2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f;
3697  vec[1] = -1.f;
3698  vec[2] = 2.f * (0.375f - y) / 0.375f - 1.f;
3699  } else {
3700  vec[0] = 2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f;
3701  vec[1] = 1.f;
3702  vec[2] = -2.f * (1.f - y) / 0.375f + 1.f;
3703  }
3704 
3705  normalize_vector(vec);
3706 
3707  return 1;
3708 }
3709 
3710 /**
3711  * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere.
3712  *
3713  * @param s filter private context
3714  * @param vec coordinates on sphere
3715  * @param width frame width
3716  * @param height frame height
3717  * @param us horizontal coordinates for interpolation window
3718  * @param vs vertical coordinates for interpolation window
3719  * @param du horizontal relative coordinate
3720  * @param dv vertical relative coordinate
3721  */
3722 static int xyz_to_tspyramid(const V360Context *s,
3723  const float *vec, int width, int height,
3724  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3725 {
3726  float uf, vf;
3727  int ui, vi;
3728  int face;
3729 
3730  xyz_to_cube(s, vec, &uf, &vf, &face);
3731 
3732  uf = (uf + 1.f) * 0.5f;
3733  vf = (vf + 1.f) * 0.5f;
3734 
3735  switch (face) {
3736  case UP:
3737  uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f;
3738  vf = 0.375f - 0.375f * vf;
3739  break;
3740  case FRONT:
3741  uf = 0.5f * uf;
3742  break;
3743  case DOWN:
3744  uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf;
3745  vf = 1.f - 0.375f * vf;
3746  break;
3747  case LEFT:
3748  vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f;
3749  uf = 0.1875f * uf + 0.8125f;
3750  break;
3751  case RIGHT:
3752  vf = 0.375f * uf - 0.75f * uf * vf + vf;
3753  uf = 0.1875f * uf + 0.5f;
3754  break;
3755  case BACK:
3756  uf = 0.125f * uf + 0.6875f;
3757  vf = 0.25f * vf + 0.375f;
3758  break;
3759  }
3760 
3761  uf *= width;
3762  vf *= height;
3763 
3764  ui = floorf(uf);
3765  vi = floorf(vf);
3766 
3767  *du = uf - ui;
3768  *dv = vf - vi;
3769 
3770  for (int i = 0; i < 4; i++) {
3771  for (int j = 0; j < 4; j++) {
3772  us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3773  vs[i][j] = reflecty(vi + i - 1, height);
3774  }
3775  }
3776 
3777  return 1;
3778 }
3779 
3780 /**
3781  * Calculate 3D coordinates on sphere for corresponding frame position in octahedron format.
3782  *
3783  * @param s filter private context
3784  * @param i horizontal position on frame [0, width)
3785  * @param j vertical position on frame [0, height)
3786  * @param width frame width
3787  * @param height frame height
3788  * @param vec coordinates on sphere
3789  */
3790 static int octahedron_to_xyz(const V360Context *s,
3791  int i, int j, int width, int height,
3792  float *vec)
3793 {
3794  const float x = ((i + 0.5f) / width) * 2.f - 1.f;
3795  const float y = ((j + 0.5f) / height) * 2.f - 1.f;
3796  const float ax = fabsf(x);
3797  const float ay = fabsf(y);
3798 
3799  vec[2] = 1.f - (ax + ay);
3800  if (ax + ay > 1.f) {
3801  vec[0] = (1.f - ay) * FFSIGN(x);
3802  vec[1] = (1.f - ax) * FFSIGN(y);
3803  } else {
3804  vec[0] = x;
3805  vec[1] = y;
3806  }
3807 
3808  normalize_vector(vec);
3809 
3810  return 1;
3811 }
3812 
3813 /**
3814  * Calculate frame position in octahedron format for corresponding 3D coordinates on sphere.
3815  *
3816  * @param s filter private context
3817  * @param vec coordinates on sphere
3818  * @param width frame width
3819  * @param height frame height
3820  * @param us horizontal coordinates for interpolation window
3821  * @param vs vertical coordinates for interpolation window
3822  * @param du horizontal relative coordinate
3823  * @param dv vertical relative coordinate
3824  */
3825 static int xyz_to_octahedron(const V360Context *s,
3826  const float *vec, int width, int height,
3827  int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3828 {
3829  float uf, vf, zf;
3830  int ui, vi;
3831  float div = fabsf(vec[0]) + fabsf(vec[1]) + fabsf(vec[2]);
3832 
3833  uf = vec[0] / div;
3834  vf = vec[1] / div;
3835  zf = vec[2];
3836 
3837  if (zf < 0.f) {
3838  zf = vf;
3839  vf = (1.f - fabsf(uf)) * FFSIGN(zf);
3840  uf = (1.f - fabsf(zf)) * FFSIGN(uf);
3841  }
3842 
3843  uf = uf * 0.5f + 0.5f;
3844  vf = vf * 0.5f + 0.5f;
3845 
3846  uf *= width;
3847  vf *= height;
3848 
3849  ui = floorf(uf);
3850  vi = floorf(vf);
3851 
3852  *du = uf - ui;
3853  *dv = vf - vi;
3854 
3855  for (int i = 0; i < 4; i++) {
3856  for (int j = 0; j < 4; j++) {
3857  us[i][j] = av_clip(ui + j - 1, 0, width - 1);
3858  vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
3859  }
3860  }
3861 
3862  return 1;
3863 }
3864 
3865 static void multiply_quaternion(float c[4], const float a[4], const float b[4])
3866 {
3867  c[0] = a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3];
3868  c[1] = a[1] * b[0] + a[0] * b[1] + a[2] * b[3] - a[3] * b[2];
3869  c[2] = a[2] * b[0] + a[0] * b[2] + a[3] * b[1] - a[1] * b[3];
3870  c[3] = a[3] * b[0] + a[0] * b[3] + a[1] * b[2] - a[2] * b[1];
3871 }
3872 
3873 static void conjugate_quaternion(float d[4], const float q[4])
3874 {
3875  d[0] = q[0];
3876  d[1] = -q[1];
3877  d[2] = -q[2];
3878  d[3] = -q[3];
3879 }
3880 
3881 /**
3882  * Calculate rotation quaternion for yaw/pitch/roll angles.
3883  */
3884 static inline void calculate_rotation(float yaw, float pitch, float roll,
3885  float rot_quaternion[2][4],
3886  const int rotation_order[3])
3887 {
3888  const float yaw_rad = yaw * M_PI / 180.f;
3889  const float pitch_rad = pitch * M_PI / 180.f;
3890  const float roll_rad = roll * M_PI / 180.f;
3891 
3892  const float sin_yaw = sinf(yaw_rad * 0.5f);
3893  const float cos_yaw = cosf(yaw_rad * 0.5f);
3894  const float sin_pitch = sinf(pitch_rad * 0.5f);
3895  const float cos_pitch = cosf(pitch_rad * 0.5f);
3896  const float sin_roll = sinf(roll_rad * 0.5f);
3897  const float cos_roll = cosf(roll_rad * 0.5f);
3898 
3899  float m[3][4];
3900  float tmp[2][4];
3901 
3902  m[0][0] = cos_yaw; m[0][1] = 0.f; m[0][2] = sin_yaw; m[0][3] = 0.f;
3903  m[1][0] = cos_pitch; m[1][1] = sin_pitch; m[1][2] = 0.f; m[1][3] = 0.f;
3904  m[2][0] = cos_roll; m[2][1] = 0.f; m[2][2] = 0.f; m[2][3] = sin_roll;
3905 
3906  multiply_quaternion(tmp[0], rot_quaternion[0], m[rotation_order[0]]);
3907  multiply_quaternion(tmp[1], tmp[0], m[rotation_order[1]]);
3908  multiply_quaternion(rot_quaternion[0], tmp[1], m[rotation_order[2]]);
3909 
3910  conjugate_quaternion(rot_quaternion[1], rot_quaternion[0]);
3911 }
3912 
3913 /**
3914  * Rotate vector with given rotation quaternion.
3915  *
3916  * @param rot_quaternion rotation quaternion
3917  * @param vec vector
3918  */
3919 static inline void rotate(const float rot_quaternion[2][4],
3920  float *vec)
3921 {
3922  float qv[4], temp[4], rqv[4];
3923 
3924  qv[0] = 0.f;
3925  qv[1] = vec[0];
3926  qv[2] = vec[1];
3927  qv[3] = vec[2];
3928 
3929  multiply_quaternion(temp, rot_quaternion[0], qv);
3930  multiply_quaternion(rqv, temp, rot_quaternion[1]);
3931 
3932  vec[0] = rqv[1];
3933  vec[1] = rqv[2];
3934  vec[2] = rqv[3];
3935 }
3936 
3937 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3938  float *modifier)
3939 {
3940  modifier[0] = h_flip ? -1.f : 1.f;
3941  modifier[1] = v_flip ? -1.f : 1.f;
3942  modifier[2] = d_flip ? -1.f : 1.f;
3943 }
3944 
3945 static inline void mirror(const float *modifier, float *vec)
3946 {
3947  vec[0] *= modifier[0];
3948  vec[1] *= modifier[1];
3949  vec[2] *= modifier[2];
3950 }
3951 
3952 static inline void input_flip(int16_t u[4][4], int16_t v[4][4], int w, int h, int hflip, int vflip)
3953 {
3954  if (hflip) {
3955  for (int i = 0; i < 4; i++) {
3956  for (int j = 0; j < 4; j++)
3957  u[i][j] = w - 1 - u[i][j];
3958  }
3959  }
3960 
3961  if (vflip) {
3962  for (int i = 0; i < 4; i++) {
3963  for (int j = 0; j < 4; j++)
3964  v[i][j] = h - 1 - v[i][j];
3965  }
3966  }
3967 }
3968 
3969 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3970 {
3971  const int pr_height = s->pr_height[p];
3972 
3973  for (int n = 0; n < s->nb_threads; n++) {
3974  SliceXYRemap *r = &s->slice_remap[n];
3975  const int slice_start = (pr_height * n ) / s->nb_threads;
3976  const int slice_end = (pr_height * (n + 1)) / s->nb_threads;
3977  const int height = slice_end - slice_start;
3978 
3979  if (!r->u[p])
3980  r->u[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3981  if (!r->v[p])
3982  r->v[p] = av_calloc(s->uv_linesize[p] * height, sizeof_uv);
3983  if (!r->u[p] || !r->v[p])
3984  return AVERROR(ENOMEM);
3985  if (sizeof_ker) {
3986  if (!r->ker[p])
3987  r->ker[p] = av_calloc(s->uv_linesize[p] * height, sizeof_ker);
3988  if (!r->ker[p])
3989  return AVERROR(ENOMEM);
3990  }
3991 
3992  if (sizeof_mask && !p) {
3993  if (!r->mask)
3994  r->mask = av_calloc(s->pr_width[p] * height, sizeof_mask);
3995  if (!r->mask)
3996  return AVERROR(ENOMEM);
3997  }
3998  }
3999 
4000  return 0;
4001 }
4002 
4003 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
4004 {
4005  switch (format) {
4006  case ORTHOGRAPHIC:
4007  {
4008  const float d = 0.5f * hypotf(w, h);
4009  const float l = sinf(d_fov * M_PI / 360.f) / d;
4010 
4011  *h_fov = asinf(w * 0.5 * l) * 360.f / M_PI;
4012  *v_fov = asinf(h * 0.5 * l) * 360.f / M_PI;
4013 
4014  if (d_fov > 180.f) {
4015  *h_fov = 180.f - *h_fov;
4016  *v_fov = 180.f - *v_fov;
4017  }
4018  }
4019  break;
4020  case EQUISOLID:
4021  {
4022  const float d = 0.5f * hypotf(w, h);
4023  const float l = d / (sinf(d_fov * M_PI / 720.f));
4024 
4025  *h_fov = 2.f * asinf(w * 0.5f / l) * 360.f / M_PI;
4026  *v_fov = 2.f * asinf(h * 0.5f / l) * 360.f / M_PI;
4027  }
4028  break;
4029  case STEREOGRAPHIC:
4030  {
4031  const float d = 0.5f * hypotf(w, h);
4032  const float l = d / (tanf(d_fov * M_PI / 720.f));
4033 
4034  *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI;
4035  *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI;
4036  }
4037  break;
4038  case DUAL_FISHEYE:
4039  {
4040  const float d = 0.5f * hypotf(w * 0.5f, h);
4041 
4042  *h_fov = d / w * 2.f * d_fov;
4043  *v_fov = d / h * d_fov;
4044  }
4045  break;
4046  case FISHEYE:
4047  {
4048  const float d = 0.5f * hypotf(w, h);
4049 
4050  *h_fov = d / w * d_fov;
4051  *v_fov = d / h * d_fov;
4052  }
4053  break;
4054  case FLAT:
4055  default:
4056  {
4057  const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f);
4058  const float d = hypotf(w, h);
4059 
4060  *h_fov = atan2f(da * w, d) * 360.f / M_PI;
4061  *v_fov = atan2f(da * h, d) * 360.f / M_PI;
4062 
4063  if (*h_fov < 0.f)
4064  *h_fov += 360.f;
4065  if (*v_fov < 0.f)
4066  *v_fov += 360.f;
4067  }
4068  break;
4069  }
4070 }
4071 
4072 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
4073 {
4074  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
4075  outw[0] = outw[3] = w;
4076  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
4077  outh[0] = outh[3] = h;
4078 }
4079 
4080 // Calculate remap data
4081 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
4082 {
4083  V360Context *s = ctx->priv;
4084  SliceXYRemap *r = &s->slice_remap[jobnr];
4085 
4086  for (int p = 0; p < s->nb_allocated; p++) {
4087  const int max_value = s->max_value;
4088  const int width = s->pr_width[p];
4089  const int uv_linesize = s->uv_linesize[p];
4090  const int height = s->pr_height[p];
4091  const int in_width = s->inplanewidth[p];
4092  const int in_height = s->inplaneheight[p];
4093  const int slice_start = (height * jobnr ) / nb_jobs;
4094  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
4095  const int elements = s->elements;
4096  float du, dv;
4097  float vec[3];
4098  XYRemap rmap;
4099 
4100  for (int j = slice_start; j < slice_end; j++) {
4101  for (int i = 0; i < width; i++) {
4102  int16_t *u = r->u[p] + ((j - slice_start) * uv_linesize + i) * elements;
4103  int16_t *v = r->v[p] + ((j - slice_start) * uv_linesize + i) * elements;
4104  int16_t *ker = r->ker[p] + ((j - slice_start) * uv_linesize + i) * elements;
4105  uint8_t *mask8 = p ? NULL : r->mask + ((j - slice_start) * s->pr_width[0] + i);
4106  uint16_t *mask16 = p ? NULL : (uint16_t *)r->mask + ((j - slice_start) * s->pr_width[0] + i);
4107  int in_mask, out_mask;
4108 
4109  if (s->out_transpose)
4110  out_mask = s->out_transform(s, j, i, height, width, vec);
4111  else
4112  out_mask = s->out_transform(s, i, j, width, height, vec);
4113  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4114  rotate(s->rot_quaternion, vec);
4115  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
4116  normalize_vector(vec);
4117  mirror(s->output_mirror_modifier, vec);
4118  if (s->in_transpose)
4119  in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
4120  else
4121  in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
4122  input_flip(rmap.u, rmap.v, in_width, in_height, s->ih_flip, s->iv_flip);
4123  av_assert1(!isnan(du) && !isnan(dv));
4124  s->calculate_kernel(du, dv, &rmap, u, v, ker);
4125 
4126  if (!p && r->mask) {
4127  if (s->mask_size == 1) {
4128  mask8[0] = 255 * (out_mask & in_mask);
4129  } else {
4130  mask16[0] = max_value * (out_mask & in_mask);
4131  }
4132  }
4133  }
4134  }
4135  }
4136 
4137  return 0;
4138 }
4139 
4140 static int config_output(AVFilterLink *outlink)
4141 {
4142  AVFilterContext *ctx = outlink->src;
4143  AVFilterLink *inlink = ctx->inputs[0];
4144  V360Context *s = ctx->priv;
4146  const int depth = desc->comp[0].depth;
4147  const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
4148  int sizeof_uv;
4149  int sizeof_ker;
4150  int err;
4151  int h, w;
4152  int in_offset_h, in_offset_w;
4153  int out_offset_h, out_offset_w;
4154  float hf, wf;
4155  int (*prepare_out)(AVFilterContext *ctx);
4156  int have_alpha;
4157 
4158  s->max_value = (1 << depth) - 1;
4159 
4160  switch (s->interp) {
4161  case NEAREST:
4163  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
4164  s->elements = 1;
4165  sizeof_uv = sizeof(int16_t) * s->elements;
4166  sizeof_ker = 0;
4167  break;
4168  case BILINEAR:
4170  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
4171  s->elements = 2 * 2;
4172  sizeof_uv = sizeof(int16_t) * s->elements;
4173  sizeof_ker = sizeof(int16_t) * s->elements;
4174  break;
4175  case LAGRANGE9:
4177  s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice;
4178  s->elements = 3 * 3;
4179  sizeof_uv = sizeof(int16_t) * s->elements;
4180  sizeof_ker = sizeof(int16_t) * s->elements;
4181  break;
4182  case BICUBIC:
4184  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4185  s->elements = 4 * 4;
4186  sizeof_uv = sizeof(int16_t) * s->elements;
4187  sizeof_ker = sizeof(int16_t) * s->elements;
4188  break;
4189  case LANCZOS:
4191  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4192  s->elements = 4 * 4;
4193  sizeof_uv = sizeof(int16_t) * s->elements;
4194  sizeof_ker = sizeof(int16_t) * s->elements;
4195  break;
4196  case SPLINE16:
4198  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4199  s->elements = 4 * 4;
4200  sizeof_uv = sizeof(int16_t) * s->elements;
4201  sizeof_ker = sizeof(int16_t) * s->elements;
4202  break;
4203  case GAUSSIAN:
4205  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4206  s->elements = 4 * 4;
4207  sizeof_uv = sizeof(int16_t) * s->elements;
4208  sizeof_ker = sizeof(int16_t) * s->elements;
4209  break;
4210  case MITCHELL:
4212  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
4213  s->elements = 4 * 4;
4214  sizeof_uv = sizeof(int16_t) * s->elements;
4215  sizeof_ker = sizeof(int16_t) * s->elements;
4216  break;
4217  default:
4218  av_assert0(0);
4219  }
4220 
4221  ff_v360_init(s, depth);
4222 
4223  for (int order = 0; order < NB_RORDERS; order++) {
4224  const char c = s->rorder[order];
4225  int rorder;
4226 
4227  if (c == '\0') {
4228  av_log(ctx, AV_LOG_WARNING,
4229  "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
4230  s->rotation_order[0] = YAW;
4231  s->rotation_order[1] = PITCH;
4232  s->rotation_order[2] = ROLL;
4233  break;
4234  }
4235 
4236  rorder = get_rorder(c);
4237  if (rorder == -1) {
4238  av_log(ctx, AV_LOG_WARNING,
4239  "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
4240  s->rotation_order[0] = YAW;
4241  s->rotation_order[1] = PITCH;
4242  s->rotation_order[2] = ROLL;
4243  break;
4244  }
4245 
4246  s->rotation_order[order] = rorder;
4247  }
4248 
4249  switch (s->in_stereo) {
4250  case STEREO_2D:
4251  w = inlink->w;
4252  h = inlink->h;
4253  in_offset_w = in_offset_h = 0;
4254  break;
4255  case STEREO_SBS:
4256  w = inlink->w / 2;
4257  h = inlink->h;
4258  in_offset_w = w;
4259  in_offset_h = 0;
4260  break;
4261  case STEREO_TB:
4262  w = inlink->w;
4263  h = inlink->h / 2;
4264  in_offset_w = 0;
4265  in_offset_h = h;
4266  break;
4267  default:
4268  av_assert0(0);
4269  }
4270 
4271  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
4272  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
4273 
4274  s->in_width = s->inplanewidth[0];
4275  s->in_height = s->inplaneheight[0];
4276 
4277  if (s->id_fov > 0.f)
4278  fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
4279 
4280  if (s->in_transpose)
4281  FFSWAP(int, s->in_width, s->in_height);
4282 
4283  switch (s->in) {
4284  case EQUIRECTANGULAR:
4286  err = 0;
4287  wf = w;
4288  hf = h;
4289  break;
4290  case CUBEMAP_3_2:
4292  err = prepare_cube_in(ctx);
4293  wf = w / 3.f * 4.f;
4294  hf = h;
4295  break;
4296  case CUBEMAP_1_6:
4298  err = prepare_cube_in(ctx);
4299  wf = w * 4.f;
4300  hf = h / 3.f;
4301  break;
4302  case CUBEMAP_6_1:
4304  err = prepare_cube_in(ctx);
4305  wf = w / 3.f * 2.f;
4306  hf = h * 2.f;
4307  break;
4308  case EQUIANGULAR:
4309  s->in_transform = xyz_to_eac;
4310  err = prepare_eac_in(ctx);
4311  wf = w;
4312  hf = h / 9.f * 8.f;
4313  break;
4314  case FLAT:
4316  err = prepare_flat_in(ctx);
4317  wf = w;
4318  hf = h;
4319  break;
4320  case PERSPECTIVE:
4321  av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
4322  return AVERROR(EINVAL);
4323  case DUAL_FISHEYE:
4325  err = prepare_fisheye_in(ctx);
4326  wf = w;
4327  hf = h;
4328  break;
4329  case BARREL:
4331  err = 0;
4332  wf = w / 5.f * 4.f;
4333  hf = h;
4334  break;
4335  case STEREOGRAPHIC:
4337  err = prepare_stereographic_in(ctx);
4338  wf = w;
4339  hf = h / 2.f;
4340  break;
4341  case MERCATOR:
4343  err = 0;
4344  wf = w;
4345  hf = h / 2.f;
4346  break;
4347  case BALL:
4349  err = 0;
4350  wf = w;
4351  hf = h / 2.f;
4352  break;
4353  case HAMMER:
4355  err = 0;
4356  wf = w;
4357  hf = h;
4358  break;
4359  case SINUSOIDAL:
4361  err = 0;
4362  wf = w;
4363  hf = h;
4364  break;
4365  case FISHEYE:
4367  err = prepare_fisheye_in(ctx);
4368  wf = w * 2;
4369  hf = h;
4370  break;
4371  case PANNINI:
4373  err = 0;
4374  wf = w;
4375  hf = h;
4376  break;
4377  case CYLINDRICAL:
4379  err = prepare_cylindrical_in(ctx);
4380  wf = w;
4381  hf = h * 2.f;
4382  break;
4383  case TETRAHEDRON:
4385  err = 0;
4386  wf = w;
4387  hf = h;
4388  break;
4389  case BARREL_SPLIT:
4391  err = 0;
4392  wf = w * 4.f / 3.f;
4393  hf = h;
4394  break;
4395  case TSPYRAMID:
4397  err = 0;
4398  wf = w;
4399  hf = h;
4400  break;
4401  case HEQUIRECTANGULAR:
4403  err = 0;
4404  wf = w * 2.f;
4405  hf = h;
4406  break;
4407  case EQUISOLID:
4409  err = prepare_equisolid_in(ctx);
4410  wf = w;
4411  hf = h / 2.f;
4412  break;
4413  case ORTHOGRAPHIC:
4415  err = prepare_orthographic_in(ctx);
4416  wf = w;
4417  hf = h / 2.f;
4418  break;
4419  case OCTAHEDRON:
4421  err = 0;
4422  wf = w;
4423  hf = h / 2.f;
4424  break;
4425  default:
4426  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
4427  return AVERROR_BUG;
4428  }
4429 
4430  if (err != 0) {
4431  return err;
4432  }
4433 
4434  switch (s->out) {
4435  case EQUIRECTANGULAR:
4437  prepare_out = NULL;
4438  w = lrintf(wf);
4439  h = lrintf(hf);
4440  break;
4441  case CUBEMAP_3_2:
4443  prepare_out = prepare_cube_out;
4444  w = lrintf(wf / 4.f * 3.f);
4445  h = lrintf(hf);
4446  break;
4447  case CUBEMAP_1_6:
4449  prepare_out = prepare_cube_out;
4450  w = lrintf(wf / 4.f);
4451  h = lrintf(hf * 3.f);
4452  break;
4453  case CUBEMAP_6_1:
4455  prepare_out = prepare_cube_out;
4456  w = lrintf(wf / 2.f * 3.f);
4457  h = lrintf(hf / 2.f);
4458  break;
4459  case EQUIANGULAR:
4461  prepare_out = prepare_eac_out;
4462  w = lrintf(wf);
4463  h = lrintf(hf / 8.f * 9.f);
4464  break;
4465  case FLAT:
4467  prepare_out = prepare_flat_out;
4468  w = lrintf(wf);
4469  h = lrintf(hf);
4470  break;
4471  case DUAL_FISHEYE:
4473  prepare_out = prepare_fisheye_out;
4474  w = lrintf(wf);
4475  h = lrintf(hf);
4476  break;
4477  case BARREL:
4479  prepare_out = NULL;
4480  w = lrintf(wf / 4.f * 5.f);
4481  h = lrintf(hf);
4482  break;
4483  case STEREOGRAPHIC:
4485  prepare_out = prepare_stereographic_out;
4486  w = lrintf(wf);
4487  h = lrintf(hf * 2.f);
4488  break;
4489  case MERCATOR:
4491  prepare_out = NULL;
4492  w = lrintf(wf);
4493  h = lrintf(hf * 2.f);
4494  break;
4495  case BALL:
4497  prepare_out = NULL;
4498  w = lrintf(wf);
4499  h = lrintf(hf * 2.f);
4500  break;
4501  case HAMMER:
4503  prepare_out = NULL;
4504  w = lrintf(wf);
4505  h = lrintf(hf);
4506  break;
4507  case SINUSOIDAL:
4509  prepare_out = NULL;
4510  w = lrintf(wf);
4511  h = lrintf(hf);
4512  break;
4513  case FISHEYE:
4515  prepare_out = prepare_fisheye_out;
4516  w = lrintf(wf * 0.5f);
4517  h = lrintf(hf);
4518  break;
4519  case PANNINI:
4521  prepare_out = NULL;
4522  w = lrintf(wf);
4523  h = lrintf(hf);
4524  break;
4525  case CYLINDRICAL:
4527  prepare_out = prepare_cylindrical_out;
4528  w = lrintf(wf);
4529  h = lrintf(hf * 0.5f);
4530  break;
4531  case PERSPECTIVE:
4533  prepare_out = NULL;
4534  w = lrintf(wf / 2.f);
4535  h = lrintf(hf);
4536  break;
4537  case TETRAHEDRON:
4539  prepare_out = NULL;
4540  w = lrintf(wf);
4541  h = lrintf(hf);
4542  break;
4543  case BARREL_SPLIT:
4545  prepare_out = NULL;
4546  w = lrintf(wf / 4.f * 3.f);
4547  h = lrintf(hf);
4548  break;
4549  case TSPYRAMID:
4551  prepare_out = NULL;
4552  w = lrintf(wf);
4553  h = lrintf(hf);
4554  break;
4555  case HEQUIRECTANGULAR:
4557  prepare_out = NULL;
4558  w = lrintf(wf / 2.f);
4559  h = lrintf(hf);
4560  break;
4561  case EQUISOLID:
4563  prepare_out = prepare_equisolid_out;
4564  w = lrintf(wf);
4565  h = lrintf(hf * 2.f);
4566  break;
4567  case ORTHOGRAPHIC:
4569  prepare_out = prepare_orthographic_out;
4570  w = lrintf(wf);
4571  h = lrintf(hf * 2.f);
4572  break;
4573  case OCTAHEDRON:
4575  prepare_out = NULL;
4576  w = lrintf(wf);
4577  h = lrintf(hf * 2.f);
4578  break;
4579  default:
4580  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
4581  return AVERROR_BUG;
4582  }
4583 
4584  // Override resolution with user values if specified
4585  if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4586  s->out == FLAT && s->d_fov == 0.f) {
4587  w = s->width;
4588  h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
4589  } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4590  s->out == FLAT && s->d_fov == 0.f) {
4591  h = s->height;
4592  w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
4593  } else if (s->width > 0 && s->height > 0) {
4594  w = s->width;
4595  h = s->height;
4596  } else if (s->width > 0 || s->height > 0) {
4597  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
4598  return AVERROR(EINVAL);
4599  } else {
4600  if (s->out_transpose)
4601  FFSWAP(int, w, h);
4602 
4603  if (s->in_transpose)
4604  FFSWAP(int, w, h);
4605  }
4606 
4607  s->width = w;
4608  s->height = h;
4609 
4610  if (s->d_fov > 0.f)
4611  fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
4612 
4613  if (prepare_out) {
4614  err = prepare_out(ctx);
4615  if (err != 0)
4616  return err;
4617  }
4618 
4619  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
4620 
4621  switch (s->out_stereo) {
4622  case STEREO_2D:
4623  out_offset_w = out_offset_h = 0;
4624  break;
4625  case STEREO_SBS:
4626  out_offset_w = w;
4627  out_offset_h = 0;
4628  w *= 2;
4629  break;
4630  case STEREO_TB:
4631  out_offset_w = 0;
4632  out_offset_h = h;
4633  h *= 2;
4634  break;
4635  default:
4636  av_assert0(0);
4637  }
4638 
4639  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
4640  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
4641 
4642  for (int i = 0; i < 4; i++)
4643  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
4644 
4645  outlink->h = h;
4646  outlink->w = w;
4647 
4648  s->nb_threads = FFMIN(outlink->h, ff_filter_get_nb_threads(ctx));
4650  have_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
4651 
4652  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
4653  s->nb_allocated = 1;
4654  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
4655  } else {
4656  s->nb_allocated = 2;
4657  s->map[0] = s->map[3] = 0;
4658  s->map[1] = s->map[2] = 1;
4659  }
4660 
4661  if (!s->slice_remap)
4662  s->slice_remap = av_calloc(s->nb_threads, sizeof(*s->slice_remap));
4663  if (!s->slice_remap)
4664  return AVERROR(ENOMEM);
4665 
4666  for (int i = 0; i < s->nb_allocated; i++) {
4667  err = allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
4668  if (err < 0)
4669  return err;
4670  }
4671 
4672  calculate_rotation(s->yaw, s->pitch, s->roll,
4674 
4676 
4677  ctx->internal->execute(ctx, v360_slice, NULL, NULL, s->nb_threads);
4678 
4679  return 0;
4680 }
4681 
4683 {
4684  AVFilterContext *ctx = inlink->dst;
4685  AVFilterLink *outlink = ctx->outputs[0];
4686  V360Context *s = ctx->priv;
4687  AVFrame *out;
4688  ThreadData td;
4689 
4690  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
4691  if (!out) {
4692  av_frame_free(&in);
4693  return AVERROR(ENOMEM);
4694  }
4695  av_frame_copy_props(out, in);
4696 
4697  td.in = in;
4698  td.out = out;
4699 
4700  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, s->nb_threads);
4701 
4702  av_frame_free(&in);
4703  return ff_filter_frame(outlink, out);
4704 }
4705 
4706 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
4707  char *res, int res_len, int flags)
4708 {
4709  V360Context *s = ctx->priv;
4710  int ret;
4711 
4712  s->yaw = s->pitch = s->roll = 0.f;
4713 
4714  ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
4715  if (ret < 0)
4716  return ret;
4717 
4718  return config_output(ctx->outputs[0]);
4719 }
4720 
4722 {
4723  V360Context *s = ctx->priv;
4724 
4725  s->rot_quaternion[0][0] = 1.f;
4726  s->rot_quaternion[0][1] = s->rot_quaternion[0][2] = s->rot_quaternion[0][3] = 0.f;
4727 
4728  return 0;
4729 }
4730 
4732 {
4733  V360Context *s = ctx->priv;
4734 
4735  for (int n = 0; n < s->nb_threads && s->slice_remap; n++) {
4736  SliceXYRemap *r = &s->slice_remap[n];
4737 
4738  for (int p = 0; p < s->nb_allocated; p++) {
4739  av_freep(&r->u[p]);
4740  av_freep(&r->v[p]);
4741  av_freep(&r->ker[p]);
4742  }
4743 
4744  av_freep(&r->mask);
4745  }
4746 
4747  av_freep(&s->slice_remap);
4748 }
4749 
4750 static const AVFilterPad inputs[] = {
4751  {
4752  .name = "default",
4753  .type = AVMEDIA_TYPE_VIDEO,
4754  .filter_frame = filter_frame,
4755  },
4756  { NULL }
4757 };
4758 
4759 static const AVFilterPad outputs[] = {
4760  {
4761  .name = "default",
4762  .type = AVMEDIA_TYPE_VIDEO,
4763  .config_props = config_output,
4764  },
4765  { NULL }
4766 };
4767 
4769  .name = "v360",
4770  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
4771  .priv_size = sizeof(V360Context),
4772  .init = init,
4773  .uninit = uninit,
4775  .inputs = inputs,
4776  .outputs = outputs,
4777  .priv_class = &v360_class,
4780 };
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:502
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
static __device__ float floorf(float a)
Definition: cuda_runtime.h:172
#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:314
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:3478
#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.
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:3790
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:3158
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:278
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
static __device__ float ceilf(float a)
Definition: cuda_runtime.h:175
#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:3825
static void set_mirror_modifier(int h_flip, int v_flip, int d_flip, float *modifier)
Definition: vf_v360.c:3937
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:4140
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:4003
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:3969
#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:1093
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:3919
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:264
#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
#define av_clip
Definition: common.h:122
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:4768
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 __device__ float fabsf(float a)
Definition: cuda_runtime.h:181
static int xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:3282
#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 av_clipf
Definition: common.h:170
#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:3241
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:3572
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:3332
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:3945
#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:115
#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:881
const char * r
Definition: vf_curves.c:116
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:4072
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:3884
Definition: v360.h:68
int in_offset_w[4]
Definition: v360.h:162
#define FFMAX(a, b)
Definition: common.h:103
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