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