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