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