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