]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_v360.c
avfilter/vf_v360: add partial size setup for flat
[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 h   = hypotf(vec[0], vec[1]);
2488     const float lh  = h > 0.f ? h : 1.f;
2489     const float phi = atan2f(h, -vec[2]) / M_PI;
2490
2491     float uf =  vec[0] / lh * phi * s->input_mirror_modifier[0] / s->iflat_range[0];
2492     float vf = -vec[1] / lh * phi * s->input_mirror_modifier[1] / s->iflat_range[1];
2493
2494     const int visible = hypotf(uf, vf) <= 0.5f;
2495     int ui, vi;
2496
2497     uf = (uf + 0.5f) * width;
2498     vf = (vf + 0.5f) * height;
2499
2500     ui = floorf(uf);
2501     vi = floorf(vf);
2502
2503     *du = visible ? uf - ui : 0.f;
2504     *dv = visible ? vf - vi : 0.f;
2505
2506     for (int i = 0; i < 4; i++) {
2507         for (int j = 0; j < 4; j++) {
2508             us[i][j] = visible ? av_clip(ui + j - 1, 0, width  - 1) : 0;
2509             vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2510         }
2511     }
2512
2513     return visible;
2514 }
2515
2516 /**
2517  * Calculate 3D coordinates on sphere for corresponding frame position in pannini format.
2518  *
2519  * @param s filter private context
2520  * @param i horizontal position on frame [0, width)
2521  * @param j vertical position on frame [0, height)
2522  * @param width frame width
2523  * @param height frame height
2524  * @param vec coordinates on sphere
2525  */
2526 static int pannini_to_xyz(const V360Context *s,
2527                           int i, int j, int width, int height,
2528                           float *vec)
2529 {
2530     const float uf = ((2.f * i + 1.f) / width  - 1.f);
2531     const float vf = ((2.f * j + 1.f) / height - 1.f);
2532
2533     const float d = s->h_fov;
2534     const float k = uf * uf / ((d + 1.f) * (d + 1.f));
2535     const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f);
2536     const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
2537     const float S = (d + 1.f) / (d + clon);
2538     const float lon = -(M_PI + atan2f(uf, S * clon));
2539     const float lat = -atan2f(vf, S);
2540
2541     vec[0] = sinf(lon) * cosf(lat);
2542     vec[1] = sinf(lat);
2543     vec[2] = cosf(lon) * cosf(lat);
2544
2545     normalize_vector(vec);
2546
2547     return 1;
2548 }
2549
2550 /**
2551  * Prepare data for processing cylindrical output format.
2552  *
2553  * @param ctx filter context
2554  *
2555  * @return error code
2556  */
2557 static int prepare_cylindrical_out(AVFilterContext *ctx)
2558 {
2559     V360Context *s = ctx->priv;
2560
2561     s->flat_range[0] = M_PI * s->h_fov / 360.f;
2562     s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
2563
2564     return 0;
2565 }
2566
2567 /**
2568  * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical format.
2569  *
2570  * @param s filter private context
2571  * @param i horizontal position on frame [0, width)
2572  * @param j vertical position on frame [0, height)
2573  * @param width frame width
2574  * @param height frame height
2575  * @param vec coordinates on sphere
2576  */
2577 static int cylindrical_to_xyz(const V360Context *s,
2578                               int i, int j, int width, int height,
2579                               float *vec)
2580 {
2581     const float uf = s->flat_range[0] * ((2.f * i + 1.f) / width  - 1.f);
2582     const float vf = s->flat_range[1] * ((2.f * j + 1.f) / height - 1.f);
2583
2584     const float phi   = uf;
2585     const float theta = atanf(vf);
2586
2587     const float sin_phi   = sinf(phi);
2588     const float cos_phi   = cosf(phi);
2589     const float sin_theta = sinf(theta);
2590     const float cos_theta = cosf(theta);
2591
2592     vec[0] =  cos_theta * sin_phi;
2593     vec[1] = -sin_theta;
2594     vec[2] = -cos_theta * cos_phi;
2595
2596     normalize_vector(vec);
2597
2598     return 1;
2599 }
2600
2601 /**
2602  * Prepare data for processing cylindrical input format.
2603  *
2604  * @param ctx filter context
2605  *
2606  * @return error code
2607  */
2608 static int prepare_cylindrical_in(AVFilterContext *ctx)
2609 {
2610     V360Context *s = ctx->priv;
2611
2612     s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
2613     s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
2614
2615     return 0;
2616 }
2617
2618 /**
2619  * Calculate frame position in cylindrical format for corresponding 3D coordinates on sphere.
2620  *
2621  * @param s filter private context
2622  * @param vec coordinates on sphere
2623  * @param width frame width
2624  * @param height frame height
2625  * @param us horizontal coordinates for interpolation window
2626  * @param vs vertical coordinates for interpolation window
2627  * @param du horizontal relative coordinate
2628  * @param dv vertical relative coordinate
2629  */
2630 static int xyz_to_cylindrical(const V360Context *s,
2631                               const float *vec, int width, int height,
2632                               int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2633 {
2634     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0];
2635     const float theta = atan2f(-vec[1], hypotf(vec[0], vec[2])) * s->input_mirror_modifier[1] / s->iflat_range[1];
2636     int visible, ui, vi;
2637     float uf, vf;
2638
2639     uf = (phi + 1.f) * (width - 1) / 2.f;
2640     vf = (tanf(theta) + 1.f) * height / 2.f;
2641     ui = floorf(uf);
2642     vi = floorf(vf);
2643
2644     visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
2645               theta <=  M_PI * s->iv_fov / 180.f &&
2646               theta >= -M_PI * s->iv_fov / 180.f;
2647
2648     *du = uf - ui;
2649     *dv = vf - vi;
2650
2651     for (int i = 0; i < 4; i++) {
2652         for (int j = 0; j < 4; j++) {
2653             us[i][j] = visible ? av_clip(ui + j - 1, 0, width  - 1) : 0;
2654             vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
2655         }
2656     }
2657
2658     return visible;
2659 }
2660
2661 /**
2662  * Calculate 3D coordinates on sphere for corresponding frame position in perspective format.
2663  *
2664  * @param s filter private context
2665  * @param i horizontal position on frame [0, width)
2666  * @param j vertical position on frame [0, height)
2667  * @param width frame width
2668  * @param height frame height
2669  * @param vec coordinates on sphere
2670  */
2671 static int perspective_to_xyz(const V360Context *s,
2672                               int i, int j, int width, int height,
2673                               float *vec)
2674 {
2675     const float uf = ((2.f * i + 1.f) / width  - 1.f);
2676     const float vf = ((2.f * j + 1.f) / height - 1.f);
2677     const float rh = hypotf(uf, vf);
2678     const float sinzz = 1.f - rh * rh;
2679     const float h = 1.f + s->v_fov;
2680     const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h);
2681     const float sinz2 = sinz * sinz;
2682
2683     if (sinz2 <= 1.f) {
2684         const float cosz = sqrtf(1.f - sinz2);
2685
2686         const float theta = asinf(cosz);
2687         const float phi   = atan2f(uf, vf);
2688
2689         const float sin_phi   = sinf(phi);
2690         const float cos_phi   = cosf(phi);
2691         const float sin_theta = sinf(theta);
2692         const float cos_theta = cosf(theta);
2693
2694         vec[0] =  cos_theta * sin_phi;
2695         vec[1] =  sin_theta;
2696         vec[2] = -cos_theta * cos_phi;
2697     } else {
2698         vec[0] =  0.f;
2699         vec[1] = -1.f;
2700         vec[2] =  0.f;
2701         return 0;
2702     }
2703
2704     normalize_vector(vec);
2705     return 1;
2706 }
2707
2708 /**
2709  * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron format.
2710  *
2711  * @param s filter private context
2712  * @param i horizontal position on frame [0, width)
2713  * @param j vertical position on frame [0, height)
2714  * @param width frame width
2715  * @param height frame height
2716  * @param vec coordinates on sphere
2717  */
2718 static int tetrahedron_to_xyz(const V360Context *s,
2719                               int i, int j, int width, int height,
2720                               float *vec)
2721 {
2722     const float uf = (float)i / width;
2723     const float vf = (float)j / height;
2724
2725     vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
2726     vec[1] = 1.f - vf * 2.f;
2727     vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
2728
2729     normalize_vector(vec);
2730
2731     return 1;
2732 }
2733
2734 /**
2735  * Calculate frame position in tetrahedron format for corresponding 3D coordinates on sphere.
2736  *
2737  * @param s filter private context
2738  * @param vec coordinates on sphere
2739  * @param width frame width
2740  * @param height frame height
2741  * @param us horizontal coordinates for interpolation window
2742  * @param vs vertical coordinates for interpolation window
2743  * @param du horizontal relative coordinate
2744  * @param dv vertical relative coordinate
2745  */
2746 static int xyz_to_tetrahedron(const V360Context *s,
2747                               const float *vec, int width, int height,
2748                               int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2749 {
2750     const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f;
2751     const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f;
2752     const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f;
2753     const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f;
2754     const float d = FFMAX(d0, FFMAX3(d1, d2, d3));
2755
2756     float uf, vf, x, y, z;
2757     int ui, vi;
2758
2759     x =  vec[0] / d;
2760     y =  vec[1] / d;
2761     z = -vec[2] / d;
2762
2763     vf = 0.5f - y * 0.5f * s->input_mirror_modifier[1];
2764
2765     if ((x + y >= 0.f &&  y + z >= 0.f && -z - x <= 0.f) ||
2766         (x + y <= 0.f && -y + z >= 0.f &&  z - x >= 0.f)) {
2767         uf = 0.25f * x * s->input_mirror_modifier[0] + 0.25f;
2768     }  else {
2769         uf = 0.75f - 0.25f * x * s->input_mirror_modifier[0];
2770     }
2771
2772     uf *= width;
2773     vf *= height;
2774
2775     ui = floorf(uf);
2776     vi = floorf(vf);
2777
2778     *du = uf - ui;
2779     *dv = vf - vi;
2780
2781     for (int i = 0; i < 4; i++) {
2782         for (int j = 0; j < 4; j++) {
2783             us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
2784             vs[i][j] = reflecty(vi + i - 1, height);
2785         }
2786     }
2787
2788     return 1;
2789 }
2790
2791 /**
2792  * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format.
2793  *
2794  * @param s filter private context
2795  * @param i horizontal position on frame [0, width)
2796  * @param j vertical position on frame [0, height)
2797  * @param width frame width
2798  * @param height frame height
2799  * @param vec coordinates on sphere
2800  */
2801 static int dfisheye_to_xyz(const V360Context *s,
2802                            int i, int j, int width, int height,
2803                            float *vec)
2804 {
2805     const float scale = 1.f + s->out_pad;
2806
2807     const float ew = width / 2.f;
2808     const float eh = height;
2809
2810     const int ei = i >= ew ? i - ew : i;
2811     const float m = i >= ew ? -1.f : 1.f;
2812
2813     const float uf = ((2.f * ei) / ew - 1.f) * scale;
2814     const float vf = ((2.f * j + 1.f) / eh - 1.f) * scale;
2815
2816     const float h     = hypotf(uf, vf);
2817     const float lh    = h > 0.f ? h : 1.f;
2818     const float theta = m * M_PI_2 * (1.f - h);
2819
2820     const float sin_theta = sinf(theta);
2821     const float cos_theta = cosf(theta);
2822
2823     vec[0] = cos_theta * m * -uf / lh;
2824     vec[1] = cos_theta *     -vf / lh;
2825     vec[2] = sin_theta;
2826
2827     normalize_vector(vec);
2828
2829     return 1;
2830 }
2831
2832 /**
2833  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
2834  *
2835  * @param s filter private context
2836  * @param vec coordinates on sphere
2837  * @param width frame width
2838  * @param height frame height
2839  * @param us horizontal coordinates for interpolation window
2840  * @param vs vertical coordinates for interpolation window
2841  * @param du horizontal relative coordinate
2842  * @param dv vertical relative coordinate
2843  */
2844 static int xyz_to_dfisheye(const V360Context *s,
2845                            const float *vec, int width, int height,
2846                            int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2847 {
2848     const float scale = 1.f - s->in_pad;
2849
2850     const float ew = width / 2.f;
2851     const float eh = height;
2852
2853     const float h     = hypotf(vec[0], vec[1]);
2854     const float lh    = h > 0.f ? h : 1.f;
2855     const float theta = acosf(fabsf(vec[2])) / M_PI;
2856
2857     float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
2858     float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
2859
2860     int ui, vi;
2861     int u_shift;
2862
2863     if (vec[2] >= 0.f) {
2864         u_shift = 0;
2865     } else {
2866         u_shift = ceilf(ew);
2867         uf = ew - uf;
2868     }
2869
2870     ui = floorf(uf);
2871     vi = floorf(vf);
2872
2873     *du = uf - ui;
2874     *dv = vf - vi;
2875
2876     for (int i = 0; i < 4; i++) {
2877         for (int j = 0; j < 4; j++) {
2878             us[i][j] = av_clip(u_shift + ui + j - 1, 0, width  - 1);
2879             vs[i][j] = av_clip(          vi + i - 1, 0, height - 1);
2880         }
2881     }
2882
2883     return 1;
2884 }
2885
2886 /**
2887  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
2888  *
2889  * @param s filter private context
2890  * @param i horizontal position on frame [0, width)
2891  * @param j vertical position on frame [0, height)
2892  * @param width frame width
2893  * @param height frame height
2894  * @param vec coordinates on sphere
2895  */
2896 static int barrel_to_xyz(const V360Context *s,
2897                          int i, int j, int width, int height,
2898                          float *vec)
2899 {
2900     const float scale = 0.99f;
2901     float l_x, l_y, l_z;
2902
2903     if (i < 4 * width / 5) {
2904         const float theta_range = M_PI_4;
2905
2906         const int ew = 4 * width / 5;
2907         const int eh = height;
2908
2909         const float phi   = ((2.f * i) / ew - 1.f) * M_PI        / scale;
2910         const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
2911
2912         const float sin_phi   = sinf(phi);
2913         const float cos_phi   = cosf(phi);
2914         const float sin_theta = sinf(theta);
2915         const float cos_theta = cosf(theta);
2916
2917         l_x =  cos_theta * sin_phi;
2918         l_y = -sin_theta;
2919         l_z = -cos_theta * cos_phi;
2920     } else {
2921         const int ew = width  / 5;
2922         const int eh = height / 2;
2923
2924         float uf, vf;
2925
2926         if (j < eh) {   // UP
2927             uf = 2.f * (i - 4 * ew) / ew - 1.f;
2928             vf = 2.f * (j         ) / eh - 1.f;
2929
2930             uf /= scale;
2931             vf /= scale;
2932
2933             l_x =  uf;
2934             l_y =  1.f;
2935             l_z = -vf;
2936         } else {            // DOWN
2937             uf = 2.f * (i - 4 * ew) / ew - 1.f;
2938             vf = 2.f * (j -     eh) / eh - 1.f;
2939
2940             uf /= scale;
2941             vf /= scale;
2942
2943             l_x =  uf;
2944             l_y = -1.f;
2945             l_z =  vf;
2946         }
2947     }
2948
2949     vec[0] = l_x;
2950     vec[1] = l_y;
2951     vec[2] = l_z;
2952
2953     normalize_vector(vec);
2954
2955     return 1;
2956 }
2957
2958 /**
2959  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
2960  *
2961  * @param s filter private context
2962  * @param vec coordinates on sphere
2963  * @param width frame width
2964  * @param height frame height
2965  * @param us horizontal coordinates for interpolation window
2966  * @param vs vertical coordinates for interpolation window
2967  * @param du horizontal relative coordinate
2968  * @param dv vertical relative coordinate
2969  */
2970 static int xyz_to_barrel(const V360Context *s,
2971                          const float *vec, int width, int height,
2972                          int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
2973 {
2974     const float scale = 0.99f;
2975
2976     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
2977     const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
2978     const float theta_range = M_PI_4;
2979
2980     int ew, eh;
2981     int u_shift, v_shift;
2982     float uf, vf;
2983     int ui, vi;
2984
2985     if (theta > -theta_range && theta < theta_range) {
2986         ew = 4 * width / 5;
2987         eh = height;
2988
2989         u_shift = s->ih_flip ? width / 5 : 0;
2990         v_shift = 0;
2991
2992         uf = (phi   / M_PI        * scale + 1.f) * ew / 2.f;
2993         vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
2994     } else {
2995         ew = width  / 5;
2996         eh = height / 2;
2997
2998         u_shift = s->ih_flip ? 0 : 4 * ew;
2999
3000         if (theta < 0.f) {  // UP
3001             uf =  vec[0] / vec[1];
3002             vf = -vec[2] / vec[1];
3003             v_shift = 0;
3004         } else {            // DOWN
3005             uf = -vec[0] / vec[1];
3006             vf = -vec[2] / vec[1];
3007             v_shift = eh;
3008         }
3009
3010         uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
3011         vf *= s->input_mirror_modifier[1];
3012
3013         uf = 0.5f * ew * (uf * scale + 1.f);
3014         vf = 0.5f * eh * (vf * scale + 1.f);
3015     }
3016
3017     ui = floorf(uf);
3018     vi = floorf(vf);
3019
3020     *du = uf - ui;
3021     *dv = vf - vi;
3022
3023     for (int i = 0; i < 4; i++) {
3024         for (int j = 0; j < 4; j++) {
3025             us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3026             vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3027         }
3028     }
3029
3030     return 1;
3031 }
3032
3033 /**
3034  * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere.
3035  *
3036  * @param s filter private context
3037  * @param vec coordinates on sphere
3038  * @param width frame width
3039  * @param height frame height
3040  * @param us horizontal coordinates for interpolation window
3041  * @param vs vertical coordinates for interpolation window
3042  * @param du horizontal relative coordinate
3043  * @param dv vertical relative coordinate
3044  */
3045 static int xyz_to_barrelsplit(const V360Context *s,
3046                               const float *vec, int width, int height,
3047                               int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3048 {
3049     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
3050     const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
3051
3052     const float theta_range = M_PI_4;
3053
3054     int ew, eh;
3055     int u_shift, v_shift;
3056     float uf, vf;
3057     int ui, vi;
3058
3059     if (theta >= -theta_range && theta <= theta_range) {
3060         const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad;
3061         const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
3062
3063         ew = width / 3 * 2;
3064         eh = height / 2;
3065
3066         u_shift = s->ih_flip ? width / 3 : 0;
3067         v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0;
3068
3069         uf = fmodf(phi, M_PI_2) / M_PI_2;
3070         vf = theta / M_PI_4;
3071
3072         if (v_shift)
3073             uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f);
3074
3075         uf = (uf * scalew + 1.f) * width  / 3.f;
3076         vf = (vf * scaleh + 1.f) * height / 4.f;
3077     } else {
3078         const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width  / 3.f) : 1.f - s->in_pad;
3079         const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad;
3080         int v_offset = 0;
3081
3082         ew = width  / 3;
3083         eh = height / 4;
3084
3085         u_shift = s->ih_flip ? 0 : 2 * ew;
3086
3087         if (theta <= 0.f && theta >= -M_PI_2 &&
3088             phi <= M_PI_2 && phi >= -M_PI_2) {
3089             uf =  vec[0] / vec[1];
3090             vf = -vec[2] / vec[1];
3091             v_shift = 0;
3092             v_offset = -eh;
3093         } else if (theta >= 0.f && theta <= M_PI_2 &&
3094                    phi <= M_PI_2 && phi >= -M_PI_2) {
3095             uf = -vec[0] / vec[1];
3096             vf = -vec[2] / vec[1];
3097             v_shift = height * 0.25f;
3098         } else if (theta <= 0.f && theta >= -M_PI_2) {
3099             uf = -vec[0] / vec[1];
3100             vf =  vec[2] / vec[1];
3101             v_shift = height * 0.5f;
3102             v_offset = -eh;
3103         } else {
3104             uf = vec[0] / vec[1];
3105             vf = vec[2] / vec[1];
3106             v_shift = height * 0.75f;
3107         }
3108
3109         uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
3110         vf *= s->input_mirror_modifier[1];
3111
3112         uf = 0.5f * width / 3.f * (uf * scalew + 1.f);
3113         vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset;
3114     }
3115
3116     ui = floorf(uf);
3117     vi = floorf(vf);
3118
3119     *du = uf - ui;
3120     *dv = vf - vi;
3121
3122     for (int i = 0; i < 4; i++) {
3123         for (int j = 0; j < 4; j++) {
3124             us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3125             vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3126         }
3127     }
3128
3129     return 1;
3130 }
3131
3132 /**
3133  * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format.
3134  *
3135  * @param s filter private context
3136  * @param i horizontal position on frame [0, width)
3137  * @param j vertical position on frame [0, height)
3138  * @param width frame width
3139  * @param height frame height
3140  * @param vec coordinates on sphere
3141  */
3142 static int barrelsplit_to_xyz(const V360Context *s,
3143                               int i, int j, int width, int height,
3144                               float *vec)
3145 {
3146     const float x = (i + 0.5f) / width;
3147     const float y = (j + 0.5f) / height;
3148     float l_x, l_y, l_z;
3149
3150     if (x < 2.f / 3.f) {
3151         const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad;
3152         const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
3153
3154         const float back = floorf(y * 2.f);
3155
3156         const float phi   = ((3.f / 2.f * x - 0.5f) / scalew - back + 1.f) * M_PI;
3157         const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI;
3158
3159         const float sin_phi   = sinf(phi);
3160         const float cos_phi   = cosf(phi);
3161         const float sin_theta = sinf(theta);
3162         const float cos_theta = cosf(theta);
3163
3164         l_x = -cos_theta * sin_phi;
3165         l_y = -sin_theta;
3166         l_z =  cos_theta * cos_phi;
3167     } else {
3168         const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width  / 3.f) : 1.f - s->out_pad;
3169         const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad;
3170
3171         const int face = floorf(y * 4.f);
3172         float uf, vf;
3173
3174         uf = x * 3.f - 2.f;
3175
3176         switch (face) {
3177         case 0:
3178             vf = y * 2.f;
3179             uf = 1.f - uf;
3180             vf = 0.5f - vf;
3181
3182             l_x = (0.5f - uf) / scalew;
3183             l_y =  0.5f;
3184             l_z = (-0.5f + vf) / scaleh;
3185             break;
3186         case 1:
3187             vf = y * 2.f;
3188             uf = 1.f - uf;
3189             vf = 1.f - (vf - 0.5f);
3190
3191             l_x = (0.5f - uf) / scalew;
3192             l_y = -0.5f;
3193             l_z = (0.5f - vf) / scaleh;
3194             break;
3195         case 2:
3196             vf = y * 2.f - 0.5f;
3197             vf = 1.f - (1.f - vf);
3198
3199             l_x = (0.5f - uf) / scalew;
3200             l_y = 0.5f;
3201             l_z = (-0.5f + vf) / scaleh;
3202             break;
3203         case 3:
3204             vf = y * 2.f - 1.5f;
3205
3206             l_x = (0.5f - uf) / scalew;
3207             l_y = -0.5f;
3208             l_z = (0.5f - vf) / scaleh;
3209             break;
3210         }
3211     }
3212
3213     vec[0] = l_x;
3214     vec[1] = l_y;
3215     vec[2] = l_z;
3216
3217     normalize_vector(vec);
3218
3219     return 1;
3220 }
3221
3222 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
3223 {
3224     for (int i = 0; i < 3; i++) {
3225         for (int j = 0; j < 3; j++) {
3226             float sum = 0.f;
3227
3228             for (int k = 0; k < 3; k++)
3229                 sum += a[i][k] * b[k][j];
3230
3231             c[i][j] = sum;
3232         }
3233     }
3234 }
3235
3236 /**
3237  * Calculate rotation matrix for yaw/pitch/roll angles.
3238  */
3239 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
3240                                              float rot_mat[3][3],
3241                                              const int rotation_order[3])
3242 {
3243     const float yaw_rad   = yaw   * M_PI / 180.f;
3244     const float pitch_rad = pitch * M_PI / 180.f;
3245     const float roll_rad  = roll  * M_PI / 180.f;
3246
3247     const float sin_yaw   = sinf(-yaw_rad);
3248     const float cos_yaw   = cosf(-yaw_rad);
3249     const float sin_pitch = sinf(pitch_rad);
3250     const float cos_pitch = cosf(pitch_rad);
3251     const float sin_roll  = sinf(roll_rad);
3252     const float cos_roll  = cosf(roll_rad);
3253
3254     float m[3][3][3];
3255     float temp[3][3];
3256
3257     m[0][0][0] =  cos_yaw;  m[0][0][1] = 0;          m[0][0][2] =  sin_yaw;
3258     m[0][1][0] =  0;        m[0][1][1] = 1;          m[0][1][2] =  0;
3259     m[0][2][0] = -sin_yaw;  m[0][2][1] = 0;          m[0][2][2] =  cos_yaw;
3260
3261     m[1][0][0] = 1;         m[1][0][1] = 0;          m[1][0][2] =  0;
3262     m[1][1][0] = 0;         m[1][1][1] = cos_pitch;  m[1][1][2] = -sin_pitch;
3263     m[1][2][0] = 0;         m[1][2][1] = sin_pitch;  m[1][2][2] =  cos_pitch;
3264
3265     m[2][0][0] = cos_roll;  m[2][0][1] = -sin_roll;  m[2][0][2] =  0;
3266     m[2][1][0] = sin_roll;  m[2][1][1] =  cos_roll;  m[2][1][2] =  0;
3267     m[2][2][0] = 0;         m[2][2][1] =  0;         m[2][2][2] =  1;
3268
3269     multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
3270     multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
3271 }
3272
3273 /**
3274  * Rotate vector with given rotation matrix.
3275  *
3276  * @param rot_mat rotation matrix
3277  * @param vec vector
3278  */
3279 static inline void rotate(const float rot_mat[3][3],
3280                           float *vec)
3281 {
3282     const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
3283     const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
3284     const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
3285
3286     vec[0] = x_tmp;
3287     vec[1] = y_tmp;
3288     vec[2] = z_tmp;
3289 }
3290
3291 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3292                                        float *modifier)
3293 {
3294     modifier[0] = h_flip ? -1.f : 1.f;
3295     modifier[1] = v_flip ? -1.f : 1.f;
3296     modifier[2] = d_flip ? -1.f : 1.f;
3297 }
3298
3299 static inline void mirror(const float *modifier, float *vec)
3300 {
3301     vec[0] *= modifier[0];
3302     vec[1] *= modifier[1];
3303     vec[2] *= modifier[2];
3304 }
3305
3306 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3307 {
3308     if (!s->u[p])
3309         s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3310     if (!s->v[p])
3311         s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3312     if (!s->u[p] || !s->v[p])
3313         return AVERROR(ENOMEM);
3314     if (sizeof_ker) {
3315         if (!s->ker[p])
3316             s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
3317         if (!s->ker[p])
3318             return AVERROR(ENOMEM);
3319     }
3320
3321     if (sizeof_mask && !p) {
3322         if (!s->mask)
3323             s->mask = av_calloc(s->pr_width[p] * s->pr_height[p], sizeof_mask);
3324         if (!s->mask)
3325             return AVERROR(ENOMEM);
3326     }
3327
3328     return 0;
3329 }
3330
3331 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
3332 {
3333     switch (format) {
3334     case FISHEYE:
3335         {
3336             const float d = 0.5f * hypotf(w, h);
3337
3338             *h_fov = d / h * d_fov;
3339             *v_fov = d / w * d_fov;
3340         }
3341         break;
3342     case FLAT:
3343     default:
3344         {
3345             const float da = tanf(0.5 * FFMIN(d_fov, 359.f) * M_PI / 180.f);
3346             const float d = hypotf(w, h);
3347
3348             *h_fov = atan2f(da * w, d) * 360.f / M_PI;
3349             *v_fov = atan2f(da * h, d) * 360.f / M_PI;
3350
3351             if (*h_fov < 0.f)
3352                 *h_fov += 360.f;
3353             if (*v_fov < 0.f)
3354                 *v_fov += 360.f;
3355         }
3356         break;
3357     }
3358 }
3359
3360 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
3361 {
3362     outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
3363     outw[0] = outw[3] = w;
3364     outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
3365     outh[0] = outh[3] = h;
3366 }
3367
3368 // Calculate remap data
3369 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
3370 {
3371     V360Context *s = ctx->priv;
3372
3373     for (int p = 0; p < s->nb_allocated; p++) {
3374         const int max_value = s->max_value;
3375         const int width = s->pr_width[p];
3376         const int uv_linesize = s->uv_linesize[p];
3377         const int height = s->pr_height[p];
3378         const int in_width = s->inplanewidth[p];
3379         const int in_height = s->inplaneheight[p];
3380         const int slice_start = (height *  jobnr     ) / nb_jobs;
3381         const int slice_end   = (height * (jobnr + 1)) / nb_jobs;
3382         float du, dv;
3383         float vec[3];
3384         XYRemap rmap;
3385
3386         for (int j = slice_start; j < slice_end; j++) {
3387             for (int i = 0; i < width; i++) {
3388                 int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
3389                 int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
3390                 int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
3391                 uint8_t *mask8 = p ? NULL : s->mask + (j * s->pr_width[0] + i);
3392                 uint16_t *mask16 = p ? NULL : (uint16_t *)s->mask + (j * s->pr_width[0] + i);
3393                 int in_mask, out_mask;
3394
3395                 if (s->out_transpose)
3396                     out_mask = s->out_transform(s, j, i, height, width, vec);
3397                 else
3398                     out_mask = s->out_transform(s, i, j, width, height, vec);
3399                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3400                 rotate(s->rot_mat, vec);
3401                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3402                 normalize_vector(vec);
3403                 mirror(s->output_mirror_modifier, vec);
3404                 if (s->in_transpose)
3405                     in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
3406                 else
3407                     in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
3408                 av_assert1(!isnan(du) && !isnan(dv));
3409                 s->calculate_kernel(du, dv, &rmap, u, v, ker);
3410
3411                 if (!p && s->mask) {
3412                     if (s->mask_size == 1) {
3413                         mask8[0] = 255 * (out_mask & in_mask);
3414                     } else {
3415                         mask16[0] = max_value * (out_mask & in_mask);
3416                     }
3417                 }
3418             }
3419         }
3420     }
3421
3422     return 0;
3423 }
3424
3425 static int config_output(AVFilterLink *outlink)
3426 {
3427     AVFilterContext *ctx = outlink->src;
3428     AVFilterLink *inlink = ctx->inputs[0];
3429     V360Context *s = ctx->priv;
3430     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
3431     const int depth = desc->comp[0].depth;
3432     const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
3433     int sizeof_uv;
3434     int sizeof_ker;
3435     int err;
3436     int h, w;
3437     int in_offset_h, in_offset_w;
3438     int out_offset_h, out_offset_w;
3439     float hf, wf;
3440     int (*prepare_out)(AVFilterContext *ctx);
3441     int have_alpha;
3442
3443     s->max_value = (1 << depth) - 1;
3444     s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
3445     s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
3446
3447     switch (s->interp) {
3448     case NEAREST:
3449         s->calculate_kernel = nearest_kernel;
3450         s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
3451         s->elements = 1;
3452         sizeof_uv = sizeof(int16_t) * s->elements;
3453         sizeof_ker = 0;
3454         break;
3455     case BILINEAR:
3456         s->calculate_kernel = bilinear_kernel;
3457         s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
3458         s->elements = 2 * 2;
3459         sizeof_uv = sizeof(int16_t) * s->elements;
3460         sizeof_ker = sizeof(int16_t) * s->elements;
3461         break;
3462     case BICUBIC:
3463         s->calculate_kernel = bicubic_kernel;
3464         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3465         s->elements = 4 * 4;
3466         sizeof_uv = sizeof(int16_t) * s->elements;
3467         sizeof_ker = sizeof(int16_t) * s->elements;
3468         break;
3469     case LANCZOS:
3470         s->calculate_kernel = lanczos_kernel;
3471         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3472         s->elements = 4 * 4;
3473         sizeof_uv = sizeof(int16_t) * s->elements;
3474         sizeof_ker = sizeof(int16_t) * s->elements;
3475         break;
3476     case SPLINE16:
3477         s->calculate_kernel = spline16_kernel;
3478         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3479         s->elements = 4 * 4;
3480         sizeof_uv = sizeof(int16_t) * s->elements;
3481         sizeof_ker = sizeof(int16_t) * s->elements;
3482         break;
3483     case GAUSSIAN:
3484         s->calculate_kernel = gaussian_kernel;
3485         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3486         s->elements = 4 * 4;
3487         sizeof_uv = sizeof(int16_t) * s->elements;
3488         sizeof_ker = sizeof(int16_t) * s->elements;
3489         break;
3490     default:
3491         av_assert0(0);
3492     }
3493
3494     ff_v360_init(s, depth);
3495
3496     for (int order = 0; order < NB_RORDERS; order++) {
3497         const char c = s->rorder[order];
3498         int rorder;
3499
3500         if (c == '\0') {
3501             av_log(ctx, AV_LOG_WARNING,
3502                    "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
3503             s->rotation_order[0] = YAW;
3504             s->rotation_order[1] = PITCH;
3505             s->rotation_order[2] = ROLL;
3506             break;
3507         }
3508
3509         rorder = get_rorder(c);
3510         if (rorder == -1) {
3511             av_log(ctx, AV_LOG_WARNING,
3512                    "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
3513             s->rotation_order[0] = YAW;
3514             s->rotation_order[1] = PITCH;
3515             s->rotation_order[2] = ROLL;
3516             break;
3517         }
3518
3519         s->rotation_order[order] = rorder;
3520     }
3521
3522     switch (s->in_stereo) {
3523     case STEREO_2D:
3524         w = inlink->w;
3525         h = inlink->h;
3526         in_offset_w = in_offset_h = 0;
3527         break;
3528     case STEREO_SBS:
3529         w = inlink->w / 2;
3530         h = inlink->h;
3531         in_offset_w = w;
3532         in_offset_h = 0;
3533         break;
3534     case STEREO_TB:
3535         w = inlink->w;
3536         h = inlink->h / 2;
3537         in_offset_w = 0;
3538         in_offset_h = h;
3539         break;
3540     default:
3541         av_assert0(0);
3542     }
3543
3544     set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
3545     set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
3546
3547     s->in_width = s->inplanewidth[0];
3548     s->in_height = s->inplaneheight[0];
3549
3550     if (s->id_fov > 0.f)
3551         fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
3552
3553     if (s->in_transpose)
3554         FFSWAP(int, s->in_width, s->in_height);
3555
3556     switch (s->in) {
3557     case EQUIRECTANGULAR:
3558         s->in_transform = xyz_to_equirect;
3559         err = 0;
3560         wf = w;
3561         hf = h;
3562         break;
3563     case CUBEMAP_3_2:
3564         s->in_transform = xyz_to_cube3x2;
3565         err = prepare_cube_in(ctx);
3566         wf = w / 3.f * 4.f;
3567         hf = h;
3568         break;
3569     case CUBEMAP_1_6:
3570         s->in_transform = xyz_to_cube1x6;
3571         err = prepare_cube_in(ctx);
3572         wf = w * 4.f;
3573         hf = h / 3.f;
3574         break;
3575     case CUBEMAP_6_1:
3576         s->in_transform = xyz_to_cube6x1;
3577         err = prepare_cube_in(ctx);
3578         wf = w / 3.f * 2.f;
3579         hf = h * 2.f;
3580         break;
3581     case EQUIANGULAR:
3582         s->in_transform = xyz_to_eac;
3583         err = prepare_eac_in(ctx);
3584         wf = w;
3585         hf = h / 9.f * 8.f;
3586         break;
3587     case FLAT:
3588         s->in_transform = xyz_to_flat;
3589         err = prepare_flat_in(ctx);
3590         wf = w;
3591         hf = h;
3592         break;
3593     case PERSPECTIVE:
3594     case PANNINI:
3595         av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
3596         return AVERROR(EINVAL);
3597     case DUAL_FISHEYE:
3598         s->in_transform = xyz_to_dfisheye;
3599         err = 0;
3600         wf = w;
3601         hf = h;
3602         break;
3603     case BARREL:
3604         s->in_transform = xyz_to_barrel;
3605         err = 0;
3606         wf = w / 5.f * 4.f;
3607         hf = h;
3608         break;
3609     case STEREOGRAPHIC:
3610         s->in_transform = xyz_to_stereographic;
3611         err = prepare_stereographic_in(ctx);
3612         wf = w;
3613         hf = h / 2.f;
3614         break;
3615     case MERCATOR:
3616         s->in_transform = xyz_to_mercator;
3617         err = 0;
3618         wf = w;
3619         hf = h / 2.f;
3620         break;
3621     case BALL:
3622         s->in_transform = xyz_to_ball;
3623         err = 0;
3624         wf = w;
3625         hf = h / 2.f;
3626         break;
3627     case HAMMER:
3628         s->in_transform = xyz_to_hammer;
3629         err = 0;
3630         wf = w;
3631         hf = h;
3632         break;
3633     case SINUSOIDAL:
3634         s->in_transform = xyz_to_sinusoidal;
3635         err = 0;
3636         wf = w;
3637         hf = h;
3638         break;
3639     case FISHEYE:
3640         s->in_transform = xyz_to_fisheye;
3641         err = prepare_fisheye_in(ctx);
3642         wf = w * 2;
3643         hf = h;
3644         break;
3645     case CYLINDRICAL:
3646         s->in_transform = xyz_to_cylindrical;
3647         err = prepare_cylindrical_in(ctx);
3648         wf = w;
3649         hf = h * 2.f;
3650         break;
3651     case TETRAHEDRON:
3652         s->in_transform = xyz_to_tetrahedron;
3653         err = 0;
3654         wf = w;
3655         hf = h;
3656         break;
3657     case BARREL_SPLIT:
3658         s->in_transform = xyz_to_barrelsplit;
3659         err = 0;
3660         wf = w * 4.f / 3.f;
3661         hf = h;
3662         break;
3663     default:
3664         av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
3665         return AVERROR_BUG;
3666     }
3667
3668     if (err != 0) {
3669         return err;
3670     }
3671
3672     switch (s->out) {
3673     case EQUIRECTANGULAR:
3674         s->out_transform = equirect_to_xyz;
3675         prepare_out = NULL;
3676         w = lrintf(wf);
3677         h = lrintf(hf);
3678         break;
3679     case CUBEMAP_3_2:
3680         s->out_transform = cube3x2_to_xyz;
3681         prepare_out = prepare_cube_out;
3682         w = lrintf(wf / 4.f * 3.f);
3683         h = lrintf(hf);
3684         break;
3685     case CUBEMAP_1_6:
3686         s->out_transform = cube1x6_to_xyz;
3687         prepare_out = prepare_cube_out;
3688         w = lrintf(wf / 4.f);
3689         h = lrintf(hf * 3.f);
3690         break;
3691     case CUBEMAP_6_1:
3692         s->out_transform = cube6x1_to_xyz;
3693         prepare_out = prepare_cube_out;
3694         w = lrintf(wf / 2.f * 3.f);
3695         h = lrintf(hf / 2.f);
3696         break;
3697     case EQUIANGULAR:
3698         s->out_transform = eac_to_xyz;
3699         prepare_out = prepare_eac_out;
3700         w = lrintf(wf);
3701         h = lrintf(hf / 8.f * 9.f);
3702         break;
3703     case FLAT:
3704         s->out_transform = flat_to_xyz;
3705         prepare_out = prepare_flat_out;
3706         w = lrintf(wf);
3707         h = lrintf(hf);
3708         break;
3709     case DUAL_FISHEYE:
3710         s->out_transform = dfisheye_to_xyz;
3711         prepare_out = NULL;
3712         w = lrintf(wf);
3713         h = lrintf(hf);
3714         break;
3715     case BARREL:
3716         s->out_transform = barrel_to_xyz;
3717         prepare_out = NULL;
3718         w = lrintf(wf / 4.f * 5.f);
3719         h = lrintf(hf);
3720         break;
3721     case STEREOGRAPHIC:
3722         s->out_transform = stereographic_to_xyz;
3723         prepare_out = prepare_stereographic_out;
3724         w = lrintf(wf);
3725         h = lrintf(hf * 2.f);
3726         break;
3727     case MERCATOR:
3728         s->out_transform = mercator_to_xyz;
3729         prepare_out = NULL;
3730         w = lrintf(wf);
3731         h = lrintf(hf * 2.f);
3732         break;
3733     case BALL:
3734         s->out_transform = ball_to_xyz;
3735         prepare_out = NULL;
3736         w = lrintf(wf);
3737         h = lrintf(hf * 2.f);
3738         break;
3739     case HAMMER:
3740         s->out_transform = hammer_to_xyz;
3741         prepare_out = NULL;
3742         w = lrintf(wf);
3743         h = lrintf(hf);
3744         break;
3745     case SINUSOIDAL:
3746         s->out_transform = sinusoidal_to_xyz;
3747         prepare_out = NULL;
3748         w = lrintf(wf);
3749         h = lrintf(hf);
3750         break;
3751     case FISHEYE:
3752         s->out_transform = fisheye_to_xyz;
3753         prepare_out = prepare_fisheye_out;
3754         w = lrintf(wf * 0.5f);
3755         h = lrintf(hf);
3756         break;
3757     case PANNINI:
3758         s->out_transform = pannini_to_xyz;
3759         prepare_out = NULL;
3760         w = lrintf(wf);
3761         h = lrintf(hf);
3762         break;
3763     case CYLINDRICAL:
3764         s->out_transform = cylindrical_to_xyz;
3765         prepare_out = prepare_cylindrical_out;
3766         w = lrintf(wf);
3767         h = lrintf(hf * 0.5f);
3768         break;
3769     case PERSPECTIVE:
3770         s->out_transform = perspective_to_xyz;
3771         prepare_out = NULL;
3772         w = lrintf(wf / 2.f);
3773         h = lrintf(hf);
3774         break;
3775     case TETRAHEDRON:
3776         s->out_transform = tetrahedron_to_xyz;
3777         prepare_out = NULL;
3778         w = lrintf(wf);
3779         h = lrintf(hf);
3780         break;
3781     case BARREL_SPLIT:
3782         s->out_transform = barrelsplit_to_xyz;
3783         prepare_out = NULL;
3784         w = lrintf(wf / 4.f * 3.f);
3785         h = lrintf(hf);
3786         break;
3787     default:
3788         av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
3789         return AVERROR_BUG;
3790     }
3791
3792     // Override resolution with user values if specified
3793     if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
3794         s->out == FLAT && s->d_fov == 0.f) {
3795         w = s->width;
3796         h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
3797     } else if (s->width <= 0 && s->height > 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
3798         s->out == FLAT && s->d_fov == 0.f) {
3799         h = s->height;
3800         w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
3801     } else if (s->width > 0 && s->height > 0) {
3802         w = s->width;
3803         h = s->height;
3804     } else if (s->width > 0 || s->height > 0) {
3805         av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
3806         return AVERROR(EINVAL);
3807     } else {
3808         if (s->out_transpose)
3809             FFSWAP(int, w, h);
3810
3811         if (s->in_transpose)
3812             FFSWAP(int, w, h);
3813     }
3814
3815     s->width  = w;
3816     s->height = h;
3817
3818     if (s->d_fov > 0.f)
3819         fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
3820
3821     if (prepare_out) {
3822         err = prepare_out(ctx);
3823         if (err != 0)
3824             return err;
3825     }
3826
3827     set_dimensions(s->pr_width, s->pr_height, w, h, desc);
3828
3829     s->out_width = s->pr_width[0];
3830     s->out_height = s->pr_height[0];
3831
3832     if (s->out_transpose)
3833         FFSWAP(int, s->out_width, s->out_height);
3834
3835     switch (s->out_stereo) {
3836     case STEREO_2D:
3837         out_offset_w = out_offset_h = 0;
3838         break;
3839     case STEREO_SBS:
3840         out_offset_w = w;
3841         out_offset_h = 0;
3842         w *= 2;
3843         break;
3844     case STEREO_TB:
3845         out_offset_w = 0;
3846         out_offset_h = h;
3847         h *= 2;
3848         break;
3849     default:
3850         av_assert0(0);
3851     }
3852
3853     set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
3854     set_dimensions(s->planewidth, s->planeheight, w, h, desc);
3855
3856     for (int i = 0; i < 4; i++)
3857         s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
3858
3859     outlink->h = h;
3860     outlink->w = w;
3861
3862     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
3863     have_alpha   = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
3864
3865     if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
3866         s->nb_allocated = 1;
3867         s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
3868     } else {
3869         s->nb_allocated = 2;
3870         s->map[0] = s->map[3] = 0;
3871         s->map[1] = s->map[2] = 1;
3872     }
3873
3874     for (int i = 0; i < s->nb_allocated; i++)
3875         allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
3876
3877     calculate_rotation_matrix(s->yaw, s->pitch, s->roll, s->rot_mat, s->rotation_order);
3878     set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier);
3879
3880     ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3881
3882     return 0;
3883 }
3884
3885 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
3886 {
3887     AVFilterContext *ctx = inlink->dst;
3888     AVFilterLink *outlink = ctx->outputs[0];
3889     V360Context *s = ctx->priv;
3890     AVFrame *out;
3891     ThreadData td;
3892
3893     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
3894     if (!out) {
3895         av_frame_free(&in);
3896         return AVERROR(ENOMEM);
3897     }
3898     av_frame_copy_props(out, in);
3899
3900     td.in = in;
3901     td.out = out;
3902
3903     ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
3904
3905     av_frame_free(&in);
3906     return ff_filter_frame(outlink, out);
3907 }
3908
3909 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
3910                            char *res, int res_len, int flags)
3911 {
3912     int ret;
3913
3914     ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
3915     if (ret < 0)
3916         return ret;
3917
3918     return config_output(ctx->outputs[0]);
3919 }
3920
3921 static av_cold void uninit(AVFilterContext *ctx)
3922 {
3923     V360Context *s = ctx->priv;
3924
3925     for (int p = 0; p < s->nb_allocated; p++) {
3926         av_freep(&s->u[p]);
3927         av_freep(&s->v[p]);
3928         av_freep(&s->ker[p]);
3929     }
3930     av_freep(&s->mask);
3931 }
3932
3933 static const AVFilterPad inputs[] = {
3934     {
3935         .name         = "default",
3936         .type         = AVMEDIA_TYPE_VIDEO,
3937         .filter_frame = filter_frame,
3938     },
3939     { NULL }
3940 };
3941
3942 static const AVFilterPad outputs[] = {
3943     {
3944         .name         = "default",
3945         .type         = AVMEDIA_TYPE_VIDEO,
3946         .config_props = config_output,
3947     },
3948     { NULL }
3949 };
3950
3951 AVFilter ff_vf_v360 = {
3952     .name          = "v360",
3953     .description   = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
3954     .priv_size     = sizeof(V360Context),
3955     .uninit        = uninit,
3956     .query_formats = query_formats,
3957     .inputs        = inputs,
3958     .outputs       = outputs,
3959     .priv_class    = &v360_class,
3960     .flags         = AVFILTER_FLAG_SLICE_THREADS,
3961     .process_command = process_command,
3962 };