You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4192 lines
135KB

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