This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit dce56e75ebd74296009b33502f7e997f0e3b8c2b Author: TADANO Tokumei <[email protected]> AuthorDate: Tue Sep 22 01:19:32 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Thu Oct 1 14:57:38 2026 +0000 avfilter/v360_vulkan: support GoPro Max inputs Add GoPro Max 360 projection for input. Add tentative Equi-Angular Cubemap (EAC) projection for output (for testing). Signed-off-by: TADANO Tokumei <[email protected]> --- libavfilter/Makefile | 2 +- libavfilter/v360.h | 1 + libavfilter/vf_v360_vulkan.c | 194 ++++++++++++++++++++++++++++++-------- libavfilter/vulkan/v360.comp.glsl | 167 +++++++++++++++++++++++++++++++- 4 files changed, 322 insertions(+), 42 deletions(-) diff --git a/libavfilter/Makefile b/libavfilter/Makefile index ac194bead7..15d0b23af9 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -560,7 +560,7 @@ OBJS-$(CONFIG_UNSHARP_OPENCL_FILTER) += vf_unsharp_opencl.o opencl.o \ OBJS-$(CONFIG_UNTILE_FILTER) += vf_untile.o OBJS-$(CONFIG_USPP_FILTER) += vf_uspp.o qp_table.o OBJS-$(CONFIG_V360_FILTER) += vf_v360.o -OBJS-$(CONFIG_V360_VULKAN_FILTER) += vf_v360_vulkan.o +OBJS-$(CONFIG_V360_VULKAN_FILTER) += vf_v360_vulkan.o framesync.o OBJS-$(CONFIG_VAGUEDENOISER_FILTER) += vf_vaguedenoiser.o OBJS-$(CONFIG_VARBLUR_FILTER) += vf_varblur.o framesync.o OBJS-$(CONFIG_VECTORSCOPE_FILTER) += vf_vectorscope.o diff --git a/libavfilter/v360.h b/libavfilter/v360.h index b3660c234c..f62f37df08 100644 --- a/libavfilter/v360.h +++ b/libavfilter/v360.h @@ -55,6 +55,7 @@ enum Projections { ORTHOGRAPHIC, OCTAHEDRON, CYLINDRICALEA, + GOPROMAX, NB_PROJECTIONS, }; diff --git a/libavfilter/vf_v360_vulkan.c b/libavfilter/vf_v360_vulkan.c index 85b4c6bfb5..1780f7c106 100644 --- a/libavfilter/vf_v360_vulkan.c +++ b/libavfilter/vf_v360_vulkan.c @@ -22,6 +22,7 @@ #include "v360.h" #include "filters.h" #include "video.h" +#include "framesync.h" extern const unsigned char ff_v360_comp_spv_data[]; extern const unsigned int ff_v360_comp_spv_len; @@ -35,6 +36,7 @@ struct PushData { typedef struct V360ulkanContext { FFVulkanContext vkctx; + FFFrameSync fs; int initialized; FFVkExecPool e; @@ -51,6 +53,7 @@ typedef struct V360ulkanContext { float yaw, pitch, roll; char *rorder; int rotation_order[3]; + int overlap; } V360VulkanContext; static int get_rorder(char c) @@ -198,6 +201,7 @@ static void config_params(AVFilterContext *ctx, AVFilterLink *inlink) s->iv_fov = 180.f; break; case EQUIRECTANGULAR: /* unchangeable */ + case GOPROMAX: s->ih_fov = 360.f; s->iv_fov = 180.f; break; @@ -335,6 +339,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx) case DUAL_FISHEYE: hf = wf * sar / 2.f; break; + case EQUIANGULAR: + hf = wf * sar / 3.f * 2.f; + break; case STEREOGRAPHIC: case FISHEYE: hf = wf * sar; @@ -353,6 +360,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx) case FLAT: hf = (float)inlink->h / s->pd.iflat_range[1] / 2.f; break; + case GOPROMAX: + hf = (float)inlink->h * 2.f; + break; default: hf = (float)inlink->h; break; @@ -376,6 +386,9 @@ static av_cold int calculate_output_size(AVFilterContext *ctx) case DUAL_FISHEYE: wf = hf * 2.f; break; + case EQUIANGULAR: + wf = hf * 3.f / 2.f; + break; case STEREOGRAPHIC: case FISHEYE: wf = hf; @@ -404,6 +417,10 @@ static av_cold int calculate_output_size(AVFilterContext *ctx) min_w = 2; min_h = 1; break; + case EQUIANGULAR: + min_w = 3; + min_h = 2; + break; default: min_w = 1; min_h = 1; @@ -438,7 +455,7 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, FF_VK_DEFAULT_EXEC_CONTEXTS, 0, 0, 0, NULL)); - SPEC_LIST_CREATE(sl, 10, 7*sizeof(int) + 3*sizeof(float)) + SPEC_LIST_CREATE(sl, 17, 14*sizeof(int) + 3*sizeof(float)) SPEC_LIST_ADD(sl, 0, 32, s->out); SPEC_LIST_ADD(sl, 1, 32, s->in); @@ -453,6 +470,19 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) SPEC_LIST_ADD(sl, 8, 32, FF_CEIL_RSHIFT(in->width, desc->log2_chroma_w)); SPEC_LIST_ADD(sl, 9, 32, FF_CEIL_RSHIFT(in->height, desc->log2_chroma_h)); + if (s->in == GOPROMAX) { + int cube_size = in->height; + int gopro_cube_width = (in->width - cube_size) / 2; + + SPEC_LIST_ADD(sl, 10, 32, cube_size); + SPEC_LIST_ADD(sl, 11, 32, gopro_cube_width); + SPEC_LIST_ADD(sl, 12, 32, s->overlap); + SPEC_LIST_ADD(sl, 13, 32, gopro_cube_width + cube_size); + SPEC_LIST_ADD(sl, 14, 32, gopro_cube_width / 2 - s->overlap); + SPEC_LIST_ADD(sl, 15, 32, gopro_cube_width / 2); + SPEC_LIST_ADD(sl, 16, 32, gopro_cube_width / 2 + s->overlap); + } + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, sl, (uint32_t []) { 16, 16, 1 }, 0); @@ -460,19 +490,25 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) VK_SHADER_STAGE_COMPUTE_BIT); const FFVulkanDescriptorSetBinding desc_set[] = { + { /* output_img */ + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, + }, { /* input_img */ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .stages = VK_SHADER_STAGE_COMPUTE_BIT, .elems = planes, .samplers = DUP_SAMPLER(s->sampler), }, - { /* output_img */ - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .elems = planes, + { /* input2_img */ + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, + .samplers = DUP_SAMPLER(s->sampler), }, }; - ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 2, 0); + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 3, 0); RET(ff_vk_shader_link(vkctx, &s->shd, ff_v360_comp_spv_data, @@ -486,13 +522,20 @@ fail: return err; } -static int v360_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +static int v360_vulkan_filter_frame(FFFrameSync *fs) { int err; - AVFrame *out = NULL; - AVFilterContext *ctx = link->dst; - V360VulkanContext *s = ctx->priv; + AVFilterContext *ctx = fs->parent; + V360VulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in[2] = { NULL }; + AVFrame *out = NULL; + + for (int i = 0; i < ctx->nb_inputs; i++) { + RET(ff_framesync_get_frame(fs, i, &in[i], 0)); + if (!in[i]) + return 0; + } out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { @@ -500,27 +543,37 @@ static int v360_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - if (!s->initialized) - RET(init_filter(ctx, in)); - - RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, - out, in, s->sampler, 1, - &s->pd, sizeof(s->pd))); - - err = av_frame_copy_props(out, in); - if (err < 0) - goto fail; + if (!s->initialized) { + AVHWFramesContext *fc0 = (AVHWFramesContext*)in[0]->hw_frames_ctx->data; + for (int i = 1; i < ctx->nb_inputs; i++) { + AVHWFramesContext *fci = (AVHWFramesContext*)in[i]->hw_frames_ctx->data; + if (fc0->sw_format != fci->sw_format) { + av_log(ctx, AV_LOG_ERROR, "Input %d has a different format " + "from input 0\n", i); + err = AVERROR(EINVAL); + goto fail; + } + } + RET(init_filter(ctx, in[0])); + } - av_frame_free(&in); + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out, + in, ctx->nb_inputs, s->sampler, 1, + &s->pd, sizeof(s->pd))); + RET(av_frame_copy_props(out, in[0])); return ff_filter_frame(outlink, out); fail: - av_frame_free(&in); av_frame_free(&out); return err; } +static int v360_vulkan_activate(AVFilterContext *ctx) +{ + V360VulkanContext *s = ctx->priv; + return ff_framesync_activate(&s->fs); +} static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, char *res, int res_len, int flags) @@ -539,13 +592,80 @@ static av_cold int v360_vulkan_config_output(AVFilterLink *outlink) { int err; AVFilterContext *ctx = outlink->src; + V360VulkanContext *s = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; + if (s->in == GOPROMAX) { + if (s->overlap == 0) { + if (inlink->h <= 960) { + s->overlap = 32; + } + else if (inlink->h < 1920) { + s->overlap = 64; + } + else + s->overlap = 96; + } + if (ctx->nb_inputs != 2) { + av_log(ctx, AV_LOG_ERROR, "GoPro Max. requires 2 input streams.\n"); + return AVERROR(EINVAL); + } + if ((inlink->w != ctx->inputs[1]->w) || + (inlink->h != ctx->inputs[1]->h) || + (inlink->w < inlink->h * 3) || + (inlink->w > inlink->h * 3 + s->overlap * 2)) { + av_log(ctx, AV_LOG_ERROR, "Incompatible inputs for GoPro Max. (%dx%d + %dx%d)\n", + inlink->w, inlink->h, ctx->inputs[1]->w, ctx->inputs[1]->h); + return AVERROR(EINVAL); + } + } + else if (ctx->nb_inputs != 1) { + av_log(ctx, AV_LOG_ERROR, "Too many inputs (%d)\n", ctx->nb_inputs); + return AVERROR(EINVAL); + } + config_params(ctx, inlink); RET(calculate_output_size(ctx)); - RET(ff_vk_filter_config_output(outlink)); + RET(ff_framesync_init(&s->fs, ctx, ctx->nb_inputs)); + for (int i = 0; i < ctx->nb_inputs; i++) { + s->fs.in[i].time_base = ctx->inputs[i]->time_base; + s->fs.in[i].sync = 1; + s->fs.in[i].before = EXT_STOP; + s->fs.in[i].after = EXT_STOP; + } + s->fs.on_event = &v360_vulkan_filter_frame; + + RET(ff_framesync_configure(&s->fs)); + outlink->time_base = s->fs.time_base; + +fail: + return err; +} + +static av_cold int v360_vulkan_init(AVFilterContext *avctx) +{ + int err; + V360VulkanContext *s = avctx->priv; + AVFilterPad pad = { + .type = AVMEDIA_TYPE_VIDEO, + .config_props = ff_vk_filter_config_input, + }; + + if (s->in == GOPROMAX) { + pad.name = "front"; + RET(ff_append_inpad(avctx, &pad)); + pad.name = "rear"; + RET(ff_append_inpad(avctx, &pad)); + } + else { + pad.name = "default"; + RET(ff_append_inpad(avctx, &pad)); + } + + return ff_vk_filter_init(avctx); + fail: return err; } @@ -556,6 +676,7 @@ static void v360_vulkan_uninit(AVFilterContext *avctx) FFVulkanContext *vkctx = &s->vkctx; FFVulkanFunctions *vk = &vkctx->vkfn; + ff_framesync_uninit(&s->fs); ff_vk_exec_pool_free(vkctx, &s->e); ff_vk_shader_free(vkctx, &s->shd); @@ -579,10 +700,12 @@ static const AVOption v360_vulkan_options[] = { { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" }, { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" }, { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" }, + { "gopromax", "GoPro Max (two inputs)", 0, AV_OPT_TYPE_CONST, {.i64=GOPROMAX}, 0, 0, FLAGS, "in" }, { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=FLAT}, 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" }, + { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" }, { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" }, { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" }, @@ -598,26 +721,18 @@ static const AVOption v360_vulkan_options[] = { { "v_fov", "set output vertical FOV angle", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "v_fov" }, { "ih_fov", "set input horizontal FOV angle", OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "ih_fov" }, { "iv_fov", "set input vertical FOV angle", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl = 0.0f}, 0.0f, 360.0f, DYNAMIC, "iv_fov" }, + { "overlap", "overlapped pixels for GoPro Max", OFFSET(overlap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1024, FLAGS, "overlap" }, { NULL }, }; -AVFILTER_DEFINE_CLASS(v360_vulkan); - -static const AVFilterPad v360_vulkan_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = &v360_vulkan_filter_frame, - .config_props = &ff_vk_filter_config_input, - }, -}; +FRAMESYNC_DEFINE_CLASS(v360_vulkan, V360VulkanContext, fs); static const AVFilterPad v360_vulkan_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .config_props = &v360_vulkan_config_output, + .config_props = v360_vulkan_config_output, }, }; @@ -625,13 +740,14 @@ const FFFilter ff_vf_v360_vulkan = { .p.name = "v360_vulkan", .p.description = NULL_IF_CONFIG_SMALL("Convert 360 projection of video."), .p.priv_class = &v360_vulkan_class, - .p.flags = AVFILTER_FLAG_HWDEVICE, + .p.flags = AVFILTER_FLAG_HWDEVICE | AVFILTER_FLAG_DYNAMIC_INPUTS, .priv_size = sizeof(V360VulkanContext), - .init = &ff_vk_filter_init, - .uninit = &v360_vulkan_uninit, - FILTER_INPUTS(v360_vulkan_inputs), + .preinit = v360_vulkan_framesync_preinit, + .init = v360_vulkan_init, + .uninit = v360_vulkan_uninit, + .activate = v360_vulkan_activate, FILTER_OUTPUTS(v360_vulkan_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, - .process_command = &process_command, + .process_command = process_command, }; diff --git a/libavfilter/vulkan/v360.comp.glsl b/libavfilter/vulkan/v360.comp.glsl index 131b10e3b2..982555f8d4 100644 --- a/libavfilter/vulkan/v360.comp.glsl +++ b/libavfilter/vulkan/v360.comp.glsl @@ -25,10 +25,12 @@ layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; #define EQUIRECTANGULAR 0 +#define EQUIANGULAR 3 #define FLAT 4 #define DUAL_FISHEYE 5 #define STEREOGRAPHIC 8 #define FISHEYE 13 +#define GOPROMAX 25 layout (constant_id = 0) const int out_transform = 0; layout (constant_id = 1) const int in_transform = 0; @@ -42,8 +44,17 @@ layout (constant_id = 7) const int in_h = 0; layout (constant_id = 8) const int in_chroma_w = 0; layout (constant_id = 9) const int in_chroma_h = 0; -layout (set = 0, binding = 0) uniform sampler2D input_img[]; -layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; +layout (constant_id = 10) const int in_cube_size = 0; +layout (constant_id = 11) const int gopro_cube_width = 0; +layout (constant_id = 12) const int gopro_overlap = 0; +layout (constant_id = 13) const int gopro_face_offset_2 = 0; +layout (constant_id = 14) const int gopro_blend_x_0 = 0; +layout (constant_id = 15) const int gopro_blend_x_1 = 0; +layout (constant_id = 16) const int gopro_blend_x_2 = 0; + +layout (set = 0, binding = 0) uniform writeonly image2D output_img[]; +layout (set = 0, binding = 1) uniform sampler2D input_img[]; +layout (set = 0, binding = 2) uniform sampler2D input2_img[]; layout (push_constant, scalar) uniform pushConstants { mat4 rot_mat; @@ -58,8 +69,27 @@ ivec2 in_img_size[4] = { ivec2(in_w, in_h) }; +ivec3 gopro_face_offset_x = ivec3(0, + gopro_cube_width, + gopro_face_offset_2); +ivec3 gopro_face_size_x = ivec3(gopro_cube_width, + in_cube_size, + gopro_cube_width); +ivec3 gopro_blend_x = ivec3(gopro_blend_x_0, + gopro_blend_x_1, + gopro_blend_x_2); + #define IS_WITHIN(v1, v2) any(lessThan(v1, v2)) +float atan2(in float y, in float x) +{ + return abs(x) < 1e-6 ? sign(y) * m_pi2 : atan(y, x); +} +vec2 atan2(in vec2 v, in float z) +{ + return abs(z) < 1e-6 ? sign(v) * m_pi2 : atan(v, vec2(z)); +} + void xyz_to_flat(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size) { const float r = tan(acos(v[2])); @@ -166,6 +196,133 @@ void xyz_to_dfisheye(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size) imageStore(output_img[idx], pos, res); } +// FIXME: no padding +vec3 eac_to_xyz(in ivec2 out_size, in ivec2 pos) +{ + const ivec2 cube_size = out_size / ivec2(3, 2); + const ivec2 face = pos / cube_size; + const vec2 e_uv = vec2(pos % cube_size) / vec2(cube_size); + const int f = face.y * 3 + face.x; + vec3 v; + vec2 uv; + + uv.x = (e_uv.x >= 1.0) ? 1.0 : tan((e_uv.x - 0.5) * m_pi2); + uv.y = (e_uv.y >= 1.0) ? 1.0 : tan((e_uv.y - 0.5) * m_pi2); + + // limited cube_to_xyz + switch (f) { + case 0: + v = vec3(-1.0, uv.y, uv.x); + break; + case 1: + v = vec3(uv.x, uv.y, 1.0); + break; + case 2: + v = vec3(1.0, uv.y, -uv.x); + break; + case 3: + v = vec3(-uv.y, 1.0, -uv.x); + break; + case 4: + v = vec3(-uv.y, -uv.x, -1.0); + break; + case 5: + v = vec3(-uv.y, -1.0, uv.x); + break; + } + + return normalize(v); +} + +// face order : LEFT, FRONT, RIGHT, DOWN, BACK, UP +vec3 xzy_to_eac_cube(in vec3 v) +{ + vec3 a = abs(v); + int face; + vec3 view; + vec2 uv; + + if (a.x >= a.y && a.x >= a.z) { + if (v.x >= 0.0) { // +X + face = 2; + view = vec3(-v.z, v.y, a.x); + } + else { // -X + face = 0; + view = vec3( v.z, v.y, a.x); + } + } + else if (a.y >= a.z) { + if (v.y >= 0.0) { // +Y + face = 3; + view = vec3(-v.z, -v.x, a.y); + } + else { // -Y + face = 5; + view = vec3( v.z, -v.x, a.y); + } + } + else { + if (v.z >= 0.0) { // +Z + face = 1; + view = vec3( v.x, v.y, a.z); + } + else { // -Z + face = 4; + view = vec3(-v.y, -v.x, a.z); + } + } + uv = atan2(view.xy, view.z) / m_pi2 + 0.5; + + return vec3(uv.x, uv.y, float(face)); +} + +void xyz_to_gopro(uint idx, in vec3 v, in ivec2 pos, in ivec2 in_size) +{ + vec3 uvf = xzy_to_eac_cube(v); + vec2 uv = vec2(uvf.xy); + ivec2 grid = ivec2(int(uvf.z) % 3, int(uvf.z) / 3); + vec3 face_offset = vec3(gopro_face_offset_x) / float(in_img_size[0].x); + vec2 face_size = vec2(float(gopro_face_size_x[grid.x]) / float(in_img_size[0].x), 1.0); + vec2 uv_start = vec2(face_offset[grid.x], 0.0); + vec2 uv_end = uv_start + face_size; + vec4 res; + + if (grid.x == 1) { + res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv)) + : texture(input2_img[idx], mix(uv_start, uv_end, uv)); + } else { + float cube_width = float(gopro_cube_width); + float overlap = float(gopro_overlap); + float gratio = (cube_width - overlap) / cube_width; + float blend_width = overlap / (cube_width - overlap); + vec3 blend_u = vec3(gopro_blend_x) / cube_width; + vec2 uv1 = vec2(uv.x * gratio, uv.y); + vec2 uv2 = vec2((uv.x + blend_width) * gratio, uv.y); + if (uv1.x < blend_u[0]) { + res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv1)) + : texture(input2_img[idx], mix(uv_start, uv_end, uv1)); + } else if (uv2.x > blend_u[2]) { + res = (grid.y == 0) ? texture(input_img[idx], mix(uv_start, uv_end, uv2)) + : texture(input2_img[idx], mix(uv_start, uv_end, uv2)); + } else { + vec4 res2; + if (grid.y == 0) { + res = texture(input_img[idx], mix(uv_start, uv_end, uv1)); + res2 = texture(input_img[idx], mix(uv_start, uv_end, uv2)); + } + else { + res = texture(input2_img[idx], mix(uv_start, uv_end, uv1)); + res2 = texture(input2_img[idx], mix(uv_start, uv_end, uv2)); + } + float a = (uv1.x - blend_u[0]) / (blend_u[1] - blend_u[0]); + res = mix(res, res2, a); + } + } + + imageStore(output_img[idx], pos, res); +} + void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); @@ -179,6 +336,9 @@ void main() case EQUIRECTANGULAR: v = equirect_to_xyz(out_size, pos); break; + case EQUIANGULAR: + v = eac_to_xyz(out_size, pos); + break; case FLAT: v = flat_to_xyz(out_size, pos); break; @@ -211,6 +371,9 @@ void main() case FISHEYE: xyz_to_fisheye(i, v, pos, in_size); break; + case GOPROMAX: + xyz_to_gopro(i, v, pos, in_size); + break; } } } -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
