X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavfilter%2Fvf_v360.c;h=e74509e6f2febafafc75500c7a227e1ba6974b7e;hb=7f8f886344fa0e7eded47816d6a77e40090e47ce;hp=0ea7a460ea4f9bea23c0655c09ad04dd5a81a151;hpb=976617c7d21e4b525d6edde2fb280994fc5c567c;p=ffmpeg diff --git a/libavfilter/vf_v360.c b/libavfilter/vf_v360.c index 0ea7a460ea4..e74509e6f2f 100644 --- a/libavfilter/vf_v360.c +++ b/libavfilter/vf_v360.c @@ -33,7 +33,7 @@ * 5) Remap input frame to output frame using precalculated data */ -#include +#include #include "libavutil/avassert.h" #include "libavutil/imgutils.h" @@ -64,12 +64,14 @@ static const AVOption v360_options[] = { { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" }, + { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 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" }, { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" }, { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" }, { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" }, + { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" }, { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, @@ -88,6 +90,11 @@ static const AVOption v360_options[] = { { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"}, { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"}, + { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" }, + {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" }, + { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" }, + { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" }, + { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" }, { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"}, {"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"}, @@ -98,8 +105,9 @@ static const AVOption v360_options[] = { { "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"}, { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0, FLAGS, "rorder"}, - { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 180.f, FLAGS, "h_fov"}, - { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 90.f, FLAGS, "v_fov"}, + { "h_fov", "horizontal field of view", OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, 0.00001f, 360.f, FLAGS, "h_fov"}, + { "v_fov", "vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=45.f}, 0.00001f, 360.f, FLAGS, "v_fov"}, + { "d_fov", "diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f, FLAGS, "d_fov"}, { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "h_flip"}, { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "v_flip"}, { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "d_flip"}, @@ -107,8 +115,6 @@ static const AVOption v360_options[] = { { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "iv_flip"}, { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"}, { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"}, - { "p_lon", "central projection longitude", OFFSET(p_lon), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f, FLAGS, "p_lon"}, - { "p_lat", "central projection latitude", OFFSET(p_lat), AV_OPT_TYPE_FLOAT, {.dbl=90.f}, -90.f, 90.f, FLAGS, "p_lat"}, { NULL } }; @@ -221,24 +227,31 @@ static int remap##ws##_##bits##bit_slice(AVFilterContext *ctx, void *arg, int jo const AVFrame *in = td->in; \ AVFrame *out = td->out; \ \ - for (int plane = 0; plane < s->nb_planes; plane++) { \ - const int in_linesize = in->linesize[plane]; \ - const int out_linesize = out->linesize[plane]; \ - const uint8_t *src = in->data[plane]; \ - uint8_t *dst = out->data[plane]; \ - const int width = s->planewidth[plane]; \ - const int height = s->planeheight[plane]; \ + for (int stereo = 0; stereo < 1 + s->out_stereo > STEREO_2D; stereo++) { \ + for (int plane = 0; plane < s->nb_planes; plane++) { \ + const int in_linesize = in->linesize[plane]; \ + const int out_linesize = out->linesize[plane]; \ + const int uv_linesize = s->uv_linesize[plane]; \ + const int in_offset_w = stereo ? s->in_offset_w[plane] : 0; \ + const int in_offset_h = stereo ? s->in_offset_h[plane] : 0; \ + const int out_offset_w = stereo ? s->out_offset_w[plane] : 0; \ + const int out_offset_h = stereo ? s->out_offset_h[plane] : 0; \ + const uint8_t *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 int width = s->pr_width[plane]; \ + const int height = s->pr_height[plane]; \ \ - const int slice_start = (height * jobnr ) / nb_jobs; \ - const int slice_end = (height * (jobnr + 1)) / nb_jobs; \ + const int slice_start = (height * jobnr ) / nb_jobs; \ + const int slice_end = (height * (jobnr + 1)) / nb_jobs; \ \ - for (int y = slice_start; y < slice_end; y++) { \ - const unsigned map = s->map[plane]; \ - const uint16_t *u = s->u[map] + y * width * ws * ws; \ - const uint16_t *v = s->v[map] + y * width * ws * ws; \ - const int16_t *ker = s->ker[map] + y * width * ws * ws; \ + for (int y = slice_start; y < slice_end; y++) { \ + const unsigned map = s->map[plane]; \ + const uint16_t *u = s->u[map] + y * uv_linesize * ws * ws; \ + const uint16_t *v = s->v[map] + y * uv_linesize * ws * ws; \ + const int16_t *ker = s->ker[map] + y * uv_linesize * ws * ws; \ \ - s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \ + s->remap_line(dst + y * out_linesize, width, src, in_linesize, u, v, ker); \ + } \ } \ } \ \ @@ -335,10 +348,8 @@ static void nearest_kernel(float du, float dv, const XYRemap *r_tmp, static void bilinear_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker) { - int i, j; - - for (i = 0; i < 2; i++) { - for (j = 0; j < 2; j++) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { u[i * 2 + j] = r_tmp->u[i + 1][j + 1]; v[i * 2 + j] = r_tmp->v[i + 1][j + 1]; } @@ -380,15 +391,14 @@ static inline void calculate_bicubic_coeffs(float t, float *coeffs) static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker) { - int i, j; float du_coeffs[4]; float dv_coeffs[4]; calculate_bicubic_coeffs(du, du_coeffs); calculate_bicubic_coeffs(dv, dv_coeffs); - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { u[i * 4 + j] = r_tmp->u[i][j]; v[i * 4 + j] = r_tmp->v[i][j]; ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384; @@ -404,10 +414,9 @@ static void bicubic_kernel(float du, float dv, const XYRemap *r_tmp, */ static inline void calculate_lanczos_coeffs(float t, float *coeffs) { - int i; float sum = 0.f; - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { const float x = M_PI * (t - i + 1); if (x == 0.f) { coeffs[i] = 1.f; @@ -417,7 +426,7 @@ static inline void calculate_lanczos_coeffs(float t, float *coeffs) sum += coeffs[i]; } - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { coeffs[i] /= sum; } } @@ -435,15 +444,14 @@ static inline void calculate_lanczos_coeffs(float t, float *coeffs) static void lanczos_kernel(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker) { - int i, j; float du_coeffs[4]; float dv_coeffs[4]; calculate_lanczos_coeffs(du, du_coeffs); calculate_lanczos_coeffs(dv, dv_coeffs); - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { u[i * 4 + j] = r_tmp->u[i][j]; v[i * 4 + j] = r_tmp->v[i][j]; ker[i * 4 + j] = du_coeffs[j] * dv_coeffs[i] * 16384; @@ -1043,8 +1051,8 @@ static void process_cube_coordinates(const V360Context *s, * Calculate 3D coordinates on sphere for corresponding frame position in cubemap3x2 format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1092,7 +1100,6 @@ static void xyz_to_cube3x2(const V360Context *s, float uf, vf; int ui, vi; int ewi, ehi; - int i, j; int direction, face; int u_face, v_face; @@ -1116,8 +1123,8 @@ static void xyz_to_cube3x2(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { int new_ui = ui + j; int new_vi = vi + i; int u_shift, v_shift; @@ -1163,8 +1170,8 @@ static void xyz_to_cube3x2(const V360Context *s, * Calculate 3D coordinates on sphere for corresponding frame position in cubemap1x6 format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1191,8 +1198,8 @@ static void cube1x6_to_xyz(const V360Context *s, * Calculate 3D coordinates on sphere for corresponding frame position in cubemap6x1 format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1236,7 +1243,6 @@ static void xyz_to_cube1x6(const V360Context *s, float uf, vf; int ui, vi; int ehi; - int i, j; int direction, face; xyz_to_cube(s, vec, &uf, &vf, &direction); @@ -1256,8 +1262,8 @@ static void xyz_to_cube1x6(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { int new_ui = ui + j; int new_vi = vi + i; int v_shift; @@ -1313,7 +1319,6 @@ static void xyz_to_cube6x1(const V360Context *s, float uf, vf; int ui, vi; int ewi; - int i, j; int direction, face; xyz_to_cube(s, vec, &uf, &vf, &direction); @@ -1333,8 +1338,8 @@ static void xyz_to_cube6x1(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { int new_ui = ui + j; int new_vi = vi + i; int u_shift; @@ -1373,8 +1378,8 @@ static void xyz_to_cube6x1(const V360Context *s, * Calculate 3D coordinates on sphere for corresponding frame position in equirectangular format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1396,12 +1401,32 @@ static void equirect_to_xyz(const V360Context *s, vec[2] = -cos_theta * cos_phi; } +/** + * Prepare data for processing stereographic output format. + * + * @param ctx filter context + * + * @return error code + */ +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; + + return 0; +} + /** * Calculate 3D coordinates on sphere for corresponding frame position in stereographic format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1410,30 +1435,54 @@ static void 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->h_fov / 180.f) * M_PI; - const float y = ((2.f * j) / height - 1.f) * (s->v_fov / 90.f) * M_PI_2; - const float rho = hypotf(x, y) + FLT_EPSILON; - const float c = 2.f * atan2f(rho, 0.15f / 2.f); - const float cos_c = cosf(c); - const float sin_c = sinf(c); - const float cp_x = s->p_lon / 180.f * M_PI; - const float cp_y = s->p_lat / 90.f * M_PI_2; - - const float phi = cp_x + atan2f(x * sin_c, rho * cosf(cp_y) * cos_c - y * sinf(cp_y) * sin_c); - const float theta = asinf(cos_c * sinf(cp_y) + (y * sin_c * cosf(cp_y)) / rho); + 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]; + const float xy = x * x + y * y; - 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; + vec[0] = 2.f * x / (1.f + xy); + vec[1] = (-1.f + xy) / (1.f + xy); + vec[2] = 2.f * y / (1.f + xy); normalize_vector(vec); } +/** + * Calculate frame position in stereographic format for corresponding 3D coordinates on sphere. + * + * @param s filter 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_stereographic(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 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]; + 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 frame position in equirectangular format for corresponding 3D coordinates on sphere. * @@ -1454,7 +1503,6 @@ static void xyz_to_equirect(const V360Context *s, const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1]; float uf, vf; int ui, vi; - int i, j; uf = (phi / M_PI + 1.f) * width / 2.f; vf = (theta / M_PI_2 + 1.f) * height / 2.f; @@ -1464,8 +1512,8 @@ static void xyz_to_equirect(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + 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); } @@ -1564,8 +1612,8 @@ static int prepare_eac_out(AVFilterContext *ctx) * Calculate 3D coordinates on sphere for corresponding frame position in equi-angular cubemap format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1683,7 +1731,6 @@ static void xyz_to_eac(const V360Context *s, float uf, vf; int ui, vi; - int i, j; int direction, face; int u_face, v_face; @@ -1709,8 +1756,8 @@ static void xyz_to_eac(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + 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); } @@ -1731,14 +1778,9 @@ static int prepare_flat_out(AVFilterContext *ctx) 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; - const float sin_phi = sinf(h_angle); - const float cos_phi = cosf(h_angle); - const float sin_theta = sinf(v_angle); - const float cos_theta = cosf(v_angle); - - s->flat_range[0] = cos_theta * sin_phi; - s->flat_range[1] = sin_theta; - s->flat_range[2] = -cos_theta * cos_phi; + s->flat_range[0] = tanf(h_angle); + s->flat_range[1] = tanf(v_angle); + s->flat_range[2] = -1.f; return 0; } @@ -1747,8 +1789,8 @@ static int prepare_flat_out(AVFilterContext *ctx) * Calculate 3D coordinates on sphere for corresponding frame position in flat format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1768,6 +1810,46 @@ static void flat_to_xyz(const V360Context *s, normalize_vector(vec); } +/** + * Calculate 3D coordinates on sphere for corresponding frame position in dual fisheye format. + * + * @param s filter 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 dfisheye_to_xyz(const V360Context *s, + int i, int j, int width, int height, + float *vec) +{ + const float scale = 1.f + s->out_pad; + + 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 = ((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 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); +} + /** * Calculate frame position in dual fisheye format for corresponding 3D coordinates on sphere. * @@ -1797,7 +1879,6 @@ static void xyz_to_dfisheye(const V360Context *s, int ui, vi; int u_shift; - int i, j; if (vec[2] >= 0) { u_shift = 0; @@ -1812,8 +1893,8 @@ static void xyz_to_dfisheye(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { us[i + 1][j + 1] = av_clip(u_shift + ui + j, 0, width - 1); vs[i + 1][j + 1] = av_clip( vi + i, 0, height - 1); } @@ -1824,8 +1905,8 @@ static void xyz_to_dfisheye(const V360Context *s, * Calculate 3D coordinates on sphere for corresponding frame position in barrel facebook's format. * * @param s filter context - * @param i horizontal position on frame [0, height) - * @param j vertical position on frame [0, width) + * @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 @@ -1838,7 +1919,7 @@ static void barrel_to_xyz(const V360Context *s, float l_x, l_y, l_z; if (i < 4 * width / 5) { - const float theta_range = M_PI / 4.f; + const float theta_range = M_PI_4; const int ew = 4 * width / 5; const int eh = height; @@ -1910,13 +1991,12 @@ static void xyz_to_barrel(const V360Context *s, const float phi = atan2f(vec[0], -vec[2]) * s->input_mirror_modifier[0]; const float theta = asinf(-vec[1]) * s->input_mirror_modifier[1]; - const float theta_range = M_PI / 4.f; + const float theta_range = M_PI_4; int ew, eh; int u_shift, v_shift; float uf, vf; int ui, vi; - int i, j; if (theta > -theta_range && theta < theta_range) { ew = 4 * width / 5; @@ -1956,8 +2036,8 @@ static void xyz_to_barrel(const V360Context *s, *du = uf - ui; *dv = vf - vi; - for (i = -1; i < 3; i++) { - for (j = -1; j < 3; j++) { + for (int i = -1; i < 3; i++) { + for (int j = -1; j < 3; j++) { us[i + 1][j + 1] = u_shift + av_clip(ui + j, 0, ew - 1); vs[i + 1][j + 1] = v_shift + av_clip(vi + i, 0, eh - 1); } @@ -2050,12 +2130,12 @@ static inline void mirror(const float *modifier, float *vec) static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p) { - s->u[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv); - s->v[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_uv); + 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); if (!s->u[p] || !s->v[p]) return AVERROR(ENOMEM); if (sizeof_ker) { - s->ker[p] = av_calloc(s->planewidth[p] * s->planeheight[p], sizeof_ker); + s->ker[p] = av_calloc(s->uv_linesize[p] * s->pr_height[p], sizeof_ker); if (!s->ker[p]) return AVERROR(ENOMEM); } @@ -2063,6 +2143,28 @@ static int allocate_plane(V360Context *s, int sizeof_uv, int sizeof_ker, int p) return 0; } +static void fov_from_dfov(V360Context *s, float w, float h) +{ + const float da = tanf(0.5 * FFMIN(s->d_fov, 359.f) * M_PI / 180.f); + const float d = hypotf(w, h); + + s->h_fov = atan2f(da * w, d) * 360.f / M_PI; + s->v_fov = atan2f(da * h, d) * 360.f / M_PI; + + if (s->h_fov < 0.f) + s->h_fov += 360.f; + if (s->v_fov < 0.f) + s->v_fov += 360.f; +} + +static void set_dimensions(int *outw, int *outh, int w, int h, const AVPixFmtDescriptor *desc) +{ + outw[1] = outw[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w); + outw[0] = outw[3] = w; + outh[1] = outh[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h); + outh[0] = outh[3] = h; +} + static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -2074,7 +2176,9 @@ static int config_output(AVFilterLink *outlink) int sizeof_ker; int elements; int err; - int p, h, w; + int h, w; + int in_offset_h, in_offset_w; + int out_offset_h, out_offset_w; float hf, wf; float output_mirror_modifier[3]; void (*in_transform)(const V360Context *s, @@ -2085,6 +2189,7 @@ static int config_output(AVFilterLink *outlink) float *vec); void (*calculate_kernel)(float du, float dv, const XYRemap *r_tmp, uint16_t *u, uint16_t *v, int16_t *ker); + int (*prepare_out)(AVFilterContext *ctx); float rot_mat[3][3]; s->input_mirror_modifier[0] = s->ih_flip ? -1.f : 1.f; @@ -2145,36 +2250,61 @@ static int config_output(AVFilterLink *outlink) s->rotation_order[order] = rorder; } + switch (s->in_stereo) { + case STEREO_2D: + w = inlink->w; + h = inlink->h; + in_offset_w = in_offset_h = 0; + break; + case STEREO_SBS: + w = inlink->w / 2; + h = inlink->h; + in_offset_w = w; + in_offset_h = 0; + break; + case STEREO_TB: + w = inlink->w; + h = inlink->h / 2; + in_offset_w = 0; + in_offset_h = h; + break; + default: + av_assert0(0); + } + + 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); + switch (s->in) { case EQUIRECTANGULAR: in_transform = xyz_to_equirect; err = 0; - wf = inlink->w; - hf = inlink->h; + wf = w; + hf = h; break; case CUBEMAP_3_2: in_transform = xyz_to_cube3x2; err = prepare_cube_in(ctx); - wf = inlink->w / 3.f * 4.f; - hf = inlink->h; + wf = w / 3.f * 4.f; + hf = h; break; case CUBEMAP_1_6: in_transform = xyz_to_cube1x6; err = prepare_cube_in(ctx); - wf = inlink->w * 4.f; - hf = inlink->h / 3.f; + wf = w * 4.f; + hf = h / 3.f; break; case CUBEMAP_6_1: in_transform = xyz_to_cube6x1; err = prepare_cube_in(ctx); - wf = inlink->w / 3.f * 2.f; - hf = inlink->h * 2.f; + wf = w / 3.f * 2.f; + hf = h * 2.f; break; case EQUIANGULAR: in_transform = xyz_to_eac; err = prepare_eac_in(ctx); - wf = inlink->w; - hf = inlink->h / 9.f * 8.f; + wf = w; + hf = h / 9.f * 8.f; break; case FLAT: av_log(ctx, AV_LOG_ERROR, "Flat format is not accepted as input.\n"); @@ -2182,14 +2312,20 @@ static int config_output(AVFilterLink *outlink) case DUAL_FISHEYE: in_transform = xyz_to_dfisheye; err = 0; - wf = inlink->w; - hf = inlink->h; + wf = w; + hf = h; break; case BARREL: in_transform = xyz_to_barrel; err = 0; - wf = inlink->w / 5.f * 4.f; - hf = inlink->h; + wf = w / 5.f * 4.f; + hf = h; + break; + case STEREOGRAPHIC: + in_transform = xyz_to_stereographic; + err = 0; + wf = w; + hf = h / 2.f; break; default: av_log(ctx, AV_LOG_ERROR, "Specified input format is not handled.\n"); @@ -2203,64 +2339,63 @@ static int config_output(AVFilterLink *outlink) switch (s->out) { case EQUIRECTANGULAR: out_transform = equirect_to_xyz; - err = 0; + prepare_out = NULL; w = roundf(wf); h = roundf(hf); break; case CUBEMAP_3_2: out_transform = cube3x2_to_xyz; - err = prepare_cube_out(ctx); + prepare_out = prepare_cube_out; w = roundf(wf / 4.f * 3.f); h = roundf(hf); break; case CUBEMAP_1_6: out_transform = cube1x6_to_xyz; - err = prepare_cube_out(ctx); + prepare_out = prepare_cube_out; w = roundf(wf / 4.f); h = roundf(hf * 3.f); break; case CUBEMAP_6_1: out_transform = cube6x1_to_xyz; - err = prepare_cube_out(ctx); + prepare_out = prepare_cube_out; w = roundf(wf / 2.f * 3.f); h = roundf(hf / 2.f); break; case EQUIANGULAR: out_transform = eac_to_xyz; - err = prepare_eac_out(ctx); + prepare_out = prepare_eac_out; w = roundf(wf); h = roundf(hf / 8.f * 9.f); break; case FLAT: out_transform = flat_to_xyz; - err = prepare_flat_out(ctx); - w = roundf(wf * s->flat_range[0] / s->flat_range[1] / 2.f); + prepare_out = prepare_flat_out; + w = roundf(wf); h = roundf(hf); break; case DUAL_FISHEYE: - av_log(ctx, AV_LOG_ERROR, "Dual fisheye format is not accepted as output.\n"); - return AVERROR(EINVAL); + out_transform = dfisheye_to_xyz; + prepare_out = NULL; + w = roundf(wf); + h = roundf(hf); + break; case BARREL: out_transform = barrel_to_xyz; - err = 0; + prepare_out = NULL; w = roundf(wf / 4.f * 5.f); h = roundf(hf); break; case STEREOGRAPHIC: out_transform = stereographic_to_xyz; - err = 0; - w = FFMAX(roundf(wf), roundf(hf)); - h = w; + prepare_out = prepare_stereographic_out; + w = roundf(wf); + h = roundf(hf * 2.f); break; default: av_log(ctx, AV_LOG_ERROR, "Specified output format is not handled.\n"); return AVERROR_BUG; } - if (err != 0) { - return err; - } - // Override resolution with user values if specified if (s->width > 0 && s->height > 0) { w = s->width; @@ -2276,72 +2411,93 @@ static int config_output(AVFilterLink *outlink) FFSWAP(int, w, h); } - s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(h, desc->log2_chroma_h); - s->planeheight[0] = s->planeheight[3] = h; - s->planewidth[1] = s->planewidth[2] = FF_CEIL_RSHIFT(w, desc->log2_chroma_w); - s->planewidth[0] = s->planewidth[3] = w; + if (s->d_fov > 0.f) + fov_from_dfov(s, w, h); + + if (prepare_out) { + err = prepare_out(ctx); + if (err != 0) + return err; + } + + set_dimensions(s->pr_width, s->pr_height, w, h, desc); + + switch (s->out_stereo) { + case STEREO_2D: + out_offset_w = out_offset_h = 0; + break; + case STEREO_SBS: + out_offset_w = w; + out_offset_h = 0; + w *= 2; + break; + case STEREO_TB: + out_offset_w = 0; + out_offset_h = h; + h *= 2; + break; + default: + av_assert0(0); + } + + set_dimensions(s->out_offset_w, s->out_offset_h, out_offset_w, out_offset_h, desc); + set_dimensions(s->planewidth, s->planeheight, w, h, desc); + + for (int i = 0; i < 4; i++) + s->uv_linesize[i] = FFALIGN(s->pr_width[i], 8); outlink->h = h; outlink->w = w; - s->inplaneheight[1] = s->inplaneheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); - s->inplaneheight[0] = s->inplaneheight[3] = inlink->h; - s->inplanewidth[1] = s->inplanewidth[2] = FF_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); - s->inplanewidth[0] = s->inplanewidth[3] = inlink->w; s->nb_planes = av_pix_fmt_count_planes(inlink->format); if (desc->log2_chroma_h == desc->log2_chroma_w && desc->log2_chroma_h == 0) { s->nb_allocated = 1; s->map[0] = s->map[1] = s->map[2] = s->map[3] = 0; allocate_plane(s, sizeof_uv, sizeof_ker, 0); - } else if (desc->log2_chroma_h == desc->log2_chroma_w) { + } else { s->nb_allocated = 2; s->map[0] = 0; s->map[1] = s->map[2] = 1; s->map[3] = 0; allocate_plane(s, sizeof_uv, sizeof_ker, 0); allocate_plane(s, sizeof_uv, sizeof_ker, 1); - } else { - s->nb_allocated = 3; - s->map[0] = 0; - s->map[1] = 1; - s->map[2] = 2; - s->map[3] = 0; - allocate_plane(s, sizeof_uv, sizeof_ker, 0); - allocate_plane(s, sizeof_uv, sizeof_ker, 1); - allocate_plane(s, sizeof_uv, sizeof_ker, 2); } calculate_rotation_matrix(s->yaw, s->pitch, s->roll, rot_mat, s->rotation_order); set_mirror_modifier(s->h_flip, s->v_flip, s->d_flip, output_mirror_modifier); // Calculate remap data - for (p = 0; p < s->nb_allocated; p++) { - const int width = s->planewidth[p]; - const int height = s->planeheight[p]; + for (int p = 0; p < s->nb_allocated; p++) { + const int width = s->pr_width[p]; + const int uv_linesize = s->uv_linesize[p]; + const int height = s->pr_height[p]; const int in_width = s->inplanewidth[p]; const int in_height = s->inplaneheight[p]; float du, dv; float vec[3]; XYRemap r_tmp; - int i, j; - for (i = 0; i < width; i++) { - for (j = 0; j < height; j++) { - uint16_t *u = s->u[p] + (j * width + i) * elements; - uint16_t *v = s->v[p] + (j * width + i) * elements; - int16_t *ker = s->ker[p] + (j * width + i) * elements; + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + uint16_t *u = s->u[p] + (j * uv_linesize + i) * elements; + uint16_t *v = s->v[p] + (j * uv_linesize + i) * elements; + int16_t *ker = s->ker[p] + (j * uv_linesize + i) * elements; if (s->out_transpose) out_transform(s, j, i, height, width, vec); else out_transform(s, i, j, width, height, vec); + av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2])); rotate(rot_mat, vec); + av_assert1(!isnan(vec[0]) && !isnan(vec[1]) && !isnan(vec[2])); + normalize_vector(vec); mirror(output_mirror_modifier, vec); if (s->in_transpose) in_transform(s, vec, in_height, in_width, r_tmp.v, r_tmp.u, &du, &dv); else in_transform(s, vec, in_width, in_height, r_tmp.u, r_tmp.v, &du, &dv); + av_assert1(!isnan(du) && !isnan(dv)); calculate_kernel(du, dv, &r_tmp, u, v, ker); } } @@ -2377,9 +2533,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) static av_cold void uninit(AVFilterContext *ctx) { V360Context *s = ctx->priv; - int p; - for (p = 0; p < s->nb_allocated; p++) { + for (int p = 0; p < s->nb_allocated; p++) { av_freep(&s->u[p]); av_freep(&s->v[p]); av_freep(&s->ker[p]);