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