+/**
+ * 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 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]);
+ const float phi = atan2f(vec[0], vec[2]) * cosf(theta);
+
+ const float uf = (phi / M_PI + 1.f) * width / 2.f;
+ const float vf = (theta / M_PI_2 + 1.f) * height / 2.f;
+
+ const int ui = floorf(uf);
+ const int vi = floorf(vf);
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = av_clip(ui + j - 1, 0, width - 1);
+ vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Prepare data for processing equi-angular cubemap input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_eac_in(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->in_cubemap_face_order[RIGHT] = TOP_RIGHT;
+ s->in_cubemap_face_order[LEFT] = TOP_LEFT;
+ s->in_cubemap_face_order[UP] = BOTTOM_RIGHT;
+ s->in_cubemap_face_order[DOWN] = BOTTOM_LEFT;
+ s->in_cubemap_face_order[FRONT] = TOP_MIDDLE;
+ s->in_cubemap_face_order[BACK] = BOTTOM_MIDDLE;
+
+ s->in_cubemap_face_rotation[TOP_LEFT] = ROT_0;
+ s->in_cubemap_face_rotation[TOP_MIDDLE] = ROT_0;
+ s->in_cubemap_face_rotation[TOP_RIGHT] = ROT_0;
+ s->in_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270;
+ s->in_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
+ s->in_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270;
+
+ return 0;
+}
+
+/**
+ * Prepare data for processing equi-angular cubemap output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_eac_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->out_cubemap_direction_order[TOP_LEFT] = LEFT;
+ s->out_cubemap_direction_order[TOP_MIDDLE] = FRONT;
+ s->out_cubemap_direction_order[TOP_RIGHT] = RIGHT;
+ s->out_cubemap_direction_order[BOTTOM_LEFT] = DOWN;
+ s->out_cubemap_direction_order[BOTTOM_MIDDLE] = BACK;
+ s->out_cubemap_direction_order[BOTTOM_RIGHT] = UP;
+
+ s->out_cubemap_face_rotation[TOP_LEFT] = ROT_0;
+ s->out_cubemap_face_rotation[TOP_MIDDLE] = ROT_0;
+ s->out_cubemap_face_rotation[TOP_RIGHT] = ROT_0;
+ s->out_cubemap_face_rotation[BOTTOM_LEFT] = ROT_270;
+ s->out_cubemap_face_rotation[BOTTOM_MIDDLE] = ROT_90;
+ s->out_cubemap_face_rotation[BOTTOM_RIGHT] = ROT_270;
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap 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 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;
+ const float v_pad = pixel_pad / height;
+
+ int u_face, v_face, face;
+
+ float l_x, l_y, l_z;
+
+ 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
+ // Formulas below approximate original padding as close as I could get experimentally
+
+ // Horizontal padding
+ uf = 3.f * (uf - u_pad) / (1.f - 2.f * u_pad);
+ if (uf < 0.f) {
+ u_face = 0;
+ uf -= 0.5f;
+ } else if (uf >= 3.f) {
+ u_face = 2;
+ uf -= 2.5f;
+ } else {
+ u_face = floorf(uf);
+ uf = fmodf(uf, 1.f) - 0.5f;
+ }
+
+ // Vertical padding
+ v_face = floorf(vf * 2.f);
+ vf = (vf - v_pad - 0.5f * v_face) / (0.5f - 2.f * v_pad) - 0.5f;
+
+ if (uf >= -0.5f && uf < 0.5f) {
+ uf = tanf(M_PI_2 * uf);
+ } else {
+ uf = 2.f * uf;
+ }
+ if (vf >= -0.5f && vf < 0.5f) {
+ vf = tanf(M_PI_2 * vf);
+ } else {
+ vf = 2.f * vf;
+ }
+
+ face = u_face + 3 * v_face;
+
+ switch (face) {
+ case TOP_LEFT:
+ l_x = -1.f;
+ l_y = vf;
+ l_z = uf;
+ break;
+ case TOP_MIDDLE:
+ l_x = uf;
+ l_y = vf;
+ l_z = 1.f;
+ break;
+ case TOP_RIGHT:
+ l_x = 1.f;
+ l_y = vf;
+ l_z = -uf;
+ break;
+ case BOTTOM_LEFT:
+ l_x = -vf;
+ l_y = 1.f;
+ l_z = -uf;
+ break;
+ case BOTTOM_MIDDLE:
+ l_x = -vf;
+ l_y = -uf;
+ l_z = -1.f;
+ break;
+ case BOTTOM_RIGHT:
+ l_x = -vf;
+ l_y = -1.f;
+ l_z = uf;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = l_z;
+
+ normalize_vector(vec);
+
+ return 1;
+}
+
+/**
+ * Calculate frame position in equi-angular cubemap 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_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;
+ const float v_pad = pixel_pad / height;
+
+ float uf, vf;
+ int ui, vi;
+ int direction, face;
+ int u_face, v_face;
+
+ xyz_to_cube(s, vec, &uf, &vf, &direction);
+
+ face = s->in_cubemap_face_order[direction];
+ u_face = face % 3;
+ v_face = face / 3;
+
+ uf = M_2_PI * atanf(uf) + 0.5f;
+ vf = M_2_PI * atanf(vf) + 0.5f;
+
+ // These formulas are inversed from eac_to_xyz ones
+ uf = (uf + u_face) * (1.f - 2.f * u_pad) / 3.f + u_pad;
+ vf = vf * (0.5f - 2.f * v_pad) + v_pad + 0.5f * v_face;
+
+ uf *= width;
+ vf *= height;
+
+ uf -= 0.5f;
+ vf -= 0.5f;
+
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = av_clip(ui + j - 1, 0, width - 1);
+ vs[i][j] = av_clip(vi + i - 1, 0, height - 1);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Prepare data for processing flat output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_flat_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ 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;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in flat 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 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 + 0.5f) / width - 1.f);
+ const float l_y = s->flat_range[1] * ((2.f * j + 0.5f) / height - 1.f);
+
+ vec[0] = l_x;
+ vec[1] = l_y;
+ vec[2] = 1.f;
+
+ normalize_vector(vec);
+
+ return 1;
+}
+
+/**
+ * Prepare data for processing fisheye output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_fisheye_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->flat_range[0] = s->h_fov / 180.f;
+ s->flat_range[1] = s->v_fov / 180.f;
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in fisheye 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 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 + 1.f) / height - 1.f);
+
+ const float phi = atan2f(vf, uf);
+ const float theta = M_PI_2 * (1.f - hypotf(uf, vf));
+
+ 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[2] = sin_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 h = hypotf(vec[0], vec[1]);
+ const float lh = h > 0.f ? h : 1.f;
+ const float phi = atan2f(h, vec[2]) / M_PI;
+
+ float uf = vec[0] / lh * phi / s->iflat_range[0];
+ float vf = vec[1] / lh * phi / 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 = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
+ vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
+ }
+ }
+
+ return visible;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in pannini 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 pannini_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = ((2.f * i + 1.f) / width - 1.f);
+ const float vf = ((2.f * j + 1.f) / height - 1.f);
+
+ const float d = s->h_fov;
+ const float k = uf * uf / ((d + 1.f) * (d + 1.f));
+ const float dscr = k * k * d * d - (k + 1.f) * (k * d * d - 1.f);
+ const float clon = (-k * d + sqrtf(dscr)) / (k + 1.f);
+ const float S = (d + 1.f) / (d + clon);
+ const float lon = atan2f(uf, S * clon);
+ const float lat = atan2f(vf, S);
+
+ vec[0] = sinf(lon) * cosf(lat);
+ vec[1] = sinf(lat);
+ vec[2] = cosf(lon) * cosf(lat);
+
+ normalize_vector(vec);
+
+ return 1;
+}
+
+/**
+ * Calculate frame position in pannini 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_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)
+{
+ const float phi = atan2f(vec[0], vec[2]);
+ const float theta = asinf(vec[1]);
+
+ const float d = s->ih_fov;
+ const float S = (d + 1.f) / (d + cosf(phi));
+
+ const float x = S * sinf(phi);
+ const float y = S * tanf(theta);
+
+ const float uf = (x + 1.f) * width / 2.f;
+ const float vf = (y + 1.f) * height / 2.f;
+
+ const int ui = floorf(uf);
+ const int vi = floorf(vf);
+
+ const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width && vec[2] >= 0.f;
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
+ vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
+ }
+ }
+
+ return visible;
+}
+
+/**
+ * Prepare data for processing cylindrical output format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_cylindrical_out(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->flat_range[0] = M_PI * s->h_fov / 360.f;
+ s->flat_range[1] = tanf(0.5f * s->v_fov * M_PI / 180.f);
+
+ return 0;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in cylindrical 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 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 + 1.f) / width - 1.f);
+ const float vf = s->flat_range[1] * ((2.f * j + 1.f) / height - 1.f);
+
+ const float phi = uf;
+ const float theta = atanf(vf);
+
+ 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);
+
+ return 1;
+}
+
+/**
+ * Prepare data for processing cylindrical input format.
+ *
+ * @param ctx filter context
+ *
+ * @return error code
+ */
+static int prepare_cylindrical_in(AVFilterContext *ctx)
+{
+ V360Context *s = ctx->priv;
+
+ s->iflat_range[0] = M_PI * s->ih_fov / 360.f;
+ s->iflat_range[1] = tanf(0.5f * s->iv_fov * M_PI / 180.f);
+
+ return 0;
+}
+
+/**
+ * Calculate frame position in cylindrical 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_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->iflat_range[0];
+ const float theta = asinf(vec[1]);
+
+ const float uf = (phi + 1.f) * (width - 1) / 2.f;
+ const float vf = (tanf(theta) / s->iflat_range[1] + 1.f) * height / 2.f;
+
+ const int ui = floorf(uf);
+ const int vi = floorf(vf);
+
+ const int visible = vi >= 0 && vi < height && ui >= 0 && ui < width &&
+ theta <= M_PI * s->iv_fov / 180.f &&
+ theta >= -M_PI * s->iv_fov / 180.f;
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = visible ? av_clip(ui + j - 1, 0, width - 1) : 0;
+ vs[i][j] = visible ? av_clip(vi + i - 1, 0, height - 1) : 0;
+ }
+ }
+
+ return visible;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in perspective 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 perspective_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float uf = ((2.f * i + 1.f) / width - 1.f);
+ const float vf = ((2.f * j + 1.f) / height - 1.f);
+ const float rh = hypotf(uf, vf);
+ const float sinzz = 1.f - rh * rh;
+ const float h = 1.f + s->v_fov;
+ const float sinz = (h - sqrtf(sinzz)) / (h / rh + rh / h);
+ const float sinz2 = sinz * sinz;
+
+ if (sinz2 <= 1.f) {
+ const float cosz = sqrtf(1.f - sinz2);
+
+ const float theta = asinf(cosz);
+ const float phi = atan2f(uf, vf);
+
+ 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] = cos_theta * cos_phi;
+ vec[2] = sin_theta;
+ } else {
+ vec[0] = 0.f;
+ vec[1] = 1.f;
+ vec[2] = 0.f;
+ return 0;
+ }
+
+ 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)
+{
+ const float d0 = vec[0] * 1.f + vec[1] * 1.f + vec[2] *-1.f;
+ const float d1 = vec[0] *-1.f + vec[1] *-1.f + vec[2] *-1.f;
+ const float d2 = vec[0] * 1.f + vec[1] *-1.f + vec[2] * 1.f;
+ const float d3 = vec[0] *-1.f + vec[1] * 1.f + vec[2] * 1.f;
+ const float d = FFMAX(d0, FFMAX3(d1, d2, d3));
+
+ float uf, vf, x, y, z;
+ int ui, vi;
+
+ x = vec[0] / d;
+ y = vec[1] / d;
+ z = -vec[2] / d;
+
+ vf = 0.5f - y * 0.5f;
+
+ 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 + 0.25f;
+ } else {
+ uf = 0.75f - 0.25f * x;
+ }
+
+ uf *= width;
+ vf *= height;
+
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = reflectx(ui + j - 1, vi + i - 1, width, height);
+ vs[i][j] = reflecty(vi + i - 1, height);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye 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 dfisheye_to_xyz(const V360Context *s,
+ int i, int j, int width, int height,
+ float *vec)
+{
+ const float ew = width / 2.f;
+ const float eh = height;
+
+ const int ei = i >= ew ? i - ew : i;
+ const float m = i >= ew ? 1.f : -1.f;
+
+ const float uf = s->flat_range[0] * ((2.f * ei) / ew - 1.f);
+ const float vf = s->flat_range[1] * ((2.f * j + 1.f) / eh - 1.f);
+
+ 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_theta = sinf(theta);
+ const float cos_theta = cosf(theta);
+
+ vec[0] = cos_theta * m * uf / lh;
+ vec[1] = cos_theta * vf / lh;
+ vec[2] = sin_theta;
+
+ normalize_vector(vec);
+
+ return 1;
+}
+
+/**
+ * Calculate frame position in dual 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_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 ew = width / 2.f;
+ const float eh = height;
+
+ 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 * (vec[0] / lh) / s->iflat_range[0] + 0.5f) * ew;
+ float vf = (theta * (vec[1] / lh) / s->iflat_range[1] + 0.5f) * eh;
+
+ int ui, vi;
+ int u_shift;
+
+ if (vec[2] >= 0.f) {
+ u_shift = ceilf(ew);
+ } else {
+ u_shift = 0;
+ uf = ew - uf;
+ }
+
+ ui = floorf(uf);
+ vi = floorf(vf);
+
+ *du = uf - ui;
+ *dv = vf - vi;
+
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ us[i][j] = av_clip(u_shift + ui + j - 1, 0, width - 1);
+ vs[i][j] = av_clip( vi + i - 1, 0, height - 1);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's 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 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;
+
+ if (i < 4 * width / 5) {
+ const float theta_range = M_PI_4;
+
+ const int ew = 4 * width / 5;
+ const int eh = height;
+
+ const float phi = ((2.f * i) / ew - 1.f) * M_PI / scale;
+ const float theta = ((2.f * j) / eh - 1.f) * theta_range / scale;
+
+ const float sin_phi = sinf(phi);
+ const float cos_phi = cosf(phi);
+ const float sin_theta = sinf(theta);
+ const float cos_theta = cosf(theta);
+
+ l_x = cos_theta * sin_phi;
+ l_y = sin_theta;
+ l_z = cos_theta * cos_phi;
+ } else {