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