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