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