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