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