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