]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_v360.c
fc120097d962a0642018fcd8510ac631fe0fd374
[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 "libavutil/avassert.h"
37 #include "libavutil/imgutils.h"
38 #include "libavutil/pixdesc.h"
39 #include "libavutil/opt.h"
40 #include "avfilter.h"
41 #include "formats.h"
42 #include "internal.h"
43 #include "video.h"
44
45 enum Projections {
46     EQUIRECTANGULAR,
47     CUBEMAP_3_2,
48     CUBEMAP_6_1,
49     EQUIANGULAR,
50     FLAT,
51     DUAL_FISHEYE,
52     BARREL,
53     CUBEMAP_1_6,
54     NB_PROJECTIONS,
55 };
56
57 enum InterpMethod {
58     NEAREST,
59     BILINEAR,
60     BICUBIC,
61     LANCZOS,
62     NB_INTERP_METHODS,
63 };
64
65 enum Faces {
66     TOP_LEFT,
67     TOP_MIDDLE,
68     TOP_RIGHT,
69     BOTTOM_LEFT,
70     BOTTOM_MIDDLE,
71     BOTTOM_RIGHT,
72     NB_FACES,
73 };
74
75 enum Direction {
76     RIGHT,  ///< Axis +X
77     LEFT,   ///< Axis -X
78     UP,     ///< Axis +Y
79     DOWN,   ///< Axis -Y
80     FRONT,  ///< Axis -Z
81     BACK,   ///< Axis +Z
82     NB_DIRECTIONS,
83 };
84
85 enum Rotation {
86     ROT_0,
87     ROT_90,
88     ROT_180,
89     ROT_270,
90     NB_ROTATIONS,
91 };
92
93 typedef struct V360Context {
94     const AVClass *class;
95     int in, out;
96     int interp;
97     int width, height;
98     char* in_forder;
99     char* out_forder;
100     char* in_frot;
101     char* out_frot;
102
103     int in_cubemap_face_order[6];
104     int out_cubemap_direction_order[6];
105     int in_cubemap_face_rotation[6];
106     int out_cubemap_face_rotation[6];
107
108     float in_pad, out_pad;
109
110     float yaw, pitch, roll;
111
112     int h_flip, v_flip, d_flip;
113
114     float h_fov, v_fov;
115     float flat_range[3];
116
117     int planewidth[4], planeheight[4];
118     int inplanewidth[4], inplaneheight[4];
119     int nb_planes;
120
121     uint16_t *u[4], *v[4];
122     int16_t *ker[4];
123
124     int (*remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
125 } V360Context;
126
127 typedef struct ThreadData {
128     AVFrame *in;
129     AVFrame *out;
130 } ThreadData;
131
132 #define OFFSET(x) offsetof(V360Context, x)
133 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
134
135 static const AVOption v360_options[] = {
136     {     "input", "set input projection",              OFFSET(in), AV_OPT_TYPE_INT,    {.i64=EQUIRECTANGULAR}, 0,    NB_PROJECTIONS-1, FLAGS, "in" },
137     {         "e", "equirectangular",                            0, AV_OPT_TYPE_CONST,  {.i64=EQUIRECTANGULAR}, 0,                   0, FLAGS, "in" },
138     {      "c3x2", "cubemap3x2",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_3_2},     0,                   0, FLAGS, "in" },
139     {      "c6x1", "cubemap6x1",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_6_1},     0,                   0, FLAGS, "in" },
140     {       "eac", "equi-angular cubemap",                       0, AV_OPT_TYPE_CONST,  {.i64=EQUIANGULAR},     0,                   0, FLAGS, "in" },
141     {  "dfisheye", "dual fisheye",                               0, AV_OPT_TYPE_CONST,  {.i64=DUAL_FISHEYE},    0,                   0, FLAGS, "in" },
142     {    "barrel", "barrel facebook's 360 format",               0, AV_OPT_TYPE_CONST,  {.i64=BARREL},          0,                   0, FLAGS, "in" },
143     {        "fb", "barrel facebook's 360 format",               0, AV_OPT_TYPE_CONST,  {.i64=BARREL},          0,                   0, FLAGS, "in" },
144     {      "c1x6", "cubemap1x6",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_1_6},     0,                   0, FLAGS, "in" },
145     {    "output", "set output projection",            OFFSET(out), AV_OPT_TYPE_INT,    {.i64=CUBEMAP_3_2},     0,    NB_PROJECTIONS-1, FLAGS, "out" },
146     {         "e", "equirectangular",                            0, AV_OPT_TYPE_CONST,  {.i64=EQUIRECTANGULAR}, 0,                   0, FLAGS, "out" },
147     {      "c3x2", "cubemap3x2",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_3_2},     0,                   0, FLAGS, "out" },
148     {      "c6x1", "cubemap6x1",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_6_1},     0,                   0, FLAGS, "out" },
149     {       "eac", "equi-angular cubemap",                       0, AV_OPT_TYPE_CONST,  {.i64=EQUIANGULAR},     0,                   0, FLAGS, "out" },
150     {      "flat", "regular video",                              0, AV_OPT_TYPE_CONST,  {.i64=FLAT},            0,                   0, FLAGS, "out" },
151     {    "barrel", "barrel facebook's 360 format",               0, AV_OPT_TYPE_CONST,  {.i64=BARREL},          0,                   0, FLAGS, "out" },
152     {        "fb", "barrel facebook's 360 format",               0, AV_OPT_TYPE_CONST,  {.i64=BARREL},          0,                   0, FLAGS, "out" },
153     {      "c1x6", "cubemap1x6",                                 0, AV_OPT_TYPE_CONST,  {.i64=CUBEMAP_1_6},     0,                   0, FLAGS, "out" },
154     {    "interp", "set interpolation method",      OFFSET(interp), AV_OPT_TYPE_INT,    {.i64=BILINEAR},        0, NB_INTERP_METHODS-1, FLAGS, "interp" },
155     {      "near", "nearest neighbour",                          0, AV_OPT_TYPE_CONST,  {.i64=NEAREST},         0,                   0, FLAGS, "interp" },
156     {   "nearest", "nearest neighbour",                          0, AV_OPT_TYPE_CONST,  {.i64=NEAREST},         0,                   0, FLAGS, "interp" },
157     {      "line", "bilinear interpolation",                     0, AV_OPT_TYPE_CONST,  {.i64=BILINEAR},        0,                   0, FLAGS, "interp" },
158     {    "linear", "bilinear interpolation",                     0, AV_OPT_TYPE_CONST,  {.i64=BILINEAR},        0,                   0, FLAGS, "interp" },
159     {      "cube", "bicubic interpolation",                      0, AV_OPT_TYPE_CONST,  {.i64=BICUBIC},         0,                   0, FLAGS, "interp" },
160     {     "cubic", "bicubic interpolation",                      0, AV_OPT_TYPE_CONST,  {.i64=BICUBIC},         0,                   0, FLAGS, "interp" },
161     {      "lanc", "lanczos interpolation",                      0, AV_OPT_TYPE_CONST,  {.i64=LANCZOS},         0,                   0, FLAGS, "interp" },
162     {   "lanczos", "lanczos interpolation",                      0, AV_OPT_TYPE_CONST,  {.i64=LANCZOS},         0,                   0, FLAGS, "interp" },
163     {         "w", "output width",                   OFFSET(width), AV_OPT_TYPE_INT,    {.i64=0},               0,           INT16_MAX, FLAGS, "w"},
164     {         "h", "output height",                 OFFSET(height), AV_OPT_TYPE_INT,    {.i64=0},               0,           INT16_MAX, FLAGS, "h"},
165     { "in_forder", "input cubemap face order",   OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"},        0,     NB_DIRECTIONS-1, FLAGS, "in_forder"},
166     {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"},        0,     NB_DIRECTIONS-1, FLAGS, "out_forder"},
167     {   "in_frot", "input cubemap face rotation",  OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"},        0,     NB_DIRECTIONS-1, FLAGS, "in_frot"},
168     {  "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"},        0,     NB_DIRECTIONS-1, FLAGS, "out_frot"},
169     {    "in_pad", "input cubemap pads",            OFFSET(in_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "in_pad"},
170     {   "out_pad", "output cubemap pads",          OFFSET(out_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "out_pad"},
171     {       "yaw", "yaw rotation",                     OFFSET(yaw), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "yaw"},
172     {     "pitch", "pitch rotation",                 OFFSET(pitch), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "pitch"},
173     {      "roll", "roll rotation",                   OFFSET(roll), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "roll"},
174     {     "h_fov", "horizontal field of view",       OFFSET(h_fov), AV_OPT_TYPE_FLOAT,  {.dbl=90.f},          0.f,               180.f, FLAGS, "h_fov"},
175     {     "v_fov", "vertical field of view",         OFFSET(v_fov), AV_OPT_TYPE_FLOAT,  {.dbl=45.f},          0.f,                90.f, FLAGS, "v_fov"},
176     {    "h_flip", "flip video horizontally",       OFFSET(h_flip), AV_OPT_TYPE_BOOL,   {.i64=0},               0,                   1, FLAGS, "h_flip"},
177     {    "v_flip", "flip video vertically",         OFFSET(v_flip), AV_OPT_TYPE_BOOL,   {.i64=0},               0,                   1, FLAGS, "v_flip"},
178     {    "d_flip", "flip video indepth",            OFFSET(d_flip), AV_OPT_TYPE_BOOL,   {.i64=0},               0,                   1, FLAGS, "d_flip"},
179     { NULL }
180 };
181
182 AVFILTER_DEFINE_CLASS(v360);
183
184 static int query_formats(AVFilterContext *ctx)
185 {
186     static const enum AVPixelFormat pix_fmts[] = {
187         // YUVA444
188         AV_PIX_FMT_YUVA444P,   AV_PIX_FMT_YUVA444P9,
189         AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12,
190         AV_PIX_FMT_YUVA444P16,
191
192         // YUVA422
193         AV_PIX_FMT_YUVA422P,   AV_PIX_FMT_YUVA422P9,
194         AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12,
195         AV_PIX_FMT_YUVA422P16,
196
197         // YUVA420
198         AV_PIX_FMT_YUVA420P,   AV_PIX_FMT_YUVA420P9,
199         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
200
201         // YUVJ
202         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
203         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
204         AV_PIX_FMT_YUVJ411P,
205
206         // YUV444
207         AV_PIX_FMT_YUV444P,   AV_PIX_FMT_YUV444P9,
208         AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12,
209         AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16,
210
211         // YUV440
212         AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV440P10,
213         AV_PIX_FMT_YUV440P12,
214
215         // YUV422
216         AV_PIX_FMT_YUV422P,   AV_PIX_FMT_YUV422P9,
217         AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12,
218         AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV422P16,
219
220         // YUV420
221         AV_PIX_FMT_YUV420P,   AV_PIX_FMT_YUV420P9,
222         AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12,
223         AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16,
224
225         // YUV411
226         AV_PIX_FMT_YUV411P,
227
228         // YUV410
229         AV_PIX_FMT_YUV410P,
230
231         // GBR
232         AV_PIX_FMT_GBRP,   AV_PIX_FMT_GBRP9,
233         AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
234         AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
235
236         // GBRA
237         AV_PIX_FMT_GBRAP,   AV_PIX_FMT_GBRAP10,
238         AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
239
240         // GRAY
241         AV_PIX_FMT_GRAY8,  AV_PIX_FMT_GRAY9,
242         AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12,
243         AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
244
245         AV_PIX_FMT_NONE
246     };
247
248     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
249     if (!fmts_list)
250         return AVERROR(ENOMEM);
251     return ff_set_common_formats(ctx, fmts_list);
252 }
253
254 /**
255  * Generate no-interpolation remapping function with a given pixel depth.
256  *
257  * @param bits number of bits per pixel
258  * @param div number of bytes per pixel
259  */
260 #define DEFINE_REMAP1(bits, div)                                                             \
261 static int remap1_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
262 {                                                                                            \
263     ThreadData *td = (ThreadData*)arg;                                                       \
264     const V360Context *s = ctx->priv;                                                        \
265     const AVFrame *in = td->in;                                                              \
266     AVFrame *out = td->out;                                                                  \
267                                                                                              \
268     int plane, x, y;                                                                         \
269                                                                                              \
270     for (plane = 0; plane < s->nb_planes; plane++) {                                         \
271         const int in_linesize  = in->linesize[plane]  / div;                                 \
272         const int out_linesize = out->linesize[plane] / div;                                 \
273         const uint##bits##_t *src = (const uint##bits##_t *)in->data[plane];                 \
274         uint##bits##_t *dst = (uint##bits##_t *)out->data[plane];                            \
275         const int width = s->planewidth[plane];                                              \
276         const int height = s->planeheight[plane];                                            \
277                                                                                              \
278         const int slice_start = (height *  jobnr     ) / nb_jobs;                            \
279         const int slice_end   = (height * (jobnr + 1)) / nb_jobs;                            \
280                                                                                              \
281         for (y = slice_start; y < slice_end; y++) {                                          \
282             const uint16_t *u = s->u[plane] + y * width;                                     \
283             const uint16_t *v = s->v[plane] + y * width;                                     \
284             uint##bits##_t *d = dst + y * out_linesize;                                      \
285             for (x = 0; x < width; x++)                                                      \
286                 *d++ = src[v[x] * in_linesize + u[x]];                                       \
287         }                                                                                    \
288     }                                                                                        \
289                                                                                              \
290     return 0;                                                                                \
291 }
292
293 DEFINE_REMAP1( 8, 1)
294 DEFINE_REMAP1(16, 2)
295
296 typedef struct XYRemap {
297     uint16_t u[4][4];
298     uint16_t v[4][4];
299     float ker[4][4];
300 } XYRemap;
301
302 /**
303  * Generate remapping function with a given window size and pixel depth.
304  *
305  * @param ws size of interpolation window
306  * @param bits number of bits per pixel
307  * @param div number of bytes per pixel
308  */
309 #define DEFINE_REMAP(ws, bits, div)                                                                        \
310 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)          \
311 {                                                                                                          \
312     ThreadData *td = (ThreadData*)arg;                                                                     \
313     const V360Context *s = ctx->priv;                                                                      \
314     const AVFrame *in = td->in;                                                                            \
315     AVFrame *out = td->out;                                                                                \
316                                                                                                            \
317     int plane, x, y, i, j;                                                                                 \
318                                                                                                            \
319     for (plane = 0; plane < s->nb_planes; plane++) {                                                       \
320         const int in_linesize  = in->linesize[plane]  / div;                                               \
321         const int out_linesize = out->linesize[plane] / div;                                               \
322         const uint##bits##_t *src = (const uint##bits##_t *)in->data[plane];                               \
323         uint##bits##_t *dst = (uint##bits##_t *)out->data[plane];                                          \
324         const int width = s->planewidth[plane];                                                            \
325         const int height = s->planeheight[plane];                                                          \
326                                                                                                            \
327         const int slice_start = (height *  jobnr     ) / nb_jobs;                                          \
328         const int slice_end   = (height * (jobnr + 1)) / nb_jobs;                                          \
329                                                                                                            \
330         for (y = slice_start; y < slice_end; y++) {                                                        \
331             uint##bits##_t *d = dst + y * out_linesize;                                                    \
332             const uint16_t *u = s->u[plane] + y * width * ws * ws;                                         \
333             const uint16_t *v = s->v[plane] + y * width * ws * ws;                                         \
334             const int16_t *ker = s->ker[plane] + y * width * ws * ws;                                      \
335             for (x = 0; x < width; x++) {                                                                  \
336                 const uint16_t *uu = u + x * ws * ws;                                                      \
337                 const uint16_t *vv = v + x * ws * ws;                                                      \
338                 const int16_t *kker = ker + x * ws * ws;                                                   \
339                 int tmp = 0;                                                                               \
340                                                                                                            \
341                 for (i = 0; i < ws; i++) {                                                                 \
342                     for (j = 0; j < ws; j++) {                                                             \
343                         tmp += kker[i * ws + j] * src[vv[i * ws + j] * in_linesize + uu[i * ws + j]];      \
344                     }                                                                                      \
345                 }                                                                                          \
346                                                                                                            \
347                 *d++ = av_clip_uint##bits(tmp >> (15 - ws));                                               \
348             }                                                                                              \
349         }                                                                                                  \
350     }                                                                                                      \
351                                                                                                            \
352     return 0;                                                                                              \
353 }
354
355 DEFINE_REMAP(2,  8, 1)
356 DEFINE_REMAP(4,  8, 1)
357 DEFINE_REMAP(2, 16, 2)
358 DEFINE_REMAP(4, 16, 2)
359
360 /**
361  * Save nearest pixel coordinates for remapping.
362  *
363  * @param du horizontal relative coordinate
364  * @param dv vertical relative coordinate
365  * @param r_tmp calculated 4x4 window
366  * @param u u remap data
367  * @param v v remap data
368  * @param ker ker remap data
369  */
370 static void nearest_kernel(float du, float dv, const XYRemap *r_tmp,
371                            uint16_t *u, uint16_t *v, int16_t *ker)
372 {
373     const int i = roundf(dv) + 1;
374     const int j = roundf(du) + 1;
375
376     u[0] = r_tmp->u[i][j];
377     v[0] = r_tmp->v[i][j];
378 }
379
380 /**
381  * Calculate kernel for bilinear interpolation.
382  *
383  * @param du horizontal relative coordinate
384  * @param dv vertical relative coordinate
385  * @param r_tmp calculated 4x4 window
386  * @param u u remap data
387  * @param v v remap data
388  * @param ker ker remap data
389  */
390 static void bilinear_kernel(float du, float dv, const XYRemap *r_tmp,
391                             uint16_t *u, uint16_t *v, int16_t *ker)
392 {
393     int i, j;
394
395     for (i = 0; i < 2; i++) {
396         for (j = 0; j < 2; j++) {
397             u[i * 2 + j] = r_tmp->u[i + 1][j + 1];
398             v[i * 2 + j] = r_tmp->v[i + 1][j + 1];
399         }
400     }
401
402     ker[0] = (1.f - du) * (1.f - dv) * 8192;
403     ker[1] =        du  * (1.f - dv) * 8192;
404     ker[2] = (1.f - du) *        dv  * 8192;
405     ker[3] =        du  *        dv  * 8192;
406 }
407
408 /**
409  * Calculate 1-dimensional cubic coefficients.
410  *
411  * @param t relative coordinate
412  * @param coeffs coefficients
413  */
414 static inline void calculate_bicubic_coeffs(float t, float *coeffs)
415 {
416     const float tt  = t * t;
417     const float ttt = t * t * t;
418
419     coeffs[0] =     - t / 3.f + tt / 2.f - ttt / 6.f;
420     coeffs[1] = 1.f - t / 2.f - tt       + ttt / 2.f;
421     coeffs[2] =       t       + tt / 2.f - ttt / 2.f;
422     coeffs[3] =     - t / 6.f            + ttt / 6.f;
423 }
424
425 /**
426  * Calculate kernel for bicubic interpolation.
427  *
428  * @param du horizontal relative coordinate
429  * @param dv vertical relative coordinate
430  * @param r_tmp calculated 4x4 window
431  * @param u u remap data
432  * @param v v remap data
433  * @param ker ker remap data
434  */
435 static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp,
436                            uint16_t *u, uint16_t *v, int16_t *ker)
437 {
438     int i, j;
439     float du_coeffs[4];
440     float dv_coeffs[4];
441
442     calculate_bicubic_coeffs(du, du_coeffs);
443     calculate_bicubic_coeffs(dv, dv_coeffs);
444
445     for (i = 0; i < 4; i++) {
446         for (j = 0; j < 4; j++) {
447             u[i * 4 + j] = r_tmp->u[i][j];
448             v[i * 4 + j] = r_tmp->v[i][j];
449             ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 2048;
450         }
451     }
452 }
453
454 /**
455  * Calculate 1-dimensional lanczos coefficients.
456  *
457  * @param t relative coordinate
458  * @param coeffs coefficients
459  */
460 static inline void calculate_lanczos_coeffs(float t, float *coeffs)
461 {
462     int i;
463     float sum = 0.f;
464
465     for (i = 0; i < 4; i++) {
466         const float x = M_PI * (t - i + 1);
467         if (x == 0.f) {
468             coeffs[i] = 1.f;
469         } else {
470             coeffs[i] = sinf(x) * sinf(x / 2.f) / (x * x / 2.f);
471         }
472         sum += coeffs[i];
473     }
474
475     for (i = 0; i < 4; i++) {
476         coeffs[i] /= sum;
477     }
478 }
479
480 /**
481  * Calculate kernel for lanczos interpolation.
482  *
483  * @param du horizontal relative coordinate
484  * @param dv vertical relative coordinate
485  * @param r_tmp calculated 4x4 window
486  * @param u u remap data
487  * @param v v remap data
488  * @param ker ker remap data
489  */
490 static void lanczos_kernel(float du, float dv, const XYRemap *r_tmp,
491                            uint16_t *u, uint16_t *v, int16_t *ker)
492 {
493     int i, j;
494     float du_coeffs[4];
495     float dv_coeffs[4];
496
497     calculate_lanczos_coeffs(du, du_coeffs);
498     calculate_lanczos_coeffs(dv, dv_coeffs);
499
500     for (i = 0; i < 4; i++) {
501         for (j = 0; j < 4; j++) {
502             u[i * 4 + j] = r_tmp->u[i][j];
503             v[i * 4 + j] = r_tmp->v[i][j];
504             ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 2048;
505         }
506     }
507 }
508
509 /**
510  * Modulo operation with only positive remainders.
511  *
512  * @param a dividend
513  * @param b divisor
514  *
515  * @return positive remainder of (a / b)
516  */
517 static inline int mod(int a, int b)
518 {
519     const int res = a % b;
520     if (res < 0) {
521         return res + b;
522     } else {
523         return res;
524     }
525 }
526
527 /**
528  * Convert char to corresponding direction.
529  * Used for cubemap options.
530  */
531 static int get_direction(char c)
532 {
533     switch (c) {
534     case 'r':
535         return RIGHT;
536     case 'l':
537         return LEFT;
538     case 'u':
539         return UP;
540     case 'd':
541         return DOWN;
542     case 'f':
543         return FRONT;
544     case 'b':
545         return BACK;
546     default:
547         return -1;
548     }
549 }
550
551 /**
552  * Convert char to corresponding rotation angle.
553  * Used for cubemap options.
554  */
555 static int get_rotation(char c)
556 {
557     switch (c) {
558     case '0':
559         return ROT_0;
560     case '1':
561         return ROT_90;
562     case '2':
563         return ROT_180;
564     case '3':
565         return ROT_270;
566     default:
567         return -1;
568     }
569 }
570
571 /**
572  * Prepare data for processing cubemap input format.
573  *
574  * @param ctx filter context
575  *
576  * @return error code
577  */
578 static int prepare_cube_in(AVFilterContext *ctx)
579 {
580     V360Context *s = ctx->priv;
581
582     for (int face = 0; face < NB_FACES; face++) {
583         const char c = s->in_forder[face];
584         int direction;
585
586         if (c == '\0') {
587             av_log(ctx, AV_LOG_ERROR,
588                    "Incomplete in_forder option. Direction for all 6 faces should be specified.\n");
589             return AVERROR(EINVAL);
590         }
591
592         direction = get_direction(c);
593         if (direction == -1) {
594             av_log(ctx, AV_LOG_ERROR,
595                    "Incorrect direction symbol '%c' in in_forder option.\n", c);
596             return AVERROR(EINVAL);
597         }
598
599         s->in_cubemap_face_order[direction] = face;
600     }
601
602     for (int face = 0; face < NB_FACES; face++) {
603         const char c = s->in_frot[face];
604         int rotation;
605
606         if (c == '\0') {
607             av_log(ctx, AV_LOG_ERROR,
608                    "Incomplete in_frot option. Rotation for all 6 faces should be specified.\n");
609             return AVERROR(EINVAL);
610         }
611
612         rotation = get_rotation(c);
613         if (rotation == -1) {
614             av_log(ctx, AV_LOG_ERROR,
615                    "Incorrect rotation symbol '%c' in in_frot option.\n", c);
616             return AVERROR(EINVAL);
617         }
618
619         s->in_cubemap_face_rotation[face] = rotation;
620     }
621
622     return 0;
623 }
624
625 /**
626  * Prepare data for processing cubemap output format.
627  *
628  * @param ctx filter context
629  *
630  * @return error code
631  */
632 static int prepare_cube_out(AVFilterContext *ctx)
633 {
634     V360Context *s = ctx->priv;
635
636     for (int face = 0; face < NB_FACES; face++) {
637         const char c = s->out_forder[face];
638         int direction;
639
640         if (c == '\0') {
641             av_log(ctx, AV_LOG_ERROR,
642                    "Incomplete out_forder option. Direction for all 6 faces should be specified.\n");
643             return AVERROR(EINVAL);
644         }
645
646         direction = get_direction(c);
647         if (direction == -1) {
648             av_log(ctx, AV_LOG_ERROR,
649                    "Incorrect direction symbol '%c' in out_forder option.\n", c);
650             return AVERROR(EINVAL);
651         }
652
653         s->out_cubemap_direction_order[face] = direction;
654     }
655
656     for (int face = 0; face < NB_FACES; face++) {
657         const char c = s->out_frot[face];
658         int rotation;
659
660         if (c == '\0') {
661             av_log(ctx, AV_LOG_ERROR,
662                    "Incomplete out_frot option. Rotation for all 6 faces should be specified.\n");
663             return AVERROR(EINVAL);
664         }
665
666         rotation = get_rotation(c);
667         if (rotation == -1) {
668             av_log(ctx, AV_LOG_ERROR,
669                    "Incorrect rotation symbol '%c' in out_frot option.\n", c);
670             return AVERROR(EINVAL);
671         }
672
673         s->out_cubemap_face_rotation[face] = rotation;
674     }
675
676     return 0;
677 }
678
679 static inline void rotate_cube_face(float *uf, float *vf, int rotation)
680 {
681     float tmp;
682
683     switch (rotation) {
684     case ROT_0:
685         break;
686     case ROT_90:
687         tmp =  *uf;
688         *uf = -*vf;
689         *vf =  tmp;
690         break;
691     case ROT_180:
692         *uf = -*uf;
693         *vf = -*vf;
694         break;
695     case ROT_270:
696         tmp = -*uf;
697         *uf =  *vf;
698         *vf =  tmp;
699         break;
700     default:
701         av_assert0(0);
702     }
703 }
704
705 static inline void rotate_cube_face_inverse(float *uf, float *vf, int rotation)
706 {
707     float tmp;
708
709     switch (rotation) {
710     case ROT_0:
711         break;
712     case ROT_90:
713         tmp = -*uf;
714         *uf =  *vf;
715         *vf =  tmp;
716         break;
717     case ROT_180:
718         *uf = -*uf;
719         *vf = -*vf;
720         break;
721     case ROT_270:
722         tmp =  *uf;
723         *uf = -*vf;
724         *vf =  tmp;
725         break;
726     default:
727         av_assert0(0);
728     }
729 }
730
731 /**
732  * Calculate 3D coordinates on sphere for corresponding cubemap position.
733  * Common operation for every cubemap.
734  *
735  * @param s filter context
736  * @param uf horizontal cubemap coordinate [0, 1)
737  * @param vf vertical cubemap coordinate [0, 1)
738  * @param face face of cubemap
739  * @param vec coordinates on sphere
740  */
741 static void cube_to_xyz(const V360Context *s,
742                         float uf, float vf, int face,
743                         float *vec)
744 {
745     const int direction = s->out_cubemap_direction_order[face];
746     float norm;
747     float l_x, l_y, l_z;
748
749     uf /= (1.f - s->out_pad);
750     vf /= (1.f - s->out_pad);
751
752     rotate_cube_face_inverse(&uf, &vf, s->out_cubemap_face_rotation[face]);
753
754     switch (direction) {
755     case RIGHT:
756         l_x =  1.f;
757         l_y = -vf;
758         l_z =  uf;
759         break;
760     case LEFT:
761         l_x = -1.f;
762         l_y = -vf;
763         l_z = -uf;
764         break;
765     case UP:
766         l_x =  uf;
767         l_y =  1.f;
768         l_z = -vf;
769         break;
770     case DOWN:
771         l_x =  uf;
772         l_y = -1.f;
773         l_z =  vf;
774         break;
775     case FRONT:
776         l_x =  uf;
777         l_y = -vf;
778         l_z = -1.f;
779         break;
780     case BACK:
781         l_x = -uf;
782         l_y = -vf;
783         l_z =  1.f;
784         break;
785     }
786
787     norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
788     vec[0] = l_x / norm;
789     vec[1] = l_y / norm;
790     vec[2] = l_z / norm;
791 }
792
793 /**
794  * Calculate cubemap position for corresponding 3D coordinates on sphere.
795  * Common operation for every cubemap.
796  *
797  * @param s filter context
798  * @param vec coordinated on sphere
799  * @param uf horizontal cubemap coordinate [0, 1)
800  * @param vf vertical cubemap coordinate [0, 1)
801  * @param direction direction of view
802  */
803 static void xyz_to_cube(const V360Context *s,
804                         const float *vec,
805                         float *uf, float *vf, int *direction)
806 {
807     const float phi   = atan2f(vec[0], -vec[2]);
808     const float theta = asinf(-vec[1]);
809     float phi_norm, theta_threshold;
810     int face;
811
812     if (phi >= -M_PI_4 && phi < M_PI_4) {
813         *direction = FRONT;
814         phi_norm = phi;
815     } else if (phi >= -(M_PI_2 + M_PI_4) && phi < -M_PI_4) {
816         *direction = LEFT;
817         phi_norm = phi + M_PI_2;
818     } else if (phi >= M_PI_4 && phi < M_PI_2 + M_PI_4) {
819         *direction = RIGHT;
820         phi_norm = phi - M_PI_2;
821     } else {
822         *direction = BACK;
823         phi_norm = phi + ((phi > 0.f) ? -M_PI : M_PI);
824     }
825
826     theta_threshold = atanf(cosf(phi_norm));
827     if (theta > theta_threshold) {
828         *direction = DOWN;
829     } else if (theta < -theta_threshold) {
830         *direction = UP;
831     }
832
833     switch (*direction) {
834     case RIGHT:
835         *uf =  vec[2] / vec[0];
836         *vf = -vec[1] / vec[0];
837         break;
838     case LEFT:
839         *uf =  vec[2] / vec[0];
840         *vf =  vec[1] / vec[0];
841         break;
842     case UP:
843         *uf =  vec[0] / vec[1];
844         *vf = -vec[2] / vec[1];
845         break;
846     case DOWN:
847         *uf = -vec[0] / vec[1];
848         *vf = -vec[2] / vec[1];
849         break;
850     case FRONT:
851         *uf = -vec[0] / vec[2];
852         *vf =  vec[1] / vec[2];
853         break;
854     case BACK:
855         *uf = -vec[0] / vec[2];
856         *vf = -vec[1] / vec[2];
857         break;
858     default:
859         av_assert0(0);
860     }
861
862     face = s->in_cubemap_face_order[*direction];
863     rotate_cube_face(uf, vf, s->in_cubemap_face_rotation[face]);
864 }
865
866 /**
867  * Find position on another cube face in case of overflow/underflow.
868  * Used for calculation of interpolation window.
869  *
870  * @param s filter context
871  * @param uf horizontal cubemap coordinate
872  * @param vf vertical cubemap coordinate
873  * @param direction direction of view
874  * @param new_uf new horizontal cubemap coordinate
875  * @param new_vf new vertical cubemap coordinate
876  * @param face face position on cubemap
877  */
878 static void process_cube_coordinates(const V360Context *s,
879                                      float uf, float vf, int direction,
880                                      float *new_uf, float *new_vf, int *face)
881 {
882     /*
883      *  Cubemap orientation
884      *
885      *           width
886      *         <------->
887      *         +-------+
888      *         |       |                              U
889      *         | up    |                   h       ------->
890      * +-------+-------+-------+-------+ ^ e      |
891      * |       |       |       |       | | i    V |
892      * | left  | front | right | back  | | g      |
893      * +-------+-------+-------+-------+ v h      v
894      *         |       |                   t
895      *         | down  |
896      *         +-------+
897      */
898
899     *face = s->in_cubemap_face_order[direction];
900     rotate_cube_face_inverse(&uf, &vf, s->in_cubemap_face_rotation[*face]);
901
902     if ((uf < -1.f || uf >= 1.f) && (vf < -1.f || vf >= 1.f)) {
903         // There are no pixels to use in this case
904         *new_uf = uf;
905         *new_vf = vf;
906     } else if (uf < -1.f) {
907         uf += 2.f;
908         switch (direction) {
909         case RIGHT:
910             direction = FRONT;
911             *new_uf =  uf;
912             *new_vf =  vf;
913             break;
914         case LEFT:
915             direction = BACK;
916             *new_uf =  uf;
917             *new_vf =  vf;
918             break;
919         case UP:
920             direction = LEFT;
921             *new_uf =  vf;
922             *new_vf = -uf;
923             break;
924         case DOWN:
925             direction = LEFT;
926             *new_uf = -vf;
927             *new_vf =  uf;
928             break;
929         case FRONT:
930             direction = LEFT;
931             *new_uf =  uf;
932             *new_vf =  vf;
933             break;
934         case BACK:
935             direction = RIGHT;
936             *new_uf =  uf;
937             *new_vf =  vf;
938             break;
939         default:
940             av_assert0(0);
941         }
942     } else if (uf >= 1.f) {
943         uf -= 2.f;
944         switch (direction) {
945         case RIGHT:
946             direction = BACK;
947             *new_uf =  uf;
948             *new_vf =  vf;
949             break;
950         case LEFT:
951             direction = FRONT;
952             *new_uf =  uf;
953             *new_vf =  vf;
954             break;
955         case UP:
956             direction = RIGHT;
957             *new_uf = -vf;
958             *new_vf =  uf;
959             break;
960         case DOWN:
961             direction = RIGHT;
962             *new_uf =  vf;
963             *new_vf = -uf;
964             break;
965         case FRONT:
966             direction = RIGHT;
967             *new_uf =  uf;
968             *new_vf =  vf;
969             break;
970         case BACK:
971             direction = LEFT;
972             *new_uf =  uf;
973             *new_vf =  vf;
974             break;
975         default:
976             av_assert0(0);
977         }
978     } else if (vf < -1.f) {
979         vf += 2.f;
980         switch (direction) {
981         case RIGHT:
982             direction = UP;
983             *new_uf =  vf;
984             *new_vf = -uf;
985             break;
986         case LEFT:
987             direction = UP;
988             *new_uf = -vf;
989             *new_vf =  uf;
990             break;
991         case UP:
992             direction = BACK;
993             *new_uf = -uf;
994             *new_vf = -vf;
995             break;
996         case DOWN:
997             direction = FRONT;
998             *new_uf =  uf;
999             *new_vf =  vf;
1000             break;
1001         case FRONT:
1002             direction = UP;
1003             *new_uf =  uf;
1004             *new_vf =  vf;
1005             break;
1006         case BACK:
1007             direction = UP;
1008             *new_uf = -uf;
1009             *new_vf = -vf;
1010             break;
1011         default:
1012             av_assert0(0);
1013         }
1014     } else if (vf >= 1.f) {
1015         vf -= 2.f;
1016         switch (direction) {
1017         case RIGHT:
1018             direction = DOWN;
1019             *new_uf = -vf;
1020             *new_vf =  uf;
1021             break;
1022         case LEFT:
1023             direction = DOWN;
1024             *new_uf =  vf;
1025             *new_vf = -uf;
1026             break;
1027         case UP:
1028             direction = FRONT;
1029             *new_uf =  uf;
1030             *new_vf =  vf;
1031             break;
1032         case DOWN:
1033             direction = BACK;
1034             *new_uf = -uf;
1035             *new_vf = -vf;
1036             break;
1037         case FRONT:
1038             direction = DOWN;
1039             *new_uf =  uf;
1040             *new_vf =  vf;
1041             break;
1042         case BACK:
1043             direction = DOWN;
1044             *new_uf = -uf;
1045             *new_vf = -vf;
1046             break;
1047         default:
1048             av_assert0(0);
1049         }
1050     } else {
1051         // Inside cube face
1052         *new_uf = uf;
1053         *new_vf = vf;
1054     }
1055
1056     *face = s->in_cubemap_face_order[direction];
1057     rotate_cube_face(new_uf, new_vf, s->in_cubemap_face_rotation[*face]);
1058 }
1059
1060 /**
1061  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format.
1062  *
1063  * @param s filter context
1064  * @param i horizontal position on frame [0, height)
1065  * @param j vertical position on frame [0, width)
1066  * @param width frame width
1067  * @param height frame height
1068  * @param vec coordinates on sphere
1069  */
1070 static void cube3x2_to_xyz(const V360Context *s,
1071                            int i, int j, int width, int height,
1072                            float *vec)
1073 {
1074     const float ew = width  / 3.f;
1075     const float eh = height / 2.f;
1076
1077     const int u_face = floorf(i / ew);
1078     const int v_face = floorf(j / eh);
1079     const int face = u_face + 3 * v_face;
1080
1081     const int u_shift = ceilf(ew * u_face);
1082     const int v_shift = ceilf(eh * v_face);
1083     const int ewi = ceilf(ew * (u_face + 1)) - u_shift;
1084     const int ehi = ceilf(eh * (v_face + 1)) - v_shift;
1085
1086     const float uf = 2.f * (i - u_shift) / ewi - 1.f;
1087     const float vf = 2.f * (j - v_shift) / ehi - 1.f;
1088
1089     cube_to_xyz(s, uf, vf, face, vec);
1090 }
1091
1092 /**
1093  * Calculate frame position in cubemap3x2 format for corresponding 3D coordinates on sphere.
1094  *
1095  * @param s filter context
1096  * @param vec coordinates on sphere
1097  * @param width frame width
1098  * @param height frame height
1099  * @param us horizontal coordinates for interpolation window
1100  * @param vs vertical coordinates for interpolation window
1101  * @param du horizontal relative coordinate
1102  * @param dv vertical relative coordinate
1103  */
1104 static void xyz_to_cube3x2(const V360Context *s,
1105                            const float *vec, int width, int height,
1106                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1107 {
1108     const float ew = width  / 3.f;
1109     const float eh = height / 2.f;
1110     float uf, vf;
1111     int ui, vi;
1112     int ewi, ehi;
1113     int i, j;
1114     int direction, face;
1115     int u_face, v_face;
1116
1117     xyz_to_cube(s, vec, &uf, &vf, &direction);
1118
1119     uf *= (1.f - s->in_pad);
1120     vf *= (1.f - s->in_pad);
1121
1122     face = s->in_cubemap_face_order[direction];
1123     u_face = face % 3;
1124     v_face = face / 3;
1125     ewi = ceilf(ew * (u_face + 1)) - ceilf(ew * u_face);
1126     ehi = ceilf(eh * (v_face + 1)) - ceilf(eh * v_face);
1127
1128     uf = 0.5f * ewi * (uf + 1.f);
1129     vf = 0.5f * ehi * (vf + 1.f);
1130
1131     ui = floorf(uf);
1132     vi = floorf(vf);
1133
1134     *du = uf - ui;
1135     *dv = vf - vi;
1136
1137     for (i = -1; i < 3; i++) {
1138         for (j = -1; j < 3; j++) {
1139             int new_ui = ui + j;
1140             int new_vi = vi + i;
1141             int u_shift, v_shift;
1142             int new_ewi, new_ehi;
1143
1144             if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1145                 face = s->in_cubemap_face_order[direction];
1146
1147                 u_face = face % 3;
1148                 v_face = face / 3;
1149                 u_shift = ceilf(ew * u_face);
1150                 v_shift = ceilf(eh * v_face);
1151             } else {
1152                 uf = 2.f * new_ui / ewi - 1.f;
1153                 vf = 2.f * new_vi / ehi - 1.f;
1154
1155                 uf /= (1.f - s->in_pad);
1156                 vf /= (1.f - s->in_pad);
1157
1158                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1159
1160                 uf *= (1.f - s->in_pad);
1161                 vf *= (1.f - s->in_pad);
1162
1163                 u_face = face % 3;
1164                 v_face = face / 3;
1165                 u_shift = ceilf(ew * u_face);
1166                 v_shift = ceilf(eh * v_face);
1167                 new_ewi = ceilf(ew * (u_face + 1)) - u_shift;
1168                 new_ehi = ceilf(eh * (v_face + 1)) - v_shift;
1169
1170                 new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1171                 new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1172             }
1173
1174
1175             us[i + 1][j + 1] = u_shift + new_ui;
1176             vs[i + 1][j + 1] = v_shift + new_vi;
1177         }
1178     }
1179 }
1180
1181 /**
1182  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format.
1183  *
1184  * @param s filter context
1185  * @param i horizontal position on frame [0, height)
1186  * @param j vertical position on frame [0, width)
1187  * @param width frame width
1188  * @param height frame height
1189  * @param vec coordinates on sphere
1190  */
1191 static void cube1x6_to_xyz(const V360Context *s,
1192                            int i, int j, int width, int height,
1193                            float *vec)
1194 {
1195     const float ew = width;
1196     const float eh = height / 6.f;
1197
1198     const int face = floorf(j / eh);
1199
1200     const int v_shift = ceilf(eh * face);
1201     const int ehi = ceilf(eh * (face + 1)) - v_shift;
1202
1203     const float uf = 2.f *  i            / ew  - 1.f;
1204     const float vf = 2.f * (j - v_shift) / ehi - 1.f;
1205
1206     cube_to_xyz(s, uf, vf, face, vec);
1207 }
1208
1209 /**
1210  * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format.
1211  *
1212  * @param s filter context
1213  * @param i horizontal position on frame [0, height)
1214  * @param j vertical position on frame [0, width)
1215  * @param width frame width
1216  * @param height frame height
1217  * @param vec coordinates on sphere
1218  */
1219 static void cube6x1_to_xyz(const V360Context *s,
1220                            int i, int j, int width, int height,
1221                            float *vec)
1222 {
1223     const float ew = width / 6.f;
1224     const float eh = height;
1225
1226     const int face = floorf(i / ew);
1227
1228     const int u_shift = ceilf(ew * face);
1229     const int ewi = ceilf(ew * (face + 1)) - u_shift;
1230
1231     const float uf = 2.f * (i - u_shift) / ewi - 1.f;
1232     const float vf = 2.f *  j            / eh  - 1.f;
1233
1234     cube_to_xyz(s, uf, vf, face, vec);
1235 }
1236
1237 /**
1238  * Calculate frame position in cubemap1x6 format for corresponding 3D coordinates on sphere.
1239  *
1240  * @param s filter context
1241  * @param vec coordinates on sphere
1242  * @param width frame width
1243  * @param height frame height
1244  * @param us horizontal coordinates for interpolation window
1245  * @param vs vertical coordinates for interpolation window
1246  * @param du horizontal relative coordinate
1247  * @param dv vertical relative coordinate
1248  */
1249 static void xyz_to_cube1x6(const V360Context *s,
1250                            const float *vec, int width, int height,
1251                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1252 {
1253     const float eh = height / 6.f;
1254     const int ewi = width;
1255     float uf, vf;
1256     int ui, vi;
1257     int ehi;
1258     int i, j;
1259     int direction, face;
1260
1261     xyz_to_cube(s, vec, &uf, &vf, &direction);
1262
1263     uf *= (1.f - s->in_pad);
1264     vf *= (1.f - s->in_pad);
1265
1266     face = s->in_cubemap_face_order[direction];
1267     ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
1268
1269     uf = 0.5f * ewi * (uf + 1.f);
1270     vf = 0.5f * ehi * (vf + 1.f);
1271
1272     ui = floorf(uf);
1273     vi = floorf(vf);
1274
1275     *du = uf - ui;
1276     *dv = vf - vi;
1277
1278     for (i = -1; i < 3; i++) {
1279         for (j = -1; j < 3; j++) {
1280             int new_ui = ui + j;
1281             int new_vi = vi + i;
1282             int v_shift;
1283             int new_ehi;
1284
1285             if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1286                 face = s->in_cubemap_face_order[direction];
1287
1288                 v_shift = ceilf(eh * face);
1289             } else {
1290                 uf = 2.f * new_ui / ewi - 1.f;
1291                 vf = 2.f * new_vi / ehi - 1.f;
1292
1293                 uf /= (1.f - s->in_pad);
1294                 vf /= (1.f - s->in_pad);
1295
1296                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1297
1298                 uf *= (1.f - s->in_pad);
1299                 vf *= (1.f - s->in_pad);
1300
1301                 v_shift = ceilf(eh * face);
1302                 new_ehi = ceilf(eh * (face + 1)) - v_shift;
1303
1304                 new_ui = av_clip(roundf(0.5f *     ewi * (uf + 1.f)), 0,     ewi - 1);
1305                 new_vi = av_clip(roundf(0.5f * new_ehi * (vf + 1.f)), 0, new_ehi - 1);
1306             }
1307
1308
1309             us[i + 1][j + 1] =           new_ui;
1310             vs[i + 1][j + 1] = v_shift + new_vi;
1311         }
1312     }
1313 }
1314
1315 /**
1316  * Calculate frame position in cubemap6x1 format for corresponding 3D coordinates on sphere.
1317  *
1318  * @param s filter context
1319  * @param vec coordinates on sphere
1320  * @param width frame width
1321  * @param height frame height
1322  * @param us horizontal coordinates for interpolation window
1323  * @param vs vertical coordinates for interpolation window
1324  * @param du horizontal relative coordinate
1325  * @param dv vertical relative coordinate
1326  */
1327 static void xyz_to_cube6x1(const V360Context *s,
1328                            const float *vec, int width, int height,
1329                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1330 {
1331     const float ew = width / 6.f;
1332     const int ehi = height;
1333     float uf, vf;
1334     int ui, vi;
1335     int ewi;
1336     int i, j;
1337     int direction, face;
1338
1339     xyz_to_cube(s, vec, &uf, &vf, &direction);
1340
1341     uf *= (1.f - s->in_pad);
1342     vf *= (1.f - s->in_pad);
1343
1344     face = s->in_cubemap_face_order[direction];
1345     ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
1346
1347     uf = 0.5f * ewi * (uf + 1.f);
1348     vf = 0.5f * ehi * (vf + 1.f);
1349
1350     ui = floorf(uf);
1351     vi = floorf(vf);
1352
1353     *du = uf - ui;
1354     *dv = vf - vi;
1355
1356     for (i = -1; i < 3; i++) {
1357         for (j = -1; j < 3; j++) {
1358             int new_ui = ui + j;
1359             int new_vi = vi + i;
1360             int u_shift;
1361             int new_ewi;
1362
1363             if (new_ui >= 0 && new_ui < ewi && new_vi >= 0 && new_vi < ehi) {
1364                 face = s->in_cubemap_face_order[direction];
1365
1366                 u_shift = ceilf(ew * face);
1367             } else {
1368                 uf = 2.f * new_ui / ewi - 1.f;
1369                 vf = 2.f * new_vi / ehi - 1.f;
1370
1371                 uf /= (1.f - s->in_pad);
1372                 vf /= (1.f - s->in_pad);
1373
1374                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
1375
1376                 uf *= (1.f - s->in_pad);
1377                 vf *= (1.f - s->in_pad);
1378
1379                 u_shift = ceilf(ew * face);
1380                 new_ewi = ceilf(ew * (face + 1)) - u_shift;
1381
1382                 new_ui = av_clip(roundf(0.5f * new_ewi * (uf + 1.f)), 0, new_ewi - 1);
1383                 new_vi = av_clip(roundf(0.5f *     ehi * (vf + 1.f)), 0,     ehi - 1);
1384             }
1385
1386
1387             us[i + 1][j + 1] = u_shift + new_ui;
1388             vs[i + 1][j + 1] =           new_vi;
1389         }
1390     }
1391 }
1392
1393 /**
1394  * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format.
1395  *
1396  * @param s filter context
1397  * @param i horizontal position on frame [0, height)
1398  * @param j vertical position on frame [0, width)
1399  * @param width frame width
1400  * @param height frame height
1401  * @param vec coordinates on sphere
1402  */
1403 static void equirect_to_xyz(const V360Context *s,
1404                             int i, int j, int width, int height,
1405                             float *vec)
1406 {
1407     const float phi   = ((2.f * i) / width  - 1.f) * M_PI;
1408     const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
1409
1410     const float sin_phi   = sinf(phi);
1411     const float cos_phi   = cosf(phi);
1412     const float sin_theta = sinf(theta);
1413     const float cos_theta = cosf(theta);
1414
1415     vec[0] =  cos_theta * sin_phi;
1416     vec[1] = -sin_theta;
1417     vec[2] = -cos_theta * cos_phi;
1418 }
1419
1420 /**
1421  * Calculate frame position in equirectangular format for corresponding 3D coordinates on sphere.
1422  *
1423  * @param s filter context
1424  * @param vec coordinates on sphere
1425  * @param width frame width
1426  * @param height frame height
1427  * @param us horizontal coordinates for interpolation window
1428  * @param vs vertical coordinates for interpolation window
1429  * @param du horizontal relative coordinate
1430  * @param dv vertical relative coordinate
1431  */
1432 static void xyz_to_equirect(const V360Context *s,
1433                             const float *vec, int width, int height,
1434                             uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1435 {
1436     const float phi   = atan2f(vec[0], -vec[2]);
1437     const float theta = asinf(-vec[1]);
1438     float uf, vf;
1439     int ui, vi;
1440     int i, j;
1441
1442     uf = (phi   / M_PI   + 1.f) * width  / 2.f;
1443     vf = (theta / M_PI_2 + 1.f) * height / 2.f;
1444     ui = floorf(uf);
1445     vi = floorf(vf);
1446
1447     *du = uf - ui;
1448     *dv = vf - vi;
1449
1450     for (i = -1; i < 3; i++) {
1451         for (j = -1; j < 3; j++) {
1452             us[i + 1][j + 1] = mod(ui + j, width);
1453             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1454         }
1455     }
1456 }
1457
1458 /**
1459  * Prepare data for processing equi-angular cubemap input format.
1460  *
1461  * @param ctx filter context
1462
1463  * @return error code
1464  */
1465 static int prepare_eac_in(AVFilterContext *ctx)
1466 {
1467     V360Context *s = ctx->priv;
1468
1469     s->in_cubemap_face_order[RIGHT] = TOP_RIGHT;
1470     s->in_cubemap_face_order[LEFT]  = TOP_LEFT;
1471     s->in_cubemap_face_order[UP]    = BOTTOM_RIGHT;
1472     s->in_cubemap_face_order[DOWN]  = BOTTOM_LEFT;
1473     s->in_cubemap_face_order[FRONT] = TOP_MIDDLE;
1474     s->in_cubemap_face_order[BACK]  = BOTTOM_MIDDLE;
1475
1476     s->in_cubemap_face_rotation[TOP_LEFT]      = ROT_0;
1477     s->in_cubemap_face_rotation[TOP_MIDDLE]    = ROT_0;
1478     s->in_cubemap_face_rotation[TOP_RIGHT]     = ROT_0;
1479     s->in_cubemap_face_rotation[BOTTOM_LEFT]   = ROT_270;
1480     s->in_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
1481     s->in_cubemap_face_rotation[BOTTOM_RIGHT]  = ROT_270;
1482
1483     return 0;
1484 }
1485
1486 /**
1487  * Prepare data for processing equi-angular cubemap output format.
1488  *
1489  * @param ctx filter context
1490  *
1491  * @return error code
1492  */
1493 static int prepare_eac_out(AVFilterContext *ctx)
1494 {
1495     V360Context *s = ctx->priv;
1496
1497     s->out_cubemap_direction_order[TOP_LEFT]      = LEFT;
1498     s->out_cubemap_direction_order[TOP_MIDDLE]    = FRONT;
1499     s->out_cubemap_direction_order[TOP_RIGHT]     = RIGHT;
1500     s->out_cubemap_direction_order[BOTTOM_LEFT]   = DOWN;
1501     s->out_cubemap_direction_order[BOTTOM_MIDDLE] = BACK;
1502     s->out_cubemap_direction_order[BOTTOM_RIGHT]  = UP;
1503
1504     s->out_cubemap_face_rotation[TOP_LEFT]      = ROT_0;
1505     s->out_cubemap_face_rotation[TOP_MIDDLE]    = ROT_0;
1506     s->out_cubemap_face_rotation[TOP_RIGHT]     = ROT_0;
1507     s->out_cubemap_face_rotation[BOTTOM_LEFT]   = ROT_270;
1508     s->out_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
1509     s->out_cubemap_face_rotation[BOTTOM_RIGHT]  = ROT_270;
1510
1511     return 0;
1512 }
1513
1514 /**
1515  * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format.
1516  *
1517  * @param s filter context
1518  * @param i horizontal position on frame [0, height)
1519  * @param j vertical position on frame [0, width)
1520  * @param width frame width
1521  * @param height frame height
1522  * @param vec coordinates on sphere
1523  */
1524 static void eac_to_xyz(const V360Context *s,
1525                        int i, int j, int width, int height,
1526                        float *vec)
1527 {
1528     const float pixel_pad = 2;
1529     const float u_pad = pixel_pad / width;
1530     const float v_pad = pixel_pad / height;
1531
1532     int u_face, v_face, face;
1533
1534     float l_x, l_y, l_z;
1535     float norm;
1536
1537     float uf = (float)i / width;
1538     float vf = (float)j / height;
1539
1540     // EAC has 2-pixel padding on faces except between faces on the same row
1541     // Padding pixels seems not to be stretched with tangent as regular pixels
1542     // Formulas below approximate original padding as close as I could get experimentally
1543
1544     // Horizontal padding
1545     uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
1546     if (uf < 0.f) {
1547         u_face = 0;
1548         uf -= 0.5f;
1549     } else if (uf >= 3.f) {
1550         u_face = 2;
1551         uf -= 2.5f;
1552     } else {
1553         u_face = floorf(uf);
1554         uf = fmodf(uf, 1.f) - 0.5f;
1555     }
1556
1557     // Vertical padding
1558     v_face = floorf(vf * 2.f);
1559     vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
1560
1561     if (uf >= -0.5f && uf < 0.5f) {
1562         uf = tanf(M_PI_2 * uf);
1563     } else {
1564         uf = 2.f * uf;
1565     }
1566     if (vf >= -0.5f && vf < 0.5f) {
1567         vf = tanf(M_PI_2 * vf);
1568     } else {
1569         vf = 2.f * vf;
1570     }
1571
1572     face = u_face + 3 * v_face;
1573
1574     switch (face) {
1575     case TOP_LEFT:
1576         l_x = -1.f;
1577         l_y = -vf;
1578         l_z = -uf;
1579         break;
1580     case TOP_MIDDLE:
1581         l_x =  uf;
1582         l_y = -vf;
1583         l_z = -1.f;
1584         break;
1585     case TOP_RIGHT:
1586         l_x =  1.f;
1587         l_y = -vf;
1588         l_z =  uf;
1589         break;
1590     case BOTTOM_LEFT:
1591         l_x = -vf;
1592         l_y = -1.f;
1593         l_z =  uf;
1594         break;
1595     case BOTTOM_MIDDLE:
1596         l_x = -vf;
1597         l_y =  uf;
1598         l_z =  1.f;
1599         break;
1600     case BOTTOM_RIGHT:
1601         l_x = -vf;
1602         l_y =  1.f;
1603         l_z = -uf;
1604         break;
1605     default:
1606         av_assert0(0);
1607     }
1608
1609     norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
1610     vec[0] = l_x / norm;
1611     vec[1] = l_y / norm;
1612     vec[2] = l_z / norm;
1613 }
1614
1615 /**
1616  * Calculate frame position in equi-angular cubemap format for corresponding 3D coordinates on sphere.
1617  *
1618  * @param s filter context
1619  * @param vec coordinates on sphere
1620  * @param width frame width
1621  * @param height frame height
1622  * @param us horizontal coordinates for interpolation window
1623  * @param vs vertical coordinates for interpolation window
1624  * @param du horizontal relative coordinate
1625  * @param dv vertical relative coordinate
1626  */
1627 static void xyz_to_eac(const V360Context *s,
1628                        const float *vec, int width, int height,
1629                        uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1630 {
1631     const float pixel_pad = 2;
1632     const float u_pad = pixel_pad / width;
1633     const float v_pad = pixel_pad / height;
1634
1635     float uf, vf;
1636     int ui, vi;
1637     int i, j;
1638     int direction, face;
1639     int u_face, v_face;
1640
1641     xyz_to_cube(s, vec, &uf, &vf, &direction);
1642
1643     face = s->in_cubemap_face_order[direction];
1644     u_face = face % 3;
1645     v_face = face / 3;
1646
1647     uf = M_2_PI * atanf(uf) + 0.5f;
1648     vf = M_2_PI * atanf(vf) + 0.5f;
1649
1650     // These formulas are inversed from eac_to_xyz ones
1651     uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
1652     vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
1653
1654     uf *= width;
1655     vf *= height;
1656
1657     ui = floorf(uf);
1658     vi = floorf(vf);
1659
1660     *du = uf - ui;
1661     *dv = vf - vi;
1662
1663     for (i = -1; i < 3; i++) {
1664         for (j = -1; j < 3; j++) {
1665             us[i + 1][j + 1] = av_clip(ui + j, 0, width  - 1);
1666             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
1667         }
1668     }
1669 }
1670
1671 /**
1672  * Prepare data for processing flat output format.
1673  *
1674  * @param ctx filter context
1675  *
1676  * @return error code
1677  */
1678 static int prepare_flat_out(AVFilterContext *ctx)
1679 {
1680     V360Context *s = ctx->priv;
1681
1682     const float h_angle = 0.5f * s->h_fov * M_PI / 180.f;
1683     const float v_angle = 0.5f * s->v_fov * M_PI / 180.f;
1684
1685     const float sin_phi   = sinf(h_angle);
1686     const float cos_phi   = cosf(h_angle);
1687     const float sin_theta = sinf(v_angle);
1688     const float cos_theta = cosf(v_angle);
1689
1690     s->flat_range[0] =  cos_theta * sin_phi;
1691     s->flat_range[1] =  sin_theta;
1692     s->flat_range[2] = -cos_theta * cos_phi;
1693
1694     return 0;
1695 }
1696
1697 /**
1698  * Calculate 3D coordinates on sphere for corresponding frame position in flat format.
1699  *
1700  * @param s filter context
1701  * @param i horizontal position on frame [0, height)
1702  * @param j vertical position on frame [0, width)
1703  * @param width frame width
1704  * @param height frame height
1705  * @param vec coordinates on sphere
1706  */
1707 static void flat_to_xyz(const V360Context *s,
1708                         int i, int j, int width, int height,
1709                         float *vec)
1710 {
1711     const float l_x =  s->flat_range[0] * (2.f * i / width  - 1.f);
1712     const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
1713     const float l_z =  s->flat_range[2];
1714
1715     const float norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
1716
1717     vec[0] = l_x / norm;
1718     vec[1] = l_y / norm;
1719     vec[2] = l_z / norm;
1720 }
1721
1722 /**
1723  * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere.
1724  *
1725  * @param s filter context
1726  * @param vec coordinates on sphere
1727  * @param width frame width
1728  * @param height frame height
1729  * @param us horizontal coordinates for interpolation window
1730  * @param vs vertical coordinates for interpolation window
1731  * @param du horizontal relative coordinate
1732  * @param dv vertical relative coordinate
1733  */
1734 static void xyz_to_dfisheye(const V360Context *s,
1735                             const float *vec, int width, int height,
1736                             uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1737 {
1738     const float scale = 1.f - s->in_pad;
1739
1740     const float ew = width / 2.f;
1741     const float eh = height;
1742
1743     const float phi   = atan2f(-vec[1], -vec[0]);
1744     const float theta = acosf(fabsf(vec[2])) / M_PI;
1745
1746     float uf = (theta * cosf(phi) * scale + 0.5f) * ew;
1747     float vf = (theta * sinf(phi) * scale + 0.5f) * eh;
1748
1749     int ui, vi;
1750     int u_shift;
1751     int i, j;
1752
1753     if (vec[2] >= 0) {
1754         u_shift = 0;
1755     } else {
1756         u_shift = ceilf(ew);
1757         uf = ew - uf;
1758     }
1759
1760     ui = floorf(uf);
1761     vi = floorf(vf);
1762
1763     *du = uf - ui;
1764     *dv = vf - vi;
1765
1766     for (i = -1; i < 3; i++) {
1767         for (j = -1; j < 3; j++) {
1768             us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width  - 1);
1769             vs[i + 1][j + 1] = av_clip(          vi + i, 0, height - 1);
1770         }
1771     }
1772 }
1773
1774 /**
1775  * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format.
1776  *
1777  * @param s filter context
1778  * @param i horizontal position on frame [0, height)
1779  * @param j vertical position on frame [0, width)
1780  * @param width frame width
1781  * @param height frame height
1782  * @param vec coordinates on sphere
1783  */
1784 static void barrel_to_xyz(const V360Context *s,
1785                           int i, int j, int width, int height,
1786                           float *vec)
1787 {
1788     const float scale = 0.99f;
1789     float l_x, l_y, l_z;
1790
1791     if (i < 4 * width / 5) {
1792         const float theta_range = M_PI / 4.f;
1793
1794         const int ew = 4 * width / 5;
1795         const int eh = height;
1796
1797         const float phi   = ((2.f * i) / ew - 1.f) * M_PI        / scale;
1798         const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
1799
1800         const float sin_phi   = sinf(phi);
1801         const float cos_phi   = cosf(phi);
1802         const float sin_theta = sinf(theta);
1803         const float cos_theta = cosf(theta);
1804
1805         l_x =  cos_theta * sin_phi;
1806         l_y = -sin_theta;
1807         l_z = -cos_theta * cos_phi;
1808     } else {
1809         const int ew = width  / 5;
1810         const int eh = height / 2;
1811
1812         float uf, vf;
1813         float norm;
1814
1815         if (j < eh) {   // UP
1816             uf = 2.f * (i - 4 * ew) / ew  - 1.f;
1817             vf = 2.f * (j         ) / eh - 1.f;
1818
1819             uf /= scale;
1820             vf /= scale;
1821
1822             l_x =  uf;
1823             l_y =  1.f;
1824             l_z = -vf;
1825         } else {            // DOWN
1826             uf = 2.f * (i - 4 * ew) / ew - 1.f;
1827             vf = 2.f * (j -     eh) / eh - 1.f;
1828
1829             uf /= scale;
1830             vf /= scale;
1831
1832             l_x =  uf;
1833             l_y = -1.f;
1834             l_z =  vf;
1835         }
1836
1837         norm = sqrtf(l_x * l_x + l_y * l_y + l_z * l_z);
1838
1839         l_x /= norm;
1840         l_y /= norm;
1841         l_z /= norm;
1842     }
1843
1844     vec[0] = l_x;
1845     vec[1] = l_y;
1846     vec[2] = l_z;
1847 }
1848
1849 /**
1850  * Calculate frame position in barrel facebook's format for corresponding 3D coordinates on sphere.
1851  *
1852  * @param s filter context
1853  * @param vec coordinates on sphere
1854  * @param width frame width
1855  * @param height frame height
1856  * @param us horizontal coordinates for interpolation window
1857  * @param vs vertical coordinates for interpolation window
1858  * @param du horizontal relative coordinate
1859  * @param dv vertical relative coordinate
1860  */
1861 static void xyz_to_barrel(const V360Context *s,
1862                           const float *vec, int width, int height,
1863                           uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
1864 {
1865     const float scale = 0.99f;
1866
1867     const float phi   = atan2f(vec[0], -vec[2]);
1868     const float theta = asinf(-vec[1]);
1869     const float theta_range = M_PI / 4.f;
1870
1871     int ew, eh;
1872     int u_shift, v_shift;
1873     float uf, vf;
1874     int ui, vi;
1875     int i, j;
1876
1877     if (theta > -theta_range && theta < theta_range) {
1878         ew = 4 * width / 5;
1879         eh = height;
1880
1881         u_shift = 0;
1882         v_shift = 0;
1883
1884         uf = (phi   / M_PI        * scale + 1.f) * ew / 2.f;
1885         vf = (theta / theta_range * scale + 1.f) * eh / 2.f;
1886     } else {
1887         ew = width  / 5;
1888         eh = height / 2;
1889
1890         u_shift = 4 * ew;
1891
1892         if (theta < 0.f) {  // UP
1893             uf =  vec[0] / vec[1];
1894             vf = -vec[2] / vec[1];
1895             v_shift = 0;
1896         } else {            // DOWN
1897             uf = -vec[0] / vec[1];
1898             vf = -vec[2] / vec[1];
1899             v_shift = eh;
1900         }
1901
1902         uf = 0.5f * ew * (uf * scale + 1.f);
1903         vf = 0.5f * eh * (vf * scale + 1.f);
1904     }
1905
1906     ui = floorf(uf);
1907     vi = floorf(vf);
1908
1909     *du = uf - ui;
1910     *dv = vf - vi;
1911
1912     for (i = -1; i < 3; i++) {
1913         for (j = -1; j < 3; j++) {
1914             us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1);
1915             vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
1916         }
1917     }
1918
1919 }
1920
1921 /**
1922  * Calculate rotation matrix for yaw/pitch/roll angles.
1923  */
1924 static inline void calculate_rotation_matrix(float yaw, float pitch, float roll,
1925                                              float rot_mat[3][3])
1926 {
1927     const float yaw_rad   = yaw   * M_PI / 180.f;
1928     const float pitch_rad = pitch * M_PI / 180.f;
1929     const float roll_rad  = roll  * M_PI / 180.f;
1930
1931     const float sin_yaw   = sinf(-yaw_rad);
1932     const float cos_yaw   = cosf(-yaw_rad);
1933     const float sin_pitch = sinf(pitch_rad);
1934     const float cos_pitch = cosf(pitch_rad);
1935     const float sin_roll  = sinf(roll_rad);
1936     const float cos_roll  = cosf(roll_rad);
1937
1938     rot_mat[0][0] = sin_yaw * sin_pitch * sin_roll + cos_yaw * cos_roll;
1939     rot_mat[0][1] = sin_yaw * sin_pitch * cos_roll - cos_yaw * sin_roll;
1940     rot_mat[0][2] = sin_yaw * cos_pitch;
1941
1942     rot_mat[1][0] = cos_pitch * sin_roll;
1943     rot_mat[1][1] = cos_pitch * cos_roll;
1944     rot_mat[1][2] = -sin_pitch;
1945
1946     rot_mat[2][0] = cos_yaw * sin_pitch * sin_roll - sin_yaw * cos_roll;
1947     rot_mat[2][1] = cos_yaw * sin_pitch * cos_roll + sin_yaw * sin_roll;
1948     rot_mat[2][2] = cos_yaw * cos_pitch;
1949 }
1950
1951 /**
1952  * Rotate vector with given rotation matrix.
1953  *
1954  * @param rot_mat rotation matrix
1955  * @param vec vector
1956  */
1957 static inline void rotate(const float rot_mat[3][3],
1958                           float *vec)
1959 {
1960     const float x_tmp = vec[0] * rot_mat[0][0] + vec[1] * rot_mat[0][1] + vec[2] * rot_mat[0][2];
1961     const float y_tmp = vec[0] * rot_mat[1][0] + vec[1] * rot_mat[1][1] + vec[2] * rot_mat[1][2];
1962     const float z_tmp = vec[0] * rot_mat[2][0] + vec[1] * rot_mat[2][1] + vec[2] * rot_mat[2][2];
1963
1964     vec[0] = x_tmp;
1965     vec[1] = y_tmp;
1966     vec[2] = z_tmp;
1967 }
1968
1969 static inline void set_mirror_modifier(int h_flip, int v_flip, int d_flip,
1970                                        float *modifier)
1971 {
1972     modifier[0] = h_flip ? -1.f : 1.f;
1973     modifier[1] = v_flip ? -1.f : 1.f;
1974     modifier[2] = d_flip ? -1.f : 1.f;
1975 }
1976
1977 static inline void mirror(const float *modifier, float *vec)
1978 {
1979     vec[0] *= modifier[0];
1980     vec[1] *= modifier[1];
1981     vec[2] *= modifier[2];
1982 }
1983
1984 static int config_output(AVFilterLink *outlink)
1985 {
1986     AVFilterContext *ctx = outlink->src;
1987     AVFilterLink *inlink = ctx->inputs[0];
1988     V360Context *s = ctx->priv;
1989     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1990     const int depth = desc->comp[0].depth;
1991     int sizeof_uv;
1992     int sizeof_ker;
1993     int elements;
1994     int err;
1995     int p, h, w;
1996     float hf, wf;
1997     float mirror_modifier[3];
1998     void (*in_transform)(const V360Context *s,
1999                          const float *vec, int width, int height,
2000                          uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv);
2001     void (*out_transform)(const V360Context *s,
2002                           int i, int j, int width, int height,
2003                           float *vec);
2004     void (*calculate_kernel)(float du, float dv, const XYRemap *r_tmp,
2005                              uint16_t *u, uint16_t *v, int16_t *ker);
2006     float rot_mat[3][3];
2007
2008     switch (s->interp) {
2009     case NEAREST:
2010         calculate_kernel = nearest_kernel;
2011         s->remap_slice = depth <= 8 ? remap1_8bit_slice : remap1_16bit_slice;
2012         elements = 1;
2013         sizeof_uv = sizeof(uint16_t) * elements;
2014         sizeof_ker = 0;
2015         break;
2016     case BILINEAR:
2017         calculate_kernel = bilinear_kernel;
2018         s->remap_slice = depth <= 8 ? remap2_8bit_slice : remap2_16bit_slice;
2019         elements = 2 * 2;
2020         sizeof_uv = sizeof(uint16_t) * elements;
2021         sizeof_ker = sizeof(uint16_t) * elements;
2022         break;
2023     case BICUBIC:
2024         calculate_kernel = bicubic_kernel;
2025         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2026         elements = 4 * 4;
2027         sizeof_uv = sizeof(uint16_t) * elements;
2028         sizeof_ker = sizeof(uint16_t) * elements;
2029         break;
2030     case LANCZOS:
2031         calculate_kernel = lanczos_kernel;
2032         s->remap_slice = depth <= 8 ? remap4_8bit_slice : remap4_16bit_slice;
2033         elements = 4 * 4;
2034         sizeof_uv = sizeof(uint16_t) * elements;
2035         sizeof_ker = sizeof(uint16_t) * elements;
2036         break;
2037     default:
2038         av_assert0(0);
2039     }
2040
2041     switch (s->in) {
2042     case EQUIRECTANGULAR:
2043         in_transform = xyz_to_equirect;
2044         err = 0;
2045         wf = inlink->w;
2046         hf = inlink->h;
2047         break;
2048     case CUBEMAP_3_2:
2049         in_transform = xyz_to_cube3x2;
2050         err = prepare_cube_in(ctx);
2051         wf = inlink->w / 3.f * 4.f;
2052         hf = inlink->h;
2053         break;
2054     case CUBEMAP_1_6:
2055         in_transform = xyz_to_cube1x6;
2056         err = prepare_cube_in(ctx);
2057         wf = inlink->w * 4.f;
2058         hf = inlink->h / 3.f;
2059         break;
2060     case CUBEMAP_6_1:
2061         in_transform = xyz_to_cube6x1;
2062         err = prepare_cube_in(ctx);
2063         wf = inlink->w / 3.f * 2.f;
2064         hf = inlink->h * 2.f;
2065         break;
2066     case EQUIANGULAR:
2067         in_transform = xyz_to_eac;
2068         err = prepare_eac_in(ctx);
2069         wf = inlink->w;
2070         hf = inlink->h / 9.f * 8.f;
2071         break;
2072     case FLAT:
2073         av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n");
2074         return AVERROR(EINVAL);
2075     case DUAL_FISHEYE:
2076         in_transform = xyz_to_dfisheye;
2077         err = 0;
2078         wf = inlink->w;
2079         hf = inlink->h;
2080         break;
2081     case BARREL:
2082         in_transform = xyz_to_barrel;
2083         err = 0;
2084         wf = inlink->w / 5.f * 4.f;
2085         hf = inlink->h;
2086         break;
2087     default:
2088         av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
2089         return AVERROR_BUG;
2090     }
2091
2092     if (err != 0) {
2093         return err;
2094     }
2095
2096     switch (s->out) {
2097     case EQUIRECTANGULAR:
2098         out_transform = equirect_to_xyz;
2099         err = 0;
2100         w = roundf(wf);
2101         h = roundf(hf);
2102         break;
2103     case CUBEMAP_3_2:
2104         out_transform = cube3x2_to_xyz;
2105         err = prepare_cube_out(ctx);
2106         w = roundf(wf / 4.f * 3.f);
2107         h = roundf(hf);
2108         break;
2109     case CUBEMAP_1_6:
2110         out_transform = cube1x6_to_xyz;
2111         err = prepare_cube_out(ctx);
2112         w = roundf(wf / 4.f);
2113         h = roundf(hf * 3.f);
2114         break;
2115     case CUBEMAP_6_1:
2116         out_transform = cube6x1_to_xyz;
2117         err = prepare_cube_out(ctx);
2118         w = roundf(wf / 2.f * 3.f);
2119         h = roundf(hf / 2.f);
2120         break;
2121     case EQUIANGULAR:
2122         out_transform = eac_to_xyz;
2123         err = prepare_eac_out(ctx);
2124         w = roundf(wf);
2125         h = roundf(hf / 8.f * 9.f);
2126         break;
2127     case FLAT:
2128         out_transform = flat_to_xyz;
2129         err = prepare_flat_out(ctx);
2130         w = roundf(wf * s->flat_range[0] / s->flat_range[1] / 2.f);
2131         h = roundf(hf);
2132         break;
2133     case DUAL_FISHEYE:
2134         av_log(ctx, AV_LOG_ERROR, "Dual fisheye format is not accepted as output.\n");
2135         return AVERROR(EINVAL);
2136     case BARREL:
2137         out_transform = barrel_to_xyz;
2138         err = 0;
2139         w = roundf(wf / 4.f * 5.f);
2140         h = roundf(hf);
2141         break;
2142     default:
2143         av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
2144         return AVERROR_BUG;
2145     }
2146
2147     if (err != 0) {
2148         return err;
2149     }
2150
2151     // Override resolution with user values if specified
2152     if (s->width > 0 && s->height > 0) {
2153         w = s->width;
2154         h = s->height;
2155     } else if (s->width > 0 || s->height > 0) {
2156         av_log(ctx, AV_LOG_ERROR, "Both width and height values should be specified.\n");
2157         return AVERROR(EINVAL);
2158     }
2159
2160     s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
2161     s->planeheight[0] = s->planeheight[3] = h;
2162     s->planewidth[1]  = s->planewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
2163     s->planewidth[0]  = s->planewidth[3] = w;
2164
2165     outlink->h = h;
2166     outlink->w = w;
2167
2168     s->inplaneheight[1] = s->inplaneheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
2169     s->inplaneheight[0] = s->inplaneheight[3] = inlink->h;
2170     s->inplanewidth[1]  = s->inplanewidth[2]  = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
2171     s->inplanewidth[0]  = s->inplanewidth[3]  = inlink->w;
2172     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
2173
2174     for (p = 0; p < s->nb_planes; p++) {
2175         s->u[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv);
2176         s->v[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv);
2177         if (!s->u[p] || !s->v[p])
2178             return AVERROR(ENOMEM);
2179         if (sizeof_ker) {
2180             s->ker[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_ker);
2181             if (!s->ker[p])
2182                 return AVERROR(ENOMEM);
2183         }
2184     }
2185
2186     calculate_rotation_matrix(s->yaw, s->pitch, s->roll, rot_mat);
2187     set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, mirror_modifier);
2188
2189     // Calculate remap data
2190     for (p = 0; p < s->nb_planes; p++) {
2191         const int width = s->planewidth[p];
2192         const int height = s->planeheight[p];
2193         const int in_width = s->inplanewidth[p];
2194         const int in_height = s->inplaneheight[p];
2195         float du, dv;
2196         float vec[3];
2197         XYRemap r_tmp;
2198         int i, j;
2199
2200         for (i = 0; i < width; i++) {
2201             for (j = 0; j < height; j++) {
2202                 uint16_t *u = s->u[p] + (j * width + i) * elements;
2203                 uint16_t *v = s->v[p] + (j * width + i) * elements;
2204                 int16_t *ker = s->ker[p] + (j * width + i) * elements;
2205
2206                 out_transform(s, i, j, width, height, vec);
2207                 rotate(rot_mat, vec);
2208                 mirror(mirror_modifier, vec);
2209                 in_transform(s, vec, in_width, in_height, r_tmp.u, r_tmp.v, &du, &dv);
2210                 calculate_kernel(du, dv, &r_tmp, u, v, ker);
2211             }
2212         }
2213     }
2214
2215     return 0;
2216 }
2217
2218 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
2219 {
2220     AVFilterContext *ctx = inlink->dst;
2221     AVFilterLink *outlink = ctx->outputs[0];
2222     V360Context *s = ctx->priv;
2223     AVFrame *out;
2224     ThreadData td;
2225
2226     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
2227     if (!out) {
2228         av_frame_free(&in);
2229         return AVERROR(ENOMEM);
2230     }
2231     av_frame_copy_props(out, in);
2232
2233     td.in = in;
2234     td.out = out;
2235
2236     ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2237
2238     av_frame_free(&in);
2239     return ff_filter_frame(outlink, out);
2240 }
2241
2242 static av_cold void uninit(AVFilterContext *ctx)
2243 {
2244     V360Context *s = ctx->priv;
2245     int p;
2246
2247     for (p = 0; p < s->nb_planes; p++) {
2248         av_freep(&s->u[p]);
2249         av_freep(&s->v[p]);
2250         av_freep(&s->ker[p]);
2251     }
2252 }
2253
2254 static const AVFilterPad inputs[] = {
2255     {
2256         .name         = "default",
2257         .type         = AVMEDIA_TYPE_VIDEO,
2258         .filter_frame = filter_frame,
2259     },
2260     { NULL }
2261 };
2262
2263 static const AVFilterPad outputs[] = {
2264     {
2265         .name         = "default",
2266         .type         = AVMEDIA_TYPE_VIDEO,
2267         .config_props = config_output,
2268     },
2269     { NULL }
2270 };
2271
2272 AVFilter ff_vf_v360 = {
2273     .name          = "v360",
2274     .description   = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."),
2275     .priv_size     = sizeof(V360Context),
2276     .uninit        = uninit,
2277     .query_formats = query_formats,
2278     .inputs        = inputs,
2279     .outputs       = outputs,
2280     .priv_class    = &v360_class,
2281     .flags         = AVFILTER_FLAG_SLICE_THREADS,
2282 };