VideoUpscalerBridge upscales video on-device with a video super-resolution model (RelayVSR / SeedVR2 style), powered by stable-diffusion.cpp.
Use it on clips produced by StableDiffusionBridge.txt2video or on your own frames.
It owns its own native context, independent from StableDiffusionBridge: you can load it while a generation model is loaded, or after releasing it.
Platform support
- Android: supported
- iOS: supported
- JVM / Desktop: supported
- WASM: not available (every call fails or returns an empty array)
Public API
expect object VideoUpscalerBridge {
fun initModel(
diffusionModelPath: String,
vaePath: String,
upscaleFactor: Int = 2,
threads: Int = -1,
): Boolean
fun isLoaded(): Boolean
fun upscale(
packedFrames: ByteArray,
seed: Long = -1L,
windowSize: Int = 0,
temporalChunkSize: Int = 0,
): ByteArray
fun release()
}initModel(...)
Loads the upscaler model and its VAE.
diffusionModelPath,vaePath: the upscaler’s diffusion model and its matching VAE.upscaleFactor:2or4. RelayVSR requires it to match both checkpoints.threads: CPU threads,-1for the default.
Calling initModel again replaces the previously loaded upscaler.
upscale(...)
Upscales a packed frame sequence and returns it in the same layout. It blocks until finished — call it from a background thread.
The layout is the one txt2video returns:
- a 16-byte little-endian header:
frameCount,width,height,fps(each anInt32) - followed by
frameCount * width * height * 4bytes of RGBA frames
All frames must have the same size. The output header carries the new width and height.
check(VideoUpscalerBridge.initModel(modelPath, vaePath, upscaleFactor = 2))
val packed = StableDiffusionBridge.txt2video(prompt = "a paper boat on a river")
val upscaled = VideoUpscalerBridge.upscale(packed)
check(upscaled.isNotEmpty()) { "Upscaling failed" }
// Parse `upscaled` exactly like a txt2video result.Parameters
seed: negative keeps the library default.windowSize: RelayVSR reference window, including both endpoints.0uses the library default (16).temporalChunkSize: SeedVR2 independent temporal chunk, in input frames (1or4n+1).0uses the library default (5).
Returns an empty array on failure.
isLoaded() / release()
isLoaded() is true after a successful initModel. release() frees the upscaler.
Practical notes
- Upscaling is heavy: memory grows with frame count and output size. Upscale short clips, or in chunks, on mobile.
- The model files are separate from your generation model; download them like any other model.
- Progress reporting is not available for the upscaler.
