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