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