]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/vf_v360.c
avfilter/vf_v360: add mask option, unset pixels are marked as transparent
[ffmpeg] / libavfilter / vf_v360.c
index 0c5c6a7775aebdc795012bda29f4fb6625a777fa..2fc9d222b6d61c9223e849ee0593d3e979f53024 100644 (file)
@@ -72,7 +72,9 @@ static const AVOption v360_options[] = {
     {      "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" },
+    {   "fisheye", "fisheye",                                    0, AV_OPT_TYPE_CONST,  {.i64=FISHEYE},         0,                   0, FLAGS, "in" },
     {"cylindrical", "cylindrical",                               0, AV_OPT_TYPE_CONST,  {.i64=CYLINDRICAL},     0,                   0, FLAGS, "in" },
+    {"tetrahedron", "tetrahedron",                               0, AV_OPT_TYPE_CONST,  {.i64=TETRAHEDRON},     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" },
@@ -95,6 +97,7 @@ static const AVOption v360_options[] = {
     {   "pannini", "pannini",                                    0, AV_OPT_TYPE_CONST,  {.i64=PANNINI},         0,                   0, FLAGS, "out" },
     {"cylindrical", "cylindrical",                               0, AV_OPT_TYPE_CONST,  {.i64=CYLINDRICAL},     0,                   0, FLAGS, "out" },
     {"perspective", "perspective",                               0, AV_OPT_TYPE_CONST,  {.i64=PERSPECTIVE},     0,                   0, FLAGS, "out" },
+    {"tetrahedron", "tetrahedron",                               0, AV_OPT_TYPE_CONST,  {.i64=TETRAHEDRON},     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" },
@@ -140,6 +143,7 @@ static const AVOption v360_options[] = {
     {    "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT,  {.dbl=90.f},     0.00001f,               360.f, FLAGS, "ih_fov"},
     {    "iv_fov", "input vertical field of view",  OFFSET(iv_fov), AV_OPT_TYPE_FLOAT,  {.dbl=45.f},     0.00001f,               360.f, FLAGS, "iv_fov"},
     {    "id_fov", "input diagonal field of view",  OFFSET(id_fov), AV_OPT_TYPE_FLOAT,  {.dbl=0.f},           0.f,               360.f, FLAGS, "id_fov"},
+    {"alpha_mask", "build mask in alpha plane",      OFFSET(alpha), AV_OPT_TYPE_BOOL,   {.i64=0},               0,                   1, FLAGS, "alpha"},
     { NULL }
 };
 
@@ -147,6 +151,7 @@ AVFILTER_DEFINE_CLASS(v360);
 
 static int query_formats(AVFilterContext *ctx)
 {
+    V360Context *s = ctx->priv;
     static const enum AVPixelFormat pix_fmts[] = {
         // YUVA444
         AV_PIX_FMT_YUVA444P,   AV_PIX_FMT_YUVA444P9,
@@ -208,8 +213,21 @@ static int query_formats(AVFilterContext *ctx)
 
         AV_PIX_FMT_NONE
     };
+    static const enum AVPixelFormat alpha_pix_fmts[] = {
+        AV_PIX_FMT_YUVA444P,   AV_PIX_FMT_YUVA444P9,
+        AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12,
+        AV_PIX_FMT_YUVA444P16,
+        AV_PIX_FMT_YUVA422P,   AV_PIX_FMT_YUVA422P9,
+        AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12,
+        AV_PIX_FMT_YUVA422P16,
+        AV_PIX_FMT_YUVA420P,   AV_PIX_FMT_YUVA420P9,
+        AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
+        AV_PIX_FMT_GBRAP,   AV_PIX_FMT_GBRAP10,
+        AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
+        AV_PIX_FMT_NONE
+    };
 
-    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
+    AVFilterFormats *fmts_list = ff_make_format_list(s->alpha ? alpha_pix_fmts : pix_fmts);
     if (!fmts_list)
         return AVERROR(ENOMEM);
     return ff_set_common_formats(ctx, fmts_list);
@@ -260,6 +278,7 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo
             const uint8_t *const src = in->data[plane] +                                                   \
                                                    in_offset_h * in_linesize + in_offset_w * (bits >> 3);  \
             uint8_t *dst = out->data[plane] + out_offset_h * out_linesize + out_offset_w * (bits >> 3);    \
+            const uint8_t *mask = plane == 3 ? s->mask : NULL;                                             \
             const int width = s->pr_width[plane];                                                          \
             const int height = s->pr_height[plane];                                                        \
                                                                                                            \
@@ -272,6 +291,10 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo
                 const int16_t *const ker = s->ker[map] + y * uv_linesize * ws * ws;                        \
                                                                                                            \
                 s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker);                 \
+            }                                                                                              \
+                                                                                                           \
+            for (int y = slice_start; y < slice_end && mask; y++) {                                        \
+                memcpy(dst + y * out_linesize, mask + y * width * (bits >> 3), width * (bits >> 3));       \
             }                                                                                              \
         }                                                                                                  \
     }                                                                                                      \
@@ -1180,9 +1203,9 @@ static void process_cube_coordinates(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void cube3x2_to_xyz(const V360Context *s,
-                           int i, int j, int width, int height,
-                           float *vec)
+static int 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;
@@ -1203,6 +1226,8 @@ static void cube3x2_to_xyz(const V360Context *s,
     const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
 
     cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
+
+    return 1;
 }
 
 /**
@@ -1217,9 +1242,9 @@ static void cube3x2_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     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;
@@ -1292,6 +1317,8 @@ static void xyz_to_cube3x2(const V360Context *s,
             vs[i + 1][j + 1] = v_shift + new_vi;
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1304,9 +1331,9 @@ static void xyz_to_cube3x2(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void cube1x6_to_xyz(const V360Context *s,
-                           int i, int j, int width, int height,
-                           float *vec)
+static int 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;
@@ -1323,6 +1350,8 @@ static void cube1x6_to_xyz(const V360Context *s,
     const float vf = 2.f * (j - v_shift + 0.5f) / ehi - 1.f;
 
     cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
+
+    return 1;
 }
 
 /**
@@ -1335,9 +1364,9 @@ static void cube1x6_to_xyz(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void cube6x1_to_xyz(const V360Context *s,
-                           int i, int j, int width, int height,
-                           float *vec)
+static int 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;
@@ -1354,6 +1383,8 @@ static void cube6x1_to_xyz(const V360Context *s,
     const float vf = 2.f * (j           + 0.5f) / eh  - 1.f;
 
     cube_to_xyz(s, uf, vf, face, vec, scalew, scaleh);
+
+    return 1;
 }
 
 /**
@@ -1368,9 +1399,9 @@ static void cube6x1_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     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;
@@ -1432,6 +1463,8 @@ static void xyz_to_cube1x6(const V360Context *s,
             vs[i + 1][j + 1] = v_shift + new_vi;
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1446,9 +1479,9 @@ static void xyz_to_cube1x6(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     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;
@@ -1510,6 +1543,8 @@ static void xyz_to_cube6x1(const V360Context *s,
             vs[i + 1][j + 1] =           new_vi;
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1522,9 +1557,9 @@ static void xyz_to_cube6x1(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void equirect_to_xyz(const V360Context *s,
-                            int i, int j, int width, int height,
-                            float *vec)
+static int equirect_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 = ((2.f * j) / height - 1.f) * M_PI_2;
@@ -1537,6 +1572,8 @@ static void equirect_to_xyz(const V360Context *s,
     vec[0] =  cos_theta * sin_phi;
     vec[1] = -sin_theta;
     vec[2] = -cos_theta * cos_phi;
+
+    return 1;
 }
 
 /**
@@ -1566,9 +1603,9 @@ static int prepare_stereographic_out(AVFilterContext *ctx)
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void stereographic_to_xyz(const V360Context *s,
-                                 int i, int j, int width, int height,
-                                 float *vec)
+static int stereographic_to_xyz(const V360Context *s,
+                                int i, int j, int width, int height,
+                                float *vec)
 {
     const float x = ((2.f * i) / width  - 1.f) * s->flat_range[0];
     const float y = ((2.f * j) / height - 1.f) * s->flat_range[1];
@@ -1579,6 +1616,25 @@ static void stereographic_to_xyz(const V360Context *s,
     vec[2] = 2.f * y / (1.f + xy);
 
     normalize_vector(vec);
+
+    return 1;
+}
+
+/**
+ * Prepare data for processing stereographic input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_stereographic_in(AVFilterContext *ctx)
+{
+    V360Context *s = ctx->priv;
+
+    s->iflat_range[0] = tanf(FFMIN(s->ih_fov, 359.f) * M_PI / 720.f);
+    s->iflat_range[1] = tanf(FFMIN(s->iv_fov, 359.f) * M_PI / 720.f);
+
+    return 0;
 }
 
 /**
@@ -1593,29 +1649,33 @@ static void stereographic_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
-    const float x = av_clipf(vec[0] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[0];
-    const float y = av_clipf(vec[2] / (1.f - vec[1]), -1.f, 1.f) * s->input_mirror_modifier[1];
+    const float x = vec[0] / (1.f - vec[1]) / s->iflat_range[0] * s->input_mirror_modifier[0];
+    const float y = vec[2] / (1.f - vec[1]) / s->iflat_range[1] * s->input_mirror_modifier[1];
     float uf, vf;
-    int ui, vi;
+    int visible, 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;
+    visible = isfinite(x) && isfinite(y) && vi >= 0 && vi < height && ui >= 0 && ui < width;
+
+    *du = visible ? uf - ui : 0.f;
+    *dv = visible ? vf - vi : 0.f;
 
     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);
+            us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width  - 1) : 0;
+            vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
         }
     }
+
+    return visible;
 }
 
 /**
@@ -1630,9 +1690,9 @@ static void xyz_to_stereographic(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
     const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1];
@@ -1653,6 +1713,8 @@ static void xyz_to_equirect(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1684,9 +1746,9 @@ static int prepare_flat_in(AVFilterContext *ctx)
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float theta = acosf(vec[2]);
     const float r = tanf(theta);
@@ -1715,6 +1777,8 @@ static void xyz_to_flat(const V360Context *s,
             vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
         }
     }
+
+    return visible;
 }
 
 /**
@@ -1729,9 +1793,9 @@ static void xyz_to_flat(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
     const float theta = -vec[1] * s->input_mirror_modifier[1];
@@ -1752,6 +1816,8 @@ static void xyz_to_mercator(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1764,9 +1830,9 @@ static void xyz_to_mercator(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void mercator_to_xyz(const V360Context *s,
-                            int i, int j, int width, int height,
-                            float *vec)
+static int 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 + M_PI_2;
     const float y   = ((2.f * j) / height - 1.f) * M_PI;
@@ -1780,6 +1846,8 @@ static void mercator_to_xyz(const V360Context *s,
     vec[0] = sin_theta * cos_phi;
     vec[1] = cos_theta;
     vec[2] = sin_theta * sin_phi;
+
+    return 1;
 }
 
 /**
@@ -1794,9 +1862,9 @@ static void mercator_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float l = hypotf(vec[0], vec[1]);
     const float r = sqrtf(1.f + vec[2]) / M_SQRT2;
@@ -1818,6 +1886,8 @@ static void xyz_to_ball(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1830,9 +1900,9 @@ static void xyz_to_ball(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void ball_to_xyz(const V360Context *s,
-                        int i, int j, int width, int height,
-                        float *vec)
+static int ball_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;
@@ -1848,7 +1918,10 @@ static void ball_to_xyz(const V360Context *s,
         vec[0] =  0.f;
         vec[1] = -1.f;
         vec[2] =  0.f;
+        return 0;
     }
+
+    return 1;
 }
 
 /**
@@ -1861,9 +1934,9 @@ static void ball_to_xyz(const V360Context *s,
  * @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)
+static int 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);
@@ -1886,6 +1959,8 @@ static void hammer_to_xyz(const V360Context *s,
     vec[2] = -w * (bb  - aa) / (aa + bb);
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -1900,9 +1975,9 @@ static void hammer_to_xyz(const V360Context *s,
  * @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,
-                          int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
+static int 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)
 {
     const float theta = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0];
 
@@ -1926,6 +2001,8 @@ static void xyz_to_hammer(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -1938,9 +2015,9 @@ static void xyz_to_hammer(const V360Context *s,
  * @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)
+static int 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);
@@ -1955,6 +2032,8 @@ static void sinusoidal_to_xyz(const V360Context *s,
     vec[2] = -cos_theta * cos_phi;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -1969,9 +2048,9 @@ static void sinusoidal_to_xyz(const V360Context *s,
  * @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,
-                              int16_t us[4][4], int16_t vs[4][4], float *du, float *dv)
+static int 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)
 {
     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);
@@ -1992,6 +2071,8 @@ static void xyz_to_sinusoidal(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -2092,9 +2173,9 @@ static int prepare_eac_out(AVFilterContext *ctx)
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void eac_to_xyz(const V360Context *s,
-                       int i, int j, int width, int height,
-                       float *vec)
+static int eac_to_xyz(const V360Context *s,
+                      int i, int j, int width, int height,
+                      float *vec)
 {
     const float pixel_pad = 2;
     const float u_pad = pixel_pad / width;
@@ -2181,6 +2262,8 @@ static void eac_to_xyz(const V360Context *s,
     vec[2] = l_z;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2195,9 +2278,9 @@ static void eac_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float pixel_pad = 2;
     const float u_pad = pixel_pad / width;
@@ -2239,6 +2322,8 @@ static void xyz_to_eac(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -2268,9 +2353,9 @@ static int prepare_flat_out(AVFilterContext *ctx)
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void flat_to_xyz(const V360Context *s,
-                        int i, int j, int width, int height,
-                        float *vec)
+static int flat_to_xyz(const V360Context *s,
+                       int i, int j, int width, int height,
+                       float *vec)
 {
     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);
@@ -2280,6 +2365,8 @@ static void flat_to_xyz(const V360Context *s,
     vec[2] = -1.f;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2309,9 +2396,9 @@ static int prepare_fisheye_out(AVFilterContext *ctx)
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void fisheye_to_xyz(const V360Context *s,
-                           int i, int j, int width, int height,
-                           float *vec)
+static int fisheye_to_xyz(const V360Context *s,
+                          int i, int j, int width, int height,
+                          float *vec)
 {
     const float uf = s->flat_range[0] * ((2.f * i) / width  - 1.f);
     const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
@@ -2324,6 +2411,69 @@ static void fisheye_to_xyz(const V360Context *s,
     vec[2] = sinf(theta);
 
     normalize_vector(vec);
+
+    return 1;
+}
+
+/**
+ * Prepare data for processing fisheye input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_fisheye_in(AVFilterContext *ctx)
+{
+    V360Context *s = ctx->priv;
+
+    s->iflat_range[0] = s->ih_fov / 180.f;
+    s->iflat_range[1] = s->iv_fov / 180.f;
+
+    return 0;
+}
+
+/**
+ * Calculate frame position in fisheye 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 int 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)
+{
+    const float phi   = -atan2f(hypotf(vec[0], vec[1]), -vec[2]) / M_PI;
+    const float theta = -atan2f(vec[0], vec[1]);
+
+    float uf = sinf(theta) * phi * s->input_mirror_modifier[0] / s->iflat_range[0];
+    float vf = cosf(theta) * phi * s->input_mirror_modifier[1] / s->iflat_range[1];
+
+    const int visible = hypotf(uf, vf) <= 0.5f;
+    int ui, vi;
+
+    uf = (uf + 0.5f) * width;
+    vf = (vf + 0.5f) * height;
+
+    ui = floorf(uf);
+    vi = floorf(vf);
+
+    *du = visible ? uf - ui : 0.f;
+    *dv = visible ? vf - vi : 0.f;
+
+    for (int i = -1; i < 3; i++) {
+        for (int j = -1; j < 3; j++) {
+            us[i + 1][j + 1] = visible ? av_clip(ui + j, 0, width  - 1) : 0;
+            vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
+        }
+    }
+
+    return visible;
 }
 
 /**
@@ -2336,9 +2486,9 @@ static void fisheye_to_xyz(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void pannini_to_xyz(const V360Context *s,
-                           int i, int j, int width, int height,
-                           float *vec)
+static int pannini_to_xyz(const V360Context *s,
+                          int i, int j, int width, int height,
+                          float *vec)
 {
     const float uf = ((2.f * i) / width  - 1.f);
     const float vf = ((2.f * j) / height - 1.f);
@@ -2356,6 +2506,8 @@ static void pannini_to_xyz(const V360Context *s,
     vec[2] = cosf(lon) * cosf(lat);
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2385,9 +2537,9 @@ static int prepare_cylindrical_out(AVFilterContext *ctx)
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void cylindrical_to_xyz(const V360Context *s,
-                               int i, int j, int width, int height,
-                               float *vec)
+static int cylindrical_to_xyz(const V360Context *s,
+                              int i, int j, int width, int height,
+                              float *vec)
 {
     const float uf = s->flat_range[0] * ((2.f * i) / width  - 1.f);
     const float vf = s->flat_range[1] * ((2.f * j) / height - 1.f);
@@ -2405,6 +2557,8 @@ static void cylindrical_to_xyz(const V360Context *s,
     vec[2] = -cos_theta * cos_phi;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2436,9 +2590,9 @@ static int prepare_cylindrical_in(AVFilterContext *ctx)
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float phi   = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0] / s->iflat_range[0];
     const float theta = atan2f(-vec[1], hypotf(vec[0], vec[2])) * s->input_mirror_modifier[1] / s->iflat_range[1];
@@ -2463,6 +2617,8 @@ static void xyz_to_cylindrical(const V360Context *s,
             vs[i + 1][j + 1] = visible ? av_clip(vi + i, 0, height - 1) : 0;
         }
     }
+
+    return visible;
 }
 
 /**
@@ -2475,9 +2631,9 @@ static void xyz_to_cylindrical(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void perspective_to_xyz(const V360Context *s,
-                               int i, int j, int width, int height,
-                               float *vec)
+static int perspective_to_xyz(const V360Context *s,
+                              int i, int j, int width, int height,
+                              float *vec)
 {
     const float uf = ((2.f * i) / width  - 1.f);
     const float vf = ((2.f * j) / height - 1.f);
@@ -2505,9 +2661,97 @@ static void perspective_to_xyz(const V360Context *s,
         vec[0] =  0.f;
         vec[1] = -1.f;
         vec[2] =  0.f;
+        return 0;
     }
 
     normalize_vector(vec);
+    return 1;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in tetrahedron 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 int tetrahedron_to_xyz(const V360Context *s,
+                              int i, int j, int width, int height,
+                              float *vec)
+{
+    const float uf = (float)i / width;
+    const float vf = (float)j / height;
+
+    vec[0] = uf < 0.5f ? uf * 4.f - 1.f : 3.f - uf * 4.f;
+    vec[1] = 1.f - vf * 2.f;
+    vec[2] = 2.f * fabsf(1.f - fabsf(1.f - uf * 2.f + vf)) - 1.f;
+
+    normalize_vector(vec);
+
+    return 1;
+}
+
+/**
+ * Calculate frame position in tetrahedron 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 int 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)
+{
+    float d = 0.5f * (vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
+
+    const float d0 = (vec[0] * 0.5f + vec[1] * 0.5f + vec[2] *-0.5f) / d;
+    const float d1 = (vec[0] *-0.5f + vec[1] *-0.5f + vec[2] *-0.5f) / d;
+    const float d2 = (vec[0] * 0.5f + vec[1] *-0.5f + vec[2] * 0.5f) / d;
+    const float d3 = (vec[0] *-0.5f + vec[1] * 0.5f + vec[2] * 0.5f) / d;
+
+    float uf, vf, x, y, z;
+    int ui, vi;
+
+    d = FFMAX(d0, FFMAX3(d1, d2, d3));
+
+    x =  vec[0] / d;
+    y =  vec[1] / d;
+    z = -vec[2] / d;
+
+    vf = 0.5f - y * 0.5f * s->input_mirror_modifier[1];
+
+    if ((x + y >= 0.f &&  y + z >= 0.f && -z - x <= 0.f) ||
+        (x + y <= 0.f && -y + z >= 0.f &&  z - x >= 0.f)) {
+        uf = 0.25f * x * s->input_mirror_modifier[0] + 0.25f;
+    }  else {
+        uf = 0.75f - 0.25f * x * s->input_mirror_modifier[0];
+    }
+
+    uf *= width;
+    vf *= height;
+
+    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] = mod(ui + j, width);
+            vs[i + 1][j + 1] = av_clip(vi + i, 0, height - 1);
+        }
+    }
+
+    return 1;
 }
 
 /**
@@ -2520,9 +2764,9 @@ static void perspective_to_xyz(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void dfisheye_to_xyz(const V360Context *s,
-                            int i, int j, int width, int height,
-                            float *vec)
+static int dfisheye_to_xyz(const V360Context *s,
+                           int i, int j, int width, int height,
+                           float *vec)
 {
     const float scale = 1.f + s->out_pad;
 
@@ -2547,6 +2791,8 @@ static void dfisheye_to_xyz(const V360Context *s,
     vec[2] = sin_theta;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2561,9 +2807,9 @@ static void dfisheye_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float scale = 1.f - s->in_pad;
 
@@ -2599,6 +2845,8 @@ static void xyz_to_dfisheye(const V360Context *s,
             vs[i + 1][j + 1] = av_clip(          vi + i, 0, height - 1);
         }
     }
+
+    return 1;
 }
 
 /**
@@ -2611,9 +2859,9 @@ static void xyz_to_dfisheye(const V360Context *s,
  * @param height frame height
  * @param vec coordinates on sphere
  */
-static void barrel_to_xyz(const V360Context *s,
-                          int i, int j, int width, int height,
-                          float *vec)
+static int barrel_to_xyz(const V360Context *s,
+                         int i, int j, int width, int height,
+                         float *vec)
 {
     const float scale = 0.99f;
     float l_x, l_y, l_z;
@@ -2669,6 +2917,8 @@ static void barrel_to_xyz(const V360Context *s,
     vec[2] = l_z;
 
     normalize_vector(vec);
+
+    return 1;
 }
 
 /**
@@ -2683,9 +2933,9 @@ static void barrel_to_xyz(const V360Context *s,
  * @param du horizontal relative coordinate
  * @param dv vertical relative coordinate
  */
-static void 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)
+static int 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)
 {
     const float scale = 0.99f;
 
@@ -2742,6 +2992,8 @@ static void xyz_to_barrel(const V360Context *s,
             vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1);
         }
     }
+
+    return 1;
 }
 
 static void multiply_matrix(float c[3][3], const float a[3][3], const float b[3][3])
@@ -2828,7 +3080,7 @@ static inline void mirror(const float *modifier, float *vec)
     vec[2] *= modifier[2];
 }
 
-static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
+static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int sizeof_mask, int p)
 {
     s->u[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
     s->v[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_uv);
@@ -2840,21 +3092,42 @@ static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p)
             return AVERROR(ENOMEM);
     }
 
+    if (sizeof_mask && !p) {
+        s->mask = av_calloc(s->pr_width[p] * s->pr_height[p], sizeof_mask);
+        if (!s->mask)
+            return AVERROR(ENOMEM);
+    }
+
     return 0;
 }
 
-static void fov_from_dfov(float d_fov, float w, float h, float *h_fov, float *v_fov)
+static void fov_from_dfov(int format, float d_fov, float w, float h, float *h_fov, float *v_fov)
 {
-    const float da = tanf(0.5 * FFMIN(d_fov, 359.f) * M_PI / 180.f);
-    const float d = hypotf(w, h);
+    switch (format) {
+    case FISHEYE:
+        {
+            const float d = 0.5f * hypotf(w, h);
+
+            *h_fov = d / h * d_fov;
+            *v_fov = d / w * d_fov;
+        }
+        break;
+    case FLAT:
+    default:
+        {
+            const float da = tanf(0.5 * FFMIN(d_fov, 359.f) * M_PI / 180.f);
+            const float d = hypotf(w, h);
 
-    *h_fov = atan2f(da * w, d) * 360.f / M_PI;
-    *v_fov = atan2f(da * h, d) * 360.f / M_PI;
+            *h_fov = atan2f(da * w, d) * 360.f / M_PI;
+            *v_fov = atan2f(da * h, d) * 360.f / M_PI;
 
-    if (*h_fov < 0.f)
-        *h_fov += 360.f;
-    if (*v_fov < 0.f)
-        *v_fov += 360.f;
+            if (*h_fov < 0.f)
+                *h_fov += 360.f;
+            if (*v_fov < 0.f)
+                *v_fov += 360.f;
+        }
+        break;
+    }
 }
 
 static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc)
@@ -2871,6 +3144,7 @@ static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobn
     V360Context *s = ctx->priv;
 
     for (int p = 0; p < s->nb_allocated; p++) {
+        const int max_value = s->max_value;
         const int width = s->pr_width[p];
         const int uv_linesize = s->uv_linesize[p];
         const int height = s->pr_height[p];
@@ -2887,22 +3161,33 @@ static av_always_inline int v360_slice(AVFilterContext *ctx, void *arg, int jobn
                 int16_t *u = s->u[p] + (j * uv_linesize + i) * s->elements;
                 int16_t *v = s->v[p] + (j * uv_linesize + i) * s->elements;
                 int16_t *ker = s->ker[p] + (j * uv_linesize + i) * s->elements;
+                uint8_t *mask8 = p ? NULL : s->mask + (j * s->pr_width[0] + i);
+                uint16_t *mask16 = p ? NULL : (uint16_t *)s->mask + (j * s->pr_width[0] + i);
+                int in_mask, out_mask;
 
                 if (s->out_transpose)
-                    s->out_transform(s, j, i, height, width, vec);
+                    out_mask = s->out_transform(s, j, i, height, width, vec);
                 else
-                    s->out_transform(s, i, j, width, height, vec);
+                    out_mask = s->out_transform(s, i, j, width, height, vec);
                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
                 rotate(s->rot_mat, vec);
                 av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2]));
                 normalize_vector(vec);
                 mirror(s->output_mirror_modifier, vec);
                 if (s->in_transpose)
-                    s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
+                    in_mask = s->in_transform(s, vec, in_height, in_width, rmap.v, rmap.u, &du, &dv);
                 else
-                    s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
+                    in_mask = s->in_transform(s, vec, in_width, in_height, rmap.u, rmap.v, &du, &dv);
                 av_assert1(!isnan(du) && !isnan(dv));
                 s->calculate_kernel(du, dv, &rmap, u, v, ker);
+
+                if (!p && s->mask) {
+                    if (s->mask_size == 1) {
+                        mask8[0] = 255 * (out_mask & in_mask);
+                    } else {
+                        mask16[0] = max_value * (out_mask & in_mask);
+                    }
+                }
             }
         }
     }
@@ -2917,6 +3202,7 @@ static int config_output(AVFilterLink *outlink)
     V360Context *s = ctx->priv;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     const int depth = desc->comp[0].depth;
+    const int sizeof_mask = s->mask_size = (depth + 7) >> 3;
     int sizeof_uv;
     int sizeof_ker;
     int err;
@@ -2925,7 +3211,9 @@ static int config_output(AVFilterLink *outlink)
     int out_offset_h, out_offset_w;
     float hf, wf;
     int (*prepare_out)(AVFilterContext *ctx);
+    int have_alpha;
 
+    s->max_value = (1 << depth) - 1;
     s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f;
     s->input_mirror_modifier[1] = s->iv_flip ? -1.f : 1.f;
 
@@ -3027,7 +3315,7 @@ static int config_output(AVFilterLink *outlink)
     s->in_height = s->inplaneheight[0];
 
     if (s->id_fov > 0.f)
-        fov_from_dfov(s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
+        fov_from_dfov(s->in, s->id_fov, w, h, &s->ih_fov, &s->iv_fov);
 
     if (s->in_transpose)
         FFSWAP(int, s->in_width, s->in_height);
@@ -3071,7 +3359,6 @@ static int config_output(AVFilterLink *outlink)
         break;
     case PERSPECTIVE:
     case PANNINI:
-    case FISHEYE:
         av_log(ctx, AV_LOG_ERROR, "Supplied format is not accepted as input.\n");
         return AVERROR(EINVAL);
     case DUAL_FISHEYE:
@@ -3088,7 +3375,7 @@ static int config_output(AVFilterLink *outlink)
         break;
     case STEREOGRAPHIC:
         s->in_transform = xyz_to_stereographic;
-        err = 0;
+        err = prepare_stereographic_in(ctx);
         wf = w;
         hf = h / 2.f;
         break;
@@ -3116,12 +3403,24 @@ static int config_output(AVFilterLink *outlink)
         wf = w;
         hf = h;
         break;
+    case FISHEYE:
+        s->in_transform = xyz_to_fisheye;
+        err = prepare_fisheye_in(ctx);
+        wf = w * 2;
+        hf = h;
+        break;
     case CYLINDRICAL:
         s->in_transform = xyz_to_cylindrical;
         err = prepare_cylindrical_in(ctx);
         wf = w;
         hf = h * 2.f;
         break;
+    case TETRAHEDRON:
+        s->in_transform = xyz_to_tetrahedron;
+        err = 0;
+        wf = w;
+        hf = h;
+        break;
     default:
         av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n");
         return AVERROR_BUG;
@@ -3234,6 +3533,12 @@ static int config_output(AVFilterLink *outlink)
         w = lrintf(wf / 2.f);
         h = lrintf(hf);
         break;
+    case TETRAHEDRON:
+        s->out_transform = tetrahedron_to_xyz;
+        prepare_out = NULL;
+        w = lrintf(wf);
+        h = lrintf(hf);
+        break;
     default:
         av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n");
         return AVERROR_BUG;
@@ -3255,7 +3560,7 @@ static int config_output(AVFilterLink *outlink)
     }
 
     if (s->d_fov > 0.f)
-        fov_from_dfov(s->d_fov, w, h, &s->h_fov, &s->v_fov);
+        fov_from_dfov(s->out, s->d_fov, w, h, &s->h_fov, &s->v_fov);
 
     if (prepare_out) {
         err = prepare_out(ctx);
@@ -3299,6 +3604,7 @@ static int config_output(AVFilterLink *outlink)
     outlink->w = w;
 
     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
+    have_alpha   = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA);
 
     if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) {
         s->nb_allocated = 1;
@@ -3310,7 +3616,7 @@ static int config_output(AVFilterLink *outlink)
     }
 
     for (int i = 0; i < s->nb_allocated; i++)
-        allocate_plane(s, sizeof_uv, sizeof_ker, i);
+        allocate_plane(s, sizeof_uv, sizeof_ker, sizeof_mask * have_alpha * s->alpha, i);
 
     calculate_rotation_matrix(s->yaw, s->pitch, s->roll, s->rot_mat, s->rotation_order);
     set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, s->output_mirror_modifier);
@@ -3353,6 +3659,7 @@ static av_cold void uninit(AVFilterContext *ctx)
         av_freep(&s->v[p]);
         av_freep(&s->ker[p]);
     }
+    av_freep(&s->mask);
 }
 
 static const AVFilterPad inputs[] = {