Temporal video support for llama.cpp — frame extraction, preprocessing, and captioning service for Qwen3.5 GGUF models
Project description
llama-video
Temporal video captioning for llama.cpp — frame extraction, super-frame preprocessing, and M-RoPE temporal encoding for Qwen3.5 GGUF models.
Why a patch? Sending multiple images to llama.cpp gives zero temporal understanding. The model needs 6-channel super-frames (Conv3D) and temporal M-RoPE positions to reason about motion, sequence, and change. This patch adds that.
Supported Models
All Qwen3.5 vision models (same vision encoder across all sizes):
| Model | Active Params | Total Params | Notes |
|---|---|---|---|
| Qwen3.5-0.8B | 0.8B | 0.8B | Smallest, fast iteration |
| Qwen3.5-3B | 3B | 3B | Good balance |
| Qwen3.5-35B-A3B | 3B | 35B | MoE — recommended starting point |
| Qwen3.5-122B-A10B | 10B | 122B | MoE — best quality/VRAM ratio |
| Qwen3.5-397B-A17B | 17B | 397B | Largest MoE |
You need two files per model: a GGUF model and a mmproj (vision projector).
Requirements
| Dependency | Version | Notes |
|---|---|---|
| Python | 3.11+ | |
| ffmpeg | any recent | Frame extraction. Must be on PATH. |
| CMake | 3.21+ | Building llama.cpp |
| CUDA toolkit | 11.7+ | For GPU inference. CPU works but is very slow. |
| git | any recent | Patch application |
Windows additional: Visual Studio 2019+ with C++ workload (for MSVC compiler), or MinGW-w64.
Setup
Step 1: Install llama-video
pip install llama-video
# With Gradio WebUI:
pip install "llama-video[ui]"
Or from source:
git clone https://github.com/Cobdog/llama-video.git
cd llama-video
pip install ".[ui]"
Step 2: Clone and patch llama.cpp
The patch targets llama.cpp commit cc18f96. If master has moved forward and the patch fails, pin to this commit.
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
git checkout cc18f96
cd ..
llama-video-patch ./llama.cpp
Or manually:
cd llama.cpp
git checkout cc18f96
git apply /path/to/llama-video/patches/video-support-20260323.patch
Step 3: Build llama.cpp
Interactive build (recommended)
If you installed from source, the included build script prompts for GPU backend, build type, and parallel job count:
./scripts/build.sh ./llama.cpp
If you installed via pip, grab the script directly:
curl -O https://raw.githubusercontent.com/Cobdog/llama-video/main/scripts/build.sh
chmod +x build.sh
./build.sh ./llama.cpp
It supports CUDA, HIP (AMD), Vulkan, Metal (macOS), and CPU-only. It will auto-detect nvcc in common locations (/opt/cuda/bin, /usr/local/cuda/bin) if it's not already on your PATH.
Manual build — Linux
cd llama.cpp
# CUDA (recommended)
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j$(nproc)
# CPU only (slow, but works)
cmake -B build
cmake --build build --config Release -j$(nproc)
Manual build — Windows (MSVC)
Open x64 Native Tools Command Prompt (from Visual Studio):
cd llama.cpp
:: CUDA
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release
:: CPU only
cmake -B build
cmake --build build --config Release
The server binary will be at build\bin\Release\llama-server.exe.
Manual build — Windows (MinGW)
cd llama.cpp
cmake -B build -G "MinGW Makefiles" -DGGML_CUDA=ON
cmake --build build -j%NUMBER_OF_PROCESSORS%
Verify GPU support: After building, check that
GGML_CUDA:BOOL=ON(or your chosen backend) appears inllama.cpp/build/CMakeCache.txt. If it saysOFF,nvccwasn't found during configuration — see Windows-Specific Notes.
Step 4: Download a model
Download both the GGUF model and mmproj from HuggingFace. Example for Qwen3.5-35B-A3B:
# From https://huggingface.co/Qwen/Qwen3.5-35B-A3B-GGUF (or a community quant)
# You need:
# - The model GGUF (e.g., qwen3.5-35b-a3b-q4_k_m.gguf)
# - The mmproj GGUF (e.g., mmproj-Qwen3.5-35B-A3B-F16.gguf)
Step 5: Start llama-server
Linux
./llama.cpp/build/bin/llama-server \
-m /path/to/model.gguf \
--mmproj /path/to/mmproj.gguf \
--host 0.0.0.0 --port 8080 \
--ctx-size 65536 --jinja
Windows
llama.cpp\build\bin\Release\llama-server.exe ^
-m C:\models\model.gguf ^
--mmproj C:\models\mmproj.gguf ^
--host 0.0.0.0 --port 8080 ^
--ctx-size 65536 --jinja
Important flags:
--ctx-size 65536— context window size. Higher = more frames but more VRAM. 65K is a good starting point.--jinja— required for Qwen3.5's chat template.--port 8080— default port that llama-video expects. Change viaLLAMA_SERVER_URLenv var.
Wait for the server to print llama server listening before proceeding.
Step 6: Caption a video
Python API:
import asyncio
from llama_video import Extractor, Preprocessor, Settings, get_preset
from llama_video.client import LlamaServerClient
async def caption(video_path: str) -> str:
settings = Settings()
extractor = Extractor(settings.extractor)
preprocessor = Preprocessor(settings.model)
client = LlamaServerClient(settings.server)
frames = await extractor.extract_frames_async(video_path)
video_input = preprocessor.process(frames, fps=2.0)
result = await client.caption_video(
video_input,
prompt="Describe what happens in this video.",
preset=get_preset("default"),
)
await client.close()
return result
print(asyncio.run(caption("my_video.mp4")))
Gradio WebUI (requires pip install "llama-video[ui]"):
llama-video-ui
# Opens at http://localhost:7860
FastAPI service:
llama-video-server
# API at http://localhost:9000
curl -X POST http://localhost:9000/v1/caption \
-H "Content-Type: application/json" \
-d '{"video_path": "/path/to/video.mp4"}'
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
LLAMA_SERVER_URL |
http://localhost:8080 |
llama-server base URL |
LLAMA_SERVER_TIMEOUT |
120 |
Request timeout in seconds |
LLAMA_VIDEO_DEFAULT_FPS |
2.0 |
Frame extraction rate |
LLAMA_VIDEO_MAX_FRAMES |
64 |
Max frames to extract per video |
LLAMA_VIDEO_FFMPEG_PATH |
ffmpeg |
Path to ffmpeg binary |
Inference Presets
Based on official Qwen team recommendations:
| Preset | Temperature | Top P | Top K | Presence Penalty | Use Case |
|---|---|---|---|---|---|
default |
1.0 | 0.95 | 20 | 1.5 | General captioning (thinking mode) |
precise |
0.6 | 0.95 | 20 | 0.0 | Precise/factual descriptions |
Prompt Templates
Built-in templates with {variable} substitution:
| Template | Mode | Description |
|---|---|---|
general |
both | Basic description |
detailed |
both | Characters, setting, actions, atmosphere |
motion |
video | Movement and action focus |
composition |
both | Framing, camera, lighting, color |
character |
both | Character focus (accepts {character_name}) |
narrative |
both | Screenplay-style narration |
Caveats and Limitations
Patch Compatibility
The patch was built against llama.cpp master as of 2026-03-23. It modifies files in tools/mtmd/ (clip.cpp, mtmd.cpp, mtmd.h, mtmd-helper.cpp) and examples/server/server.cpp. If upstream has refactored these files, the patch may not apply cleanly.
If the patch fails to apply:
cd llama.cpp
git log --oneline -1 # Note your current commit
git apply --check /path/to/patches/video-support-20260323.patch # Dry run to see conflicts
You may need to apply manually or wait for an updated patch.
Context Window vs. VRAM
More frames and higher resolution = more vision tokens = more VRAM. A rough guide:
| Setting | Vision Tokens | Notes |
|---|---|---|
| 4 frames, 280x280 | ~200 | Minimal, fast |
| 8 frames, 560x560 | ~3,200 | Good balance |
| 16 frames, 1120x1120 | ~50,000 | High quality, needs 65K+ context |
| 64 frames, 1920x1080 | ~200,000+ | Will exceed most context windows |
The WebUI shows a live token budget bar so you can tune before running inference.
Known Limitations
- One llama-server at a time. Each server instance loads the full model into VRAM. The run script guards against accidental duplicates.
- ffmpeg must be on PATH. The library calls ffmpeg as a subprocess. On Windows, either add it to PATH or set
LLAMA_VIDEO_FFMPEG_PATHto the full path. - Thinking mode is verbose. Qwen3.5 with
defaultpreset uses thinking mode, which produces internal reasoning before the final caption. This is normal — the library extracts the final answer automatically. Set timeout accordingly (120s+ for longer videos). - No audio processing. Only visual frames are extracted. Audio tracks are ignored.
- Super-frame pairing. Frames are paired sequentially (frame 0+1, 2+3, ...). An odd number of frames duplicates the last frame to form a complete pair. Extracting an even number of frames avoids this.
Windows-Specific Notes
- Use forward slashes or raw strings for paths in Python:
r"C:\videos\clip.mp4"or"C:/videos/clip.mp4". - If using PowerShell, environment variables are set with
$env:LLAMA_SERVER_URL = "http://localhost:8080". - The
llama-video-patchcommand usesgit apply— make suregitis on your PATH. - CUDA builds require the CUDA toolkit and MSVC (not MinGW) for best compatibility.
Project Structure
llama-video/
├── src/llama_video/ # Python library
│ ├── extractor.py # ffmpeg frame extraction
│ ├── preprocessor.py # Super-frame construction + grid THW
│ ├── client.py # llama-server HTTP client
│ ├── server.py # FastAPI captioning service
│ ├── webui.py # Gradio experimentation UI
│ ├── config.py # Settings and presets
│ ├── templates.py # Prompt templates
│ ├── tokens.py # Token budget estimation
│ ├── history.py # SQLite caption history
│ ├── batch.py # Batch captioning
│ ├── image.py # Single-image captioning
│ ├── patch_cli.py # llama-video-patch CLI
│ ├── debug_cli.py # llama-video-debug CLI
│ ├── errors.py # Exception hierarchy
│ └── types.py # Core data types (Frame, SuperFrame, etc.)
├── patches/ # C patches for llama.cpp
├── scripts/ # Setup, build, and run scripts
└── tests/ # Unit, integration, and smoke tests
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llama_video-0.1.3.tar.gz.
File metadata
- Download URL: llama_video-0.1.3.tar.gz
- Upload date:
- Size: 47.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2cd505fbcb6af9f980128ff4551951511f5bc8f85177754759380673522831e
|
|
| MD5 |
64f3c7344817cfefa4cdf0f58424e6e9
|
|
| BLAKE2b-256 |
0e41fd0e1c550646666d64d057d418b298f9a5ccbf3e989f377dea82f0e3a2f7
|
File details
Details for the file llama_video-0.1.3-py3-none-any.whl.
File metadata
- Download URL: llama_video-0.1.3-py3-none-any.whl
- Upload date:
- Size: 53.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f590d1b3b51f527b9fc96f8beac964380a331a776a5fbad7183fcdcb0c0fddde
|
|
| MD5 |
632f63c7ef06693ce9f1d19014db8130
|
|
| BLAKE2b-256 |
ba5831f4c1689a1083386c1a14878d381009e2e03a6882b949ae7ce52d8137b1
|