]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_v360.c
avfilter/vf_v360: add pannini input support
[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,                 1.f,TFLAGS, "in_pad"},
137     {   "out_pad", "percent output cubemap pads",  OFFSET(out_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f,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 scale = 1.f + s->out_pad;
2995
2996     const float ew = width / 2.f;
2997     const float eh = height;
2998
2999     const int ei = i >= ew ? i - ew : i;
3000     const float m = i >= ew ? 1.f : -1.f;
3001
3002     const float uf = ((2.f * ei) / ew - 1.f) * scale;
3003     const float vf = ((2.f * j + 1.f) / eh - 1.f) * scale;
3004
3005     const float h     = hypotf(uf, vf);
3006     const float lh    = h > 0.f ? h : 1.f;
3007     const float theta = m * M_PI_2 * (1.f - h);
3008
3009     const float sin_theta = sinf(theta);
3010     const float cos_theta = cosf(theta);
3011
3012     vec[0] = cos_theta * m * uf / lh;
3013     vec[1] = cos_theta *     vf / lh;
3014     vec[2] = sin_theta;
3015
3016     normalize_vector(vec);
3017
3018     return 1;
3019 }
3020
3021 /**
3022  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
3023  *
3024  * @param s filter private context
3025  * @param vec coordinates on sphere
3026  * @param width frame width
3027  * @param height frame height
3028  * @param us horizontal coordinates for interpolation window
3029  * @param vs vertical coordinates for interpolation window
3030  * @param du horizontal relative coordinate
3031  * @param dv vertical relative coordinate
3032  */
3033 static int xyz_to_dfisheye(const V360Context *s,
3034                            const float *vec, int width, int height,
3035                            int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3036 {
3037     const float scale = 1.f - s->in_pad;
3038
3039     const float ew = width / 2.f;
3040     const float eh = height;
3041
3042     const float h     = hypotf(vec[0], vec[1]);
3043     const float lh    = h > 0.f ? h : 1.f;
3044     const float theta = acosf(fabsf(vec[2])) / M_PI;
3045
3046     float uf = (theta * (vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
3047     float vf = (theta * (vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
3048
3049     int ui, vi;
3050     int u_shift;
3051
3052     if (vec[2] >= 0.f) {
3053         u_shift = ceilf(ew);
3054     } else {
3055         u_shift = 0;
3056         uf = ew - uf;
3057     }
3058
3059     ui = floorf(uf);
3060     vi = floorf(vf);
3061
3062     *du = uf - ui;
3063     *dv = vf - vi;
3064
3065     for (int i = 0; i < 4; i++) {
3066         for (int j = 0; j < 4; j++) {
3067             us[i][j] = av_clip(u_shift + ui + j - 1, 0, width  - 1);
3068             vs[i][j] = av_clip(          vi + i - 1, 0, height - 1);
3069         }
3070     }
3071
3072     return 1;
3073 }
3074
3075 /**
3076  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
3077  *
3078  * @param s filter private context
3079  * @param i horizontal position on frame [0, width)
3080  * @param j vertical position on frame [0, height)
3081  * @param width frame width
3082  * @param height frame height
3083  * @param vec coordinates on sphere
3084  */
3085 static int barrel_to_xyz(const V360Context *s,
3086                          int i, int j, int width, int height,
3087                          float *vec)
3088 {
3089     const float scale = 0.99f;
3090     float l_x, l_y, l_z;
3091
3092     if (i < 4 * width / 5) {
3093         const float theta_range = M_PI_4;
3094
3095         const int ew = 4 * width / 5;
3096         const int eh = height;
3097
3098         const float phi   = ((2.f * i) / ew - 1.f) * M_PI        / scale;
3099         const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
3100
3101         const float sin_phi   = sinf(phi);
3102         const float cos_phi   = cosf(phi);
3103         const float sin_theta = sinf(theta);
3104         const float cos_theta = cosf(theta);
3105
3106         l_x = cos_theta * sin_phi;
3107         l_y = sin_theta;
3108         l_z = cos_theta * cos_phi;
3109     } else {
3110         const int ew = width  / 5;
3111         const int eh = height / 2;
3112
3113         float uf, vf;
3114
3115         if (j < eh) {   // UP
3116             uf = 2.f * (i - 4 * ew) / ew - 1.f;
3117             vf = 2.f * (j         ) / eh - 1.f;
3118
3119             uf /= scale;
3120             vf /= scale;
3121
3122             l_x =  uf;
3123             l_y = -1.f;
3124             l_z =  vf;
3125         } else {            // DOWN
3126             uf = 2.f * (i - 4 * ew) / ew - 1.f;
3127             vf = 2.f * (j -     eh) / eh - 1.f;
3128
3129             uf /= scale;
3130             vf /= scale;
3131
3132             l_x =  uf;
3133             l_y =  1.f;
3134             l_z = -vf;
3135         }
3136     }
3137
3138     vec[0] = l_x;
3139     vec[1] = l_y;
3140     vec[2] = l_z;
3141
3142     normalize_vector(vec);
3143
3144     return 1;
3145 }
3146
3147 /**
3148  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
3149  *
3150  * @param s filter private context
3151  * @param vec coordinates on sphere
3152  * @param width frame width
3153  * @param height frame height
3154  * @param us horizontal coordinates for interpolation window
3155  * @param vs vertical coordinates for interpolation window
3156  * @param du horizontal relative coordinate
3157  * @param dv vertical relative coordinate
3158  */
3159 static int xyz_to_barrel(const V360Context *s,
3160                          const float *vec, int width, int height,
3161                          int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3162 {
3163     const float scale = 0.99f;
3164
3165     const float phi   = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0];
3166     const float theta = asinf(vec[1]) * s->input_mirror_modifier[1];
3167     const float theta_range = M_PI_4;
3168
3169     int ew, eh;
3170     int u_shift, v_shift;
3171     float uf, vf;
3172     int ui, vi;
3173
3174     if (theta > -theta_range && theta < theta_range) {
3175         ew = 4 * width / 5;
3176         eh = height;
3177
3178         u_shift = s->ih_flip ? width / 5 : 0;
3179         v_shift = 0;
3180
3181         uf = (phi   / M_PI        * scale + 1.f) * ew / 2.f;
3182         vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
3183     } else {
3184         ew = width  / 5;
3185         eh = height / 2;
3186
3187         u_shift = s->ih_flip ? 0 : 4 * ew;
3188
3189         if (theta < 0.f) {  // UP
3190             uf = -vec[0] / vec[1];
3191             vf = -vec[2] / vec[1];
3192             v_shift = 0;
3193         } else {            // DOWN
3194             uf =  vec[0] / vec[1];
3195             vf = -vec[2] / vec[1];
3196             v_shift = eh;
3197         }
3198
3199         uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
3200         vf *= s->input_mirror_modifier[1];
3201
3202         uf = 0.5f * ew * (uf * scale + 1.f);
3203         vf = 0.5f * eh * (vf * scale + 1.f);
3204     }
3205
3206     ui = floorf(uf);
3207     vi = floorf(vf);
3208
3209     *du = uf - ui;
3210     *dv = vf - vi;
3211
3212     for (int i = 0; i < 4; i++) {
3213         for (int j = 0; j < 4; j++) {
3214             us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3215             vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3216         }
3217     }
3218
3219     return 1;
3220 }
3221
3222 /**
3223  * Calculate frame position in barrel split facebook's format for corresponding 3D coordinates on sphere.
3224  *
3225  * @param s filter private context
3226  * @param vec coordinates on sphere
3227  * @param width frame width
3228  * @param height frame height
3229  * @param us horizontal coordinates for interpolation window
3230  * @param vs vertical coordinates for interpolation window
3231  * @param du horizontal relative coordinate
3232  * @param dv vertical relative coordinate
3233  */
3234 static int xyz_to_barrelsplit(const V360Context *s,
3235                               const float *vec, int width, int height,
3236                               int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3237 {
3238     const float phi   = atan2f(vec[0], vec[2]) * s->input_mirror_modifier[0];
3239     const float theta = asinf(vec[1]) * s->input_mirror_modifier[1];
3240
3241     const float theta_range = M_PI_4;
3242
3243     int ew, eh;
3244     int u_shift, v_shift;
3245     float uf, vf;
3246     int ui, vi;
3247
3248     if (theta >= -theta_range && theta <= theta_range) {
3249         const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width * 2.f / 3.f) : 1.f - s->in_pad;
3250         const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 2.f) : 1.f - s->in_pad;
3251
3252         ew = width / 3 * 2;
3253         eh = height / 2;
3254
3255         u_shift = s->ih_flip ? width / 3 : 0;
3256         v_shift = phi >= M_PI_2 || phi < -M_PI_2 ? eh : 0;
3257
3258         uf = fmodf(phi, M_PI_2) / M_PI_2;
3259         vf = theta / M_PI_4;
3260
3261         if (v_shift)
3262             uf = uf >= 0.f ? fmodf(uf - 1.f, 1.f) : fmodf(uf + 1.f, 1.f);
3263
3264         uf = (uf * scalew + 1.f) * width  / 3.f;
3265         vf = (vf * scaleh + 1.f) * height / 4.f;
3266     } else {
3267         const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (width  / 3.f) : 1.f - s->in_pad;
3268         const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (height / 4.f) : 1.f - s->in_pad;
3269         int v_offset = 0;
3270
3271         ew = width  / 3;
3272         eh = height / 4;
3273
3274         u_shift = s->ih_flip ? 0 : 2 * ew;
3275
3276         if (theta <= 0.f && theta >= -M_PI_2 &&
3277             phi <= M_PI_2 && phi >= -M_PI_2) {
3278             uf = -vec[0] / vec[1];
3279             vf = -vec[2] / vec[1];
3280             v_shift = 0;
3281             v_offset = -eh;
3282         } else if (theta >= 0.f && theta <= M_PI_2 &&
3283                    phi <= M_PI_2 && phi >= -M_PI_2) {
3284             uf =  vec[0] / vec[1];
3285             vf = -vec[2] / vec[1];
3286             v_shift = height * 0.25f;
3287         } else if (theta <= 0.f && theta >= -M_PI_2) {
3288             uf =  vec[0] / vec[1];
3289             vf =  vec[2] / vec[1];
3290             v_shift = height * 0.5f;
3291             v_offset = -eh;
3292         } else {
3293             uf = -vec[0] / vec[1];
3294             vf =  vec[2] / vec[1];
3295             v_shift = height * 0.75f;
3296         }
3297
3298         uf *= s->input_mirror_modifier[0] * s->input_mirror_modifier[1];
3299         vf *= s->input_mirror_modifier[1];
3300
3301         uf = 0.5f * width / 3.f * (uf * scalew + 1.f);
3302         vf = height * 0.25f * (vf * scaleh + 1.f) + v_offset;
3303     }
3304
3305     ui = floorf(uf);
3306     vi = floorf(vf);
3307
3308     *du = uf - ui;
3309     *dv = vf - vi;
3310
3311     for (int i = 0; i < 4; i++) {
3312         for (int j = 0; j < 4; j++) {
3313             us[i][j] = u_shift + av_clip(ui + j - 1, 0, ew - 1);
3314             vs[i][j] = v_shift + av_clip(vi + i - 1, 0, eh - 1);
3315         }
3316     }
3317
3318     return 1;
3319 }
3320
3321 /**
3322  * Calculate 3D coordinates on sphere for corresponding frame position in barrel split facebook's format.
3323  *
3324  * @param s filter private context
3325  * @param i horizontal position on frame [0, width)
3326  * @param j vertical position on frame [0, height)
3327  * @param width frame width
3328  * @param height frame height
3329  * @param vec coordinates on sphere
3330  */
3331 static int barrelsplit_to_xyz(const V360Context *s,
3332                               int i, int j, int width, int height,
3333                               float *vec)
3334 {
3335     const float x = (i + 0.5f) / width;
3336     const float y = (j + 0.5f) / height;
3337     float l_x, l_y, l_z;
3338
3339     if (x < 2.f / 3.f) {
3340         const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width * 2.f / 3.f) : 1.f - s->out_pad;
3341         const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 2.f) : 1.f - s->out_pad;
3342
3343         const float back = floorf(y * 2.f);
3344
3345         const float phi   = ((3.f / 2.f * x - 0.5f) / scalew - back) * M_PI;
3346         const float theta = (y - 0.25f - 0.5f * back) / scaleh * M_PI;
3347
3348         const float sin_phi   = sinf(phi);
3349         const float cos_phi   = cosf(phi);
3350         const float sin_theta = sinf(theta);
3351         const float cos_theta = cosf(theta);
3352
3353         l_x = cos_theta * sin_phi;
3354         l_y = sin_theta;
3355         l_z = cos_theta * cos_phi;
3356     } else {
3357         const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (width  / 3.f) : 1.f - s->out_pad;
3358         const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (height / 4.f) : 1.f - s->out_pad;
3359
3360         const int face = floorf(y * 4.f);
3361         float uf, vf;
3362
3363         uf = x * 3.f - 2.f;
3364
3365         switch (face) {
3366         case 0:
3367             vf = y * 2.f;
3368             uf = 1.f - uf;
3369             vf = 0.5f - vf;
3370
3371             l_x = (0.5f - uf) / scalew;
3372             l_y = -0.5f;
3373             l_z = (0.5f - vf) / scaleh;
3374             break;
3375         case 1:
3376             vf = y * 2.f;
3377             uf = 1.f - uf;
3378             vf = 1.f - (vf - 0.5f);
3379
3380             l_x = (0.5f - uf) / scalew;
3381             l_y =  0.5f;
3382             l_z = (-0.5f + vf) / scaleh;
3383             break;
3384         case 2:
3385             vf = y * 2.f - 0.5f;
3386             vf = 1.f - (1.f - vf);
3387
3388             l_x = (0.5f - uf) / scalew;
3389             l_y = -0.5f;
3390             l_z = (0.5f - vf) / scaleh;
3391             break;
3392         case 3:
3393             vf = y * 2.f - 1.5f;
3394
3395             l_x = (0.5f - uf) / scalew;
3396             l_y =  0.5f;
3397             l_z = (-0.5f + vf) / scaleh;
3398             break;
3399         }
3400     }
3401
3402     vec[0] = l_x;
3403     vec[1] = l_y;
3404     vec[2] = l_z;
3405
3406     normalize_vector(vec);
3407
3408     return 1;
3409 }
3410
3411 /**
3412  * Calculate 3D coordinates on sphere for corresponding frame position in tspyramid format.
3413  *
3414  * @param s filter private context
3415  * @param i horizontal position on frame [0, width)
3416  * @param j vertical position on frame [0, height)
3417  * @param width frame width
3418  * @param height frame height
3419  * @param vec coordinates on sphere
3420  */
3421 static int tspyramid_to_xyz(const V360Context *s,
3422                             int i, int j, int width, int height,
3423                             float *vec)
3424 {
3425     const float x = (i + 0.5f) / width;
3426     const float y = (j + 0.5f) / height;
3427
3428     if (x < 0.5f) {
3429         vec[0] =  x * 4.f - 1.f;
3430         vec[1] = (y * 2.f - 1.f);
3431         vec[2] = 1.f;
3432     } else if (x >= 0.6875f && x < 0.8125f &&
3433                y >= 0.375f  && y < 0.625f) {
3434         vec[0] = -(x - 0.6875f) * 16.f + 1.f;
3435         vec[1] = (y - 0.375f) * 8.f - 1.f;
3436         vec[2] = -1.f;
3437     } else if (0.5f <= x && x < 0.6875f &&
3438                ((0.f <= y && y < 0.375f && y >= 2.f * (x - 0.5f)) ||
3439                 (0.375f <= y && y < 0.625f) ||
3440                 (0.625f <= y && y < 1.f && y <= 2.f * (1.f - x)))) {
3441         vec[0] =  1.f;
3442         vec[1] =  2.f * (y - 2.f * x + 1.f) / (3.f - 4.f * x) - 1.f;
3443         vec[2] = -2.f * (x - 0.5f) / 0.1875f + 1.f;
3444     } else if (0.8125f <= x && x < 1.f &&
3445                ((0.f <= y && y < 0.375f && x >= (1.f - y / 2.f)) ||
3446                 (0.375f <= y && y < 0.625f) ||
3447                 (0.625f <= y && y < 1.f && y <= (2.f * x - 1.f)))) {
3448         vec[0] = -1.f;
3449         vec[1] =  2.f * (y + 2.f * x - 2.f) / (4.f * x - 3.f) - 1.f;
3450         vec[2] =  2.f * (x - 0.8125f) / 0.1875f - 1.f;
3451     } else if (0.f <= y && y < 0.375f &&
3452                ((0.5f <= x && x < 0.8125f && y < 2.f * (x - 0.5f)) ||
3453                 (0.6875f <= x && x < 0.8125f) ||
3454                 (0.8125f <= x && x < 1.f && x < (1.f - y / 2.f)))) {
3455         vec[0] =  2.f * (1.f - x - 0.5f * y) / (0.5f - y) - 1.f;
3456         vec[1] = -1.f;
3457         vec[2] =  2.f * (0.375f - y) / 0.375f - 1.f;
3458     } else {
3459         vec[0] =  2.f * (0.5f - x + 0.5f * y) / (y - 0.5f) - 1.f;
3460         vec[1] =  1.f;
3461         vec[2] = -2.f * (1.f - y) / 0.375f + 1.f;
3462     }
3463
3464     normalize_vector(vec);
3465
3466     return 1;
3467 }
3468
3469 /**
3470  * Calculate frame position in tspyramid format for corresponding 3D coordinates on sphere.
3471  *
3472  * @param s filter private context
3473  * @param vec coordinates on sphere
3474  * @param width frame width
3475  * @param height frame height
3476  * @param us horizontal coordinates for interpolation window
3477  * @param vs vertical coordinates for interpolation window
3478  * @param du horizontal relative coordinate
3479  * @param dv vertical relative coordinate
3480  */
3481 static int xyz_to_tspyramid(const V360Context *s,
3482                             const float *vec, int width, int height,
3483                             int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
3484 {
3485     float uf, vf;
3486     int ui, vi;
3487     int face;
3488
3489     xyz_to_cube(s, vec, &uf, &vf, &face);
3490
3491     uf = (uf + 1.f) * 0.5f;
3492     vf = (vf + 1.f) * 0.5f;
3493
3494     switch (face) {
3495     case UP:
3496         uf = 0.1875f * vf - 0.375f * uf * vf - 0.125f * uf + 0.8125f;
3497         vf = 0.375f - 0.375f * vf;
3498         break;
3499     case FRONT:
3500         uf = 0.5f * uf;
3501         break;
3502     case DOWN:
3503         uf = 1.f - 0.1875f * vf - 0.5f * uf + 0.375f * uf * vf;
3504         vf = 1.f - 0.375f * vf;
3505         break;
3506     case LEFT:
3507         vf = 0.25f * vf + 0.75f * uf * vf - 0.375f * uf + 0.375f;
3508         uf = 0.1875f * uf + 0.8125f;
3509         break;
3510     case RIGHT:
3511         vf = 0.375f * uf - 0.75f * uf * vf + vf;
3512         uf = 0.1875f * uf + 0.5f;
3513         break;
3514     case BACK:
3515         uf = 0.125f * uf + 0.6875f;
3516         vf = 0.25f * vf + 0.375f;
3517         break;
3518     }
3519
3520     uf *= width;
3521     vf *= height;
3522
3523     ui = floorf(uf);
3524     vi = floorf(vf);
3525
3526     *du = uf - ui;
3527     *dv = vf - vi;
3528
3529     for (int i = 0; i < 4; i++) {
3530         for (int j = 0; j < 4; j++) {
3531             us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
3532             vs[i][j] = reflecty(vi + i - 1, height);
3533         }
3534     }
3535
3536     return 1;
3537 }
3538
3539 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
3540 {
3541     for (int i = 0; i < 3; i++) {
3542         for (int j = 0; j < 3; j++) {
3543             float sum = 0.f;
3544
3545             for (int k = 0; k < 3; k++)
3546                 sum += a[i][k] * b[k][j];
3547
3548             c[i][j] = sum;
3549         }
3550     }
3551 }
3552
3553 /**
3554  * Calculate rotation matrix for yaw/pitch/roll angles.
3555  */
3556 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
3557                                              float rot_mat[3][3],
3558                                              const int rotation_order[3])
3559 {
3560     const float yaw_rad   = yaw   * M_PI / 180.f;
3561     const float pitch_rad = pitch * M_PI / 180.f;
3562     const float roll_rad  = roll  * M_PI / 180.f;
3563
3564     const float sin_yaw   = sinf(yaw_rad);
3565     const float cos_yaw   = cosf(yaw_rad);
3566     const float sin_pitch = sinf(pitch_rad);
3567     const float cos_pitch = cosf(pitch_rad);
3568     const float sin_roll  = sinf(roll_rad);
3569     const float cos_roll  = cosf(roll_rad);
3570
3571     float m[3][3][3];
3572     float temp[3][3];
3573
3574     m[0][0][0] =  cos_yaw;  m[0][0][1] = 0;          m[0][0][2] =  sin_yaw;
3575     m[0][1][0] =  0;        m[0][1][1] = 1;          m[0][1][2] =  0;
3576     m[0][2][0] = -sin_yaw;  m[0][2][1] = 0;          m[0][2][2] =  cos_yaw;
3577
3578     m[1][0][0] = 1;         m[1][0][1] = 0;          m[1][0][2] =  0;
3579     m[1][1][0] = 0;         m[1][1][1] = cos_pitch;  m[1][1][2] = -sin_pitch;
3580     m[1][2][0] = 0;         m[1][2][1] = sin_pitch;  m[1][2][2] =  cos_pitch;
3581
3582     m[2][0][0] = cos_roll;  m[2][0][1] = -sin_roll;  m[2][0][2] =  0;
3583     m[2][1][0] = sin_roll;  m[2][1][1] =  cos_roll;  m[2][1][2] =  0;
3584     m[2][2][0] = 0;         m[2][2][1] =  0;         m[2][2][2] =  1;
3585
3586     multiply_matrix(temp, m[rotation_order[0]], m[rotation_order[1]]);
3587     multiply_matrix(rot_mat, temp, m[rotation_order[2]]);
3588 }
3589
3590 /**
3591  * Rotate vector with given rotation matrix.
3592  *
3593  * @param rot_mat rotation matrix
3594  * @param vec vector
3595  */
3596 static inline void rotate(const float rot_mat[3][3],
3597                           float *vec)
3598 {
3599     const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
3600     const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
3601     const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
3602
3603     vec[0] = x_tmp;
3604     vec[1] = y_tmp;
3605     vec[2] = z_tmp;
3606 }
3607
3608 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
3609                                        float *modifier)
3610 {
3611     modifier[0] = h_flip ? -1.f : 1.f;
3612     modifier[1] = v_flip ? -1.f : 1.f;
3613     modifier[2] = d_flip ? -1.f : 1.f;
3614 }
3615
3616 static inline void mirror(const float *modifier, float *vec)
3617 {
3618     vec[0] *= modifier[0];
3619     vec[1] *= modifier[1];
3620     vec[2] *= modifier[2];
3621 }
3622
3623 static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
3624 {
3625     if (!s->u[p])
3626         s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3627     if (!s->v[p])
3628         s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
3629     if (!s->u[p] || !s->v[p])
3630         return AVERROR(ENOMEM);
3631     if (sizeof_ker) {
3632         if (!s->ker[p])
3633             s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker);
3634         if (!s->ker[p])
3635             return AVERROR(ENOMEM);
3636     }
3637
3638     if (sizeof_mask && !p) {
3639         if (!s->mask)
3640             s->mask = av_calloc(s->pr_width[p] * s->pr_height[p], sizeof_mask);
3641         if (!s->mask)
3642             return AVERROR(ENOMEM);
3643     }
3644
3645     return 0;
3646 }
3647
3648 static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
3649 {
3650     switch (format) {
3651     case STEREOGRAPHIC:
3652         {
3653             const float d = 0.5f * hypotf(w, h);
3654             const float l = d / (tanf(d_fov * M_PI / 720.f));
3655
3656             *h_fov = 2.f * atan2f(w * 0.5f, l) * 360.f / M_PI;
3657             *v_fov = 2.f * atan2f(h * 0.5f, l) * 360.f / M_PI;
3658         }
3659         break;
3660     case FISHEYE:
3661         {
3662             const float d = 0.5f * hypotf(w, h);
3663
3664             *h_fov = d / w * d_fov;
3665             *v_fov = d / h * d_fov;
3666         }
3667         break;
3668     case FLAT:
3669     default:
3670         {
3671             const float da = tanf(0.5f * FFMIN(d_fov, 359.f) * M_PI / 180.f);
3672             const float d = hypotf(w, h);
3673
3674             *h_fov = atan2f(da * w, d) * 360.f / M_PI;
3675             *v_fov = atan2f(da * h, d) * 360.f / M_PI;
3676
3677             if (*h_fov < 0.f)
3678                 *h_fov += 360.f;
3679             if (*v_fov < 0.f)
3680                 *v_fov += 360.f;
3681         }
3682         break;
3683     }
3684 }
3685
3686 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
3687 {
3688     outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
3689     outw[0] = outw[3] = w;
3690     outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
3691     outh[0] = outh[3] = h;
3692 }
3693
3694 // Calculate remap data
3695 static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
3696 {
3697     V360Context *s = ctx->priv;
3698
3699     for (int p = 0; p < s->nb_allocated; p++) {
3700         const int max_value = s->max_value;
3701         const int width = s->pr_width[p];
3702         const int uv_linesize = s->uv_linesize[p];
3703         const int height = s->pr_height[p];
3704         const int in_width = s->inplanewidth[p];
3705         const int in_height = s->inplaneheight[p];
3706         const int slice_start = (height *  jobnr     ) / nb_jobs;
3707         const int slice_end   = (height * (jobnr + 1)) / nb_jobs;
3708         float du, dv;
3709         float vec[3];
3710         XYRemap rmap;
3711
3712         for (int j = slice_start; j < slice_end; j++) {
3713             for (int i = 0; i < width; i++) {
3714                 int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
3715                 int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
3716                 int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
3717                 uint8_t *mask8 = p ? NULL : s->mask + (j * s->pr_width[0] + i);
3718                 uint16_t *mask16 = p ? NULL : (uint16_t *)s->mask + (j * s->pr_width[0] + i);
3719                 int in_mask, out_mask;
3720
3721                 if (s->out_transpose)
3722                     out_mask = s->out_transform(s, j, i, height, width, vec);
3723                 else
3724                     out_mask = s->out_transform(s, i, j, width, height, vec);
3725                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3726                 rotate(s->rot_mat, vec);
3727                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
3728                 normalize_vector(vec);
3729                 mirror(s->output_mirror_modifier, vec);
3730                 if (s->in_transpose)
3731                     in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
3732                 else
3733                     in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
3734                 av_assert1(!isnan(du) && !isnan(dv));
3735                 s->calculate_kernel(du, dv, &rmap, u, v, ker);
3736
3737                 if (!p && s->mask) {
3738                     if (s->mask_size == 1) {
3739                         mask8[0] = 255 * (out_mask & in_mask);
3740                     } else {
3741                         mask16[0] = max_value * (out_mask & in_mask);
3742                     }
3743                 }
3744             }
3745         }
3746     }
3747
3748     return 0;
3749 }
3750
3751 static int config_output(AVFilterLink *outlink)
3752 {
3753     AVFilterContext *ctx = outlink->src;
3754     AVFilterLink *inlink = ctx->inputs[0];
3755     V360Context *s = ctx->priv;
3756     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
3757     const int depth = desc->comp[0].depth;
3758     const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
3759     int sizeof_uv;
3760     int sizeof_ker;
3761     int err;
3762     int h, w;
3763     int in_offset_h, in_offset_w;
3764     int out_offset_h, out_offset_w;
3765     float hf, wf;
3766     int (*prepare_out)(AVFilterContext *ctx);
3767     int have_alpha;
3768
3769     s->max_value = (1 << depth) - 1;
3770     s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
3771     s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
3772
3773     switch (s->interp) {
3774     case NEAREST:
3775         s->calculate_kernel = nearest_kernel;
3776         s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
3777         s->elements = 1;
3778         sizeof_uv = sizeof(int16_t) * s->elements;
3779         sizeof_ker = 0;
3780         break;
3781     case BILINEAR:
3782         s->calculate_kernel = bilinear_kernel;
3783         s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
3784         s->elements = 2 * 2;
3785         sizeof_uv = sizeof(int16_t) * s->elements;
3786         sizeof_ker = sizeof(int16_t) * s->elements;
3787         break;
3788     case LAGRANGE9:
3789         s->calculate_kernel = lagrange_kernel;
3790         s->remap_slice = depth <= 8 ? remap3_8bit_slice : remap3_16bit_slice;
3791         s->elements = 3 * 3;
3792         sizeof_uv = sizeof(int16_t) * s->elements;
3793         sizeof_ker = sizeof(int16_t) * s->elements;
3794         break;
3795     case BICUBIC:
3796         s->calculate_kernel = bicubic_kernel;
3797         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3798         s->elements = 4 * 4;
3799         sizeof_uv = sizeof(int16_t) * s->elements;
3800         sizeof_ker = sizeof(int16_t) * s->elements;
3801         break;
3802     case LANCZOS:
3803         s->calculate_kernel = lanczos_kernel;
3804         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3805         s->elements = 4 * 4;
3806         sizeof_uv = sizeof(int16_t) * s->elements;
3807         sizeof_ker = sizeof(int16_t) * s->elements;
3808         break;
3809     case SPLINE16:
3810         s->calculate_kernel = spline16_kernel;
3811         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3812         s->elements = 4 * 4;
3813         sizeof_uv = sizeof(int16_t) * s->elements;
3814         sizeof_ker = sizeof(int16_t) * s->elements;
3815         break;
3816     case GAUSSIAN:
3817         s->calculate_kernel = gaussian_kernel;
3818         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
3819         s->elements = 4 * 4;
3820         sizeof_uv = sizeof(int16_t) * s->elements;
3821         sizeof_ker = sizeof(int16_t) * s->elements;
3822         break;
3823     default:
3824         av_assert0(0);
3825     }
3826
3827     ff_v360_init(s, depth);
3828
3829     for (int order = 0; order < NB_RORDERS; order++) {
3830         const char c = s->rorder[order];
3831         int rorder;
3832
3833         if (c == '\0') {
3834             av_log(ctx, AV_LOG_WARNING,
3835                    "Incomplete rorder option. Direction for all 3 rotation orders should be specified. Switching to default rorder.\n");
3836             s->rotation_order[0] = YAW;
3837             s->rotation_order[1] = PITCH;
3838             s->rotation_order[2] = ROLL;
3839             break;
3840         }
3841
3842         rorder = get_rorder(c);
3843         if (rorder == -1) {
3844             av_log(ctx, AV_LOG_WARNING,
3845                    "Incorrect rotation order symbol '%c' in rorder option. Switching to default rorder.\n", c);
3846             s->rotation_order[0] = YAW;
3847             s->rotation_order[1] = PITCH;
3848             s->rotation_order[2] = ROLL;
3849             break;
3850         }
3851
3852         s->rotation_order[order] = rorder;
3853     }
3854
3855     switch (s->in_stereo) {
3856     case STEREO_2D:
3857         w = inlink->w;
3858         h = inlink->h;
3859         in_offset_w = in_offset_h = 0;
3860         break;
3861     case STEREO_SBS:
3862         w = inlink->w / 2;
3863         h = inlink->h;
3864         in_offset_w = w;
3865         in_offset_h = 0;
3866         break;
3867     case STEREO_TB:
3868         w = inlink->w;
3869         h = inlink->h / 2;
3870         in_offset_w = 0;
3871         in_offset_h = h;
3872         break;
3873     default:
3874         av_assert0(0);
3875     }
3876
3877     set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
3878     set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
3879
3880     s->in_width = s->inplanewidth[0];
3881     s->in_height = s->inplaneheight[0];
3882
3883     if (s->id_fov > 0.f)
3884         fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
3885
3886     if (s->in_transpose)
3887         FFSWAP(int, s->in_width, s->in_height);
3888
3889     switch (s->in) {
3890     case EQUIRECTANGULAR:
3891         s->in_transform = xyz_to_equirect;
3892         err = 0;
3893         wf = w;
3894         hf = h;
3895         break;
3896     case CUBEMAP_3_2:
3897         s->in_transform = xyz_to_cube3x2;
3898         err = prepare_cube_in(ctx);
3899         wf = w / 3.f * 4.f;
3900         hf = h;
3901         break;
3902     case CUBEMAP_1_6:
3903         s->in_transform = xyz_to_cube1x6;
3904         err = prepare_cube_in(ctx);
3905         wf = w * 4.f;
3906         hf = h / 3.f;
3907         break;
3908     case CUBEMAP_6_1:
3909         s->in_transform = xyz_to_cube6x1;
3910         err = prepare_cube_in(ctx);
3911         wf = w / 3.f * 2.f;
3912         hf = h * 2.f;
3913         break;
3914     case EQUIANGULAR:
3915         s->in_transform = xyz_to_eac;
3916         err = prepare_eac_in(ctx);
3917         wf = w;
3918         hf = h / 9.f * 8.f;
3919         break;
3920     case FLAT:
3921         s->in_transform = xyz_to_flat;
3922         err = prepare_flat_in(ctx);
3923         wf = w;
3924         hf = h;
3925         break;
3926     case PERSPECTIVE:
3927         av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
3928         return AVERROR(EINVAL);
3929     case DUAL_FISHEYE:
3930         s->in_transform = xyz_to_dfisheye;
3931         err = 0;
3932         wf = w;
3933         hf = h;
3934         break;
3935     case BARREL:
3936         s->in_transform = xyz_to_barrel;
3937         err = 0;
3938         wf = w / 5.f * 4.f;
3939         hf = h;
3940         break;
3941     case STEREOGRAPHIC:
3942         s->in_transform = xyz_to_stereographic;
3943         err = prepare_stereographic_in(ctx);
3944         wf = w;
3945         hf = h / 2.f;
3946         break;
3947     case MERCATOR:
3948         s->in_transform = xyz_to_mercator;
3949         err = 0;
3950         wf = w;
3951         hf = h / 2.f;
3952         break;
3953     case BALL:
3954         s->in_transform = xyz_to_ball;
3955         err = 0;
3956         wf = w;
3957         hf = h / 2.f;
3958         break;
3959     case HAMMER:
3960         s->in_transform = xyz_to_hammer;
3961         err = 0;
3962         wf = w;
3963         hf = h;
3964         break;
3965     case SINUSOIDAL:
3966         s->in_transform = xyz_to_sinusoidal;
3967         err = 0;
3968         wf = w;
3969         hf = h;
3970         break;
3971     case FISHEYE:
3972         s->in_transform = xyz_to_fisheye;
3973         err = prepare_fisheye_in(ctx);
3974         wf = w * 2;
3975         hf = h;
3976         break;
3977     case PANNINI:
3978         s->in_transform = xyz_to_pannini;
3979         err = 0;
3980         wf = w;
3981         hf = h;
3982         break;
3983     case CYLINDRICAL:
3984         s->in_transform = xyz_to_cylindrical;
3985         err = prepare_cylindrical_in(ctx);
3986         wf = w;
3987         hf = h * 2.f;
3988         break;
3989     case TETRAHEDRON:
3990         s->in_transform = xyz_to_tetrahedron;
3991         err = 0;
3992         wf = w;
3993         hf = h;
3994         break;
3995     case BARREL_SPLIT:
3996         s->in_transform = xyz_to_barrelsplit;
3997         err = 0;
3998         wf = w * 4.f / 3.f;
3999         hf = h;
4000         break;
4001     case TSPYRAMID:
4002         s->in_transform = xyz_to_tspyramid;
4003         err = 0;
4004         wf = w;
4005         hf = h;
4006         break;
4007     case HEQUIRECTANGULAR:
4008         s->in_transform = xyz_to_hequirect;
4009         err = 0;
4010         wf = w * 2.f;
4011         hf = h;
4012         break;
4013     default:
4014         av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
4015         return AVERROR_BUG;
4016     }
4017
4018     if (err != 0) {
4019         return err;
4020     }
4021
4022     switch (s->out) {
4023     case EQUIRECTANGULAR:
4024         s->out_transform = equirect_to_xyz;
4025         prepare_out = NULL;
4026         w = lrintf(wf);
4027         h = lrintf(hf);
4028         break;
4029     case CUBEMAP_3_2:
4030         s->out_transform = cube3x2_to_xyz;
4031         prepare_out = prepare_cube_out;
4032         w = lrintf(wf / 4.f * 3.f);
4033         h = lrintf(hf);
4034         break;
4035     case CUBEMAP_1_6:
4036         s->out_transform = cube1x6_to_xyz;
4037         prepare_out = prepare_cube_out;
4038         w = lrintf(wf / 4.f);
4039         h = lrintf(hf * 3.f);
4040         break;
4041     case CUBEMAP_6_1:
4042         s->out_transform = cube6x1_to_xyz;
4043         prepare_out = prepare_cube_out;
4044         w = lrintf(wf / 2.f * 3.f);
4045         h = lrintf(hf / 2.f);
4046         break;
4047     case EQUIANGULAR:
4048         s->out_transform = eac_to_xyz;
4049         prepare_out = prepare_eac_out;
4050         w = lrintf(wf);
4051         h = lrintf(hf / 8.f * 9.f);
4052         break;
4053     case FLAT:
4054         s->out_transform = flat_to_xyz;
4055         prepare_out = prepare_flat_out;
4056         w = lrintf(wf);
4057         h = lrintf(hf);
4058         break;
4059     case DUAL_FISHEYE:
4060         s->out_transform = dfisheye_to_xyz;
4061         prepare_out = NULL;
4062         w = lrintf(wf);
4063         h = lrintf(hf);
4064         break;
4065     case BARREL:
4066         s->out_transform = barrel_to_xyz;
4067         prepare_out = NULL;
4068         w = lrintf(wf / 4.f * 5.f);
4069         h = lrintf(hf);
4070         break;
4071     case STEREOGRAPHIC:
4072         s->out_transform = stereographic_to_xyz;
4073         prepare_out = prepare_stereographic_out;
4074         w = lrintf(wf);
4075         h = lrintf(hf * 2.f);
4076         break;
4077     case MERCATOR:
4078         s->out_transform = mercator_to_xyz;
4079         prepare_out = NULL;
4080         w = lrintf(wf);
4081         h = lrintf(hf * 2.f);
4082         break;
4083     case BALL:
4084         s->out_transform = ball_to_xyz;
4085         prepare_out = NULL;
4086         w = lrintf(wf);
4087         h = lrintf(hf * 2.f);
4088         break;
4089     case HAMMER:
4090         s->out_transform = hammer_to_xyz;
4091         prepare_out = NULL;
4092         w = lrintf(wf);
4093         h = lrintf(hf);
4094         break;
4095     case SINUSOIDAL:
4096         s->out_transform = sinusoidal_to_xyz;
4097         prepare_out = NULL;
4098         w = lrintf(wf);
4099         h = lrintf(hf);
4100         break;
4101     case FISHEYE:
4102         s->out_transform = fisheye_to_xyz;
4103         prepare_out = prepare_fisheye_out;
4104         w = lrintf(wf * 0.5f);
4105         h = lrintf(hf);
4106         break;
4107     case PANNINI:
4108         s->out_transform = pannini_to_xyz;
4109         prepare_out = NULL;
4110         w = lrintf(wf);
4111         h = lrintf(hf);
4112         break;
4113     case CYLINDRICAL:
4114         s->out_transform = cylindrical_to_xyz;
4115         prepare_out = prepare_cylindrical_out;
4116         w = lrintf(wf);
4117         h = lrintf(hf * 0.5f);
4118         break;
4119     case PERSPECTIVE:
4120         s->out_transform = perspective_to_xyz;
4121         prepare_out = NULL;
4122         w = lrintf(wf / 2.f);
4123         h = lrintf(hf);
4124         break;
4125     case TETRAHEDRON:
4126         s->out_transform = tetrahedron_to_xyz;
4127         prepare_out = NULL;
4128         w = lrintf(wf);
4129         h = lrintf(hf);
4130         break;
4131     case BARREL_SPLIT:
4132         s->out_transform = barrelsplit_to_xyz;
4133         prepare_out = NULL;
4134         w = lrintf(wf / 4.f * 3.f);
4135         h = lrintf(hf);
4136         break;
4137     case TSPYRAMID:
4138         s->out_transform = tspyramid_to_xyz;
4139         prepare_out = NULL;
4140         w = lrintf(wf);
4141         h = lrintf(hf);
4142         break;
4143     case HEQUIRECTANGULAR:
4144         s->out_transform = hequirect_to_xyz;
4145         prepare_out = NULL;
4146         w = lrintf(wf / 2.f);
4147         h = lrintf(hf);
4148         break;
4149     default:
4150         av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
4151         return AVERROR_BUG;
4152     }
4153
4154     // Override resolution with user values if specified
4155     if (s->width > 0 && s->height <= 0 && s->h_fov > 0.f && s->v_fov > 0.f &&
4156         s->out == FLAT && s->d_fov == 0.f) {
4157         w = s->width;
4158         h = w / tanf(s->h_fov * M_PI / 360.f) * tanf(s->v_fov * M_PI / 360.f);
4159     } else 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         h = s->height;
4162         w = h / tanf(s->v_fov * M_PI / 360.f) * tanf(s->h_fov * M_PI / 360.f);
4163     } else if (s->width > 0 && s->height > 0) {
4164         w = s->width;
4165         h = s->height;
4166     } else if (s->width > 0 || s->height > 0) {
4167         av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
4168         return AVERROR(EINVAL);
4169     } else {
4170         if (s->out_transpose)
4171             FFSWAP(int, w, h);
4172
4173         if (s->in_transpose)
4174             FFSWAP(int, w, h);
4175     }
4176
4177     s->width  = w;
4178     s->height = h;
4179
4180     if (s->d_fov > 0.f)
4181         fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
4182
4183     if (prepare_out) {
4184         err = prepare_out(ctx);
4185         if (err != 0)
4186             return err;
4187     }
4188
4189     set_dimensions(s->pr_width, s->pr_height, w, h, desc);
4190
4191     switch (s->out_stereo) {
4192     case STEREO_2D:
4193         out_offset_w = out_offset_h = 0;
4194         break;
4195     case STEREO_SBS:
4196         out_offset_w = w;
4197         out_offset_h = 0;
4198         w *= 2;
4199         break;
4200     case STEREO_TB:
4201         out_offset_w = 0;
4202         out_offset_h = h;
4203         h *= 2;
4204         break;
4205     default:
4206         av_assert0(0);
4207     }
4208
4209     set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc);
4210     set_dimensions(s->planewidth, s->planeheight, w, h, desc);
4211
4212     for (int i = 0; i < 4; i++)
4213         s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8);
4214
4215     outlink->h = h;
4216     outlink->w = w;
4217
4218     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
4219     have_alpha   = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
4220
4221     if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
4222         s->nb_allocated = 1;
4223         s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0;
4224     } else {
4225         s->nb_allocated = 2;
4226         s->map[0] = s->map[3] = 0;
4227         s->map[1] = s->map[2] = 1;
4228     }
4229
4230     for (int i = 0; i < s->nb_allocated; i++)
4231         allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
4232
4233     calculate_rotation_matrix(s->yaw, s->pitch, s->roll, s->rot_mat, s->rotation_order);
4234     set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier);
4235
4236     ctx->internal->execute(ctx, v360_slice, NULL, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
4237
4238     return 0;
4239 }
4240
4241 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
4242 {
4243     AVFilterContext *ctx = inlink->dst;
4244     AVFilterLink *outlink = ctx->outputs[0];
4245     V360Context *s = ctx->priv;
4246     AVFrame *out;
4247     ThreadData td;
4248
4249     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
4250     if (!out) {
4251         av_frame_free(&in);
4252         return AVERROR(ENOMEM);
4253     }
4254     av_frame_copy_props(out, in);
4255
4256     td.in = in;
4257     td.out = out;
4258
4259     ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
4260
4261     av_frame_free(&in);
4262     return ff_filter_frame(outlink, out);
4263 }
4264
4265 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
4266                            char *res, int res_len, int flags)
4267 {
4268     int ret;
4269
4270     ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
4271     if (ret < 0)
4272         return ret;
4273
4274     return config_output(ctx->outputs[0]);
4275 }
4276
4277 static av_cold void uninit(AVFilterContext *ctx)
4278 {
4279     V360Context *s = ctx->priv;
4280
4281     for (int p = 0; p < s->nb_allocated; p++) {
4282         av_freep(&s->u[p]);
4283         av_freep(&s->v[p]);
4284         av_freep(&s->ker[p]);
4285     }
4286     av_freep(&s->mask);
4287 }
4288
4289 static const AVFilterPad inputs[] = {
4290     {
4291         .name         = "default",
4292         .type         = AVMEDIA_TYPE_VIDEO,
4293         .filter_frame = filter_frame,
4294     },
4295     { NULL }
4296 };
4297
4298 static const AVFilterPad outputs[] = {
4299     {
4300         .name         = "default",
4301         .type         = AVMEDIA_TYPE_VIDEO,
4302         .config_props = config_output,
4303     },
4304     { NULL }
4305 };
4306
4307 AVFilter ff_vf_v360 = {
4308     .name          = "v360",
4309     .description   = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
4310     .priv_size     = sizeof(V360Context),
4311     .uninit        = uninit,
4312     .query_formats = query_formats,
4313     .inputs        = inputs,
4314     .outputs       = outputs,
4315     .priv_class    = &v360_class,
4316     .flags         = AVFILTER_FLAG_SLICE_THREADS,
4317     .process_command = process_command,
4318 };