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