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