Windows-native capture/replay module with Python bindings
Project description
memoir-capture
Windows-native screen capture module with Python bindings for real-time frame analysis and deterministic replay recording.
Memoir captures frames from a window or monitor using Windows Graphics Capture (WGC), delivers them to Python as NumPy arrays, and optionally records them to HEVC video with per-frame metadata — all without GPU-to-CPU roundtrips in the recording path.
Features
- WGC capture — continuous frame capture from any window or monitor
- NumPy delivery — BGRA frames as
(H, W, 4)uint8 arrays via bounded queue - NVENC recording — GPU-accelerated HEVC encoding via FFmpeg, lossless YUV444 by default
- Binary metadata —
.metasidecar with per-frame keyboard state, timestamps, and frame IDs - Dynamic recording — start/stop recording without restarting capture
- Frame-accurate keyboard — key state snapshot at the exact moment each frame is accepted
- Typed Python API — full type annotations, dataclasses, context managers
Requirements
- Windows 10 1903+ (for WGC
CreateFreeThreaded) - NVIDIA GPU with NVENC support
- Python 3.10+
- Visual Studio 2022 (for building from source)
Installation
From PyPI (prebuilt)
pip install memoir-capture
From source
# Clone with vcpkg
git clone https://github.com/lunavod/memoir-capture.git
cd Memoir
git clone https://github.com/microsoft/vcpkg.git vcpkg --depth 1
.\vcpkg\bootstrap-vcpkg.bat -disableMetrics
# Build
pip install build numpy
python -m build --wheel
# Install
pip install dist\memoir_capture-*.whl
The first build takes ~15 minutes (vcpkg builds FFmpeg). Subsequent builds use cached binaries.
For local development without installing:
.\scripts\build.ps1
# memoir_capture/ package is ready to import from the project root
Quick Start
Capture frames
import memoir_capture
engine = memoir_capture.CaptureEngine(
memoir_capture.MonitorTarget(0), # primary monitor
max_fps=10.0,
)
engine.start()
for packet in engine.frames():
with packet:
img = packet.cpu_bgra # numpy (H, W, 4) uint8
print(f"Frame {packet.frame_id}: {img.shape}")
print(f"Keys: 0x{packet.keyboard_mask:016x}")
break
engine.stop()
Capture a specific window
engine = memoir_capture.CaptureEngine(
memoir_capture.WindowTitleTarget(r"(?i)notepad"),
max_fps=30.0,
)
Record to MP4
engine = memoir_capture.CaptureEngine(
memoir_capture.MonitorTarget(0),
max_fps=10.0,
record_width=1920,
record_height=1080,
record_gop=1, # 1 = all-intra (frame-accurate seeking)
)
engine.start()
info = engine.start_recording("session_001")
print(f"Recording to {info.video_path}") # session_001.mp4
for i, packet in enumerate(engine.frames()):
packet.release()
if i >= 99:
break
engine.stop_recording() # finalizes .mp4 + .meta
engine.stop()
Read metadata
meta = memoir_capture.MetaReader.read("session_001.meta")
print(f"Keys tracked: {[k.name for k in meta.keys]}")
for row in meta.rows:
print(f"Frame {row.frame_id}: keyboard=0x{row.keyboard_mask:016x}")
Write metadata (for synthetic replays)
from memoir_capture import MetaWriter, MetaKeyEntry, MetaRow
keys = [MetaKeyEntry(0, 0x57, "W"), MetaKeyEntry(1, 0x41, "A")]
with MetaWriter("synthetic.meta", keys) as w:
w.write_row(MetaRow(
frame_id=0, record_frame_index=0,
capture_qpc=0, host_accept_qpc=0,
keyboard_mask=0b01,
width=1920, height=1080, analysis_stride=7680,
))
Context manager
with memoir_capture.CaptureEngine(memoir_capture.MonitorTarget(0)) as engine:
packet = engine.get_next_frame(timeout_ms=2000)
if packet:
with packet:
process(packet.cpu_bgra)
API Reference
CaptureEngine(target, *, max_fps, ...)
| Parameter | Default | Description |
|---|---|---|
target |
required | MonitorTarget(index), WindowTitleTarget(regex), or WindowExeTarget(regex) |
max_fps |
10.0 |
Maximum accepted frame rate |
analysis_queue_capacity |
1 |
Bounded queue size for Python delivery |
capture_cursor |
False |
Include cursor in capture |
record_width |
1920 |
Recording output width |
record_height |
1080 |
Recording output height |
record_gop |
1 |
GOP size (1 = all-intra, higher = smaller files) |
Methods: start(), stop(), get_next_frame(timeout_ms), frames(), start_recording(base_path), stop_recording(), is_recording(), stats()
FramePacket
| Property | Type | Description |
|---|---|---|
frame_id |
int |
Monotonic ID (only accepted frames get IDs) |
cpu_bgra |
np.ndarray |
(H, W, 4) uint8 BGRA pixel data |
keyboard_mask |
int |
64-bit bitmask of tracked keys |
capture_qpc |
int |
WGC capture timestamp (100ns units) |
width, height, stride |
int |
Frame dimensions |
Supports with packet: (auto-release) and explicit packet.release().
Recording Quality
Default encoding: lossless HEVC (QP=0), YUV 4:4:4, full color range.
| Setting | File size (10s, 1080p) |
|---|---|
| GOP=1 (all-intra) | ~52 MB |
| GOP=10 | ~17 MB |
| GOP=30 | ~14 MB |
Architecture
WGC FrameArrived (thread pool)
│
├─ FPS limiter → drop if too soon
├─ Queue check → drop if full (drop-new policy)
│
├─ Accept: assign frame_id, snapshot keyboard
├─ GPU→CPU: CopyResource → staging → Map → memcpy
├─ Recording: swscale (BGRA→YUV444) → hevc_nvenc → MP4
└─ Enqueue → Python consumer
- Single
ID3D11DeviceContext+ mutex for all GPU ops - WGC
CreateFreeThreaded— no DispatcherQueue needed - Recording runs on the callback thread (well within budget at 10fps)
FramePacketowns its pixel buffer;release()frees it
Testing
# Full suite (requires display + NVIDIA GPU)
pytest -v
# Headless (CI-safe)
pytest --headless -v
License
MIT. Links against FFmpeg (LGPL 2.1+).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
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 memoir_capture-0.1.4-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: memoir_capture-0.1.4-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 9.6 MB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17ad5e3dc98aca2f74abb63e11a1944d283c434312963cb73babcff82dbc9b7a
|
|
| MD5 |
45f30d6fc03e3c02ca8e87338eb84f83
|
|
| BLAKE2b-256 |
d30b666a208ada9d2f8641e544e02eed720df5a4c7dab8fa4ca903d1c9837295
|
Provenance
The following attestation bundles were made for memoir_capture-0.1.4-cp313-cp313-win_amd64.whl:
Publisher:
build.yml on lunavod/memoir-capture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memoir_capture-0.1.4-cp313-cp313-win_amd64.whl -
Subject digest:
17ad5e3dc98aca2f74abb63e11a1944d283c434312963cb73babcff82dbc9b7a - Sigstore transparency entry: 1167208720
- Sigstore integration time:
-
Permalink:
lunavod/memoir-capture@02d1a46addbe6bd8536d721fb82d936a4ce5525a -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/lunavod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@02d1a46addbe6bd8536d721fb82d936a4ce5525a -
Trigger Event:
push
-
Statement type:
File details
Details for the file memoir_capture-0.1.4-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: memoir_capture-0.1.4-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 9.6 MB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35058c5088a82959dd23b53f06d6837663443abddd3fe7033d24f45843806cf2
|
|
| MD5 |
1b0b280eecd1c55f5d0825c270ce8889
|
|
| BLAKE2b-256 |
eaf89adc5f75c0db47990ac5e2b0c6a52fd7a0c113464edfa3bc73410e85698f
|
Provenance
The following attestation bundles were made for memoir_capture-0.1.4-cp312-cp312-win_amd64.whl:
Publisher:
build.yml on lunavod/memoir-capture
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
memoir_capture-0.1.4-cp312-cp312-win_amd64.whl -
Subject digest:
35058c5088a82959dd23b53f06d6837663443abddd3fe7033d24f45843806cf2 - Sigstore transparency entry: 1167208634
- Sigstore integration time:
-
Permalink:
lunavod/memoir-capture@02d1a46addbe6bd8536d721fb82d936a4ce5525a -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/lunavod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@02d1a46addbe6bd8536d721fb82d936a4ce5525a -
Trigger Event:
push
-
Statement type: