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