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