Releases: BlackBlazent/blackvideo-mini-player
Release list
BlackVideo Mini Player v2.4.0 — Standalone Mode, Sprite Preview, LLM Captions, Whisper Fix, Progress %, Auto-Updater
✅ What's in This Release
🖥️ Standalone Mode — Open Without a Video
The player can now be launched with no arguments and shows a welcome screen instead of exiting.
Before (v2.3.0):
blackvideo-player.exe ← exits immediately or crashes
blackvideo-player.exe video ← only way to use it
After (v2.4.0):
blackvideo-player.exe ← opens welcome screen
- Welcome screen renders inside the SDL2 window: large
▶icon, "Open a video or drop a file here" prompt, and an Open File button in the centre — all drawn in C viaui_overlay.c - Drag and drop: drop any video file onto the window at any time (welcome screen or during playback) — handled via
SDL_DROPFILEevent, requiresSDL_EventState(SDL_DROPFILE, SDL_ENABLE)at init player.adsgains aNo_Videostate alongsidePlaying/Paused/Stoppedmain.adbno longer exits on missing argument — passes""toPlayer.Runwhich entersNo_Videostate- Right-click Open Video File and the Open button both work from the welcome screen
How it works in Ada + C:
-- player.ads
type Player_State is (No_Video, Stopped, Playing, Paused);
-- player.adb: Run() — no argument guard removed
if Video_File = "" then
State := No_Video;
UI_Overlay.Set_No_Video_Mode (True);
end if;// ui_overlay.c: draw welcome screen when g_no_video = 1
void bv_ui_draw(...) {
if (g_no_video) { draw_welcome_screen(ren, w, h); return; }
...
}🖼️ Sprite Thumbnail Preview on Hover
Hovering the seek bar shows a small thumbnail of the video at that position, with a timestamp label beneath it.
How it works:
When a video opens, a background cmd.exe /c job (same WinExec pattern as Whisper) runs:
ffmpeg -i video.mp4 -vf "fps=1/10,scale=160:-1" -q:v 5
build\cache\videoname_%04d.jpgThis extracts one frame every 10 seconds as 160px-wide JPEG thumbnails and stores them in build\cache\<video_hash>\. On a 90-minute film that is ~540 small JPEGs (~2–3 MB total), generated in roughly 10–20 seconds in the background.
Hover rendering:
ui_overlay.ctracks mouse X over the seek bar and computeshover_pos = duration * (mouse_x - bar_x) / bar_w- Frame index =
hover_pos / 10→ loads the matching JPEG viaSDL_image(orstb_image— no extra DLL) - Renders a 160×90 popup box above the scrubber with the frame and a timestamp label below it
- Popup disappears when the mouse leaves the seek bar area
New files:
src/thumb_cache.ads / .adb— Ada module: hash video path, check cache dir, launch ffmpeg job, map hover position → JPEG filenamecsrc/thumb_preview.c— C:SDL_Textureloading (viastb_image.h, header-only), draw popup box
Cache location: build\cache\<8-char-hash-of-video-path>\frame_%04d.jpg
Cleared by right-click → Clear Thumbnail Cache (new menu item).
⚡ Whisper Performance Fix — Why It Was So Slow
Root cause (the real problem):
whisper_bridge.adb's Generate() function calls C_System() (the C system() function) to run both ffmpeg and whisper-cli. C_System is fully synchronous — it blocks the calling thread until the command exits. But Launch_Whisper in player.adb calls it via WinExec with cmd.exe /c "ffmpeg ... && whisper-cli ...".
WinExec is nominally asynchronous but its behaviour with cmd.exe /c "..." on modern Windows is unreliable — the cmd shell can hold the process slot open or Windows Defender / real-time antivirus scans the newly created .wav and .srt files before releasing them, causing the job to appear stalled for minutes. Additionally, whisper-cli with the ggml-base model runs on CPU only and is genuinely slow: expect ~0.3× real-time on a mid-range CPU (a 60-second video takes ~3–5 minutes, not hours — something else is wrong).
The real culprits causing "hours":
- Windows Defender scans every
.wavand.srtfile whisper-cli writes. On some machines this causes 100% disk hold for the duration. WinExec+cmd.exe /cwith a long quoted string — Windows has a 8191-char limit oncmd.execommand lines. If the video path is long, the command silently truncates and whisper-cli never starts — butWinExecreturns success (≥ 32) andWhisper_RunningstaysTrueforever, showing "generating..." permanently.- The
ggml-basemodel is genuinely slow for long content. A 2-hour film can take 30–60 minutes on CPU.
Fixes in v2.4.0:
- Replace
WinExecwithCreateProcess— proper async process launch with aPROCESS_INFORMATIONhandle we keep alive and poll withGetExitCodeProcess(hProcess, &code); code == STILL_ACTIVE. This is reliable on all Windows versions and not affected by the cmd.exe quoting issues. - Write a sentinel
.donefile — at the end of the whisper batch,echo done > <out>.done.Check_Whisper_Donepolls for the.donefile, not the.srt, so we know the SRT is fully written before loading it. - Path length guard — if the full command string exceeds 7500 chars, the batch is written to a temp
.batfile in%TEMP%andCreateProcessrunscmd.exe /c that.batinstead. No truncation. - Timeout detection — if
Whisper_Runninghas been true for > 30 minutes with no.donefile, the status banner changes toWhisper: stalled? Check Defender / consoleand a[Whisper] TIMEOUTmessage is printed to console. - Better model advice —
WHISPER_SETUP.mdupdated with the Windows Defender exclusion path recommendation.
New Ada/C additions:
-- player.adb
Whisper_H_Process : System.Address := System.Null_Address;
Whisper_Start_Tick : Interfaces.C.unsigned := 0;
function CreateProcess (...) return Boolean
with Import, Convention => C, External_Name => "bv_create_process";
-- csrc/process_helpers.c
int bv_create_process(const char *cmd, HANDLE *hProcess_out);
int bv_process_still_running(HANDLE hProcess);
void bv_close_handle(HANDLE h);📊 Whisper / LLM Progress Percentage
Instead of a static Whisper: generating..., the status banner now shows a live percentage:
Whisper: generating... 23% [████████░░░░░░░░░░░░░░░░░░░░░░]
How whisper-cli outputs progress:
whisper-cli writes progress lines to stderr in the form:
whisper_print_progress_callback: progress = 23%
Implementation:
bv_create_processis replaced withbv_create_process_with_pipewhich creates a read pipe on stderr of the child process (usingCreatePipe+STARTUPINFO.hStdError = write_end)- Each frame,
Check_Whisper_ProgresscallsPeekNamedPipe(non-blocking) to read any available stderr bytes, scans for theprogress = NN%pattern, and callsUI_Overlay.Set_Whisper_Status("Whisper: generating... NN%") - The C overlay draws a mini progress bar beside the text in the banner
Same pipe approach applies to LLM providers that stream token output (see below).
🤖 LLM Cloud Caption Generation
Six cloud providers available from the context menu — right-click → Generate Captions (LLM) ▶ → sub-menu:
| Provider | Model used | Notes |
|---|---|---|
| Claude (Anthropic) | claude-sonnet-4-5 |
Audio via base64 or transcript |
| OpenAI | gpt-4o-audio-preview |
Native audio input |
| Gemini (Google) | gemini-1.5-pro |
Audio file upload |
| DeepSeek | deepseek-chat |
Text transcript → SRT |
| Grok (xAI) | grok-2 |
Text transcript → SRT |
Architecture — two-step approach (no audio upload for text-only providers):
- Run whisper-cli locally for a fast draft transcript (text only, no SRT timing) — takes ~30 seconds for a 60s clip with
ggml-tiny - Send the transcript text + video metadata to the LLM with a prompt: "Convert this transcript to a properly timed SRT subtitle file. Duration: Xs. Speaker count: N."
- The LLM returns a formatted
.srtwhich is auto-loaded into Track 2
For OpenAI and Gemini which support native audio, step 1 is skipped and the WAV is uploaded directly.
API key storage — %APPDATA%\BlackVideo\keys.cfg:
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=AIza...
DEEPSEEK_API_KEY=...
GROK_API_KEY=...First use: context menu shows Set API Key for [Provider] — a small SDL2 text input overlay appears (drawn in C). Keys are written to keys.cfg and reused on subsequent sessions.
HTTP in Ada + C:
csrc/llm_client.c— usesWinINet(available on all Windows, no extra DLL) for HTTPS POST. No libcurl dependency.src/llm_bridge.ads / .adb— Ada wrapper:Send_Request(Provider, Transcript, Duration)→ returns raw SRT string- Streaming responses read via
InternetReadFileloop, status banner updated with token count:Claude: generating... 142 tokens
New context menu layout:
Open Video File...
─────────────────────
Subtitle: Off
Track 1 — filename.srt
Track 2 — (empty)
Track 3 — (empty)
─────────────────────
Generate Captions (Whisper)
Translate to English (Whisper)
─────────────────────
Generate Captions (LLM) ▶
Claude (Anthropic)
OpenAI
Gemini
DeepSeek
Grok
─────────────────────
Clear Thumbnail Cache
─────────────────────
Check for Updates...
🔔 Auto-Updater — GitHub Release Check
On every launch, the player silently checks https://raw.githubusercontent.com/BlackBlazent/blackvideo-mini-player/main/latest.json using WinINet. If the version field is newer than the compiled-in version string, a notification badge appears on the ⋮ menu button.
latest.json format (add to repo root):
{
"version": "2.4.0",
"release_url": "https://github.com/BlackBlazent/blackvideo-mini-player/releases/tag/...BlackVideo Mini Player v2.3.0 — Offline Captions (Whisper), Subtitle Rendering & Track Fix
✅ What's in This Release
🎙️ Offline Caption Generation — Whisper.cpp Integration
Generate subtitles for any video without an internet connection or API key.
- Right-click → Generate Captions (Whisper) — transcribes the video's spoken audio and writes a
.srtfile beside the video. The result is auto-loaded into Track 1 when complete. - Right-click → Translate to English (Whisper) — same pipeline with Whisper's
--translateflag, which outputs English subtitles regardless of the spoken language. Useful for non-English films with no existing subtitle file. - Player stays responsive while Whisper runs — the job is launched as a detached background process via
WinExec. The player polls for the output.srtfile each frame and loads it the moment it appears. - Status banner in the control bar — shows
Whisper: generating captions...orWhisper: translating...while the job is running, and clears automatically when done. - Zero build-time dependency —
whisper_bridge.adbshells out towhisper-cli.exerather than linking againstwhisper.h. No whisper headers or import libraries needed inlib/. - Hot-swappable models — place
ggml-base.bin,ggml-small.bin,ggml-medium.bin, orggml-large.bininbuild\models\and the next job picks it up automatically. No rebuild required. - Auto-discovery —
whisper-cli.exeand the model are resolved in order:BLACKVIDEO_WHISPER_PATH/BLACKVIDEO_WHISPER_MODELenv vars →build\beside the exe → systemPATH. No config file needed in production.
Translation language: Currently translates to English only. Whisper.cpp's
--translateflag always targets English as the fixed output language. Support for translating to other target languages (Spanish, Japanese, Filipino, etc.) is planned for a future version.
📝 Real Subtitle Text Rendering
Loaded .srt files now display on-screen during playback — not just track labels in the menu.
srt_parser.adb— new module that parses.srtfiles at load time (handles UTF-8 BOM, Windows CRLF, multi-line cues, up to 4 096 cues). Stores all cues in memory for O(n) per-frame lookup.- Cue text pushed every frame —
player.adbcallsSRT_Parser.Current_Text(pos)each frame and pushes the result to the C overlay viabv_ui_set_sub_text. - Subtitle overlay in
ui_overlay.c— draws the current cue horizontally centred just above the control bar, with a semi-transparent shadow box for readability against any background colour. - Works with any externally loaded
.srtfile and with Whisper-generated.srtfiles (auto-loaded from Track 1).
🐛 Subtitle Track State Bug Fixed
The subtitle track was silently resetting to Off immediately after a track was selected or a file was loaded.
- Root cause — when
Open_Dialog(the PowerShell file picker) blocks viapopen, SDL queuesSDL_MOUSEBUTTONDOWNevents in the background. When the dialog closes and the event pump resumes, those queued events fireHandle_Button_Downagain. If the mouse had drifted over the Subtitle: Off row,CTX_SUB_NONEwas triggered automatically, settingSub_Active = -1right after the user's selection. - Fix —
Ctx_Close_Tickrecords the SDL tick whenever the context menu closes. AnySDL_MOUSEBUTTONDOWNarriving within 250 ms of that moment is discarded. This absorbs all queued dialog-close events without affecting normal clicks. - Context menu label fixed — the Subtitle: Off label in the context menu now updates to Subtitle: Track N (in red) when a track is active, so the current state is always visible without opening anything else.
⚙️ .env File — Dev / Production Path Override System
main.adbloads.envat startup usingSetEnvironmentVariableAfor eachKEY=VALUEline found in the file beside the exe. Blank lines and#comments are skipped.- Production mode — no
.envfile needed in a release package. Whisper paths resolve automatically frombuild\. The player silently skips the load step if the file is absent. - Dev mode — rename
.env.exampleto.envand setBLACKVIDEO_WHISPER_PATHandBLACKVIDEO_WHISPER_MODELto override the search paths during development (e.g. pointing to awhisper-bin-x64folder outside the project). - Never commit
.env— it is listed in.gitignoreand contains local machine paths only.
🏗️ Build System
build.batchecks for Whisper assets (non-fatal) — prints[OK]ifwhisper-cli.exeandggml-base.binare found inbuild\, otherwise prints a setup reminder. Build continues regardless.build.batgenerates.env.exampleif none exists — creates a ready-to-rename template with commented-outBLACKVIDEO_WHISPER_PATHandBLACKVIDEO_WHISPER_MODELlines.build.batcopies Whisper DLLs (whisper.dll,ggml.dll,ggml-base.dll,ggml-cpu.dll) fromlib\tobuild\alongside the other runtime DLLs, if present.
🎮 Controls
Keyboard
| Key | Action |
|---|---|
SPACE |
Play / Pause |
← / → |
Seek −5 / +5 seconds |
↑ / ↓ |
Volume +10 / −10 |
M |
Mute / Unmute |
L |
Loop toggle |
F |
Fullscreen toggle |
ESC or Q |
Quit |
UI Bar
| Control | Action |
|---|---|
| Seek bar | Click or drag to jump/scrub |
| ⏮ Prev | Seek to start |
| ⏯ Play/Pause | Toggle playback |
| ⏭ Next | Jump near end |
| 🔁 Loop | Toggle loop |
| 🔊 Volume | Mute/unmute |
1.0x Speed |
Cycle 0.5× / 1× / 1.5× / 2× |
| ⛶ Fullscreen | Toggle fullscreen |
| ⋮ Menu | Context menu |
Context Menu (Right-click)
| Item | Action |
|---|---|
| Open Video File... | Swap video (restart with new file) |
| Subtitle: Off / Track N | Toggle subtitles off, shows active track name |
| Track 1 / 2 / 3 | Activate loaded track, or pick a file for empty slots |
| Generate Captions (Whisper) | Transcribe audio → .srt (auto-loaded) |
| Translate to English (Whisper) | Transcribe + translate to English → .srt (auto-loaded) |
📦 Requirements (Windows)
- Windows 10 / 11 x64
ffmpeg.exeonPATHor inbuild\(for Whisper audio extraction)- Runtime DLLs in
build\:
| DLL | Source |
|---|---|
SDL2.dll |
SDL2 release page (MinGW) |
SDL2_ttf.dll |
SDL2_ttf-devel-2.0.12-VC |
libfreetype-6.dll |
Bundled with SDL2_ttf-devel-2.0.12-VC |
zlib1.dll |
Bundled with SDL2_ttf-devel-2.0.12-VC |
avcodec-62.dll |
FFmpeg shared build |
avdevice-62.dll |
FFmpeg shared build |
avfilter-11.dll |
FFmpeg shared build |
avformat-62.dll |
FFmpeg shared build |
avutil-60.dll |
FFmpeg shared build |
swresample-6.dll |
FFmpeg shared build |
swscale-9.dll |
FFmpeg shared build |
Optional — Whisper DLLs (only needed for caption generation):
| DLL | Source |
|---|---|
whisper.dll |
whisper-bin-x64.zip |
ggml.dll |
whisper-bin-x64.zip |
ggml-base.dll |
whisper-bin-x64.zip |
ggml-cpu.dll |
whisper-bin-x64.zip |
- GNAT Community 2021 (build-time only)
🚀 Usage
build\blackvideo-player.exe "C:\path\to\video.mp4"Generate captions after opening:
- Right-click anywhere on the video
- Click Generate Captions (Whisper) or Translate to English (Whisper)
- Wait for the status banner to clear — subtitle is auto-loaded into Track 1
📁 New Files in This Release
| File | Purpose |
|---|---|
src/srt_parser.ads/.adb |
SRT subtitle parser (BOM, CRLF, multi-line, up to 4 096 cues) |
src/whisper_bridge.ads/.adb |
Whisper.cpp offline caption bridge (no whisper.h at build time) |
.env.example |
Template for dev path overrides — rename to .env |
WHISPER_SETUP.md |
Full Whisper setup guide, model upgrade table, troubleshooting |
Built with Ada 2012 · FFmpeg 8.x · SDL2 · SDL2_ttf · whisper.cpp · GNAT Community 2021
Full Changelog: v2.2.0...v2.3.0
BlackVideo Mini Player v2.2.0 — Playback Controls Fix & Audio Stability
✅ What's in This Release
🖱️ Fully Working Playback Controls
All on-screen buttons now respond correctly to mouse clicks.
- Fixed mouse button detection —
SDL_MouseButtonEvent.buttonwas being read from byte offset 13 instead of the correct offset 16 in the SDL2 struct layout (type@0 ·timestamp@4 ·windowID@8 ·which@12 ·button@16). Every click was registering as button 0 (nothing), making play/pause, loop, mute, speed, fullscreen, and seek-bar clicks all completely unresponsive. One offset fix restores all controls. - Fixed right-click context menu — right-click (button = 3) was never detected for the same reason. The context menu (Open File, Subtitle Track selection) now opens correctly on right-click anywhere on the video.
- Seek bar and progress scrubber remain fully functional — their
x/ycoordinate offsets were already correct.
🔊 Audio Stability Fix for Long Videos
Audio no longer becomes scattered, mixed, or distorted after ~50 minutes of playback.
- Fixed ring buffer race condition —
Push_Audio(main thread) andFill_Audio(SDL audio thread) both accessedRing_Avail,Ring_Read, andRing_Writewith no synchronisation. Torn reads accumulate silently and eventually produce audible corruption after extended runtime. Fixed by wrapping allPush_Audioring writes inSDL_LockAudioDevice/SDL_UnlockAudioDevice. - Ring buffer increased from 256 KB → 2 MB (~12 seconds at 44100 Hz stereo S16). H.264 decode bursts during complex scenes can produce audio faster than real-time for short periods; the larger buffer absorbs these without overflow.
- Overflow strategy changed — drop incoming, not buffered — the old code dropped the oldest buffered audio when the ring filled, causing audible clicks in audio that was about to play. The new code silently truncates the incoming burst instead, which is inaudible.
- Audio ring flushed on every seek —
Audio.Flushis now called after all seek paths (scrubber drag, click seek bar, Prev/Next buttons, keyboard ←/→, loop restart). Previously, stale pre-seek audio would bleed through for a fraction of a second after jumping to a new position. - Fixed SDL audio device sample rate —
SDL_AUDIO_ALLOW_FREQUENCY_CHANGEwas previously passed, allowing SDL to silently open the device at a different sample rate and causing subtle pitch drift or sync issues over long files. The device is now opened withAllowed_Changes = 0(exact 44100 Hz, S16 stereo required). - Added
SDL_LockAudioDevice/SDL_UnlockAudioDevicebindings tosdl-audio.ads. - Added
Audio.Flushprocedure toaudio.ads/audio.adb.
🏗️ Build System Fixes
ui_overlay.cmoved fromsrc/→csrc/— GPRbuild was scanningsrc/and compiling the C file as Ada, producing a duplicateui_overlay.othat caused multiple definition linker errors for everybv_ui_*symbol. Thecsrc/directory is outside GPRbuild's source scan.- C overlay now compiles to
ui_overlay_c.o(notui_overlay.o) to avoid collision with the Ada-compiledui_overlay.othat GPRbuild produces fromui_overlay.adb. - GPR linker uses
-l:libSDL2_ttf.dll.a(explicit import library filename) instead of-lSDL2_ttfto guarantee the DLL import path is used rather than any static archive. - Added
-lgdi32,-lusp10,-lrpcrt4to Windows linker flags, required by HarfBuzz Uniscribe inside SDL2_ttf. - Fixed SDL2_ttf / SDL2 link order —
libSDL2_ttfmust precedelibSDL2so the linker resolves SDL2_ttf's SDL2 dependencies correctly. build.batuses%ROOT%(absolute quoted path via%CD%) for all-Iand-oflags, fixing silent gcc failures when the project path contains spaces (e.g.Vilma E. Agripo).build.batdeletes stalebuild\obj\ui_overlay.obefore building to prevent leftover objects from prior broken builds causing duplicate symbol errors.
🎮 Controls
Keyboard
| Key | Action |
|---|---|
SPACE |
Play / Pause |
← / → |
Seek −5 / +5 seconds |
↑ / ↓ |
Volume +10 / −10 |
M |
Mute / Unmute |
L |
Loop toggle |
F |
Fullscreen toggle |
ESC or Q |
Quit |
UI Bar
| Control | Action |
|---|---|
| Seek bar | Click or drag to jump/scrub |
| ⏮ Prev | Seek to start |
| ⏯ Play/Pause | Toggle playback |
| ⏭ Next | Jump near end |
| 🔁 Loop | Toggle loop |
| 🔊 Volume | Mute/unmute |
1.0x Speed |
Cycle 0.5× / 1× / 1.5× / 2× (display only) |
| ⛶ Fullscreen | Toggle fullscreen |
| ⋮ Menu | Context menu — Open File, Subtitle tracks |
Right-click anywhere on the video to open the context menu directly.
📦 Requirements (Windows)
- Windows 10 / 11 x64
- Runtime DLLs in
build\:
| DLL | Source |
|---|---|
SDL2.dll |
SDL2 release page (MinGW) |
SDL2_ttf.dll |
SDL2_ttf-devel-2.0.12-VC |
libfreetype-6.dll |
Bundled with SDL2_ttf-devel-2.0.12-VC |
zlib1.dll |
Bundled with SDL2_ttf-devel-2.0.12-VC |
avcodec-62.dll |
FFmpeg shared build |
avdevice-62.dll |
FFmpeg shared build |
avfilter-11.dll |
FFmpeg shared build |
avformat-62.dll |
FFmpeg shared build |
avutil-60.dll |
FFmpeg shared build |
swresample-6.dll |
FFmpeg shared build |
swscale-9.dll |
FFmpeg shared build |
- GNAT Community 2021 (build-time only)
Important: Use
SDL2_ttf-devel-2.0.12-VCfor the SDL2_ttf DLL. Includelibfreetype-6.dllandzlib1.dllfrom that package alongsideSDL2_ttf.dllinbuild\.
🚀 Usage
blackvideo-player.exe "C:\path\to\video.mp4"
Built with Ada 2012 · FFmpeg 8.x · SDL2 · SDL2_ttf · GNAT Community 2021
Full Changelog: 1.1.0...v2.2.0
BlackVideo Mini Player v1.1.0 — Audio, Stability & Icon
✅ What's in This Release
🔊 Working Audio Playback
Audio now plays correctly for all common video formats. Previous builds were silent or produced scraping and choppy distortion every ~2 seconds.
- Fixed planar audio format support (
AV_SAMPLE_FMT_FLTP/fmt=8) — AAC and MP3 audio tracks in MP4 files now decode cleanly. The old code only passeddata[0](left channel plane) toswr_convert; all planes are now forwarded correctly viabv_swr_convert_frame()in C, which passesframe->datadirectly to libswresample. - Fixed
SDL_MixAudioFormatself-mix bug — the callback was passing the output stream as both source and destination, doubling amplitude on every audio callback and causing audible clipping and distortion. Fixed by copying ring buffer data into a temporary buffer first, then mixingTmp → Stream. - Resampler supports any input rate/format — 48000 Hz source files (Blu-ray rips, etc.) now convert cleanly to S16 stereo 44100 Hz for SDL2 output.
- Ring buffer doubled to 256 KiB (~1.5 s capacity) to absorb decode bursts without dropping samples.
- SDL audio buffer lowered to 2048 samples for lower output latency.
🎬 Correct Video Playback Speed
Video no longer runs slower than its source FPS.
- Main loop now measures actual elapsed time per frame using
SDL_GetTicksand sleeps only the remaining budget. PreviouslySDL_Delay(frame_ms)ran after decoding, meaning decode + scale + upload time stacked on top of the frame interval. - 60 fps video now plays at 60 fps; 24 fps at 24 fps.
💾 Fixed Heap Exhaustion Crash
The player no longer crashes with STORAGE_ERROR: heap exhausted after a few seconds.
- Root cause: every decoded video frame allocated a fresh ~6.5 MB Ada heap buffer for RGB pixel data and leaked it. At 60 fps this consumed ~390 MB/sec of heap with no reclaim.
- Fixed by pre-allocating a single
RGB_Bufferat open time and reusing it every frame. Zero heap allocation per frame.
🪲 Fixed Wrong Stream Routing (NAL Unit Spam)
Hundreds of [h264] Invalid NAL unit size errors per second are gone.
- Root cause: Ada's hardcoded
AVPacket.stream_indexoffset (36) was wrong for FFmpeg 8.x. Audio packets were misidentified as video and sent to the H.264 decoder, which tried to parse AAC audio as NAL units. - Fixed with
bv_packet_stream_index()— a C helper that readsstream_indexvia the real FFmpeg header so the offset is always correct regardless of FFmpeg version.
🔗 GNAT 2021 Windows Archive Linker Fix
Eliminated the persistent archive has no index; run ranlib build failure.
- Root cause: GPRbuild recreates the mixed Ada+C archive during bind+link, overwriting any manual ranlib fix.
- Fixed by removing C from the GPR project entirely.
ffmpeg_helpers.cis compiled bybuild.batusinggccdirectly and passed as an explicit.oto the linker — no archive, no ranlib.
🖼️ Custom Application Icon
The executable now shows the BlackVideo icon in Explorer, the taskbar, and the title bar.
resources/blackvideo.o(compiled fromblackvideo.rcviawindres) is linked in the GPRLinkerpackage under thewindowscase.- Windows-only — does not affect Linux or macOS builds.
🛠️ Build Script Hardening (build.bat)
- Detects and deletes stale
.adbbody files that causedspec does not allow a bodyerrors. - Compiles
ffmpeg_helpers.cseparately withgccbefore invokinggprbuild. - Validates FFmpeg headers and
.aimport libraries before building, with clear errors on missing files. - Copies runtime DLLs to
build\on success.
🎮 Keyboard Controls
| Key | Action |
|---|---|
SPACE |
Play / Pause |
← / → |
Seek −5 / +5 seconds |
↑ / ↓ |
Volume +10 / −10 |
M |
Mute / Unmute |
F |
Fullscreen toggle |
ESC or Q |
Quit |
📦 Requirements (Windows)
- Windows 10 / 11 x64
- FFmpeg 8.x shared DLLs in
build\:avcodec-62.dll,avformat-62.dll,avutil-60.dll,swscale-9.dll,swresample-6.dll SDL2.dllinbuild\- GNAT Community 2021 (build-time only)
🚀 Usage
blackvideo-player.exe "C:\path\to\video.mp4"
blackvideo-player.exe --help
Download
Installer
Built with Ada 2012 · FFmpeg 8.x · SDL2 · GNAT Community 2021
Full Changelog: https://github.com/BlackBlazent/blackvideo-mini-player/commits/1.1.0


