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 
56 static const AVOption v360_options[] = {
57  { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" },
58  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
59  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" },
60  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "in" },
61  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" },
62  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" },
63  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" },
64  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
65  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" },
66  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" },
67  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" },
68  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" },
69  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" },
70  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" },
71  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" },
72  { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" },
73  { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
74  { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" },
75  { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" },
76  { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" },
77  { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" },
78  { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" },
79  { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
80  {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
81  { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" },
82  { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
83  { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" },
84  { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" },
85  { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" },
86  { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" },
87  { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" },
88  { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" },
89  {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" },
90  { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" },
91  { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
92  { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" },
93  { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
94  { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" },
95  { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
96  { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" },
97  { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
98  { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" },
99  { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"},
100  { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"},
101  { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
102  {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" },
103  { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" },
104  { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" },
105  { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" },
106  { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"},
107  {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"},
108  { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"},
109  { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"},
110  { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "in_pad"},
111  { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 1.f, FLAGS, "out_pad"},
112  { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fin_pad"},
113  { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS, "fout_pad"},
114  { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "yaw"},
115  { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "pitch"},
116  { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "roll"},
117  { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"},
118  { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"},
119  { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"},
120  { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"},
121  { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"},
122  { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"},
123  { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"},
124  { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "ih_flip"},
125  { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"},
126  { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"},
127  { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"},
128  { NULL }
129 };
130 
132 
134 {
135  static const enum AVPixelFormat pix_fmts[] = {
136  // YUVA444
140 
141  // YUVA422
145 
146  // YUVA420
149 
150  // YUVJ
154 
155  // YUV444
159 
160  // YUV440
163 
164  // YUV422
168 
169  // YUV420
173 
174  // YUV411
176 
177  // YUV410
179 
180  // GBR
184 
185  // GBRA
188 
189  // GRAY
193 
195  };
196 
197  AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
198  if (!fmts_list)
199  return AVERROR(ENOMEM);
200  return ff_set_common_formats(ctx, fmts_list);
201 }
202 
203 #define DEFINE_REMAP1_LINE(bits, div) \
204 static void remap1_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
205  ptrdiff_t in_linesize, \
206  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
207 { \
208  const uint##bits##_t *s = (const uint##bits##_t *)src; \
209  uint##bits##_t *d = (uint##bits##_t *)dst; \
210  \
211  in_linesize /= div; \
212  \
213  for (int x = 0; x < width; x++) \
214  d[x] = s[v[x] * in_linesize + u[x]]; \
215 }
216 
217 DEFINE_REMAP1_LINE( 8, 1)
218 DEFINE_REMAP1_LINE(16, 2)
219 
220 /**
221  * Generate remapping function with a given window size and pixel depth.
222  *
223  * @param ws size of interpolation window
224  * @param bits number of bits per pixel
225  */
226 #define DEFINE_REMAP(ws, bits) \
227 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
228 { \
229  ThreadData *td = arg; \
230  const V360Context *s = ctx->priv; \
231  const AVFrame *in = td->in; \
232  AVFrame *out = td->out; \
233  \
234  for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \
235  for (int plane = 0; plane < s->nb_planes; plane++) { \
236  const int in_linesize = in->linesize[plane]; \
237  const int out_linesize = out->linesize[plane]; \
238  const int uv_linesize = s->uv_linesize[plane]; \
239  const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \
240  const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \
241  const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \
242  const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \
243  const uint8_t *src = in->data[plane] + in_offset_h * in_linesize + in_offset_w * (bits >> 3); \
244  uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3); \
245  const int width = s->pr_width[plane]; \
246  const int height = s->pr_height[plane]; \
247  \
248  const int slice_start = (height * jobnr ) / nb_jobs; \
249  const int slice_end = (height * (jobnr + 1)) / nb_jobs; \
250  \
251  for (int y = slice_start; y < slice_end; y++) { \
252  const unsigned map = s->map[plane]; \
253  const uint16_t *u = s->u[map] + y * uv_linesize * ws * ws; \
254  const uint16_t *v = s->v[map] + y * uv_linesize * ws * ws; \
255  const int16_t *ker = s->ker[map] + y * uv_linesize * ws * ws; \
256  \
257  s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \
258  } \
259  } \
260  } \
261  \
262  return 0; \
263 }
264 
265 DEFINE_REMAP(1, 8)
266 DEFINE_REMAP(2, 8)
267 DEFINE_REMAP(4, 8)
268 DEFINE_REMAP(1, 16)
269 DEFINE_REMAP(2, 16)
270 DEFINE_REMAP(4, 16)
271 
272 #define DEFINE_REMAP_LINE(ws, bits, div) \
273 static void remap##ws##_##bits##bit_line_c(uint8_t *dst, int width, const uint8_t *src, \
274  ptrdiff_t in_linesize, \
275  const uint16_t *u, const uint16_t *v, const int16_t *ker) \
276 { \
277  const uint##bits##_t *s = (const uint##bits##_t *)src; \
278  uint##bits##_t *d = (uint##bits##_t *)dst; \
279  \
280  in_linesize /= div; \
281  \
282  for (int x = 0; x < width; x++) { \
283  const uint16_t *uu = u + x * ws * ws; \
284  const uint16_t *vv = v + x * ws * ws; \
285  const int16_t *kker = ker + x * ws * ws; \
286  int tmp = 0; \
287  \
288  for (int i = 0; i < ws; i++) { \
289  for (int j = 0; j < ws; j++) { \
290  tmp += kker[i * ws + j] * s[vv[i * ws + j] * in_linesize + uu[i * ws + j]]; \
291  } \
292  } \
293  \
294  d[x] = av_clip_uint##bits(tmp >> 14); \
295  } \
296 }
297 
298 DEFINE_REMAP_LINE(2, 8, 1)
299 DEFINE_REMAP_LINE(4, 8, 1)
300 DEFINE_REMAP_LINE(2, 16, 2)
301 DEFINE_REMAP_LINE(4, 16, 2)
302 
303 void ff_v360_init(V360Context *s, int depth)
304 {
305  switch (s->interp) {
306  case NEAREST:
307  s->remap_line = depth <= 8 ? remap1_8bit_line_c : remap1_16bit_line_c;
308  break;
309  case BILINEAR:
310  s->remap_line = depth <= 8 ? remap2_8bit_line_c : remap2_16bit_line_c;
311  break;
312  case BICUBIC:
313  case LANCZOS:
314  s->remap_line = depth <= 8 ? remap4_8bit_line_c : remap4_16bit_line_c;
315  break;
316  }
317 
318  if (ARCH_X86)
319  ff_v360_init_x86(s, depth);
320 }
321 
322 /**
323  * Save nearest pixel coordinates for remapping.
324  *
325  * @param du horizontal relative coordinate
326  * @param dv vertical relative coordinate
327  * @param rmap calculated 4x4 window
328  * @param u u remap data
329  * @param v v remap data
330  * @param ker ker remap data
331  */
332 static void nearest_kernel(float du, float dv, const XYRemap *rmap,
333  uint16_t *u, uint16_t *v, int16_t *ker)
334 {
335  const int i = roundf(dv) + 1;
336  const int j = roundf(du) + 1;
337 
338  u[0] = rmap->u[i][j];
339  v[0] = rmap->v[i][j];
340 }
341 
342 /**
343  * Calculate kernel for bilinear interpolation.
344  *
345  * @param du horizontal relative coordinate
346  * @param dv vertical relative coordinate
347  * @param rmap calculated 4x4 window
348  * @param u u remap data
349  * @param v v remap data
350  * @param ker ker remap data
351  */
352 static void bilinear_kernel(float du, float dv, const XYRemap *rmap,
353  uint16_t *u, uint16_t *v, int16_t *ker)
354 {
355  for (int i = 0; i < 2; i++) {
356  for (int j = 0; j < 2; j++) {
357  u[i * 2 + j] = rmap->u[i + 1][j + 1];
358  v[i * 2 + j] = rmap->v[i + 1][j + 1];
359  }
360  }
361 
362  ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f);
363  ker[1] = lrintf( du * (1.f - dv) * 16385.f);
364  ker[2] = lrintf((1.f - du) * dv * 16385.f);
365  ker[3] = lrintf( du * dv * 16385.f);
366 }
367 
368 /**
369  * Calculate 1-dimensional cubic coefficients.
370  *
371  * @param t relative coordinate
372  * @param coeffs coefficients
373  */
374 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
375 {
376  const float tt = t * t;
377  const float ttt = t * t * t;
378 
379  coeffs[0] = - t / 3.f + tt / 2.f - ttt / 6.f;
380  coeffs[1] = 1.f - t / 2.f - tt + ttt / 2.f;
381  coeffs[2] = t + tt / 2.f - ttt / 2.f;
382  coeffs[3] = - t / 6.f + ttt / 6.f;
383 }
384 
385 /**
386  * Calculate kernel for bicubic interpolation.
387  *
388  * @param du horizontal relative coordinate
389  * @param dv vertical relative coordinate
390  * @param rmap calculated 4x4 window
391  * @param u u remap data
392  * @param v v remap data
393  * @param ker ker remap data
394  */
395 static void bicubic_kernel(float du, float dv, const XYRemap *rmap,
396  uint16_t *u, uint16_t *v, int16_t *ker)
397 {
398  float du_coeffs[4];
399  float dv_coeffs[4];
400 
401  calculate_bicubic_coeffs(du, du_coeffs);
402  calculate_bicubic_coeffs(dv, dv_coeffs);
403 
404  for (int i = 0; i < 4; i++) {
405  for (int j = 0; j < 4; j++) {
406  u[i * 4 + j] = rmap->u[i][j];
407  v[i * 4 + j] = rmap->v[i][j];
408  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
409  }
410  }
411 }
412 
413 /**
414  * Calculate 1-dimensional lanczos coefficients.
415  *
416  * @param t relative coordinate
417  * @param coeffs coefficients
418  */
419 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
420 {
421  float sum = 0.f;
422 
423  for (int i = 0; i < 4; i++) {
424  const float x = M_PI * (t - i + 1);
425  if (x == 0.f) {
426  coeffs[i] = 1.f;
427  } else {
428  coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
429  }
430  sum += coeffs[i];
431  }
432 
433  for (int i = 0; i < 4; i++) {
434  coeffs[i] /= sum;
435  }
436 }
437 
438 /**
439  * Calculate kernel for lanczos interpolation.
440  *
441  * @param du horizontal relative coordinate
442  * @param dv vertical relative coordinate
443  * @param rmap calculated 4x4 window
444  * @param u u remap data
445  * @param v v remap data
446  * @param ker ker remap data
447  */
448 static void lanczos_kernel(float du, float dv, const XYRemap *rmap,
449  uint16_t *u, uint16_t *v, int16_t *ker)
450 {
451  float du_coeffs[4];
452  float dv_coeffs[4];
453 
454  calculate_lanczos_coeffs(du, du_coeffs);
455  calculate_lanczos_coeffs(dv, dv_coeffs);
456 
457  for (int i = 0; i < 4; i++) {
458  for (int j = 0; j < 4; j++) {
459  u[i * 4 + j] = rmap->u[i][j];
460  v[i * 4 + j] = rmap->v[i][j];
461  ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
462  }
463  }
464 }
465 
466 /**
467  * Modulo operation with only positive remainders.
468  *
469  * @param a dividend
470  * @param b divisor
471  *
472  * @return positive remainder of (a / b)
473  */
474 static inline int mod(int a, int b)
475 {
476  const int res = a % b;
477  if (res < 0) {
478  return res + b;
479  } else {
480  return res;
481  }
482 }
483 
484 /**
485  * Convert char to corresponding direction.
486  * Used for cubemap options.
487  */
488 static int get_direction(char c)
489 {
490  switch (c) {
491  case 'r':
492  return RIGHT;
493  case 'l':
494  return LEFT;
495  case 'u':
496  return UP;
497  case 'd':
498  return DOWN;
499  case 'f':
500  return FRONT;
501  case 'b':
502  return BACK;
503  default:
504  return -1;
505  }
506 }
507 
508 /**
509  * Convert char to corresponding rotation angle.
510  * Used for cubemap options.
511  */
512 static int get_rotation(char c)
513 {
514  switch (c) {
515  case '0':
516  return ROT_0;
517  case '1':
518  return ROT_90;
519  case '2':
520  return ROT_180;
521  case '3':
522  return ROT_270;
523  default:
524  return -1;
525  }
526 }
527 
528 /**
529  * Convert char to corresponding rotation order.
530  */
531 static int get_rorder(char c)
532 {
533  switch (c) {
534  case 'Y':
535  case 'y':
536  return YAW;
537  case 'P':
538  case 'p':
539  return PITCH;
540  case 'R':
541  case 'r':
542  return ROLL;
543  default:
544  return -1;
545  }
546 }
547 
548 /**
549  * Prepare data for processing cubemap input format.
550  *
551  * @param ctx filter context
552  *
553  * @return error code
554  */
556 {
557  V360Context *s = ctx->priv;
558 
559  for (int face = 0; face < NB_FACES; face++) {
560  const char c = s->in_forder[face];
561  int direction;
562 
563  if (c == '\0') {
564  av_log(ctx, AV_LOG_ERROR,
565  "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
566  return AVERROR(EINVAL);
567  }
568 
569  direction = get_direction(c);
570  if (direction == -1) {
571  av_log(ctx, AV_LOG_ERROR,
572  "Incorrect direction symbol '%c' in in_forder option.\n", c);
573  return AVERROR(EINVAL);
574  }
575 
576  s->in_cubemap_face_order[direction] = face;
577  }
578 
579  for (int face = 0; face < NB_FACES; face++) {
580  const char c = s->in_frot[face];
581  int rotation;
582 
583  if (c == '\0') {
584  av_log(ctx, AV_LOG_ERROR,
585  "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
586  return AVERROR(EINVAL);
587  }
588 
589  rotation = get_rotation(c);
590  if (rotation == -1) {
591  av_log(ctx, AV_LOG_ERROR,
592  "Incorrect rotation symbol '%c' in in_frot option.\n", c);
593  return AVERROR(EINVAL);
594  }
595 
596  s->in_cubemap_face_rotation[face] = rotation;
597  }
598 
599  return 0;
600 }
601 
602 /**
603  * Prepare data for processing cubemap output format.
604  *
605  * @param ctx filter context
606  *
607  * @return error code
608  */
610 {
611  V360Context *s = ctx->priv;
612 
613  for (int face = 0; face < NB_FACES; face++) {
614  const char c = s->out_forder[face];
615  int direction;
616 
617  if (c == '\0') {
618  av_log(ctx, AV_LOG_ERROR,
619  "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
620  return AVERROR(EINVAL);
621  }
622 
623  direction = get_direction(c);
624  if (direction == -1) {
625  av_log(ctx, AV_LOG_ERROR,
626  "Incorrect direction symbol '%c' in out_forder option.\n", c);
627  return AVERROR(EINVAL);
628  }
629 
630  s->out_cubemap_direction_order[face] = direction;
631  }
632 
633  for (int face = 0; face < NB_FACES; face++) {
634  const char c = s->out_frot[face];
635  int rotation;
636 
637  if (c == '\0') {
638  av_log(ctx, AV_LOG_ERROR,
639  "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
640  return AVERROR(EINVAL);
641  }
642 
643  rotation = get_rotation(c);
644  if (rotation == -1) {
645  av_log(ctx, AV_LOG_ERROR,
646  "Incorrect rotation symbol '%c' in out_frot option.\n", c);
647  return AVERROR(EINVAL);
648  }
649 
650  s->out_cubemap_face_rotation[face] = rotation;
651  }
652 
653  return 0;
654 }
655 
656 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
657 {
658  float tmp;
659 
660  switch (rotation) {
661  case ROT_0:
662  break;
663  case ROT_90:
664  tmp = *uf;
665  *uf = -*vf;
666  *vf = tmp;
667  break;
668  case ROT_180:
669  *uf = -*uf;
670  *vf = -*vf;
671  break;
672  case ROT_270:
673  tmp = -*uf;
674  *uf = *vf;
675  *vf = tmp;
676  break;
677  default:
678  av_assert0(0);
679  }
680 }
681 
682 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
683 {
684  float tmp;
685 
686  switch (rotation) {
687  case ROT_0:
688  break;
689  case ROT_90:
690  tmp = -*uf;
691  *uf = *vf;
692  *vf = tmp;
693  break;
694  case ROT_180:
695  *uf = -*uf;
696  *vf = -*vf;
697  break;
698  case ROT_270:
699  tmp = *uf;
700  *uf = -*vf;
701  *vf = tmp;
702  break;
703  default:
704  av_assert0(0);
705  }
706 }
707 
708 /**
709  * Normalize vector.
710  *
711  * @param vec vector
712  */
713 static void normalize_vector(float *vec)
714 {
715  const float norm = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
716 
717  vec[0] /= norm;
718  vec[1] /= norm;
719  vec[2] /= norm;
720 }
721 
722 /**
723  * Calculate 3D coordinates on sphere for corresponding cubemap position.
724  * Common operation for every cubemap.
725  *
726  * @param s filter private context
727  * @param uf horizontal cubemap coordinate [0, 1)
728  * @param vf vertical cubemap coordinate [0, 1)
729  * @param face face of cubemap
730  * @param vec coordinates on sphere
731  * @param scalew scale for uf
732  * @param scaleh scale for vf
733  */
734 static void cube_to_xyz(const V360Context *s,
735  float uf, float vf, int face,
736  float *vec, float scalew, float scaleh)
737 {
738  const int direction = s->out_cubemap_direction_order[face];
739  float l_x, l_y, l_z;
740 
741  uf /= scalew;
742  vf /= scaleh;
743 
745 
746  switch (direction) {
747  case RIGHT:
748  l_x = 1.f;
749  l_y = -vf;
750  l_z = uf;
751  break;
752  case LEFT:
753  l_x = -1.f;
754  l_y = -vf;
755  l_z = -uf;
756  break;
757  case UP:
758  l_x = uf;
759  l_y = 1.f;
760  l_z = -vf;
761  break;
762  case DOWN:
763  l_x = uf;
764  l_y = -1.f;
765  l_z = vf;
766  break;
767  case FRONT:
768  l_x = uf;
769  l_y = -vf;
770  l_z = -1.f;
771  break;
772  case BACK:
773  l_x = -uf;
774  l_y = -vf;
775  l_z = 1.f;
776  break;
777  default:
778  av_assert0(0);
779  }
780 
781  vec[0] = l_x;
782  vec[1] = l_y;
783  vec[2] = l_z;
784 
785  normalize_vector(vec);
786 }
787 
788 /**
789  * Calculate cubemap position for corresponding 3D coordinates on sphere.
790  * Common operation for every cubemap.
791  *
792  * @param s filter private context
793  * @param vec coordinated on sphere
794  * @param uf horizontal cubemap coordinate [0, 1)
795  * @param vf vertical cubemap coordinate [0, 1)
796  * @param direction direction of view
797  */
798 static void xyz_to_cube(const V360Context *s,
799  const float *vec,
800  float *uf, float *vf, int *direction)
801 {
802  const float phi = atan2f(vec[0], -vec[2]);
803  const float theta = asinf(-vec[1]);
804  float phi_norm, theta_threshold;
805  int face;
806 
807  if (phi >= -M_PI_4 && phi < M_PI_4) {
808  *direction = FRONT;
809  phi_norm = phi;
810  } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
811  *direction = LEFT;
812  phi_norm = phi + M_PI_2;
813  } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
814  *direction = RIGHT;
815  phi_norm = phi - M_PI_2;
816  } else {
817  *direction = BACK;
818  phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
819  }
820 
821  theta_threshold = atanf(cosf(phi_norm));
822  if (theta > theta_threshold) {
823  *direction = DOWN;
824  } else if (theta < -theta_threshold) {
825  *direction = UP;
826  }
827 
828  switch (*direction) {
829  case RIGHT:
830  *uf = vec[2] / vec[0];
831  *vf = -vec[1] / vec[0];
832  break;
833  case LEFT:
834  *uf = vec[2] / vec[0];
835  *vf = vec[1] / vec[0];
836  break;
837  case UP:
838  *uf = vec[0] / vec[1];
839  *vf = -vec[2] / vec[1];
840  break;
841  case DOWN:
842  *uf = -vec[0] / vec[1];
843  *vf = -vec[2] / vec[1];
844  break;
845  case FRONT:
846  *uf = -vec[0] / vec[2];
847  *vf = vec[1] / vec[2];
848  break;
849  case BACK:
850  *uf = -vec[0] / vec[2];
851  *vf = -vec[1] / vec[2];
852  break;
853  default:
854  av_assert0(0);
855  }
856 
857  face = s->in_cubemap_face_order[*direction];
858  rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
859 
860  (*uf) *= s->input_mirror_modifier[0];
861  (*vf) *= s->input_mirror_modifier[1];
862 }
863 
864 /**
865  * Find position on another cube face in case of overflow/underflow.
866  * Used for calculation of interpolation window.
867  *
868  * @param s filter private context
869  * @param uf horizontal cubemap coordinate
870  * @param vf vertical cubemap coordinate
871  * @param direction direction of view
872  * @param new_uf new horizontal cubemap coordinate
873  * @param new_vf new vertical cubemap coordinate
874  * @param face face position on cubemap
875  */
877  float uf, float vf, int direction,
878  float *new_uf, float *new_vf, int *face)
879 {
880  /*
881  * Cubemap orientation
882  *
883  * width
884  * <------->
885  * +-------+
886  * | | U
887  * | up | h ------->
888  * +-------+-------+-------+-------+ ^ e |
889  * | | | | | | i V |
890  * | left | front | right | back | | g |
891  * +-------+-------+-------+-------+ v h v
892  * | | t
893  * | down |
894  * +-------+
895  */
896 
897  *face = s->in_cubemap_face_order[direction];
899 
900  if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
901  // There are no pixels to use in this case
902  *new_uf = uf;
903  *new_vf = vf;
904  } else if (uf < -1.f) {
905  uf += 2.f;
906  switch (direction) {
907  case RIGHT:
908  direction = FRONT;
909  *new_uf = uf;
910  *new_vf = vf;
911  break;
912  case LEFT:
913  direction = BACK;
914  *new_uf = uf;
915  *new_vf = vf;
916  break;
917  case UP:
918  direction = LEFT;
919  *new_uf = vf;
920  *new_vf = -uf;
921  break;
922  case DOWN:
923  direction = LEFT;
924  *new_uf = -vf;
925  *new_vf = uf;
926  break;
927  case FRONT:
928  direction = LEFT;
929  *new_uf = uf;
930  *new_vf = vf;
931  break;
932  case BACK:
933  direction = RIGHT;
934  *new_uf = uf;
935  *new_vf = vf;
936  break;
937  default:
938  av_assert0(0);
939  }
940  } else if (uf >= 1.f) {
941  uf -= 2.f;
942  switch (direction) {
943  case RIGHT:
944  direction = BACK;
945  *new_uf = uf;
946  *new_vf = vf;
947  break;
948  case LEFT:
949  direction = FRONT;
950  *new_uf = uf;
951  *new_vf = vf;
952  break;
953  case UP:
954  direction = RIGHT;
955  *new_uf = -vf;
956  *new_vf = uf;
957  break;
958  case DOWN:
959  direction = RIGHT;
960  *new_uf = vf;
961  *new_vf = -uf;
962  break;
963  case FRONT:
964  direction = RIGHT;
965  *new_uf = uf;
966  *new_vf = vf;
967  break;
968  case BACK:
969  direction = LEFT;
970  *new_uf = uf;
971  *new_vf = vf;
972  break;
973  default:
974  av_assert0(0);
975  }
976  } else if (vf < -1.f) {
977  vf += 2.f;
978  switch (direction) {
979  case RIGHT:
980  direction = UP;
981  *new_uf = vf;
982  *new_vf = -uf;
983  break;
984  case LEFT:
985  direction = UP;
986  *new_uf = -vf;
987  *new_vf = uf;
988  break;
989  case UP:
990  direction = BACK;
991  *new_uf = -uf;
992  *new_vf = -vf;
993  break;
994  case DOWN:
995  direction = FRONT;
996  *new_uf = uf;
997  *new_vf = vf;
998  break;
999  case FRONT:
1000  direction = UP;
1001  *new_uf = uf;
1002  *new_vf = vf;
1003  break;
1004  case BACK:
1005  direction = UP;
1006  *new_uf = -uf;
1007  *new_vf = -vf;
1008  break;
1009  default:
1010  av_assert0(0);
1011  }
1012  } else if (vf >= 1.f) {
1013  vf -= 2.f;
1014  switch (direction) {
1015  case RIGHT:
1016  direction = DOWN;
1017  *new_uf = -vf;
1018  *new_vf = uf;
1019  break;
1020  case LEFT:
1021  direction = DOWN;
1022  *new_uf = vf;
1023  *new_vf = -uf;
1024  break;
1025  case UP:
1026  direction = FRONT;
1027  *new_uf = uf;
1028  *new_vf = vf;
1029  break;
1030  case DOWN:
1031  direction = BACK;
1032  *new_uf = -uf;
1033  *new_vf = -vf;
1034  break;
1035  case FRONT:
1036  direction = DOWN;
1037  *new_uf = uf;
1038  *new_vf = vf;
1039  break;
1040  case BACK:
1041  direction = DOWN;
1042  *new_uf = -uf;
1043  *new_vf = -vf;
1044  break;
1045  default:
1046  av_assert0(0);
1047  }
1048  } else {
1049  // Inside cube face
1050  *new_uf = uf;
1051  *new_vf = vf;
1052  }
1053 
1054  *face = s->in_cubemap_face_order[direction];
1055  rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1056 }
1057 
1058 /**
1059  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1060  *
1061  * @param s filter private context
1062  * @param i horizontal position on frame [0, width)
1063  * @param j vertical position on frame [0, height)
1064  * @param width frame width
1065  * @param height frame height
1066  * @param vec coordinates on sphere
1067  */
1068 static void cube3x2_to_xyz(const V360Context *s,
1069  int i, int j, int width, int height,
1070  float *vec)
1071 {
1072  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 3.f) : 1.f - s->out_pad;
1073  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 2.f) : 1.f - s->out_pad;
1074 
1075  const float ew = width / 3.f;
1076  const float eh = height / 2.f;
1077 
1078  const int u_face = floorf(i / ew);
1079  const int v_face = floorf(j / eh);
1080  const int face = u_face + 3 * v_face;
1081 
1082  const int u_shift = ceilf(ew * u_face);
1083  const int v_shift = ceilf(eh * v_face);
1084  const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1085  const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1086 
1087  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1088  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1089 
1090  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1091 }
1092 
1093 /**
1094  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1095  *
1096  * @param s filter private context
1097  * @param vec coordinates on sphere
1098  * @param width frame width
1099  * @param height frame height
1100  * @param us horizontal coordinates for interpolation window
1101  * @param vs vertical coordinates for interpolation window
1102  * @param du horizontal relative coordinate
1103  * @param dv vertical relative coordinate
1104  */
1105 static void xyz_to_cube3x2(const V360Context *s,
1106  const float *vec, int width, int height,
1107  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1108 {
1109  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 3.f) : 1.f - s->in_pad;
1110  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 2.f) : 1.f - s->in_pad;
1111  const float ew = width / 3.f;
1112  const float eh = height / 2.f;
1113  float uf, vf;
1114  int ui, vi;
1115  int ewi, ehi;
1116  int direction, face;
1117  int u_face, v_face;
1118 
1119  xyz_to_cube(s, vec, &uf, &vf, &direction);
1120 
1121  uf *= scalew;
1122  vf *= scaleh;
1123 
1124  face = s->in_cubemap_face_order[direction];
1125  u_face = face % 3;
1126  v_face = face / 3;
1127  ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1128  ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1129 
1130  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1131  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1132 
1133  ui = floorf(uf);
1134  vi = floorf(vf);
1135 
1136  *du = uf - ui;
1137  *dv = vf - vi;
1138 
1139  for (int i = -1; i < 3; i++) {
1140  for (int j = -1; j < 3; j++) {
1141  int new_ui = ui + j;
1142  int new_vi = vi + i;
1143  int u_shift, v_shift;
1144  int new_ewi, new_ehi;
1145 
1146  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1147  face = s->in_cubemap_face_order[direction];
1148 
1149  u_face = face % 3;
1150  v_face = face / 3;
1151  u_shift = ceilf(ew * u_face);
1152  v_shift = ceilf(eh * v_face);
1153  } else {
1154  uf = 2.f * new_ui / ewi - 1.f;
1155  vf = 2.f * new_vi / ehi - 1.f;
1156 
1157  uf /= scalew;
1158  vf /= scaleh;
1159 
1160  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1161 
1162  uf *= scalew;
1163  vf *= scaleh;
1164 
1165  u_face = face % 3;
1166  v_face = face / 3;
1167  u_shift = ceilf(ew * u_face);
1168  v_shift = ceilf(eh * v_face);
1169  new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1170  new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1171 
1172  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1173  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1174  }
1175 
1176  us[i + 1][j + 1] = u_shift + new_ui;
1177  vs[i + 1][j + 1] = v_shift + new_vi;
1178  }
1179  }
1180 }
1181 
1182 /**
1183  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1184  *
1185  * @param s filter private context
1186  * @param i horizontal position on frame [0, width)
1187  * @param j vertical position on frame [0, height)
1188  * @param width frame width
1189  * @param height frame height
1190  * @param vec coordinates on sphere
1191  */
1192 static void cube1x6_to_xyz(const V360Context *s,
1193  int i, int j, int width, int height,
1194  float *vec)
1195 {
1196  const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_width : 1.f - s->out_pad;
1197  const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 6.f) : 1.f - s->out_pad;
1198 
1199  const float ew = width;
1200  const float eh = height / 6.f;
1201 
1202  const int face = floorf(j / eh);
1203 
1204  const int v_shift = ceilf(eh * face);
1205  const int ehi = ceilf(eh * (face + 1)) - v_shift;
1206 
1207  const float uf = 2.f * (i + 0.5f) / ew - 1.f;
1208  const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
1209 
1210  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1211 }
1212 
1213 /**
1214  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1215  *
1216  * @param s filter private context
1217  * @param i horizontal position on frame [0, width)
1218  * @param j vertical position on frame [0, height)
1219  * @param width frame width
1220  * @param height frame height
1221  * @param vec coordinates on sphere
1222  */
1223 static void cube6x1_to_xyz(const V360Context *s,
1224  int i, int j, int width, int height,
1225  float *vec)
1226 {
1227  const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 6.f) : 1.f - s->out_pad;
1228  const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_height : 1.f - s->out_pad;
1229 
1230  const float ew = width / 6.f;
1231  const float eh = height;
1232 
1233  const int face = floorf(i / ew);
1234 
1235  const int u_shift = ceilf(ew * face);
1236  const int ewi = ceilf(ew * (face + 1)) - u_shift;
1237 
1238  const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
1239  const float vf = 2.f * (j + 0.5f) / eh - 1.f;
1240 
1241  cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
1242 }
1243 
1244 /**
1245  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1246  *
1247  * @param s filter private context
1248  * @param vec coordinates on sphere
1249  * @param width frame width
1250  * @param height frame height
1251  * @param us horizontal coordinates for interpolation window
1252  * @param vs vertical coordinates for interpolation window
1253  * @param du horizontal relative coordinate
1254  * @param dv vertical relative coordinate
1255  */
1256 static void xyz_to_cube1x6(const V360Context *s,
1257  const float *vec, int width, int height,
1258  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1259 {
1260  const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_width : 1.f - s->in_pad;
1261  const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 6.f) : 1.f - s->in_pad;
1262  const float eh = height / 6.f;
1263  const int ewi = width;
1264  float uf, vf;
1265  int ui, vi;
1266  int ehi;
1267  int direction, face;
1268 
1269  xyz_to_cube(s, vec, &uf, &vf, &direction);
1270 
1271  uf *= scalew;
1272  vf *= scaleh;
1273 
1274  face = s->in_cubemap_face_order[direction];
1275  ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1276 
1277  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1278  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1279 
1280  ui = floorf(uf);
1281  vi = floorf(vf);
1282 
1283  *du = uf - ui;
1284  *dv = vf - vi;
1285 
1286  for (int i = -1; i < 3; i++) {
1287  for (int j = -1; j < 3; j++) {
1288  int new_ui = ui + j;
1289  int new_vi = vi + i;
1290  int v_shift;
1291  int new_ehi;
1292 
1293  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1294  face = s->in_cubemap_face_order[direction];
1295 
1296  v_shift = ceilf(eh * face);
1297  } else {
1298  uf = 2.f * new_ui / ewi - 1.f;
1299  vf = 2.f * new_vi / ehi - 1.f;
1300 
1301  uf /= scalew;
1302  vf /= scaleh;
1303 
1304  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1305 
1306  uf *= scalew;
1307  vf *= scaleh;
1308 
1309  v_shift = ceilf(eh * face);
1310  new_ehi = ceilf(eh * (face + 1)) - v_shift;
1311 
1312  new_ui = av_clip(roundf(0.5f * ewi * (uf + 1.f)), 0, ewi - 1);
1313  new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1314  }
1315 
1316  us[i + 1][j + 1] = new_ui;
1317  vs[i + 1][j + 1] = v_shift + new_vi;
1318  }
1319  }
1320 }
1321 
1322 /**
1323  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1324  *
1325  * @param s filter private context
1326  * @param vec coordinates on sphere
1327  * @param width frame width
1328  * @param height frame height
1329  * @param us horizontal coordinates for interpolation window
1330  * @param vs vertical coordinates for interpolation window
1331  * @param du horizontal relative coordinate
1332  * @param dv vertical relative coordinate
1333  */
1334 static void xyz_to_cube6x1(const V360Context *s,
1335  const float *vec, int width, int height,
1336  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1337 {
1338  const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 6.f) : 1.f - s->in_pad;
1339  const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_height : 1.f - s->in_pad;
1340  const float ew = width / 6.f;
1341  const int ehi = height;
1342  float uf, vf;
1343  int ui, vi;
1344  int ewi;
1345  int direction, face;
1346 
1347  xyz_to_cube(s, vec, &uf, &vf, &direction);
1348 
1349  uf *= scalew;
1350  vf *= scaleh;
1351 
1352  face = s->in_cubemap_face_order[direction];
1353  ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1354 
1355  uf = 0.5f * ewi * (uf + 1.f) - 0.5f;
1356  vf = 0.5f * ehi * (vf + 1.f) - 0.5f;
1357 
1358  ui = floorf(uf);
1359  vi = floorf(vf);
1360 
1361  *du = uf - ui;
1362  *dv = vf - vi;
1363 
1364  for (int i = -1; i < 3; i++) {
1365  for (int j = -1; j < 3; j++) {
1366  int new_ui = ui + j;
1367  int new_vi = vi + i;
1368  int u_shift;
1369  int new_ewi;
1370 
1371  if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1372  face = s->in_cubemap_face_order[direction];
1373 
1374  u_shift = ceilf(ew * face);
1375  } else {
1376  uf = 2.f * new_ui / ewi - 1.f;
1377  vf = 2.f * new_vi / ehi - 1.f;
1378 
1379  uf /= scalew;
1380  vf /= scaleh;
1381 
1382  process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1383 
1384  uf *= scalew;
1385  vf *= scaleh;
1386 
1387  u_shift = ceilf(ew * face);
1388  new_ewi = ceilf(ew * (face + 1)) - u_shift;
1389 
1390  new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1391  new_vi = av_clip(roundf(0.5f * ehi * (vf + 1.f)), 0, ehi - 1);
1392  }
1393 
1394  us[i + 1][j + 1] = u_shift + new_ui;
1395  vs[i + 1][j + 1] = new_vi;
1396  }
1397  }
1398 }
1399 
1400 /**
1401  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1402  *
1403  * @param s filter private context
1404  * @param i horizontal position on frame [0, width)
1405  * @param j vertical position on frame [0, height)
1406  * @param width frame width
1407  * @param height frame height
1408  * @param vec coordinates on sphere
1409  */
1410 static void equirect_to_xyz(const V360Context *s,
1411  int i, int j, int width, int height,
1412  float *vec)
1413 {
1414  const float phi = ((2.f * i) / width - 1.f) * M_PI;
1415  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1416 
1417  const float sin_phi = sinf(phi);
1418  const float cos_phi = cosf(phi);
1419  const float sin_theta = sinf(theta);
1420  const float cos_theta = cosf(theta);
1421 
1422  vec[0] = cos_theta * sin_phi;
1423  vec[1] = -sin_theta;
1424  vec[2] = -cos_theta * cos_phi;
1425 }
1426 
1427 /**
1428  * Prepare data for processing stereographic output format.
1429  *
1430  * @param ctx filter context
1431  *
1432  * @return error code
1433  */
1435 {
1436  V360Context *s = ctx->priv;
1437 
1438  s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
1439  s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
1440 
1441  return 0;
1442 }
1443 
1444 /**
1445  * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format.
1446  *
1447  * @param s filter private context
1448  * @param i horizontal position on frame [0, width)
1449  * @param j vertical position on frame [0, height)
1450  * @param width frame width
1451  * @param height frame height
1452  * @param vec coordinates on sphere
1453  */
1455  int i, int j, int width, int height,
1456  float *vec)
1457 {
1458  const float x = ((2.f * i) / width - 1.f) * s->flat_range[0];
1459  const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
1460  const float xy = x * x + y * y;
1461 
1462  vec[0] = 2.f * x / (1.f + xy);
1463  vec[1] = (-1.f + xy) / (1.f + xy);
1464  vec[2] = 2.f * y / (1.f + xy);
1465 
1466  normalize_vector(vec);
1467 }
1468 
1469 /**
1470  * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere.
1471  *
1472  * @param s filter private context
1473  * @param vec coordinates on sphere
1474  * @param width frame width
1475  * @param height frame height
1476  * @param us horizontal coordinates for interpolation window
1477  * @param vs vertical coordinates for interpolation window
1478  * @param du horizontal relative coordinate
1479  * @param dv vertical relative coordinate
1480  */
1482  const float *vec, int width, int height,
1483  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1484 {
1485  const float x = av_clipf(vec[0] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[0];
1486  const float y = av_clipf(vec[2] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[1];
1487  float uf, vf;
1488  int ui, vi;
1489 
1490  uf = (x + 1.f) * width / 2.f;
1491  vf = (y + 1.f) * height / 2.f;
1492  ui = floorf(uf);
1493  vi = floorf(vf);
1494 
1495  *du = uf - ui;
1496  *dv = vf - vi;
1497 
1498  for (int i = -1; i < 3; i++) {
1499  for (int j = -1; j < 3; j++) {
1500  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1501  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1502  }
1503  }
1504 }
1505 
1506 /**
1507  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
1508  *
1509  * @param s filter private context
1510  * @param vec coordinates on sphere
1511  * @param width frame width
1512  * @param height frame height
1513  * @param us horizontal coordinates for interpolation window
1514  * @param vs vertical coordinates for interpolation window
1515  * @param du horizontal relative coordinate
1516  * @param dv vertical relative coordinate
1517  */
1518 static void xyz_to_equirect(const V360Context *s,
1519  const float *vec, int width, int height,
1520  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1521 {
1522  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1523  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1524  float uf, vf;
1525  int ui, vi;
1526 
1527  uf = (phi / M_PI + 1.f) * width / 2.f;
1528  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1529  ui = floorf(uf);
1530  vi = floorf(vf);
1531 
1532  *du = uf - ui;
1533  *dv = vf - vi;
1534 
1535  for (int i = -1; i < 3; i++) {
1536  for (int j = -1; j < 3; j++) {
1537  us[i + 1][j + 1] = mod(ui + j, width);
1538  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1539  }
1540  }
1541 }
1542 
1543 /**
1544  * Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
1545  *
1546  * @param s filter private context
1547  * @param vec coordinates on sphere
1548  * @param width frame width
1549  * @param height frame height
1550  * @param us horizontal coordinates for interpolation window
1551  * @param vs vertical coordinates for interpolation window
1552  * @param du horizontal relative coordinate
1553  * @param dv vertical relative coordinate
1554  */
1555 static void xyz_to_mercator(const V360Context *s,
1556  const float *vec, int width, int height,
1557  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1558 {
1559  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1560  const float theta = -vec[1] * s->input_mirror_modifier[1];
1561  float uf, vf;
1562  int ui, vi;
1563 
1564  uf = (phi / M_PI + 1.f) * width / 2.f;
1565  vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f;
1566  ui = floorf(uf);
1567  vi = floorf(vf);
1568 
1569  *du = uf - ui;
1570  *dv = vf - vi;
1571 
1572  for (int i = -1; i < 3; i++) {
1573  for (int j = -1; j < 3; j++) {
1574  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1575  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1576  }
1577  }
1578 }
1579 
1580 /**
1581  * Calculate 3D coordinates on sphere for corresponding frame position in mercator format.
1582  *
1583  * @param s filter private context
1584  * @param i horizontal position on frame [0, width)
1585  * @param j vertical position on frame [0, height)
1586  * @param width frame width
1587  * @param height frame height
1588  * @param vec coordinates on sphere
1589  */
1590 static void mercator_to_xyz(const V360Context *s,
1591  int i, int j, int width, int height,
1592  float *vec)
1593 {
1594  const float phi = ((2.f * i) / width - 1.f) * M_PI + M_PI_2;
1595  const float y = ((2.f * j) / height - 1.f) * M_PI;
1596  const float div = expf(2.f * y) + 1.f;
1597 
1598  const float sin_phi = sinf(phi);
1599  const float cos_phi = cosf(phi);
1600  const float sin_theta = -2.f * expf(y) / div;
1601  const float cos_theta = -(expf(2.f * y) - 1.f) / div;
1602 
1603  vec[0] = sin_theta * cos_phi;
1604  vec[1] = cos_theta;
1605  vec[2] = sin_theta * sin_phi;
1606 }
1607 
1608 /**
1609  * Calculate frame position in ball format for corresponding 3D coordinates on sphere.
1610  *
1611  * @param s filter private context
1612  * @param vec coordinates on sphere
1613  * @param width frame width
1614  * @param height frame height
1615  * @param us horizontal coordinates for interpolation window
1616  * @param vs vertical coordinates for interpolation window
1617  * @param du horizontal relative coordinate
1618  * @param dv vertical relative coordinate
1619  */
1620 static void xyz_to_ball(const V360Context *s,
1621  const float *vec, int width, int height,
1622  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1623 {
1624  const float l = hypotf(vec[0], vec[1]);
1625  const float r = sqrtf(1.f + vec[2]) / M_SQRT2;
1626  float uf, vf;
1627  int ui, vi;
1628 
1629  uf = (1.f + r * vec[0] * s->input_mirror_modifier[0] / (l > 0.f ? l : 1.f)) * width * 0.5f;
1630  vf = (1.f - r * vec[1] * s->input_mirror_modifier[1] / (l > 0.f ? l : 1.f)) * height * 0.5f;
1631 
1632  ui = floorf(uf);
1633  vi = floorf(vf);
1634 
1635  *du = uf - ui;
1636  *dv = vf - vi;
1637 
1638  for (int i = -1; i < 3; i++) {
1639  for (int j = -1; j < 3; j++) {
1640  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1641  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1642  }
1643  }
1644 }
1645 
1646 /**
1647  * Calculate 3D coordinates on sphere for corresponding frame position in ball format.
1648  *
1649  * @param s filter private context
1650  * @param i horizontal position on frame [0, width)
1651  * @param j vertical position on frame [0, height)
1652  * @param width frame width
1653  * @param height frame height
1654  * @param vec coordinates on sphere
1655  */
1656 static void ball_to_xyz(const V360Context *s,
1657  int i, int j, int width, int height,
1658  float *vec)
1659 {
1660  const float x = (2.f * i) / width - 1.f;
1661  const float y = (2.f * j) / height - 1.f;
1662  const float l = hypotf(x, y);
1663 
1664  if (l <= 1.f) {
1665  const float z = 2.f * l * sqrtf(1.f - l * l);
1666 
1667  vec[0] = z * x / (l > 0.f ? l : 1.f);
1668  vec[1] = -z * y / (l > 0.f ? l : 1.f);
1669  vec[2] = -1.f + 2.f * l * l;
1670  } else {
1671  vec[0] = 0.f;
1672  vec[1] = -1.f;
1673  vec[2] = 0.f;
1674  }
1675 }
1676 
1677 /**
1678  * Calculate 3D coordinates on sphere for corresponding frame position in hammer format.
1679  *
1680  * @param s filter private context
1681  * @param i horizontal position on frame [0, width)
1682  * @param j vertical position on frame [0, height)
1683  * @param width frame width
1684  * @param height frame height
1685  * @param vec coordinates on sphere
1686  */
1687 static void hammer_to_xyz(const V360Context *s,
1688  int i, int j, int width, int height,
1689  float *vec)
1690 {
1691  const float x = ((2.f * i) / width - 1.f);
1692  const float y = ((2.f * j) / height - 1.f);
1693 
1694  const float xx = x * x;
1695  const float yy = y * y;
1696 
1697  const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f);
1698 
1699  const float a = M_SQRT2 * x * z;
1700  const float b = 2.f * z * z - 1.f;
1701 
1702  const float aa = a * a;
1703  const float bb = b * b;
1704 
1705  const float w = sqrtf(1.f - 2.f * yy * z * z);
1706 
1707  vec[0] = w * 2.f * a * b / (aa + bb);
1708  vec[1] = -M_SQRT2 * y * z;
1709  vec[2] = -w * (bb - aa) / (aa + bb);
1710 
1711  normalize_vector(vec);
1712 }
1713 
1714 /**
1715  * Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
1716  *
1717  * @param s filter private context
1718  * @param vec coordinates on sphere
1719  * @param width frame width
1720  * @param height frame height
1721  * @param us horizontal coordinates for interpolation window
1722  * @param vs vertical coordinates for interpolation window
1723  * @param du horizontal relative coordinate
1724  * @param dv vertical relative coordinate
1725  */
1726 static void xyz_to_hammer(const V360Context *s,
1727  const float *vec, int width, int height,
1728  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1729 {
1730  const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
1731 
1732  const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f));
1733  const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z;
1734  const float y = -vec[1] / z * s->input_mirror_modifier[1];
1735  float uf, vf;
1736  int ui, vi;
1737 
1738  uf = (x + 1.f) * width / 2.f;
1739  vf = (y + 1.f) * height / 2.f;
1740  ui = floorf(uf);
1741  vi = floorf(vf);
1742 
1743  *du = uf - ui;
1744  *dv = vf - vi;
1745 
1746  for (int i = -1; i < 3; i++) {
1747  for (int j = -1; j < 3; j++) {
1748  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1749  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1750  }
1751  }
1752 }
1753 
1754 /**
1755  * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format.
1756  *
1757  * @param s filter private context
1758  * @param i horizontal position on frame [0, width)
1759  * @param j vertical position on frame [0, height)
1760  * @param width frame width
1761  * @param height frame height
1762  * @param vec coordinates on sphere
1763  */
1764 static void sinusoidal_to_xyz(const V360Context *s,
1765  int i, int j, int width, int height,
1766  float *vec)
1767 {
1768  const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1769  const float phi = ((2.f * i) / width - 1.f) * M_PI / cosf(theta);
1770 
1771  const float sin_phi = sinf(phi);
1772  const float cos_phi = cosf(phi);
1773  const float sin_theta = sinf(theta);
1774  const float cos_theta = cosf(theta);
1775 
1776  vec[0] = cos_theta * sin_phi;
1777  vec[1] = -sin_theta;
1778  vec[2] = -cos_theta * cos_phi;
1779 
1780  normalize_vector(vec);
1781 }
1782 
1783 /**
1784  * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere.
1785  *
1786  * @param s filter private context
1787  * @param vec coordinates on sphere
1788  * @param width frame width
1789  * @param height frame height
1790  * @param us horizontal coordinates for interpolation window
1791  * @param vs vertical coordinates for interpolation window
1792  * @param du horizontal relative coordinate
1793  * @param dv vertical relative coordinate
1794  */
1795 static void xyz_to_sinusoidal(const V360Context *s,
1796  const float *vec, int width, int height,
1797  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1798 {
1799  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
1800  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] * cosf(theta);
1801  float uf, vf;
1802  int ui, vi;
1803 
1804  uf = (phi / M_PI + 1.f) * width / 2.f;
1805  vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1806  ui = floorf(uf);
1807  vi = floorf(vf);
1808 
1809  *du = uf - ui;
1810  *dv = vf - vi;
1811 
1812  for (int i = -1; i < 3; i++) {
1813  for (int j = -1; j < 3; j++) {
1814  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
1815  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1816  }
1817  }
1818 }
1819 
1820 /**
1821  * Prepare data for processing equi-angular cubemap input format.
1822  *
1823  * @param ctx filter context
1824  *
1825  * @return error code
1826  */
1828 {
1829  V360Context *s = ctx->priv;
1830 
1831  if (s->ih_flip && s->iv_flip) {
1838  } else if (s->ih_flip) {
1845  } else if (s->iv_flip) {
1852  } else {
1859  }
1860 
1861  if (s->iv_flip) {
1868  } else {
1875  }
1876 
1877  return 0;
1878 }
1879 
1880 /**
1881  * Prepare data for processing equi-angular cubemap output format.
1882  *
1883  * @param ctx filter context
1884  *
1885  * @return error code
1886  */
1888 {
1889  V360Context *s = ctx->priv;
1890 
1897 
1904 
1905  return 0;
1906 }
1907 
1908 /**
1909  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
1910  *
1911  * @param s filter private context
1912  * @param i horizontal position on frame [0, width)
1913  * @param j vertical position on frame [0, height)
1914  * @param width frame width
1915  * @param height frame height
1916  * @param vec coordinates on sphere
1917  */
1918 static void eac_to_xyz(const V360Context *s,
1919  int i, int j, int width, int height,
1920  float *vec)
1921 {
1922  const float pixel_pad = 2;
1923  const float u_pad = pixel_pad / width;
1924  const float v_pad = pixel_pad / height;
1925 
1926  int u_face, v_face, face;
1927 
1928  float l_x, l_y, l_z;
1929 
1930  float uf = (i + 0.5f) / width;
1931  float vf = (j + 0.5f) / height;
1932 
1933  // EAC has 2-pixel padding on faces except between faces on the same row
1934  // Padding pixels seems not to be stretched with tangent as regular pixels
1935  // Formulas below approximate original padding as close as I could get experimentally
1936 
1937  // Horizontal padding
1938  uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
1939  if (uf < 0.f) {
1940  u_face = 0;
1941  uf -= 0.5f;
1942  } else if (uf >= 3.f) {
1943  u_face = 2;
1944  uf -= 2.5f;
1945  } else {
1946  u_face = floorf(uf);
1947  uf = fmodf(uf, 1.f) - 0.5f;
1948  }
1949 
1950  // Vertical padding
1951  v_face = floorf(vf * 2.f);
1952  vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
1953 
1954  if (uf >= -0.5f && uf < 0.5f) {
1955  uf = tanf(M_PI_2 * uf);
1956  } else {
1957  uf = 2.f * uf;
1958  }
1959  if (vf >= -0.5f && vf < 0.5f) {
1960  vf = tanf(M_PI_2 * vf);
1961  } else {
1962  vf = 2.f * vf;
1963  }
1964 
1965  face = u_face + 3 * v_face;
1966 
1967  switch (face) {
1968  case TOP_LEFT:
1969  l_x = -1.f;
1970  l_y = -vf;
1971  l_z = -uf;
1972  break;
1973  case TOP_MIDDLE:
1974  l_x = uf;
1975  l_y = -vf;
1976  l_z = -1.f;
1977  break;
1978  case TOP_RIGHT:
1979  l_x = 1.f;
1980  l_y = -vf;
1981  l_z = uf;
1982  break;
1983  case BOTTOM_LEFT:
1984  l_x = -vf;
1985  l_y = -1.f;
1986  l_z = uf;
1987  break;
1988  case BOTTOM_MIDDLE:
1989  l_x = -vf;
1990  l_y = uf;
1991  l_z = 1.f;
1992  break;
1993  case BOTTOM_RIGHT:
1994  l_x = -vf;
1995  l_y = 1.f;
1996  l_z = -uf;
1997  break;
1998  default:
1999  av_assert0(0);
2000  }
2001 
2002  vec[0] = l_x;
2003  vec[1] = l_y;
2004  vec[2] = l_z;
2005 
2006  normalize_vector(vec);
2007 }
2008 
2009 /**
2010  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
2011  *
2012  * @param s filter private context
2013  * @param vec coordinates on sphere
2014  * @param width frame width
2015  * @param height frame height
2016  * @param us horizontal coordinates for interpolation window
2017  * @param vs vertical coordinates for interpolation window
2018  * @param du horizontal relative coordinate
2019  * @param dv vertical relative coordinate
2020  */
2021 static void xyz_to_eac(const V360Context *s,
2022  const float *vec, int width, int height,
2023  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2024 {
2025  const float pixel_pad = 2;
2026  const float u_pad = pixel_pad / width;
2027  const float v_pad = pixel_pad / height;
2028 
2029  float uf, vf;
2030  int ui, vi;
2031  int direction, face;
2032  int u_face, v_face;
2033 
2034  xyz_to_cube(s, vec, &uf, &vf, &direction);
2035 
2036  face = s->in_cubemap_face_order[direction];
2037  u_face = face % 3;
2038  v_face = face / 3;
2039 
2040  uf = M_2_PI * atanf(uf) + 0.5f;
2041  vf = M_2_PI * atanf(vf) + 0.5f;
2042 
2043  // These formulas are inversed from eac_to_xyz ones
2044  uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
2045  vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
2046 
2047  uf *= width;
2048  vf *= height;
2049 
2050  uf -= 0.5f;
2051  vf -= 0.5f;
2052 
2053  ui = floorf(uf);
2054  vi = floorf(vf);
2055 
2056  *du = uf - ui;
2057  *dv = vf - vi;
2058 
2059  for (int i = -1; i < 3; i++) {
2060  for (int j = -1; j < 3; j++) {
2061  us[i + 1][j + 1] = av_clip(ui + j, 0, width - 1);
2062  vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
2063  }
2064  }
2065 }
2066 
2067 /**
2068  * Prepare data for processing flat output format.
2069  *
2070  * @param ctx filter context
2071  *
2072  * @return error code
2073  */
2075 {
2076  V360Context *s = ctx->priv;
2077 
2078  s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
2079  s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2080 
2081  return 0;
2082 }
2083 
2084 /**
2085  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
2086  *
2087  * @param s filter private context
2088  * @param i horizontal position on frame [0, width)
2089  * @param j vertical position on frame [0, height)
2090  * @param width frame width
2091  * @param height frame height
2092  * @param vec coordinates on sphere
2093  */
2094 static void flat_to_xyz(const V360Context *s,
2095  int i, int j, int width, int height,
2096  float *vec)
2097 {
2098  const float l_x = s->flat_range[0] * (2.f * i / width - 1.f);
2099  const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
2100 
2101  vec[0] = l_x;
2102  vec[1] = l_y;
2103  vec[2] = -1.f;
2104 
2105  normalize_vector(vec);
2106 }
2107 
2108 /**
2109  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
2110  *
2111  * @param s filter private context
2112  * @param i horizontal position on frame [0, width)
2113  * @param j vertical position on frame [0, height)
2114  * @param width frame width
2115  * @param height frame height
2116  * @param vec coordinates on sphere
2117  */
2118 static void dfisheye_to_xyz(const V360Context *s,
2119  int i, int j, int width, int height,
2120  float *vec)
2121 {
2122  const float scale = 1.f + s->out_pad;
2123 
2124  const float ew = width / 2.f;
2125  const float eh = height;
2126 
2127  const int ei = i >= ew ? i - ew : i;
2128  const float m = i >= ew ? -1.f : 1.f;
2129 
2130  const float uf = ((2.f * ei) / ew - 1.f) * scale;
2131  const float vf = ((2.f * j) / eh - 1.f) * scale;
2132 
2133  const float h = hypotf(uf, vf);
2134  const float lh = h > 0.f ? h : 1.f;
2135  const float theta = m * M_PI_2 * (1.f - h);
2136 
2137  const float sin_theta = sinf(theta);
2138  const float cos_theta = cosf(theta);
2139 
2140  vec[0] = cos_theta * m * -uf / lh;
2141  vec[1] = cos_theta * -vf / lh;
2142  vec[2] = sin_theta;
2143 
2144  normalize_vector(vec);
2145 }
2146 
2147 /**
2148  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
2149  *
2150  * @param s filter private context
2151  * @param vec coordinates on sphere
2152  * @param width frame width
2153  * @param height frame height
2154  * @param us horizontal coordinates for interpolation window
2155  * @param vs vertical coordinates for interpolation window
2156  * @param du horizontal relative coordinate
2157  * @param dv vertical relative coordinate
2158  */
2159 static void xyz_to_dfisheye(const V360Context *s,
2160  const float *vec, int width, int height,
2161  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2162 {
2163  const float scale = 1.f - s->in_pad;
2164 
2165  const float ew = width / 2.f;
2166  const float eh = height;
2167 
2168  const float h = hypotf(vec[0], vec[1]);
2169  const float lh = h > 0.f ? h : 1.f;
2170  const float theta = acosf(fabsf(vec[2])) / M_PI;
2171 
2172  float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
2173  float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
2174 
2175  int ui, vi;
2176  int u_shift;
2177 
2178  if (vec[2] >= 0.f) {
2179  u_shift = 0;
2180  } else {
2181  u_shift = ceilf(ew);
2182  uf = ew - uf;
2183  }
2184 
2185  ui = floorf(uf);
2186  vi = floorf(vf);
2187 
2188  *du = uf - ui;
2189  *dv = vf - vi;
2190 
2191  for (int i = -1; i < 3; i++) {
2192  for (int j = -1; j < 3; j++) {
2193  us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width - 1);
2194  vs[i + 1][j + 1] = av_clip( vi + i, 0, height - 1);
2195  }
2196  }
2197 }
2198 
2199 /**
2200  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
2201  *
2202  * @param s filter private context
2203  * @param i horizontal position on frame [0, width)
2204  * @param j vertical position on frame [0, height)
2205  * @param width frame width
2206  * @param height frame height
2207  * @param vec coordinates on sphere
2208  */
2209 static void barrel_to_xyz(const V360Context *s,
2210  int i, int j, int width, int height,
2211  float *vec)
2212 {
2213  const float scale = 0.99f;
2214  float l_x, l_y, l_z;
2215 
2216  if (i < 4 * width / 5) {
2217  const float theta_range = M_PI_4;
2218 
2219  const int ew = 4 * width / 5;
2220  const int eh = height;
2221 
2222  const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
2223  const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
2224 
2225  const float sin_phi = sinf(phi);
2226  const float cos_phi = cosf(phi);
2227  const float sin_theta = sinf(theta);
2228  const float cos_theta = cosf(theta);
2229 
2230  l_x = cos_theta * sin_phi;
2231  l_y = -sin_theta;
2232  l_z = -cos_theta * cos_phi;
2233  } else {
2234  const int ew = width / 5;
2235  const int eh = height / 2;
2236 
2237  float uf, vf;
2238 
2239  if (j < eh) { // UP
2240  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2241  vf = 2.f * (j ) / eh - 1.f;
2242 
2243  uf /= scale;
2244  vf /= scale;
2245 
2246  l_x = uf;
2247  l_y = 1.f;
2248  l_z = -vf;
2249  } else { // DOWN
2250  uf = 2.f * (i - 4 * ew) / ew - 1.f;
2251  vf = 2.f * (j - eh) / eh - 1.f;
2252 
2253  uf /= scale;
2254  vf /= scale;
2255 
2256  l_x = uf;
2257  l_y = -1.f;
2258  l_z = vf;
2259  }
2260  }
2261 
2262  vec[0] = l_x;
2263  vec[1] = l_y;
2264  vec[2] = l_z;
2265 
2266  normalize_vector(vec);
2267 }
2268 
2269 /**
2270  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
2271  *
2272  * @param s filter private context
2273  * @param vec coordinates on sphere
2274  * @param width frame width
2275  * @param height frame height
2276  * @param us horizontal coordinates for interpolation window
2277  * @param vs vertical coordinates for interpolation window
2278  * @param du horizontal relative coordinate
2279  * @param dv vertical relative coordinate
2280  */
2281 static void xyz_to_barrel(const V360Context *s,
2282  const float *vec, int width, int height,
2283  uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
2284 {
2285  const float scale = 0.99f;
2286 
2287  const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
2288  const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
2289  const float theta_range = M_PI_4;
2290 
2291  int ew, eh;
2292  int u_shift, v_shift;
2293  float uf, vf;
2294  int ui, vi;
2295 
2296  if (theta > -theta_range && theta < theta_range) {
2297  ew = 4 * width / 5;
2298  eh = height;
2299 
2300  u_shift = s->ih_flip ? width / 5 : 0;
2301  v_shift = 0;
2302 
2303  uf = (phi / M_PI * scale + 1.f) * ew / 2.f;
2304  vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
2305  } else {
2306  ew = width / 5;
2307  eh = height / 2;
2308 
2309  u_shift = s->ih_flip ? 0 : 4 * ew;
2310 
2311  if (theta < 0.f) { // UP
2312  uf = vec[0] / vec[1];
2313  vf = -vec[2] / vec[1];
2314  v_shift = 0;
2315  } else { // DOWN
2316  uf = -vec[0] / vec[1];
2317  vf = -vec[2] / vec[1];
2318  v_shift = eh;
2319  }
2320 
2321  uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
2322  vf *= s->input_mirror_modifier[1];
2323 
2324  uf = 0.5f * ew * (uf * scale + 1.f);
2325  vf = 0.5f * eh * (vf * scale + 1.f);
2326  }
2327 
2328  ui = floorf(uf);
2329  vi = floorf(vf);
2330 
2331  *du = uf - ui;
2332  *dv = vf - vi;
2333 
2334  for (int i = -1; i < 3; i++) {
2335  for (int j = -1; j < 3; j++) {
2336  us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
2337  vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
2338  }
2339  }
2340 }
2341 
2342 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
2343 {
2344  for (int i = 0; i < 3; i++) {
2345  for (int j = 0; j < 3; j++) {
2346  float sum = 0;
2347 
2348  for (int k = 0; k < 3; k++)
2349  sum += a[i][k] * b[k][j];
2350 
2351  c[i][j] = sum;
2352  }
2353  }
2354 }
2355 
2356 /**
2357  * Calculate rotation matrix for yaw/pitch/roll angles.
2358  */
2359 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
2360  float rot_mat[3][3],
2361  const int rotation_order[3])
2362 {
2363  const float yaw_rad = yaw * M_PI / 180.f;
2364  const float pitch_rad = pitch * M_PI / 180.f;
2365  const float roll_rad = roll * M_PI / 180.f;
2366 
2367  const float sin_yaw = sinf(-yaw_rad);
2368  const float cos_yaw = cosf(-yaw_rad);
2369  const float sin_pitch = sinf(pitch_rad);
2370  const float cos_pitch = cosf(pitch_rad);
2371  const float sin_roll = sinf(roll_rad);
2372  const float cos_roll = cosf(roll_rad);
2373 
2374  float m[3][3][3];
2375  float temp[3][3];
2376 
2377  m[0][0][0] = cos_yaw; m[0][0][1] = 0; m[0][0][2] = sin_yaw;
2378  m[0][1][0] = 0; m[0][1][1] = 1; m[0][1][2] = 0;
2379  m[0][2][0] = -sin_yaw; m[0][2][1] = 0; m[0][2][2] = cos_yaw;
2380 
2381  m[1][0][0] = 1; m[1][0][1] = 0; m[1][0][2] = 0;
2382  m[1][1][0] = 0; m[1][1][1] = cos_pitch; m[1][1][2] = -sin_pitch;
2383  m[1][2][0] = 0; m[1][2][1] = sin_pitch; m[1][2][2] = cos_pitch;
2384 
2385  m[2][0][0] = cos_roll; m[2][0][1] = -sin_roll; m[2][0][2] = 0;
2386  m[2][1][0] = sin_roll; m[2][1][1] = cos_roll; m[2][1][2] = 0;
2387  m[2][2][0] = 0; m[2][2][1] = 0; m[2][2][2] = 1;
2388 
2389  multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
2390  multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
2391 }
2392 
2393 /**
2394  * Rotate vector with given rotation matrix.
2395  *
2396  * @param rot_mat rotation matrix
2397  * @param vec vector
2398  */
2399 static inline void rotate(const float rot_mat[3][3],
2400  float *vec)
2401 {
2402  const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
2403  const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
2404  const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
2405 
2406  vec[0] = x_tmp;
2407  vec[1] = y_tmp;
2408  vec[2] = z_tmp;
2409 }
2410 
2411 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
2412  float *modifier)
2413 {
2414  modifier[0] = h_flip ? -1.f : 1.f;
2415  modifier[1] = v_flip ? -1.f : 1.f;
2416  modifier[2] = d_flip ? -1.f : 1.f;
2417 }
2418 
2419 static inline void mirror(const float *modifier, float *vec)
2420 {
2421  vec[0] *= modifier[0];
2422  vec[1] *= modifier[1];
2423  vec[2] *= modifier[2];
2424 }
2425 
2426 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
2427 {
2428  s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2429  s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
2430  if (!s->u[p] || !s->v[p])
2431  return AVERROR(ENOMEM);
2432  if (sizeof_ker) {
2433  s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
2434  if (!s->ker[p])
2435  return AVERROR(ENOMEM);
2436  }
2437 
2438  return 0;
2439 }
2440 
2441 static void fov_from_dfov(V360Context *s, float w, float h)
2442 {
2443  const float da = tanf(0.5 * FFMIN(s->d_fov, 359.f) * M_PI / 180.f);
2444  const float d = hypotf(w, h);
2445 
2446  s->h_fov = atan2f(da * w, d) * 360.f / M_PI;
2447  s->v_fov = atan2f(da * h, d) * 360.f / M_PI;
2448 
2449  if (s->h_fov < 0.f)
2450  s->h_fov += 360.f;
2451  if (s->v_fov < 0.f)
2452  s->v_fov += 360.f;
2453 }
2454 
2455 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
2456 {
2457  outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
2458  outw[0] = outw[3] = w;
2459  outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
2460  outh[0] = outh[3] = h;
2461 }
2462 
2463 // Calculate remap data
2464 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
2465 {
2466  V360Context *s = ctx->priv;
2467 
2468  for (int p = 0; p < s->nb_allocated; p++) {
2469  const int width = s->pr_width[p];
2470  const int uv_linesize = s->uv_linesize[p];
2471  const int height = s->pr_height[p];
2472  const int in_width = s->inplanewidth[p];
2473  const int in_height = s->inplaneheight[p];
2474  const int slice_start = (height * jobnr ) / nb_jobs;
2475  const int slice_end = (height * (jobnr + 1)) / nb_jobs;
2476  float du, dv;
2477  float vec[3];
2478  XYRemap rmap;
2479 
2480  for (int j = slice_start; j < slice_end; j++) {
2481  for (int i = 0; i < width; i++) {
2482  uint16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
2483  uint16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
2484  int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
2485 
2486  if (s->out_transpose)
2487  s->out_transform(s, j, i, height, width, vec);
2488  else
2489  s->out_transform(s, i, j, width, height, vec);
2490  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2491  rotate(s->rot_mat, vec);
2492  av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
2493  normalize_vector(vec);
2494  mirror(s->output_mirror_modifier, vec);
2495  if (s->in_transpose)
2496  s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
2497  else
2498  s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
2499  av_assert1(!isnan(du) && !isnan(dv));
2500  s->calculate_kernel(du, dv, &rmap, u, v, ker);
2501  }
2502  }
2503  }
2504 
2505  return 0;
2506 }
2507 
2508 static int config_output(AVFilterLink *outlink)
2509 {
2510  AVFilterContext *ctx = outlink->src;
2511  AVFilterLink *inlink = ctx->inputs[0];
2512  V360Context *s = ctx->priv;
2514  const int depth = desc->comp[0].depth;
2515  int sizeof_uv;
2516  int sizeof_ker;
2517  int err;
2518  int h, w;
2519  int in_offset_h, in_offset_w;
2520  int out_offset_h, out_offset_w;
2521  float hf, wf;
2522  int (*prepare_out)(AVFilterContext *ctx);
2523 
2524  s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
2525  s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
2526 
2527  switch (s->interp) {
2528  case NEAREST:
2530  s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
2531  s->elements = 1;
2532  sizeof_uv = sizeof(uint16_t) * s->elements;
2533  sizeof_ker = 0;
2534  break;
2535  case BILINEAR:
2537  s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
2538  s->elements = 2 * 2;
2539  sizeof_uv = sizeof(uint16_t) * s->elements;
2540  sizeof_ker = sizeof(uint16_t) * s->elements;
2541  break;
2542  case BICUBIC:
2544  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2545  s->elements = 4 * 4;
2546  sizeof_uv = sizeof(uint16_t) * s->elements;
2547  sizeof_ker = sizeof(uint16_t) * s->elements;
2548  break;
2549  case LANCZOS:
2551  s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2552  s->elements = 4 * 4;
2553  sizeof_uv = sizeof(uint16_t) * s->elements;
2554  sizeof_ker = sizeof(uint16_t) * s->elements;
2555  break;
2556  default:
2557  av_assert0(0);
2558  }
2559 
2560  ff_v360_init(s, depth);
2561 
2562  for (int order = 0; order < NB_RORDERS; order++) {
2563  const char c = s->rorder[order];
2564  int rorder;
2565 
2566  if (c == '\0') {
2567  av_log(ctx, AV_LOG_ERROR,
2568  "Incomplete rorder option. Direction for all 3 rotation orders should be specified.\n");
2569  return AVERROR(EINVAL);
2570  }
2571 
2572  rorder = get_rorder(c);
2573  if (rorder == -1) {
2574  av_log(ctx, AV_LOG_ERROR,
2575  "Incorrect rotation order symbol '%c' in rorder option.\n", c);
2576  return AVERROR(EINVAL);
2577  }
2578 
2579  s->rotation_order[order] = rorder;
2580  }
2581 
2582  switch (s->in_stereo) {
2583  case STEREO_2D:
2584  w = inlink->w;
2585  h = inlink->h;
2586  in_offset_w = in_offset_h = 0;
2587  break;
2588  case STEREO_SBS:
2589  w = inlink->w / 2;
2590  h = inlink->h;
2591  in_offset_w = w;
2592  in_offset_h = 0;
2593  break;
2594  case STEREO_TB:
2595  w = inlink->w;
2596  h = inlink->h / 2;
2597  in_offset_w = 0;
2598  in_offset_h = h;
2599  break;
2600  default:
2601  av_assert0(0);
2602  }
2603 
2604  set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
2605  set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
2606 
2607  s->in_width = s->inplanewidth[0];
2608  s->in_height = s->inplaneheight[0];
2609 
2610  if (s->in_transpose)
2611  FFSWAP(int, s->in_width, s->in_height);
2612 
2613  switch (s->in) {
2614  case EQUIRECTANGULAR:
2616  err = 0;
2617  wf = w;
2618  hf = h;
2619  break;
2620  case CUBEMAP_3_2:
2622  err = prepare_cube_in(ctx);
2623  wf = w / 3.f * 4.f;
2624  hf = h;
2625  break;
2626  case CUBEMAP_1_6:
2628  err = prepare_cube_in(ctx);
2629  wf = w * 4.f;
2630  hf = h / 3.f;
2631  break;
2632  case CUBEMAP_6_1:
2634  err = prepare_cube_in(ctx);
2635  wf = w / 3.f * 2.f;
2636  hf = h * 2.f;
2637  break;
2638  case EQUIANGULAR:
2639  s->in_transform = xyz_to_eac;
2640  err = prepare_eac_in(ctx);
2641  wf = w;
2642  hf = h / 9.f * 8.f;
2643  break;
2644  case FLAT:
2645  av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n");
2646  return AVERROR(EINVAL);
2647  case DUAL_FISHEYE:
2649  err = 0;
2650  wf = w;
2651  hf = h;
2652  break;
2653  case BARREL:
2655  err = 0;
2656  wf = w / 5.f * 4.f;
2657  hf = h;
2658  break;
2659  case STEREOGRAPHIC:
2661  err = 0;
2662  wf = w;
2663  hf = h / 2.f;
2664  break;
2665  case MERCATOR:
2667  err = 0;
2668  wf = w;
2669  hf = h / 2.f;
2670  break;
2671  case BALL:
2673  err = 0;
2674  wf = w;
2675  hf = h / 2.f;
2676  break;
2677  case HAMMER:
2679  err = 0;
2680  wf = w;
2681  hf = h;
2682  break;
2683  case SINUSOIDAL:
2685  err = 0;
2686  wf = w;
2687  hf = h;
2688  break;
2689  default:
2690  av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
2691  return AVERROR_BUG;
2692  }
2693 
2694  if (err != 0) {
2695  return err;
2696  }
2697 
2698  switch (s->out) {
2699  case EQUIRECTANGULAR:
2701  prepare_out = NULL;
2702  w = roundf(wf);
2703  h = roundf(hf);
2704  break;
2705  case CUBEMAP_3_2:
2707  prepare_out = prepare_cube_out;
2708  w = roundf(wf / 4.f * 3.f);
2709  h = roundf(hf);
2710  break;
2711  case CUBEMAP_1_6:
2713  prepare_out = prepare_cube_out;
2714  w = roundf(wf / 4.f);
2715  h = roundf(hf * 3.f);
2716  break;
2717  case CUBEMAP_6_1:
2719  prepare_out = prepare_cube_out;
2720  w = roundf(wf / 2.f * 3.f);
2721  h = roundf(hf / 2.f);
2722  break;
2723  case EQUIANGULAR:
2725  prepare_out = prepare_eac_out;
2726  w = roundf(wf);
2727  h = roundf(hf / 8.f * 9.f);
2728  break;
2729  case FLAT:
2731  prepare_out = prepare_flat_out;
2732  w = roundf(wf);
2733  h = roundf(hf);
2734  break;
2735  case DUAL_FISHEYE:
2737  prepare_out = NULL;
2738  w = roundf(wf);
2739  h = roundf(hf);
2740  break;
2741  case BARREL:
2743  prepare_out = NULL;
2744  w = roundf(wf / 4.f * 5.f);
2745  h = roundf(hf);
2746  break;
2747  case STEREOGRAPHIC:
2749  prepare_out = prepare_stereographic_out;
2750  w = roundf(wf);
2751  h = roundf(hf * 2.f);
2752  break;
2753  case MERCATOR:
2755  prepare_out = NULL;
2756  w = roundf(wf);
2757  h = roundf(hf * 2.f);
2758  break;
2759  case BALL:
2761  prepare_out = NULL;
2762  w = roundf(wf);
2763  h = roundf(hf * 2.f);
2764  break;
2765  case HAMMER:
2767  prepare_out = NULL;
2768  w = roundf(wf);
2769  h = roundf(hf);
2770  break;
2771  case SINUSOIDAL:
2773  prepare_out = NULL;
2774  w = roundf(wf);
2775  h = roundf(hf);
2776  break;
2777  default:
2778  av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
2779  return AVERROR_BUG;
2780  }
2781 
2782  // Override resolution with user values if specified
2783  if (s->width > 0 && s->height > 0) {
2784  w = s->width;
2785  h = s->height;
2786  } else if (s->width > 0 || s->height > 0) {
2787  av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
2788  return AVERROR(EINVAL);
2789  } else {
2790  if (s->out_transpose)
2791  FFSWAP(int, w, h);
2792 
2793  if (s->in_transpose)
2794  FFSWAP(int, w, h);
2795  }
2796 
2797  if (s->d_fov > 0.f)
2798  fov_from_dfov(s, w, h);
2799 
2800  if (prepare_out) {
2801  err = prepare_out(ctx);
2802  if (err != 0)
2803  return err;
2804  }
2805 
2806  set_dimensions(s->pr_width, s->pr_height, w, h, desc);
2807 
2808  s->out_width = s->pr_width[0];
2809  s->out_height = s->pr_height[0];
2810 
2811  if (s->out_transpose)
2812  FFSWAP(int, s->out_width, s->out_height);
2813 
2814  switch (s->out_stereo) {
2815  case STEREO_2D:
2816  out_offset_w = out_offset_h = 0;
2817  break;
2818  case STEREO_SBS:
2819  out_offset_w = w;
2820  out_offset_h = 0;
2821  w *= 2;
2822  break;
2823  case STEREO_TB:
2824  out_offset_w = 0;
2825  out_offset_h = h;
2826  h *= 2;
2827  break;
2828  default:
2829  av_assert0(0);
2830  }
2831 
2832  set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
2833  set_dimensions(s->planewidth, s->planeheight, w, h, desc);
2834 
2835  for (int i = 0; i < 4; i++)
2836  s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
2837 
2838  outlink->h = h;
2839  outlink->w = w;
2840 
2842 
2843  if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
2844  s->nb_allocated = 1;
2845  s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
2846  } else {
2847  s->nb_allocated = 2;
2848  s->map[0] = 0;
2849  s->map[1] = s->map[2] = 1;
2850  s->map[3] = 0;
2851  }
2852 
2853  for (int i = 0; i < s->nb_allocated; i++)
2854  allocate_plane(s, sizeof_uv, sizeof_ker, i);
2855 
2858 
2859  ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2860 
2861  return 0;
2862 }
2863 
2865 {
2866  AVFilterContext *ctx = inlink->dst;
2867  AVFilterLink *outlink = ctx->outputs[0];
2868  V360Context *s = ctx->priv;
2869  AVFrame *out;
2870  ThreadData td;
2871 
2872  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
2873  if (!out) {
2874  av_frame_free(&in);
2875  return AVERROR(ENOMEM);
2876  }
2877  av_frame_copy_props(out, in);
2878 
2879  td.in = in;
2880  td.out = out;
2881 
2882  ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2883 
2884  av_frame_free(&in);
2885  return ff_filter_frame(outlink, out);
2886 }
2887 
2889 {
2890  V360Context *s = ctx->priv;
2891 
2892  for (int p = 0; p < s->nb_allocated; p++) {
2893  av_freep(&s->u[p]);
2894  av_freep(&s->v[p]);
2895  av_freep(&s->ker[p]);
2896  }
2897 }
2898 
2899 static const AVFilterPad inputs[] = {
2900  {
2901  .name = "default",
2902  .type = AVMEDIA_TYPE_VIDEO,
2903  .filter_frame = filter_frame,
2904  },
2905  { NULL }
2906 };
2907 
2908 static const AVFilterPad outputs[] = {
2909  {
2910  .name = "default",
2911  .type = AVMEDIA_TYPE_VIDEO,
2912  .config_props = config_output,
2913  },
2914  { NULL }
2915 };
2916 
2918  .name = "v360",
2919  .description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
2920  .priv_size = sizeof(V360Context),
2921  .uninit = uninit,
2923  .inputs = inputs,
2924  .outputs = outputs,
2925  .priv_class = &v360_class,
2927 };
Definition: v360.h:42
Axis +Z.
Definition: v360.h:73
#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:2342
static const AVOption v360_options[]
Definition: vf_v360.c:56
#define AV_PIX_FMT_YUVA422P16
Definition: pixfmt.h:430
static void 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:2094
AVFrame * out
Definition: af_adeclick.c:488
#define AV_PIX_FMT_YUV440P10
Definition: pixfmt.h:389
uint16_t v[4][4]
Definition: v360.h:94
static void 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:1068
#define AV_PIX_FMT_YUVA422P9
Definition: pixfmt.h:422
static void xyz_to_barrel(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_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:2281
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2522
int nb_planes
Definition: v360.h:145
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
#define TOP_LEFT
Definition: movtextdec.c:47
int in_transpose
Definition: v360.h:124
AVOption.
Definition: opt.h:246
float input_mirror_modifier[2]
Definition: v360.h:131
#define AV_PIX_FMT_YUVA420P10
Definition: pixfmt.h:424
int h_flip
Definition: v360.h:123
#define AV_PIX_FMT_YUV444P14
Definition: pixfmt.h:397
static void calculate_lanczos_coeffs(float t, float *coeffs)
Calculate 1-dimensional lanczos coefficients.
Definition: vf_v360.c:419
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:407
static int prepare_stereographic_out(AVFilterContext *ctx)
Prepare data for processing stereographic output format.
Definition: vf_v360.c:1434
#define AV_PIX_FMT_YUVA422P10
Definition: pixfmt.h:425
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
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2562
Main libavfilter public API header.
else temp
Definition: vf_mcdeint.c:256
int elements
Definition: v360.h:147
static int prepare_eac_out(AVFilterContext *ctx)
Prepare data for processing equi-angular cubemap output format.
Definition: vf_v360.c:1887
int out_height
Definition: v360.h:135
static void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
Definition: vf_v360.c:682
static void lanczos_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for lanczos interpolation.
Definition: vf_v360.c:448
static void 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:1918
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
static void rotate_cube_face(float *uf, float *vf, int rotation)
Definition: vf_v360.c:656
int in
Definition: v360.h:100
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:403
static int get_rorder(char c)
Convert char to corresponding rotation order.
Definition: vf_v360.c:531
#define us(width, name, range_min, range_max, subs,...)
Definition: cbs_h2645.c:266
Definition: v360.h:37
#define AV_PIX_FMT_GRAY9
Definition: pixfmt.h:367
static void 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:1687
#define AV_PIX_FMT_YUV420P12
Definition: pixfmt.h:391
int inplanewidth[4]
Definition: v360.h:143
int out_offset_w[4]
Definition: v360.h:140
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 set_mirror_modifier(int h_flip, int v_flip, int d_flip, float *modifier)
Definition: vf_v360.c:2411
static void bicubic_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bicubic interpolation.
Definition: vf_v360.c:395
char * in_frot
Definition: v360.h:105
int pr_height[4]
Definition: v360.h:137
static int config_output(AVFilterLink *outlink)
Definition: vf_v360.c:2508
uint8_t log2_chroma_w
Amount to shift the luma width right to find the chroma width.
Definition: pixdesc.h:92
static void xyz_to_equirect(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1518
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:244
static void xyz_to_mercator(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in mercator format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1555
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:368
Axis +Y.
Definition: v360.h:70
void(* out_transform)(const struct V360Context *s, int i, int j, int width, int height, float *vec)
Definition: v360.h:157
const char * name
Pad name.
Definition: internal.h:60
#define AV_PIX_FMT_GRAY12
Definition: pixfmt.h:369
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:346
float pitch
Definition: v360.h:120
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1093
float roll
Definition: v360.h:120
static void xyz_to_cube6x1(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1334
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
float yaw
Definition: v360.h:120
#define av_cold
Definition: attributes.h:82
AVOptions.
int out_transpose
Definition: v360.h:124
int planeheight[4]
Definition: v360.h:142
static void bilinear_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Calculate kernel for bilinear interpolation.
Definition: vf_v360.c:352
static void xyz_to_eac(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_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:2021
#define f(width, name)
Definition: cbs_vp9.c:255
Axis -Y.
Definition: v360.h:71
static void 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:1192
char * out_frot
Definition: v360.h:106
static void 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:1410
#define u(width, name, range_min, range_max)
Definition: cbs_h2645.c:252
#define AV_PIX_FMT_YUVA420P9
Definition: pixfmt.h:421
Definition: v360.h:88
#define cosf(x)
Definition: libm.h:78
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:402
int height
Definition: vf_avgblur.c:61
static void xyz_to_dfisheye(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_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:2159
static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
Definition: vf_v360.c:2426
static void 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:1590
#define atanf(x)
Definition: libm.h:40
AVFilter ff_vf_v360
Definition: vf_v360.c:2917
static void 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:2209
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:102
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
#define lrintf(x)
Definition: libm_mips.h:70
int width
Definition: v360.h:102
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:400
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:734
#define AV_PIX_FMT_YUV422P12
Definition: pixfmt.h:392
#define AV_PIX_FMT_YUVA420P16
Definition: pixfmt.h:429
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:798
#define FFALIGN(x, a)
Definition: macros.h:48
int rotation_order[3]
Definition: v360.h:113
#define av_log(a,...)
A filter pad used for either input or output.
Definition: internal.h:54
#define expf(x)
Definition: libm.h:283
int fin_pad
Definition: v360.h:118
static void xyz_to_sinusoidal(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1795
int in_stereo
Definition: v360.h:115
void ff_v360_init(V360Context *s, int depth)
Definition: vf_v360.c:303
planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
Definition: pixfmt.h:176
const AVS_VideoInfo * vi
Definition: avisynth_c.h:887
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
void(* in_transform)(const struct V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Definition: v360.h:153
#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:86
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
static void mirror(const float *modifier, float *vec)
Definition: vf_v360.c:2419
static void 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:1764
int out_offset_h[4]
Definition: v360.h:140
#define DEFINE_REMAP1_LINE(bits, div)
Definition: vf_v360.c:203
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:202
#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:374
#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
static void xyz_to_stereographic(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in stereographic format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1481
const char * r
Definition: vf_curves.c:114
static void xyz_to_hammer(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1726
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
uint16_t * u[2]
Definition: v360.h:149
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:431
const char * arg
Definition: jacosubdec.c:66
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:408
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:1827
Definition: v360.h:81
static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
Definition: vf_v360.c:2455
#define AV_PIX_FMT_YUV444P10
Definition: pixfmt.h:390
Definition: v360.h:50
int in_offset_w[4]
Definition: v360.h:139
int out
Definition: v360.h:100
int out_stereo
Definition: v360.h:115
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:409
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition: pixfmt.h:70
Definition: v360.h:52
int out_cubemap_face_rotation[6]
Definition: v360.h:112
int16_t * ker[2]
Definition: v360.h:150
int in_offset_h[4]
Definition: v360.h:139
#define AV_PIX_FMT_YUV422P9
Definition: pixfmt.h:385
unsigned map[4]
Definition: v360.h:151
int ih_flip
Definition: v360.h:122
int interp
Definition: v360.h:101
int in_height
Definition: v360.h:134
#define OFFSET(x)
Definition: vf_v360.c:53
Definition: v360.h:79
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:406
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:371
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:53
static void nearest_kernel(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Save nearest pixel coordinates for remapping.
Definition: vf_v360.c:332
float in_pad
Definition: v360.h:117
#define FFMIN(a, b)
Definition: common.h:96
AVFrame * d
float out_pad
Definition: v360.h:117
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:2464
#define AV_PIX_FMT_YUVA444P12
Definition: pixfmt.h:428
Definition: v360.h:26
static int query_formats(AVFilterContext *ctx)
Definition: vf_v360.c:133
#define M_PI_2
Definition: mathematics.h:55
int inplaneheight[4]
Definition: v360.h:143
AVFrame * m
Definition: v360.h:51
AVFormatContext * ctx
Definition: movenc.c:48
Definition: v360.h:53
AVFILTER_DEFINE_CLASS(v360)
int in_cubemap_face_order[6]
Definition: v360.h:109
#define AV_PIX_FMT_YUVA444P10
Definition: pixfmt.h:426
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:876
static const AVFilterPad outputs[]
Definition: vf_v360.c:2908
int(* remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: v360.h:164
#define AV_PIX_FMT_YUV444P9
Definition: pixfmt.h:386
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:405
if(ret)
#define AV_PIX_FMT_YUV420P16
Definition: pixfmt.h:398
float v_fov
Definition: v360.h:126
float output_mirror_modifier[3]
Definition: v360.h:132
Definition: v360.h:87
#define sinf(x)
Definition: libm.h:419
#define DEFINE_REMAP_LINE(ws, bits, div)
Definition: vf_v360.c:272
#define TOP_RIGHT
Definition: movtextdec.c:49
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_v360.c:2888
#define AV_PIX_FMT_YUV420P14
Definition: pixfmt.h:395
static int prepare_flat_out(AVFilterContext *ctx)
Prepare data for processing flat output format.
Definition: vf_v360.c:2074
int planewidth[4]
Definition: v360.h:142
int out_width
Definition: v360.h:135
Used for passing data between threads.
Definition: dsddec.c:64
int in_cubemap_face_rotation[6]
Definition: v360.h:111
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 int mod(int a, int b)
Modulo operation with only positive remainders.
Definition: vf_v360.c:474
int nb_allocated
Definition: v360.h:146
#define AV_PIX_FMT_GRAY14
Definition: pixfmt.h:370
Definition: v360.h:78
static int prepare_cube_in(AVFilterContext *ctx)
Prepare data for processing cubemap input format.
Definition: vf_v360.c:555
int d_flip
Definition: v360.h:123
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_v360.c:2864
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
static void fov_from_dfov(V360Context *s, float w, float h)
Definition: vf_v360.c:2441
int out_cubemap_direction_order[6]
Definition: v360.h:110
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:387
planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
Definition: pixfmt.h:72
static void 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:2118
static const AVFilterPad inputs[]
Definition: vf_v360.c:2899
Definition: v360.h:92
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:2359
#define isnan(x)
Definition: libm.h:340
AVFrame * b
int v_flip
Definition: v360.h:123
#define DEFINE_REMAP(ws, bits)
Generate remapping function with a given window size and pixel depth.
Definition: vf_v360.c:226
const char * name
Filter name.
Definition: avfilter.h:148
#define AV_PIX_FMT_YUV440P12
Definition: pixfmt.h:393
#define AV_PIX_FMT_YUV420P9
Definition: pixfmt.h:384
char * in_forder
Definition: v360.h:103
static int prepare_cube_out(AVFilterContext *ctx)
Prepare data for processing cubemap output format.
Definition: vf_v360.c:609
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:350
int outw
Definition: vf_rotate.c:88
static av_always_inline av_const float roundf(float x)
Definition: libm.h:451
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:275
float flat_range[2]
Definition: v360.h:127
#define AV_PIX_FMT_YUV422P14
Definition: pixfmt.h:396
int pr_width[4]
Definition: v360.h:137
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:404
static void 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:1454
#define flags(name, subs,...)
Definition: cbs_av1.c:561
AVFilterInternal * internal
An opaque struct for libavfilter internal use.
Definition: avfilter.h:378
#define AV_PIX_FMT_YUV422P10
Definition: pixfmt.h:388
float rot_mat[3][3]
Definition: v360.h:129
int uv_linesize[4]
Definition: v360.h:144
#define AV_PIX_FMT_YUV444P12
Definition: pixfmt.h:394
uint16_t * v[2]
Definition: v360.h:149
#define M_SQRT2
Definition: mathematics.h:61
static void xyz_to_cube1x6(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1256
float h_fov
Definition: v360.h:126
void ff_v360_init_x86(V360Context *s, int depth)
Definition: vf_v360_init.c:41
int
static void normalize_vector(float *vec)
Normalize vector.
Definition: vf_v360.c:713
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:2399
Definition: v360.h:80
int iv_flip
Definition: v360.h:122
int fout_pad
Definition: v360.h:118
static int get_rotation(char c)
Convert char to corresponding rotation angle.
Definition: vf_v360.c:512
planar GBRA 4:4:4:4 32bpp
Definition: pixfmt.h:215
#define AV_PIX_FMT_YUVA444P9
Definition: pixfmt.h:423
#define ui(width, name)
Definition: cbs_mpeg2.c:43
Definition: v360.h:64
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:104
avfilter_execute_func * execute
Definition: internal.h:155
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2036
static void 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:1223
static void xyz_to_ball(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in ball format for corresponding 3D coordinates on sphere.
Definition: vf_v360.c:1620
Axis -Z.
Definition: v360.h:72
static int get_direction(char c)
Convert char to corresponding direction.
Definition: vf_v360.c:488
const AVPixFmtDescriptor * desc
Definition: vf_tonemap.c:196
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
uint16_t u[4][4]
Definition: v360.h:93
An instance of a filter.
Definition: avfilter.h:338
#define RIGHT
Definition: cdgraphics.c:167
static void xyz_to_cube3x2(const V360Context *s, const float *vec, int width, int height, uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere...
Definition: vf_v360.c:1105
#define av_freep(p)
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:1082
static void 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:1656
#define FFSWAP(type, a, b)
Definition: common.h:99
int outh
Definition: vf_rotate.c:88
#define LEFT
Definition: cdgraphics.c:166
AVFilterLink * inlink
Definition: vf_blend.c:56
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:107
int depth
Number of bits in the component.
Definition: pixdesc.h:58
AVFrame * a
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
float d_fov
Definition: v360.h:126
int in_width
Definition: v360.h:134
#define AV_PIX_FMT_YUV422P16
Definition: pixfmt.h:399
void(* calculate_kernel)(float du, float dv, const XYRemap *rmap, uint16_t *u, uint16_t *v, int16_t *ker)
Definition: v360.h:161
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:654
#define AV_PIX_FMT_YUVA422P12
Definition: pixfmt.h:427
static uint8_t tmp[11]
Definition: aes_ctr.c:26