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