From cec13b80a49b6964ca4718f0d2c5332b03df11c1 Mon Sep 17 00:00:00 2001
From: Concedo <39025047+LostRuins@users.noreply.github.com>
Date: Sun, 30 Aug 2026 14:49:24 +0800
Subject: [PATCH] wip on true media references for h3 video gen
---
embd_res/kcpp_sdui.embd | 124 +++++++++++-----------
expose.h | 6 +-
koboldcpp.py | 25 +++--
otherarch/sdcpp/sdtype_adapter.cpp | 159 ++++++++++++++++++++++-------
4 files changed, 207 insertions(+), 107 deletions(-)
diff --git a/embd_res/kcpp_sdui.embd b/embd_res/kcpp_sdui.embd
index 2cae75fea..85bd8361e 100644
--- a/embd_res/kcpp_sdui.embd
+++ b/embd_res/kcpp_sdui.embd
@@ -5,18 +5,18 @@
Stable UI for KoboldCpp
-
-
diff --git a/expose.h b/expose.h
index f0fef3cc1..311bf5111 100644
--- a/expose.h
+++ b/expose.h
@@ -225,10 +225,12 @@ struct sd_generation_inputs
const char * negative_prompt = nullptr;
const char * init_images = "";
const char * mask = "";
- const char * audio_data = "";
+ const char * video_start_frame = "";
+ const char * video_end_frame = "";
const int extra_images_len = 0;
const char ** extra_images = nullptr;
- const bool reverse_refimg = false;
+ const int ref_audios_len = 0;
+ const char ** ref_audios = nullptr;
const bool flip_mask = false;
const float denoising_strength = 0.0f;
const float cfg_scale = 0.0f;
diff --git a/koboldcpp.py b/koboldcpp.py
index 2a29699db..d2d854ca7 100644
--- a/koboldcpp.py
+++ b/koboldcpp.py
@@ -462,10 +462,12 @@ class sd_generation_inputs(ctypes.Structure):
("negative_prompt", ctypes.c_char_p),
("init_images", ctypes.c_char_p),
("mask", ctypes.c_char_p),
- ("audio_data", ctypes.c_char_p),
+ ("video_start_frame", ctypes.c_char_p),
+ ("video_end_frame", ctypes.c_char_p),
("extra_images_len", ctypes.c_int),
("extra_images", ctypes.POINTER(ctypes.c_char_p)),
- ("reverse_refimg", ctypes.c_bool),
+ ("ref_audios_len", ctypes.c_int),
+ ("ref_audios", ctypes.POINTER(ctypes.c_char_p)),
("flip_mask", ctypes.c_bool),
("denoising_strength", ctypes.c_float),
("cfg_scale", ctypes.c_float),
@@ -2931,6 +2933,8 @@ def sd_generate(genparams):
init_images = ("" if (not init_images_arr or len(init_images_arr)==0 or not init_images_arr[0]) else init_images_arr[0])
init_images = strip_base64_prefix(init_images)
mask = strip_base64_prefix(genparams.get("mask", ""))
+ video_start_frame = strip_base64_prefix(genparams.get("video_start_frame", ""))
+ video_end_frame = strip_base64_prefix(genparams.get("video_end_frame", ""))
flip_mask = genparams.get("inpainting_mask_invert", 0)
denoising_strength = tryparsefloat(genparams.get("denoising_strength", 0.6),0.6)
cfg_scale = tryparsefloat(genparams.get("cfg_scale", 5),5)
@@ -2959,9 +2963,9 @@ def sd_generate(genparams):
extra_images_arr = ([] if not extra_images_arr else extra_images_arr)
extra_images_arr = [img for img in extra_images_arr if img not in (None, "")]
- audio_data = next((img for img in extra_images_arr if img.startswith("data:audio")), None)
- extra_images_arr = [img for img in extra_images_arr if not img.startswith("data:audio")]
- audio_data = strip_base64_prefix(audio_data)
+ audio_refs_arr = [img for img in extra_images_arr if isinstance(img, str) and img.startswith("data:audio")]
+ extra_images_arr = [img for img in extra_images_arr if not (isinstance(img, str) and img.startswith("data:audio"))]
+ audio_refs_arr = [strip_base64_prefix(aud) for aud in audio_refs_arr]
extra_images_arr = extra_images_arr[:extra_images_max]
lora_filenames, lora_multipliers = prepare_lora_multipliers(genparams.get("lora", []))
@@ -2979,8 +2983,9 @@ def sd_generate(genparams):
vid_fps = (16 if vid_fps < 16 else (32 if vid_fps > 32 else vid_fps))
swap_refimg = (True if tryparseint(genparams.get("send_as_refimg", 0),0) else False)
- reverse_refimg = (True if tryparseint(genparams.get("reverse_refimg", 0),0) else False)
if swap_refimg and init_images and init_images != "" and not mask:
+ if not video_start_frame:
+ video_start_frame = init_images
extra_images_arr = [init_images] + extra_images_arr
init_images = ""
@@ -2989,13 +2994,17 @@ def sd_generate(genparams):
inputs.negative_prompt = negative_prompt.encode("UTF-8")
inputs.init_images = init_images.encode("UTF-8")
inputs.mask = "".encode("UTF-8") if not mask else mask.encode("UTF-8")
- inputs.audio_data = "".encode("UTF-8") if not audio_data else audio_data.encode("UTF-8")
+ inputs.video_start_frame = "".encode("UTF-8") if not video_start_frame else video_start_frame.encode("UTF-8")
+ inputs.video_end_frame = "".encode("UTF-8") if not video_end_frame else video_end_frame.encode("UTF-8")
inputs.extra_images_len = len(extra_images_arr)
inputs.extra_images = (ctypes.c_char_p * inputs.extra_images_len)()
for n, estr in enumerate(extra_images_arr):
extra_image = strip_base64_prefix(estr)
inputs.extra_images[n] = extra_image.encode("UTF-8")
- inputs.reverse_refimg = reverse_refimg
+ inputs.ref_audios_len = len(audio_refs_arr)
+ inputs.ref_audios = (ctypes.c_char_p * inputs.ref_audios_len)()
+ for n, aud in enumerate(audio_refs_arr):
+ inputs.ref_audios[n] = aud.encode("UTF-8")
inputs.flip_mask = flip_mask
inputs.cfg_scale = cfg_scale
if distilled_guidance is not None:
diff --git a/otherarch/sdcpp/sdtype_adapter.cpp b/otherarch/sdcpp/sdtype_adapter.cpp
index c443845b8..e4975c5b9 100644
--- a/otherarch/sdcpp/sdtype_adapter.cpp
+++ b/otherarch/sdcpp/sdtype_adapter.cpp
@@ -1117,12 +1117,18 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
std::string img2img_data = std::string(inputs.init_images);
std::string img2img_mask = std::string(inputs.mask);
- std::string input_audio_data = std::string(inputs.audio_data ? inputs.audio_data : "");
+ std::string video_start_frame_data = std::string(inputs.video_start_frame ? inputs.video_start_frame : "");
+ std::string video_end_frame_data = std::string(inputs.video_end_frame ? inputs.video_end_frame : "");
std::vector extra_image_data;
for(int i=0;i ref_audio_data;
+ for(int i=0;iprompt = inputs.prompt;
sd_params->negative_prompt = inputs.negative_prompt;
sd_params->cfg_scale = inputs.cfg_scale;
@@ -1209,9 +1215,9 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
}
}
- if (is_video_model(info) && extra_image_data.size() == 0 && is_img2img)
+ if (is_video_model(info) && video_start_frame_data.empty() && is_img2img && img2img_mask=="")
{
- extra_image_data.push_back(img2img_data);
+ video_start_frame_data = img2img_data;
}
// limit by image side
@@ -1256,9 +1262,23 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
//for img2img
sd_image_t input_image = {0,0,0,nullptr};
+ sd_image_t video_start_image = {0,0,0,nullptr};
+ sd_image_t video_end_image = {0,0,0,nullptr};
std::vector reference_imgs;
- std::vector wan_imgs;
+ std::vector legacy_video_imgs;
std::vector photomaker_imgs;
+ std::vector reference_audios;
+ struct ReferenceAudioCleanup {
+ std::vector& audios;
+ ~ReferenceAudioCleanup() {
+ for (auto& audio : audios) {
+ if (audio.data) {
+ free(audio.data);
+ audio.data = nullptr;
+ }
+ }
+ }
+ } reference_audio_cleanup { reference_audios };
int nx, ny, nc;
int img2imgW = sd_params->width; //for img2img input
@@ -1275,16 +1295,50 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
fflush(stdout);
+ if(input_extraimage_buffers.size()>0) //just in time free old buffer
+ {
+ for(int i=0;i sd_image_t {
+ sd_image_t image = {0,0,0,nullptr};
+ if (b64.empty()) {
+ return image;
+ }
+ int nx2, ny2, nc2;
+ uint8_t * loaded = resize_to_generation
+ ? load_image_from_b64(b64,nx2,ny2,img2imgW,img2imgH,3)
+ : load_image_from_b64(b64,nx2,ny2);
+ if(loaded)
+ {
+ input_extraimage_buffers.push_back(loaded);
+ image.width = nx2;
+ image.height = ny2;
+ image.channel = 3;
+ image.data = loaded;
+ }
+ return image;
+ };
+
+ auto push_image_if_loaded = [](std::vector& images, sd_image_t image) {
+ if(image.data)
+ {
+ images.push_back(image);
+ }
+ };
+
+ if (is_video_model(info))
+ {
+ video_start_image = load_extra_image(video_start_frame_data, true);
+ video_end_image = load_extra_image(video_end_frame_data, true);
+ }
+
if(extra_image_data.size()>0)
{
- if(input_extraimage_buffers.size()>0) //just in time free old buffer
- {
- for(int i=0;i0)
+ {
+ for(int i=0;i 0);
+ const bool has_minimax_references = info.is_minimaxh3 && (reference_imgs.size() > 0 || reference_audios.size() > 0);
+ if(has_minimax_keyframes && has_minimax_references)
+ {
+ if(!sd_is_quiet && sddebugmode==1)
+ {
+ printf("\nMiniMax-H3: keyframes and Ref2VA references cannot be mixed; using reference media and ignoring keyframes.\n");
+ }
+ video_start_image = {0,0,0,nullptr};
+ video_end_image = {0,0,0,nullptr};
+ legacy_video_imgs.clear();
+ }
+
sd_img_gen_params_t params = {};
sd_img_gen_params_init (¶ms);
params.batch_count = 1;
@@ -1481,24 +1554,38 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
vid_gen_params.video_frames = vid_req_frames;
vid_gen_params.fps = vid_fps;
vid_gen_params.vae_tiling_params = params.vae_tiling_params;
- if (!input_audio_data.empty()) {
- input_audio = load_audio_from_b64(input_audio_data);
+ if (!info.is_minimaxh3 && ref_audio_data.size()>0) {
+ input_audio = load_audio_from_b64(ref_audio_data[0]);
if (input_audio.data == nullptr) {
return sd_generation.error("KCPP SD: load audio from base64 failed!");
}
vid_gen_params.input_audio = &input_audio;
}
- if (wan_imgs.size() > 0) {
- if (wan_imgs.size() >= 2) {
- vid_gen_params.init_image = wan_imgs[0];
- vid_gen_params.end_image = wan_imgs[1];
- } else if (wan_imgs.size() == 1) {
- if (inputs.reverse_refimg) {
- vid_gen_params.end_image = wan_imgs[0];
- } else {
- vid_gen_params.init_image = wan_imgs[0];
- }
+ if (info.is_minimaxh3 && reference_imgs.size() > 0) {
+ vid_gen_params.ref_images = reference_imgs.data();
+ vid_gen_params.ref_images_count = (int)reference_imgs.size();
+ }
+ if (info.is_minimaxh3 && reference_audios.size() > 0) {
+ vid_gen_params.ref_audios = reference_audios.data();
+ vid_gen_params.ref_audios_count = (int)reference_audios.size();
+ }
+ if (legacy_video_imgs.size() >= 2) {
+ if(!video_start_image.data) {
+ video_start_image = legacy_video_imgs[0];
}
+ if(!video_end_image.data) {
+ video_end_image = legacy_video_imgs[1];
+ }
+ } else if (legacy_video_imgs.size() == 1) {
+ if(!video_start_image.data && !video_end_image.data) {
+ video_start_image = legacy_video_imgs[0];
+ }
+ }
+ if (video_start_image.data) {
+ vid_gen_params.init_image = video_start_image;
+ }
+ if (video_end_image.data) {
+ vid_gen_params.end_image = video_end_image;
}
if(!sd_is_quiet && sddebugmode==1)
{
@@ -1512,7 +1599,9 @@ sd_generation_outputs sdtype_generate(const sd_generation_inputs inputs)
<< "\nSTRENGTH:" << vid_gen_params.strength
<< "\nFRAMES:" << vid_gen_params.video_frames
<< "\nCTRL_FRM:" << vid_gen_params.control_frames_size
- << "\nINIT_IMGS:" << wan_imgs.size()
+ << "\nINIT_IMGS:" << ((vid_gen_params.init_image.data ? 1 : 0) + (vid_gen_params.end_image.data ? 1 : 0))
+ << "\nREF_IMGS:" << vid_gen_params.ref_images_count
+ << "\nREF_AUDIOS:" << vid_gen_params.ref_audios_count
<< "\nINPUT_AUDIO:" << (vid_gen_params.input_audio ? "true" : "false")
<< "\n\n";
printf("%s", ss.str().c_str());