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