]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/vf_v360.c
fate: Fix dependencies to sample files to use local paths
[ffmpeg] / libavfilter / vf_v360.c
index 79415efd6c69cccd8fb653d61d5bc26e4bde4706..a0f3c52d9351e265721db3bd887b48dbd2e12562 100644 (file)
@@ -67,6 +67,8 @@ static const AVOption v360_options[] = {
     {        "sg", "stereographic",                              0, AV_OPT_TYPE_CONST,  {.i64=STEREOGRAPHIC},   0,                   0, FLAGS, "in" },
     {  "mercator", "mercator",                                   0, AV_OPT_TYPE_CONST,  {.i64=MERCATOR},        0,                   0, FLAGS, "in" },
     {      "ball", "ball",                                       0, AV_OPT_TYPE_CONST,  {.i64=BALL},            0,                   0, FLAGS, "in" },
+    {    "hammer", "hammer",                                     0, AV_OPT_TYPE_CONST,  {.i64=HAMMER},          0,                   0, FLAGS, "in" },
+    {"sinusoidal", "sinusoidal",                                 0, AV_OPT_TYPE_CONST,  {.i64=SINUSOIDAL},      0,                   0, FLAGS, "in" },
     {    "output", "set output projection",            OFFSET(out), AV_OPT_TYPE_INT,    {.i64=CUBEMAP_3_2},     0,    NB_PROJECTIONS-1, FLAGS, "out" },
     {         "e", "equirectangular",                            0, AV_OPT_TYPE_CONST,  {.i64=EQUIRECTANGULAR}, 0,                   0, FLAGS, "out" },
     {  "equirect", "equirectangular",                            0, AV_OPT_TYPE_CONST,  {.i64=EQUIRECTANGULAR}, 0,                   0, FLAGS, "out" },
@@ -83,6 +85,8 @@ static const AVOption v360_options[] = {
     {        "sg", "stereographic",                              0, AV_OPT_TYPE_CONST,  {.i64=STEREOGRAPHIC},   0,                   0, FLAGS, "out" },
     {  "mercator", "mercator",                                   0, AV_OPT_TYPE_CONST,  {.i64=MERCATOR},        0,                   0, FLAGS, "out" },
     {      "ball", "ball",                                       0, AV_OPT_TYPE_CONST,  {.i64=BALL},            0,                   0, FLAGS, "out" },
+    {    "hammer", "hammer",                                     0, AV_OPT_TYPE_CONST,  {.i64=HAMMER},          0,                   0, FLAGS, "out" },
+    {"sinusoidal", "sinusoidal",                                 0, AV_OPT_TYPE_CONST,  {.i64=SINUSOIDAL},      0,                   0, FLAGS, "out" },
     {    "interp", "set interpolation method",      OFFSET(interp), AV_OPT_TYPE_INT,    {.i64=BILINEAR},        0, NB_INTERP_METHODS-1, FLAGS, "interp" },
     {      "near", "nearest neighbour",                          0, AV_OPT_TYPE_CONST,  {.i64=NEAREST},         0,                   0, FLAGS, "interp" },
     {   "nearest", "nearest neighbour",                          0, AV_OPT_TYPE_CONST,  {.i64=NEAREST},         0,                   0, FLAGS, "interp" },
@@ -103,8 +107,10 @@ static const AVOption v360_options[] = {
     {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"},        0,     NB_DIRECTIONS-1, FLAGS, "out_forder"},
     {   "in_frot", "input cubemap face rotation",  OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"},        0,     NB_DIRECTIONS-1, FLAGS, "in_frot"},
     {  "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"},        0,     NB_DIRECTIONS-1, FLAGS, "out_frot"},
-    {    "in_pad", "input cubemap pads",            OFFSET(in_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "in_pad"},
-    {   "out_pad", "output cubemap pads",          OFFSET(out_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "out_pad"},
+    {    "in_pad", "percent input cubemap pads",    OFFSET(in_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "in_pad"},
+    {   "out_pad", "percent output cubemap pads",  OFFSET(out_pad), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,                 1.f, FLAGS, "out_pad"},
+    {   "fin_pad", "fixed input cubemap pads",     OFFSET(fin_pad), AV_OPT_TYPE_INT,    {.i64=0},               0,                 100, FLAGS, "fin_pad"},
+    {  "fout_pad", "fixed output cubemap pads",   OFFSET(fout_pad), AV_OPT_TYPE_INT,    {.i64=0},               0,                 100, FLAGS, "fout_pad"},
     {       "yaw", "yaw rotation",                     OFFSET(yaw), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "yaw"},
     {     "pitch", "pitch rotation",                 OFFSET(pitch), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "pitch"},
     {      "roll", "roll rotation",                   OFFSET(roll), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},        -180.f,               180.f, FLAGS, "roll"},
@@ -220,7 +226,7 @@ DEFINE_REMAP1_LINE(16, 2)
 #define DEFINE_REMAP(ws, bits)                                                                             \
 static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)          \
 {                                                                                                          \
-    ThreadData *td = (ThreadData*)arg;                                                                     \
+    ThreadData *td = arg;                                                                                  \
     const V360Context *s = ctx->priv;                                                                      \
     const AVFrame *in = td->in;                                                                            \
     AVFrame *out = td->out;                                                                                \
@@ -353,10 +359,10 @@ static void bilinear_kernel(float du, float dv, const XYRemap *rmap,
         }
     }
 
-    ker[0] = (1.f - du) * (1.f - dv) * 16385;
-    ker[1] =        du  * (1.f - dv) * 16385;
-    ker[2] = (1.f - du) *        dv  * 16385;
-    ker[3] =        du  *        dv  * 16385;
+    ker[0] = lrintf((1.f - du) * (1.f - dv) * 16385.f);
+    ker[1] = lrintf(       du  * (1.f - dv) * 16385.f);
+    ker[2] = lrintf((1.f - du) *        dv  * 16385.f);
+    ker[3] = lrintf(       du  *        dv  * 16385.f);
 }
 
 /**
@@ -399,7 +405,7 @@ static void bicubic_kernel(float du, float dv, const XYRemap *rmap,
         for (int j = 0; j < 4; j++) {
             u[i * 4 + j] = rmap->u[i][j];
             v[i * 4 + j] = rmap->v[i][j];
-            ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16385;
+            ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
         }
     }
 }
@@ -452,7 +458,7 @@ static void lanczos_kernel(float du, float dv, const XYRemap *rmap,
         for (int j = 0; j < 4; j++) {
             u[i * 4 + j] = rmap->u[i][j];
             v[i * 4 + j] = rmap->v[i][j];
-            ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16385;
+            ker[i * 4 + j] = lrintf(du_coeffs[j] * dv_coeffs[i] * 16385.f);
         }
     }
 }
@@ -722,16 +728,18 @@ static void normalize_vector(float *vec)
  * @param vf vertical cubemap coordinate [0, 1)
  * @param face face of cubemap
  * @param vec coordinates on sphere
+ * @param scalew scale for uf
+ * @param scaleh scale for vf
  */
 static void cube_to_xyz(const V360Context *s,
                         float uf, float vf, int face,
-                        float *vec)
+                        float *vec, float scalew, float scaleh)
 {
     const int direction = s->out_cubemap_direction_order[face];
     float l_x, l_y, l_z;
 
-    uf /= (1.f - s->out_pad);
-    vf /= (1.f - s->out_pad);
+    uf /= scalew;
+    vf /= scaleh;
 
     rotate_cube_face_inverse(&uf, &vf, s->out_cubemap_face_rotation[face]);
 
@@ -1061,6 +1069,9 @@ static void cube3x2_to_xyz(const V360Context *s,
                            int i, int j, int width, int height,
                            float *vec)
 {
+    const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width  / 3.f) : 1.f - s->out_pad;
+    const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 2.f) : 1.f - s->out_pad;
+
     const float ew = width  / 3.f;
     const float eh = height / 2.f;
 
@@ -1076,7 +1087,7 @@ static void cube3x2_to_xyz(const V360Context *s,
     const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
     const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
 
-    cube_to_xyz(s, uf, vf, face, vec);
+    cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
 }
 
 /**
@@ -1095,6 +1106,8 @@ static void xyz_to_cube3x2(const V360Context *s,
                            const float *vec, int width, int height,
                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
 {
+    const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width  / 3.f) : 1.f - s->in_pad;
+    const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 2.f) : 1.f - s->in_pad;
     const float ew = width  / 3.f;
     const float eh = height / 2.f;
     float uf, vf;
@@ -1105,8 +1118,8 @@ static void xyz_to_cube3x2(const V360Context *s,
 
     xyz_to_cube(s, vec, &uf, &vf, &direction);
 
-    uf *= (1.f - s->in_pad);
-    vf *= (1.f - s->in_pad);
+    uf *= scalew;
+    vf *= scaleh;
 
     face = s->in_cubemap_face_order[direction];
     u_face = face % 3;
@@ -1141,13 +1154,13 @@ static void xyz_to_cube3x2(const V360Context *s,
                 uf = 2.f * new_ui / ewi - 1.f;
                 vf = 2.f * new_vi / ehi - 1.f;
 
-                uf /= (1.f - s->in_pad);
-                vf /= (1.f - s->in_pad);
+                uf /= scalew;
+                vf /= scaleh;
 
                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
 
-                uf *= (1.f - s->in_pad);
-                vf *= (1.f - s->in_pad);
+                uf *= scalew;
+                vf *= scaleh;
 
                 u_face = face % 3;
                 v_face = face / 3;
@@ -1180,6 +1193,9 @@ static void cube1x6_to_xyz(const V360Context *s,
                            int i, int j, int width, int height,
                            float *vec)
 {
+    const float scalew = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_width : 1.f - s->out_pad;
+    const float scaleh = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_height / 6.f) : 1.f - s->out_pad;
+
     const float ew = width;
     const float eh = height / 6.f;
 
@@ -1191,7 +1207,7 @@ static void cube1x6_to_xyz(const V360Context *s,
     const float uf = 2.f * (i           + 0.5f) / ew  - 1.f;
     const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
 
-    cube_to_xyz(s, uf, vf, face, vec);
+    cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
 }
 
 /**
@@ -1208,6 +1224,9 @@ static void cube6x1_to_xyz(const V360Context *s,
                            int i, int j, int width, int height,
                            float *vec)
 {
+    const float scalew = s->fout_pad > 0 ? 1.f - s->fout_pad / (s->out_width / 6.f)   : 1.f - s->out_pad;
+    const float scaleh = s->fout_pad > 0 ? 1.f - (float)(s->fout_pad) / s->out_height : 1.f - s->out_pad;
+
     const float ew = width / 6.f;
     const float eh = height;
 
@@ -1219,7 +1238,7 @@ static void cube6x1_to_xyz(const V360Context *s,
     const float uf = 2.f * (i - u_shift + 0.5f) / ewi - 1.f;
     const float vf = 2.f * (j           + 0.5f) / eh  - 1.f;
 
-    cube_to_xyz(s, uf, vf, face, vec);
+    cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
 }
 
 /**
@@ -1238,6 +1257,8 @@ static void xyz_to_cube1x6(const V360Context *s,
                            const float *vec, int width, int height,
                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
 {
+    const float scalew = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_width : 1.f - s->in_pad;
+    const float scaleh = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_height / 6.f) : 1.f - s->in_pad;
     const float eh = height / 6.f;
     const int ewi = width;
     float uf, vf;
@@ -1247,8 +1268,8 @@ static void xyz_to_cube1x6(const V360Context *s,
 
     xyz_to_cube(s, vec, &uf, &vf, &direction);
 
-    uf *= (1.f - s->in_pad);
-    vf *= (1.f - s->in_pad);
+    uf *= scalew;
+    vf *= scaleh;
 
     face = s->in_cubemap_face_order[direction];
     ehi = ceilf(eh * (face + 1)) - ceilf(eh * face);
@@ -1277,13 +1298,13 @@ static void xyz_to_cube1x6(const V360Context *s,
                 uf = 2.f * new_ui / ewi - 1.f;
                 vf = 2.f * new_vi / ehi - 1.f;
 
-                uf /= (1.f - s->in_pad);
-                vf /= (1.f - s->in_pad);
+                uf /= scalew;
+                vf /= scaleh;
 
                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
 
-                uf *= (1.f - s->in_pad);
-                vf *= (1.f - s->in_pad);
+                uf *= scalew;
+                vf *= scaleh;
 
                 v_shift = ceilf(eh * face);
                 new_ehi = ceilf(eh * (face + 1)) - v_shift;
@@ -1314,6 +1335,8 @@ static void xyz_to_cube6x1(const V360Context *s,
                            const float *vec, int width, int height,
                            uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
 {
+    const float scalew = s->fin_pad > 0 ? 1.f - s->fin_pad / (s->in_width / 6.f)   : 1.f - s->in_pad;
+    const float scaleh = s->fin_pad > 0 ? 1.f - (float)(s->fin_pad) / s->in_height : 1.f - s->in_pad;
     const float ew = width / 6.f;
     const int ehi = height;
     float uf, vf;
@@ -1323,8 +1346,8 @@ static void xyz_to_cube6x1(const V360Context *s,
 
     xyz_to_cube(s, vec, &uf, &vf, &direction);
 
-    uf *= (1.f - s->in_pad);
-    vf *= (1.f - s->in_pad);
+    uf *= scalew;
+    vf *= scaleh;
 
     face = s->in_cubemap_face_order[direction];
     ewi = ceilf(ew * (face + 1)) - ceilf(ew * face);
@@ -1353,13 +1376,13 @@ static void xyz_to_cube6x1(const V360Context *s,
                 uf = 2.f * new_ui / ewi - 1.f;
                 vf = 2.f * new_vi / ehi - 1.f;
 
-                uf /= (1.f - s->in_pad);
-                vf /= (1.f - s->in_pad);
+                uf /= scalew;
+                vf /= scaleh;
 
                 process_cube_coordinates(s, uf, vf, direction, &uf, &vf, &face);
 
-                uf *= (1.f - s->in_pad);
-                vf *= (1.f - s->in_pad);
+                uf *= scalew;
+                vf *= scaleh;
 
                 u_shift = ceilf(ew * face);
                 new_ewi = ceilf(ew * (face + 1)) - u_shift;
@@ -1412,11 +1435,8 @@ static int prepare_stereographic_out(AVFilterContext *ctx)
 {
     V360Context *s = ctx->priv;
 
-    const float h_angle = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
-    const float v_angle = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
-
-    s->flat_range[0] = h_angle;
-    s->flat_range[1] = v_angle;
+    s->flat_range[0] = tanf(FFMIN(s->h_fov, 359.f) * M_PI / 720.f);
+    s->flat_range[1] = tanf(FFMIN(s->v_fov, 359.f) * M_PI / 720.f);
 
     return 0;
 }
@@ -1537,12 +1557,12 @@ static void xyz_to_mercator(const V360Context *s,
                             uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
 {
     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
-    const float theta = 0.5f * asinhf(vec[1] / sqrtf(1.f - vec[1] * vec[1])) * s->input_mirror_modifier[1];
+    const float theta = -vec[1] * s->input_mirror_modifier[1];
     float uf, vf;
     int ui, vi;
 
-    uf = (phi   / M_PI + 1.f) * width  / 2.f;
-    vf = (theta / M_PI + 1.f) * height / 2.f;
+    uf = (phi / M_PI + 1.f) * width / 2.f;
+    vf = (av_clipf(logf((1.f + theta) / (1.f - theta)) / (2.f * M_PI), -1.f, 1.f) + 1.f) * height / 2.f;
     ui = floorf(uf);
     vi = floorf(vf);
 
@@ -1551,7 +1571,7 @@ static void xyz_to_mercator(const V360Context *s,
 
     for (int i = -1; i < 3; i++) {
         for (int j = -1; j < 3; j++) {
-            us[i + 1][j + 1] = mod(ui + j, width);
+            us[i + 1][j + 1] = av_clip(ui + j, 0, width  - 1);
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
@@ -1571,17 +1591,18 @@ static void mercator_to_xyz(const V360Context *s,
                             int i, int j, int width, int height,
                             float *vec)
 {
-    const float phi   = ((2.f * i) / width  - 1.f) * M_PI;
-    const float theta = atanf(sinhf(((2.f * j) / height - 1.f) * 2.f * M_PI));
+    const float phi = ((2.f * i) / width - 1.f) * M_PI + M_PI_2;
+    const float y   = ((2.f * j) / height - 1.f) * M_PI;
+    const float div = expf(2.f * y) + 1.f;
 
     const float sin_phi   = sinf(phi);
     const float cos_phi   = cosf(phi);
-    const float sin_theta = sinf(theta);
-    const float cos_theta = cosf(theta);
+    const float sin_theta = -2.f * expf(y) / div;
+    const float cos_theta = -(expf(2.f * y) - 1.f) / div;
 
-    vec[0] =  cos_theta * sin_phi;
-    vec[1] = -sin_theta;
-    vec[2] = -cos_theta * cos_phi;
+    vec[0] = sin_theta * cos_phi;
+    vec[1] = cos_theta;
+    vec[2] = sin_theta * sin_phi;
 }
 
 /**
@@ -1601,12 +1622,13 @@ static void xyz_to_ball(const V360Context *s,
                         uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
 {
     const float l = hypotf(vec[0], vec[1]);
-    const float r = sinf(acosf(-vec[2]) * 0.5f);
+    const float r = sqrtf(1.f + vec[2]) / M_SQRT2;
     float uf, vf;
     int ui, vi;
 
-    uf = (1.f - r * vec[0] / l) * width  / 2.f;
-    vf = (1.f + r * vec[1] / l) * height / 2.f;
+    uf = (1.f + r * vec[0] * s->input_mirror_modifier[0] / (l > 0.f ? l : 1.f)) * width  * 0.5f;
+    vf = (1.f - r * vec[1] * s->input_mirror_modifier[1] / (l > 0.f ? l : 1.f)) * height * 0.5f;
+
     ui = floorf(uf);
     vi = floorf(vf);
 
@@ -1615,7 +1637,7 @@ static void xyz_to_ball(const V360Context *s,
 
     for (int i = -1; i < 3; i++) {
         for (int j = -1; j < 3; j++) {
-            us[i + 1][j + 1] = mod(ui + j, width);
+            us[i + 1][j + 1] = av_clip(ui + j, 0, width  - 1);
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
@@ -1640,17 +1662,11 @@ static void ball_to_xyz(const V360Context *s,
     const float l = hypotf(x, y);
 
     if (l <= 1.f) {
-        const float phi   = atan2f(x, y);
-        const float theta = 2.f * asinf(l);
-
-        const float sin_phi   = sinf(phi);
-        const float cos_phi   = cosf(phi);
-        const float sin_theta = sinf(theta);
-        const float cos_theta = cosf(theta);
+        const float z = 2.f * l * sqrtf(1.f - l * l);
 
-        vec[0] =  sin_theta * sin_phi;
-        vec[1] = -sin_theta * cos_phi;
-        vec[2] = -cos_theta;
+        vec[0] =  z * x / (l > 0.f ? l : 1.f);
+        vec[1] = -z * y / (l > 0.f ? l : 1.f);
+        vec[2] = -1.f + 2.f * l * l;
     } else {
         vec[0] =  0.f;
         vec[1] = -1.f;
@@ -1658,6 +1674,149 @@ static void ball_to_xyz(const V360Context *s,
     }
 }
 
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in hammer format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void hammer_to_xyz(const V360Context *s,
+                          int i, int j, int width, int height,
+                          float *vec)
+{
+    const float x = ((2.f * i) / width  - 1.f);
+    const float y = ((2.f * j) / height - 1.f);
+
+    const float xx = x * x;
+    const float yy = y * y;
+
+    const float z = sqrtf(1.f - xx * 0.5f - yy * 0.5f);
+
+    const float a = M_SQRT2 * x * z;
+    const float b = 2.f * z * z - 1.f;
+
+    const float aa = a * a;
+    const float bb = b * b;
+
+    const float w = sqrtf(1.f - 2.f * yy * z * z);
+
+    vec[0] =  w * 2.f * a * b / (aa + bb);
+    vec[1] = -M_SQRT2 * y * z;
+    vec[2] = -w * (bb  - aa) / (aa + bb);
+
+    normalize_vector(vec);
+}
+
+/**
+ * Calculate frame position in hammer format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter private context
+ * @param vec coordinates on sphere
+ * @param width frame width
+ * @param height frame height
+ * @param us horizontal coordinates for interpolation window
+ * @param vs vertical coordinates for interpolation window
+ * @param du horizontal relative coordinate
+ * @param dv vertical relative coordinate
+ */
+static void xyz_to_hammer(const V360Context *s,
+                          const float *vec, int width, int height,
+                          uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
+{
+    const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
+
+    const float z = sqrtf(1.f + sqrtf(1.f - vec[1] * vec[1]) * cosf(theta * 0.5f));
+    const float x = sqrtf(1.f - vec[1] * vec[1]) * sinf(theta * 0.5f) / z;
+    const float y = -vec[1] / z * s->input_mirror_modifier[1];
+    float uf, vf;
+    int ui, vi;
+
+    uf = (x + 1.f) * width  / 2.f;
+    vf = (y + 1.f) * height / 2.f;
+    ui = floorf(uf);
+    vi = floorf(vf);
+
+    *du = uf - ui;
+    *dv = vf - vi;
+
+    for (int i = -1; i < 3; i++) {
+        for (int j = -1; j < 3; j++) {
+            us[i + 1][j + 1] = av_clip(ui + j, 0, width  - 1);
+            vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
+        }
+    }
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in sinusoidal format.
+ *
+ * @param s filter private context
+ * @param i horizontal position on frame [0, width)
+ * @param j vertical position on frame [0, height)
+ * @param width frame width
+ * @param height frame height
+ * @param vec coordinates on sphere
+ */
+static void sinusoidal_to_xyz(const V360Context *s,
+                              int i, int j, int width, int height,
+                              float *vec)
+{
+    const float theta = ((2.f * j) / height - 1.f) * M_PI_2;
+    const float phi   = ((2.f * i) / width  - 1.f) * M_PI / cosf(theta);
+
+    const float sin_phi   = sinf(phi);
+    const float cos_phi   = cosf(phi);
+    const float sin_theta = sinf(theta);
+    const float cos_theta = cosf(theta);
+
+    vec[0] =  cos_theta * sin_phi;
+    vec[1] = -sin_theta;
+    vec[2] = -cos_theta * cos_phi;
+
+    normalize_vector(vec);
+}
+
+/**
+ * Calculate frame position in sinusoidal format for corresponding 3D coordinates on sphere.
+ *
+ * @param s filter private context
+ * @param vec coordinates on sphere
+ * @param width frame width
+ * @param height frame height
+ * @param us horizontal coordinates for interpolation window
+ * @param vs vertical coordinates for interpolation window
+ * @param du horizontal relative coordinate
+ * @param dv vertical relative coordinate
+ */
+static void xyz_to_sinusoidal(const V360Context *s,
+                              const float *vec, int width, int height,
+                              uint16_t us[4][4], uint16_t vs[4][4], float *du, float *dv)
+{
+    const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
+    const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] * cosf(theta);
+    float uf, vf;
+    int ui, vi;
+
+    uf = (phi   / M_PI   + 1.f) * width  / 2.f;
+    vf = (theta / M_PI_2 + 1.f) * height / 2.f;
+    ui = floorf(uf);
+    vi = floorf(vf);
+
+    *du = uf - ui;
+    *dv = vf - vi;
+
+    for (int i = -1; i < 3; i++) {
+        for (int j = -1; j < 3; j++) {
+            us[i + 1][j + 1] = av_clip(ui + j, 0, width  - 1);
+            vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
+        }
+    }
+}
+
 /**
  * Prepare data for processing equi-angular cubemap input format.
  *
@@ -1768,8 +1927,8 @@ static void eac_to_xyz(const V360Context *s,
 
     float l_x, l_y, l_z;
 
-    float uf = (float)i / width;
-    float vf = (float)j / height;
+    float uf = (i + 0.5f) / width;
+    float vf = (j + 0.5f) / height;
 
     // EAC has 2-pixel padding on faces except between faces on the same row
     // Padding pixels seems not to be stretched with tangent as regular pixels
@@ -1888,6 +2047,9 @@ static void xyz_to_eac(const V360Context *s,
     uf *= width;
     vf *= height;
 
+    uf -= 0.5f;
+    vf -= 0.5f;
+
     ui = floorf(uf);
     vi = floorf(vf);
 
@@ -1913,12 +2075,8 @@ static int prepare_flat_out(AVFilterContext *ctx)
 {
     V360Context *s = ctx->priv;
 
-    const float h_angle = 0.5f * s->h_fov * M_PI / 180.f;
-    const float v_angle = 0.5f * s->v_fov * M_PI / 180.f;
-
-    s->flat_range[0] =  tanf(h_angle);
-    s->flat_range[1] =  tanf(v_angle);
-    s->flat_range[2] = -1.f;
+    s->flat_range[0] = tanf(0.5f * s->h_fov * M_PI / 180.f);
+    s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
 
     return 0;
 }
@@ -1939,11 +2097,10 @@ static void flat_to_xyz(const V360Context *s,
 {
     const float l_x =  s->flat_range[0] * (2.f * i / width  - 1.f);
     const float l_y = -s->flat_range[1] * (2.f * j / height - 1.f);
-    const float l_z =  s->flat_range[2];
 
-    vec[0] = l_x;
-    vec[1] = l_y;
-    vec[2] = l_z;
+    vec[0] =  l_x;
+    vec[1] =  l_y;
+    vec[2] = -1.f;
 
     normalize_vector(vec);
 }
@@ -1973,16 +2130,15 @@ static void dfisheye_to_xyz(const V360Context *s,
     const float uf = ((2.f * ei) / ew - 1.f) * scale;
     const float vf = ((2.f *  j) / eh - 1.f) * scale;
 
-    const float phi   = M_PI + atan2f(vf, uf * m);
-    const float theta = m * M_PI_2 * (1.f - hypotf(uf, vf));
+    const float h     = hypotf(uf, vf);
+    const float lh    = h > 0.f ? h : 1.f;
+    const float theta = m * M_PI_2 * (1.f - h);
 
-    const float sin_phi   = sinf(phi);
-    const float cos_phi   = cosf(phi);
     const float sin_theta = sinf(theta);
     const float cos_theta = cosf(theta);
 
-    vec[0] = cos_theta * cos_phi;
-    vec[1] = cos_theta * sin_phi;
+    vec[0] = cos_theta * m * -uf / lh;
+    vec[1] = cos_theta *     -vf / lh;
     vec[2] = sin_theta;
 
     normalize_vector(vec);
@@ -2009,16 +2165,17 @@ static void xyz_to_dfisheye(const V360Context *s,
     const float ew = width / 2.f;
     const float eh = height;
 
-    const float phi   = atan2f(-vec[1], -vec[0]) * s->input_mirror_modifier[0];
-    const float theta = acosf(fabsf(vec[2])) / M_PI * s->input_mirror_modifier[1];
+    const float h     = hypotf(vec[0], vec[1]);
+    const float lh    = h > 0.f ? h : 1.f;
+    const float theta = acosf(fabsf(vec[2])) / M_PI;
 
-    float uf = (theta * cosf(phi) * scale + 0.5f) * ew;
-    float vf = (theta * sinf(phi) * scale + 0.5f) * eh;
+    float uf = (theta * (-vec[0] / lh) * s->input_mirror_modifier[0] * scale + 0.5f) * ew;
+    float vf = (theta * (-vec[1] / lh) * s->input_mirror_modifier[1] * scale + 0.5f) * eh;
 
     int ui, vi;
     int u_shift;
 
-    if (vec[2] >= 0) {
+    if (vec[2] >= 0.f) {
         u_shift = 0;
     } else {
         u_shift = ceilf(ew);
@@ -2447,6 +2604,12 @@ static int config_output(AVFilterLink *outlink)
     set_dimensions(s->inplanewidth, s->inplaneheight, w, h, desc);
     set_dimensions(s->in_offset_w, s->in_offset_h, in_offset_w, in_offset_h, desc);
 
+    s->in_width = s->inplanewidth[0];
+    s->in_height = s->inplaneheight[0];
+
+    if (s->in_transpose)
+        FFSWAP(int, s->in_width, s->in_height);
+
     switch (s->in) {
     case EQUIRECTANGULAR:
         s->in_transform = xyz_to_equirect;
@@ -2503,7 +2666,7 @@ static int config_output(AVFilterLink *outlink)
         s->in_transform = xyz_to_mercator;
         err = 0;
         wf = w;
-        hf = h;
+        hf = h / 2.f;
         break;
     case BALL:
         s->in_transform = xyz_to_ball;
@@ -2511,6 +2674,18 @@ static int config_output(AVFilterLink *outlink)
         wf = w;
         hf = h / 2.f;
         break;
+    case HAMMER:
+        s->in_transform = xyz_to_hammer;
+        err = 0;
+        wf = w;
+        hf = h;
+        break;
+    case SINUSOIDAL:
+        s->in_transform = xyz_to_sinusoidal;
+        err = 0;
+        wf = w;
+        hf = h;
+        break;
     default:
         av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
         return AVERROR_BUG;
@@ -2579,7 +2754,7 @@ static int config_output(AVFilterLink *outlink)
         s->out_transform = mercator_to_xyz;
         prepare_out = NULL;
         w = roundf(wf);
-        h = roundf(hf);
+        h = roundf(hf * 2.f);
         break;
     case BALL:
         s->out_transform = ball_to_xyz;
@@ -2587,6 +2762,18 @@ static int config_output(AVFilterLink *outlink)
         w = roundf(wf);
         h = roundf(hf * 2.f);
         break;
+    case HAMMER:
+        s->out_transform = hammer_to_xyz;
+        prepare_out = NULL;
+        w = roundf(wf);
+        h = roundf(hf);
+        break;
+    case SINUSOIDAL:
+        s->out_transform = sinusoidal_to_xyz;
+        prepare_out = NULL;
+        w = roundf(wf);
+        h = roundf(hf);
+        break;
     default:
         av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
         return AVERROR_BUG;
@@ -2618,6 +2805,12 @@ static int config_output(AVFilterLink *outlink)
 
     set_dimensions(s->pr_width, s->pr_height, w, h, desc);
 
+    s->out_width = s->pr_width[0];
+    s->out_height = s->pr_height[0];
+
+    if (s->out_transpose)
+        FFSWAP(int, s->out_width, s->out_height);
+
     switch (s->out_stereo) {
     case STEREO_2D:
         out_offset_w = out_offset_h = 0;