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.

3471 lines
113KB

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