miniav_ffi 0.8.0
miniav_ffi: ^0.8.0 copied to clipboard
A Dart FFI library for the miniav project, providing bindings to the native miniav C library.
miniav_ffi CHANGELOG #
0.8.0 #
- New, optional strict capture of one approved application's window on Windows
x64, for hosts that must share exactly that window and nothing else. Ordinary
screen capture is unchanged.
- The host passes the process handle it opened when the app was approved; miniAV keeps it and the selected window for the whole capture and never falls back to ordinary capture.
- Frames are GPU-only BGRA at one fixed size, handed out the moment the GPU has finished copying them, at most three at a time and no faster than the configured rate. While the host holds all three, the newest frame waits rather than being dropped, so the window's latest picture always arrives.
- A resize, the window closing, the app exiting or the approval expiring ends the capture.
- A strict capture is used once. Destroy reports it as pending until every frame and callback has been released; one whose clean-up fails is quarantined.
- Dart bindings for every strict record and function are in
miniav_ffi_windows_strict_screen.dart. - Verified against a real window in a separate process. Left to the host: checking that the native library is genuine, user consent, and finishing its own GPU work on a frame before releasing it.
- CPU video planes cover exactly the frame's bytes. The chroma planes of 4:2:0
formats (NV12, NV21, I420, YV12, 10-bit 4:2:0) were viewed with the full frame
height and ran past the end of the frame. An MJPEG frame is one plane of
dataSizeBytes, and GPU frames always have empty planes (a Linux DMA-BUF camera frame used to get a view over its file descriptor). - Windows input capture: an event is no longer overwritten before an asynchronous receiver reads it. With two controllers reporting in one poll, the first report (or its disconnect) was lost. Events are copied into fixed rings owned by the input context (256 per controller, 256 keyboard, 1024 mouse); a receiver that falls further behind reads newer events in place of the ones it missed.
- Windows screen capture (WGC) delivers evenly spaced, fresh frames at the configured rate. The callback used to sleep out each interval while WGC queued frames, so the delivered frame was up to an interval old and the picture advanced unevenly. It now delivers the newest frame of each interval when the interval closes, stamped with the interval's time, holding a frame up to about half an interval.
- Windows window capture (WGC) reports the source lost within about a tenth of a second of the window closing. It relied on Windows announcing the close, which it does not do for every window (a closed popup whose app kept running never was), so such a capture just stopped delivering frames.
- Windows GPU frames from WGC and the Media Foundation camera are shared only after the GPU has finished writing them (a fence wait of at most 100 ms); a consumer on another device could read a partly written picture. A WGC frame still unfinished is dropped with a warning, and the camera delivers it through its CPU path. DXGI is unchanged.
- The D3D11 devices WGC and the Media Foundation camera create are multithread-protected, since their immediate contexts are used from several threads.
- Windows camera:
- The default mode prefers NV12, then YUY2, then other raw formats, MJPEG last; 720p, then 1080p, then the size nearest 720p; 30 fps, else the nearest rate.
- A raw format at a size and rate the camera offers only as MJPEG is decoded by Media Foundation instead of failing to configure, and keeps the camera's own frame timing.
- MJPEG with the GPU output preference is delivered as CPU memory instead of failing on its first frame.
- GPU frames in a texture array, mipmapped or not shareable are copied into a shareable texture.
- Per-frame notes are logged once per configuration.
- YUV frames report their colour as
MiniAVVideoBuffer.color(nativeMiniAV_VideoBuffer_GetColor): the matrix and range Media Foundation states, else BT.601 and full range for MJPEG the reader decodes, limited otherwise. The choice is logged once per configuration.
- WGC asks the compositor for at most three frames per output interval
(
MinUpdateInterval, Windows 11 24H2+); a 240 Hz display captured at 30 fps made eight GPU copies per delivered frame. - WGC logs pacing and copy timings every 10 s at info level.
- Windows Desktop Duplication reports which parts of each frame changed, as
MiniAVVideoBuffer.damage(nativeMiniAV_VideoBuffer_CopyDamage).generationcounts the frames delivered in onesourceEpoch, and partial damage is relative to the frame delivered just before. Changes from captures that were never delivered, such as a mouse-only update, carry into the next frame.- A gap in
generation, a size change orfullFramemeans the whole frame must be processed. The first frame, bad or missing metadata and more than 256 rectangles report the whole frame. - Only BGRA frames at the output's own size and orientation carry damage.
Everything else reports
null, including Windows Graphics Capture, which miniAV uses first whenever it can start.
- Screen contexts implement the new platform-interface members
(
setCaptureFrameRate,setContentHint,fallbackToCpuDelivery,cpuDeliverySurface) as unsupported.
0.7.4 #
- Increment downstream deps
0.7.3 #
- New
MiniAV_Audio_SetCaptureMirror: the capture callback also writes each block into a caller-owned single-producer/single-consumer ring, so a consumer on another thread (a web worker sharing wasm memory) can read captured audio without calling into wasm. MiniAV_AudioOutput_ConfigurereturnsMINIAV_ERROR_DEVICE_NOT_FOUNDfor adevice_idthat matches no playback device, instead of silently opening the system default. An emptydevice_idstill means the default, and the default is still used when the device list cannot be read.MiniAV_AudioOutput_GetDefaultFormathonours itsdevice_id(the device name); an unknown name falls back to the default with a warning.
0.7.2 #
- released 08/13/26 - MR
0.7.1 #
<miniav.h>includesminiav_playback.h. C code that included only<miniav.h>got an implicit declaration ofMiniAV_AudioOutput_CreateContext, truncated the returned handle and crashed. Dart callers were not affected.- Windows per-process loopback works: a
pid:target used to capture the whole system's audio. It now uses process loopback, including the target's child processes, and needs Windows 10 build 20348 / Windows 11. - Behaviour change: per-process loopback that cannot be delivered (older
Windows, activation failure, a PID that is not running) fails
Loopback.configureinstead of substituting whole-system audio.MINIAV_LOOPBACK_ALLOW_SYSTEM_FALLBACK=1restores the substitution. - New
MiniAV_Loopback_GetActiveTargetInfo(C API only) reports the scope actually captured: the process or the whole system. - Per-process loopback honours the requested rate, channels and format, and taps audio before the endpoint volume, so it is typically louder than system loopback.
- The Dart
MiniAVInputConfigbinding was 16 bytes shorter than the C struct, soInput_Configureread past its allocation into the motion fields. The bindings now match, motion types included; motion is still not delivered over FFI. - New
MiniAV_ABI_StructSize/MiniAV_ABI_StructCount/MiniAV_ABI_StructNameAtexport the size of every struct that crosses FFI, and a test checks each against Dart. - Windows loopback reports and stamps the endpoint's real format instead of the requested one, which wrote the wrong format into recordings from a 44.1 kHz or 5.1 endpoint. A request the endpoint cannot meet is logged, not faked.
- Windows window capture with
captureAudio: truedelivers audio; the process id was formatted in a case the loopback did not match. Behaviour change: screen configure andStartCapturefail when audio was requested and cannot be set up, instead of succeeding video-only. - WGC recreates its frame pool when the window is resized, so a resized window
no longer reports
dataSizeBytespast the end of the mapped buffer. - New Windows C regression tests for loopback format reporting and WGC resizing.
- Windows camera: destroying a context no longer frees the Media Foundation callback while MF still holds it, which crashed the process.
- Windows COM and Media Foundation are initialised once for the process instead
of per call. Dart isolates hop OS threads, so a per-call
CoUninitializecould tear down COM objects another isolate was using. - The WASAPI loopback capture thread joins the MTA explicitly.
- WGC stop/destroy waits for in-flight capture callbacks before freeing the context, and the lost callback may stop or destroy the context from inside it.
setLogCallbackdelivers through a Dart native port instead of aNativeCallable(new C exportsMiniAV_InitDartApi/MiniAV_SetLogPort), so a log line after the registering isolate exits no longer aborts the process. The callback is process-wide: the last isolate to set it receives the lines.stopCapture()on loopback, input and the device-change registry no longer closes aNativeCallablethe native side still holds after a timeout.hook/build.dartregisters theminiav_csources, so an edited.c/.hrebuilds the native library.code_assets,hooksandloggingare regular dependencies, so the native asset builds downstream.miniav_platform_interfaceis a caret range (^0.7.0) instead of an exact pin.
0.7.0 #
GPU buffer handoff contract (Windows) — leak fixes #
- The shared NT handle of a GPU frame is closed by miniav when the buffer is released (WGC, DXGI, MF camera); it leaked one kernel handle per frame. Import the handle before releasing the buffer, and do not close it yourself.
- Buffers queued to a
NativeCallable.listenerare released instead of dropped when capture stops; each dropped one leaked a texture and a handle. MiniAVBuffer.native_fenceis documented as never populated.
Mobile platform catch-up: Android and iOS backends for camera and screen capture. Mic capture uses the existing miniaudio module. No mobile input yet; Android loopback is deferred.
New backends #
- Android camera (Camera2 NDK, API 24+): CPU frames as NV12, NV21 or I420, and
an
AHARDWAREBUFFERGPU path on API 26+. - Android screen (MediaProjection, API 26+): the app passes a consented
MediaProjectionthrough the newMiniAV_Screen_SetAndroidMediaProjection; clearing it stops capture and fireslost_cb. RGBA CPU and AHardwareBuffer GPU paths. - iOS camera (AVFoundation): front/back naming, NV12-preferred formats and Metal
zero-copy textures. Returns
PERMISSION_DENIEDwithout prompting. - iOS screen (ReplayKit):
app_screencaptures the app, andsystem_screen_broadcastcaptures the whole system through a Broadcast Upload Extension (producer kitminiav_broadcast_senderplus a referenceSampleHandler), sharing frames through an App Group set withMiniAV_Screen_SetIOSAppGroup. - iOS mic: the
AVAudioSessionis activated around capture, so shared mic capture works unchanged.
API / infrastructure #
- New error code
MINIAV_ERROR_PERMISSION_DENIED (-23): miniAV never prompts, so apps request OS permissions first. Every code has an error string, andMiniAV_GetVersionString()reports the real version. - Dart bindings:
MINIAV_ERROR_PERMISSION_DENIEDadded (an unknown code used to throw), andMiniFFIScreenPlatform.setIOSAppGroup(String). - Android JNI plumbing, stricter platform gates for Android and iOS, CMake support for both, and a CI build matrix for Windows, Linux, macOS, Android and iOS.
- Flutter consent piece (in
miniav_flutter):requestScreenCapture(), starting the foreground service beforegetMediaProjectionas Android 14 requires.
Remote-desktop primitives (desktop output/control direction) #
- New
MiniAV_Inject_*module replays keyboard and mouse events on the local machine: WindowsSendInput, macOSCGEventPost(needs Accessibility), Linux/dev/uinput(X11 and Wayland). It takes the same event structs capture produces; missing permission isMINIAV_ERROR_PERMISSION_DENIED. No gamepad injection. MiniAV_Screen_SetCaptureCursor(ctx, bool)(before configure, off by default) draws the cursor on Windows WGC, macOS ScreenCaptureKit and Linux PipeWire. DXGI cannot, and logs a warning.MiniAVMouseEventgainswheel_delta_x(horizontal wheel) andis_absolute.- Dart:
MiniInject/MiniInjectContext,MiniScreen.setCaptureCursorand the new mouse fields; web reports injection as unsupported. - Linux absolute positioning works (separate relative-mouse and absolute-pointer devices), and macOS relative moves no longer use a stale cursor position.
0.6.0 #
- Camera timestamps are real microseconds on the shared monotonic clock on Windows, Linux and macOS (they were 100 ns units, nanoseconds and session time). macOS ScreenCaptureKit is rebased the same way.
- Linux loopback audio no longer leaks or hands out memory PipeWire reuses.
MiniAV_SetLogCallbackactually delivers; logs went to stderr only. The receiver owns each message and frees it withMiniAV_Free.- Device loss (
lost_cb) is reported by every backend: WGC, macOS ScreenCaptureKit, camera and loopback on Linux and macOS. An unplug or revoked permission used to be a silent stall. - macOS camera and screen teardown no longer races in-flight callbacks.
- macOS ScreenCaptureKit
StartCapturereports real failures (10 s timeout →MINIAV_ERROR_TIMEOUT) instead of succeeding with no frames. - Windows camera GPU path: no texture leak per frame, a flush before sharing, and correct clean-up of frames that fell back to CPU.
MiniAV_ReleaseBufferno longer leaks on invalid-context branches.- DXGI:
GetConfiguredFormatsworks again. lost_cbruns on internal threads: do not stop or destroy the context synchronously from inside it.- Added
miniav_c/NATIVE_AUDIT.md, the cross-platform audit and roadmap. - Shutdown is bounded on every platform: stop paths time out
(
MINIAV_ERROR_TIMEOUT) instead of hanging, and a thread that will not exit leaks its context (logged) rather than having it freed under it. - The callback-dispatch guard works on Linux and macOS, so
MiniAV_Disposewaits for in-flight callbacks (hot restart). - Mic input: stop/destroy after a device loss releases the device, and device loss fires once per run.
- Formats report what was negotiated: Windows and Linux camera, macOS and Linux loopback, and mic input read back the real format and list real device modes instead of hardcoded tables. Windows camera frame-rate matching is rational (30000/1001).
- Input capture on Linux (evdev) and macOS (CGEventTap + GameController). Windows input allows one active context at a time, uses monotonic timestamps and polls gamepads without drift.
- macOS camera GPU path delivers NV12/I420 as per-plane Metal textures, with a
signalled
MTLSharedEventinnative_fence. - macOS system-audio loopback works on stock macOS 13+ through ScreenCaptureKit (no BlackHole needed).
- macOS screen region capture.
- Clock conversions no longer overflow on long uptimes.
- The Media Foundation camera reports device loss after 30 consecutive read failures instead of retrying forever.
- The pre-share GPU wait on the DXGI/WGC zero-copy paths is bounded at 16 ms and logs when it times out.
- Not yet done (see
NATIVE_AUDIT.md): handingnative_fenceto the encoder, following the WASAPI default device, Windows screen region crop. - Review fixes across these changes: macOS start/stop races, Linux evdev diagonal moves, leak-on-timeout paths and format fallbacks.
0.5.11 #
- Windows screen capture (WGC + DXGI) paces frames on an absolute high-resolution schedule. A 30 fps target delivered ~31.4 fps (or ~21 fps without a raised timer resolution), which showed as regular stutter in recordings.
0.5.10 #
- DXGI screen capture raises the process's GPU scheduling priority and the capture device's GPU thread priority, so capture keeps its cadence when a game saturates the GPU. Failures are logged and capture continues.
0.5.9 #
releaseBufferSync()releases synchronously, andreleaseBuffer()delegates to it.- DXGI releases each duplication frame right after copying it, removing a frame of latency and the frame-rate cap it caused.
0.5.8 #
- fix audio buffer allocations and leak issue
0.5.7 #
- Fix logger noisiness
0.5.6 #
- Add
setLogCallbackandinstallStderrLogger, routing native log lines to Dart. - Fix a FormatException on non-UTF-8 log messages.
0.5.5 #
- fix wasapi loopback issue
0.5.4 #
- adds bindings observer lib to fix crash on hot restart
0.5.3 #
- Fix crash bug on hot refresh, fix crash on second use of recorder
0.5.2 #
- adds shared textures
0.5.1 #
- adds subscriptions and fixes lost device crashes
0.5.0 #
- adding input support
0.4.7 #
- fix loopback crackles
0.4.6 #
- fix build hook null
- update cmake toolchain
0.4.5 #
- fixed windows screen cpu path
0.4.4 #
0.4.3 #
0.4.1 #
- fix issue with num frames not being reported for audio_inputs
1.0.0 #
- Initial version.