capture-helper 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- capture_helper-0.2.2/LICENSE +29 -0
- capture_helper-0.2.2/PKG-INFO +193 -0
- capture_helper-0.2.2/README.md +148 -0
- capture_helper-0.2.2/capture_helper/__init__.py +81 -0
- capture_helper-0.2.2/capture_helper/api.py +310 -0
- capture_helper-0.2.2/capture_helper/camera.py +326 -0
- capture_helper-0.2.2/capture_helper/cli_argparse.py +382 -0
- capture_helper-0.2.2/capture_helper/cli_click.py +271 -0
- capture_helper-0.2.2/capture_helper/mcp.py +90 -0
- capture_helper-0.2.2/capture_helper/mic.py +285 -0
- capture_helper-0.2.2/capture_helper/sources.py +402 -0
- capture_helper-0.2.2/capture_helper.egg-info/PKG-INFO +193 -0
- capture_helper-0.2.2/capture_helper.egg-info/SOURCES.txt +22 -0
- capture_helper-0.2.2/capture_helper.egg-info/dependency_links.txt +1 -0
- capture_helper-0.2.2/capture_helper.egg-info/entry_points.txt +4 -0
- capture_helper-0.2.2/capture_helper.egg-info/requires.txt +24 -0
- capture_helper-0.2.2/capture_helper.egg-info/top_level.txt +1 -0
- capture_helper-0.2.2/pyproject.toml +100 -0
- capture_helper-0.2.2/setup.cfg +4 -0
- capture_helper-0.2.2/tests/test_api.py +83 -0
- capture_helper-0.2.2/tests/test_cli.py +134 -0
- capture_helper-0.2.2/tests/test_mcp.py +39 -0
- capture_helper-0.2.2/tests/test_sources.py +88 -0
- capture_helper-0.2.2/tests/test_v01_features.py +213 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Warith HARCHAOUI
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: capture-helper
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Capture Helper โ OBS-inspired (no GUI) capture, processing, and publishing for the AI Helpers stack. Multi-surface: library + argparse CLI + click CLI + FastAPI HTTP surface + MCP tools over the INPUT layer (cameras / microphones).
|
|
5
|
+
Author-email: Warith HARCHAOUI <warithmetics@deraison.ai>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/warith-harchaoui/capture-helper
|
|
8
|
+
Project-URL: Issues, https://github.com/warith-harchaoui/capture-helper/issues
|
|
9
|
+
Keywords: capture,ffmpeg,camera,microphone,screen,obs,rtmp,hls
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Multimedia :: Video :: Capture
|
|
20
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Capture/Recording
|
|
21
|
+
Requires-Python: <3.14,>=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: os-helper>=1.4.2
|
|
25
|
+
Requires-Dist: numpy>=1.23
|
|
26
|
+
Provides-Extra: cli
|
|
27
|
+
Requires-Dist: click<9,>=8.1; extra == "cli"
|
|
28
|
+
Provides-Extra: api
|
|
29
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "api"
|
|
30
|
+
Requires-Dist: uvicorn<1,>=0.30; extra == "api"
|
|
31
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "api"
|
|
32
|
+
Provides-Extra: mcp
|
|
33
|
+
Requires-Dist: fastapi-mcp<1,>=0.3; extra == "mcp"
|
|
34
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "mcp"
|
|
35
|
+
Requires-Dist: uvicorn<1,>=0.30; extra == "mcp"
|
|
36
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "mcp"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
39
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
40
|
+
Requires-Dist: click<9,>=8.1; extra == "dev"
|
|
41
|
+
Requires-Dist: fastapi<1,>=0.111; extra == "dev"
|
|
42
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "dev"
|
|
43
|
+
Requires-Dist: fastapi-mcp<1,>=0.3; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# Capture Helper
|
|
47
|
+
|
|
48
|
+
[๐ซ๐ท](https://github.com/warith-harchaoui/capture-helper/blob/main/LISEZMOI.md) ยท [๐ฌ๐ง](https://github.com/warith-harchaoui/capture-helper/blob/main/README.md)
|
|
49
|
+
|
|
50
|
+
[](https://github.com/warith-harchaoui/capture-helper/actions/workflows/ci.yml) [](https://github.com/warith-harchaoui/capture-helper/blob/main/LICENSE) [](#)
|
|
51
|
+
|
|
52
|
+
`Capture Helper` belongs to a collection of libraries called `AI Helpers` developed for building Artificial Intelligence.
|
|
53
|
+
|
|
54
|
+
**OBS-inspired** (no GUI) capture + processing + publishing layer for the AI Helpers stack. Library-shaped: cross-platform camera / microphone / screen / window / application-audio sources, composable filter chains, multi-source mixing, and emit-to-publish primitives for live YouTube / Twitch RTMP, HLS, and Icecast โ designed to plug into [video-helper](https://github.com/warith-harchaoui/video-helper) and [podcast-helper](https://github.com/warith-harchaoui/podcast-helper) for downstream frame / PCM contracts.
|
|
55
|
+
|
|
56
|
+
[๐ AI Helpers](https://harchaoui.org/warith/ai-helpers)
|
|
57
|
+
|
|
58
|
+
[](https://harchaoui.org/warith/ai-helpers)
|
|
59
|
+
|
|
60
|
+
## Status โ v0.1.0 INPUT layer
|
|
61
|
+
|
|
62
|
+
What ships today:
|
|
63
|
+
|
|
64
|
+
- `SourceKind` literal (`"camera"` | `"microphone"`)
|
|
65
|
+
- `Source` typed dict (kind, name, index, platform, driver)
|
|
66
|
+
- `MicFrame` typed dict (mirrors [`podcast_helper.PcmFrame`](https://github.com/warith-harchaoui/podcast-helper))
|
|
67
|
+
- `list_sources(kind=None)` โ cross-platform device enumeration via `ffmpeg -list_devices` (macOS avfoundation / Windows dshow / Linux v4l2 + pulse)
|
|
68
|
+
- `pick_source(kind, *, name_substring=..., index=...)` โ pick the first matching device, raises `ValueError` if nothing matches
|
|
69
|
+
- `iter_camera_frames(source, *, width=..., height=..., output_width=..., output_height=..., fps=..., max_frames=...)` โ yields **`(H, W, 3)` BGR uint8 numpy arrays**, same contract as `video_helper.extract_frames`
|
|
70
|
+
- `iter_mic_audio(source, *, target_sample_rate=16000, to_mono=True, frame_ms=20)` โ async iterator yielding `MicFrame`s, same contract as `podcast_helper.extract_audio_stream`
|
|
71
|
+
- `ffmpeg_input_args(source)` โ exposed low-level helper for users wiring their own ffmpeg pipelines
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import asyncio
|
|
75
|
+
import capture_helper as ch
|
|
76
|
+
|
|
77
|
+
# Enumerate available devices
|
|
78
|
+
for s in ch.list_sources():
|
|
79
|
+
print(f"{s['kind']:10s} [{s['index']}] {s['name']:40s} (driver={s['driver']})")
|
|
80
|
+
# camera [0] FaceTime HD Camera (driver=avfoundation)
|
|
81
|
+
# microphone [0] Built-in Microphone (driver=avfoundation)
|
|
82
|
+
|
|
83
|
+
# Camera โ numpy BGR frames (drop-in for video_helper.extract_frames)
|
|
84
|
+
cam = ch.pick_source("camera")
|
|
85
|
+
for frame in ch.iter_camera_frames(cam, output_width=640, output_height=360,
|
|
86
|
+
fps=30, max_frames=300):
|
|
87
|
+
# frame.shape == (360, 640, 3), dtype uint8, BGR.
|
|
88
|
+
do_something(frame)
|
|
89
|
+
|
|
90
|
+
# Microphone โ async PCM stream (drop-in for podcast_helper.extract_audio_stream)
|
|
91
|
+
async def listen():
|
|
92
|
+
mic = ch.pick_source("microphone")
|
|
93
|
+
async for f in ch.iter_mic_audio(mic, target_sample_rate=16000,
|
|
94
|
+
to_mono=True, frame_ms=20):
|
|
95
|
+
# f["pcm"].shape == (320,) โ 20ms @ 16kHz mono.
|
|
96
|
+
await asr.feed(f["pcm"])
|
|
97
|
+
asyncio.run(listen())
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Roadmap
|
|
101
|
+
|
|
102
|
+
| Version | Layer | Scope |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| v0.0.1 | INPUT scaffold | `list_sources` + types |
|
|
105
|
+
| **v0.1.0** (this release) | INPUT | `pick_source(...)` + `iter_camera_frames(source, ...)` + `iter_mic_audio(source, ...)` โ composes with video-helper / podcast-helper contracts |
|
|
106
|
+
| **v0.2.0** | INPUT extended | Screen / window capture; basic filter chain (noise gate, gain, scale) |
|
|
107
|
+
| **v0.3.0** | PROCESS | Scenes / mixer โ `mix_audio([sources], levels=[...])` + `compose_video([sources], layout=...)` |
|
|
108
|
+
| **v0.4.0** | PUBLISH | `emit_to_youtube_live(...)`, `emit_to_twitch_live(...)`, `emit_to_rtmp(...)`, `emit_to_hls(...)`, `emit_audio_to_icecast(...)` |
|
|
109
|
+
| **v0.5.0** | OUTPUT virtual | `output_to_virtual_camera(...)` (pyvirtualcam etc.), `output_to_virtual_mic(...)` |
|
|
110
|
+
| **v0.6.0** | OBS integration | OBS WebSocket client (react to scene / stream events) |
|
|
111
|
+
|
|
112
|
+
For a full cookbook (per-OS ffmpeg input strings, snapshot capture, live preview, ASR / VAD wiring), see [๐ EXAMPLES.md](https://github.com/warith-harchaoui/capture-helper/blob/main/EXAMPLES.md).
|
|
113
|
+
|
|
114
|
+
## Multi-surface exposure
|
|
115
|
+
|
|
116
|
+
`capture-helper` ships the same INPUT layer through **five surfaces**
|
|
117
|
+
so it plugs in wherever you already work โ no rewrite needed.
|
|
118
|
+
|
|
119
|
+
| Surface | Install | Entry point | Use case |
|
|
120
|
+
| --- | --- | --- | --- |
|
|
121
|
+
| **Python library** | `pip install โฆ@v0.2.0` | `import capture_helper as ch` | Notebooks, scripts, other AI Helpers |
|
|
122
|
+
| **argparse CLI** | *(no extra)* | `capture-helper โฆ` | Shells, cron, CI, container CMD |
|
|
123
|
+
| **click CLI** | `[cli]` extra | `capture-helper-click โฆ` | Users on a click-native stack (completion, colored `--help`) |
|
|
124
|
+
| **FastAPI HTTP** | `[api]` extra | `uvicorn capture_helper.api:app` | Reverse-proxied service, JSON / multipart clients |
|
|
125
|
+
| **MCP tools** | `[api,mcp]` extras | `capture-helper-mcp` | LLM agents (Claude Desktop, custom MCP clients) |
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# CLI (argparse โ always available)
|
|
129
|
+
capture-helper list-sources
|
|
130
|
+
capture-helper pick-source --kind camera --name FaceTime
|
|
131
|
+
capture-helper capture-mic --output mic.wav --seconds 3
|
|
132
|
+
|
|
133
|
+
# CLI (click twin โ same subcommands)
|
|
134
|
+
capture-helper-click list-sources
|
|
135
|
+
capture-helper-click capture-camera --output-dir frames/ \
|
|
136
|
+
--output-width 640 --output-height 360 --max-frames 30
|
|
137
|
+
|
|
138
|
+
# HTTP surface
|
|
139
|
+
uvicorn capture_helper.api:app --host 0.0.0.0 --port 8000
|
|
140
|
+
curl http://localhost:8000/sources
|
|
141
|
+
curl -o frames.zip \
|
|
142
|
+
'http://localhost:8000/capture/camera?output_width=320&output_height=240&max_frames=10'
|
|
143
|
+
|
|
144
|
+
# MCP surface (FastAPI + fastapi-mcp)
|
|
145
|
+
capture-helper-mcp # serves HTTP routes + MCP endpoint on :8000
|
|
146
|
+
|
|
147
|
+
# Docker (ships FastAPI + MCP by default)
|
|
148
|
+
docker build -t capture-helper .
|
|
149
|
+
docker run --rm -p 8000:8000 capture-helper
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For a GUI vision (device wall + PGM/PVW cueing, not a CLI mirror),
|
|
153
|
+
see [๐ GUI.md](https://github.com/warith-harchaoui/capture-helper/blob/main/GUI.md). For a competitive comparison against
|
|
154
|
+
OpenCV / PyAV / sounddevice / OBS / FFmpeg CLI / GStreamer, see
|
|
155
|
+
[๐ LANDSCAPE.md](https://github.com/warith-harchaoui/capture-helper/blob/main/LANDSCAPE.md).
|
|
156
|
+
|
|
157
|
+
## Installation
|
|
158
|
+
|
|
159
|
+
**Prerequisites** โ **Python 3.10โ3.13** and **git**, **ffmpeg**, **PortAudio**, cross-platform:
|
|
160
|
+
|
|
161
|
+
- ๐ **macOS** ([Homebrew](https://brew.sh)): `brew install python git ffmpeg portaudio`
|
|
162
|
+
- ๐ง **Ubuntu/Debian**: `sudo apt update && sudo apt install -y python3 python3-pip git ffmpeg portaudio19-dev`
|
|
163
|
+
- ๐ช **Windows** (PowerShell): `winget install Python.Python.3.12 Git.Git Gyan.FFmpeg` (PortAudio ships inside the Python wheels)
|
|
164
|
+
|
|
165
|
+
Then install the package:
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pip install --force-reinstall --no-cache-dir \
|
|
170
|
+
git+https://github.com/warith-harchaoui/capture-helper.git@v0.2.2
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Optional extras (pick what you need):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pip install 'capture-helper[cli] @ git+โฆ@v0.2.0' # click CLI
|
|
177
|
+
pip install 'capture-helper[api] @ git+โฆ@v0.2.0' # FastAPI HTTP
|
|
178
|
+
pip install 'capture-helper[api,mcp] @ git+โฆ@v0.2.0' # MCP tools
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
You still need `ffmpeg` on PATH for device enumeration and live capture to return anything:
|
|
182
|
+
|
|
183
|
+
- macOS ๐ : `brew install ffmpeg`
|
|
184
|
+
|
|
185
|
+
(install `brew` thanks to [brew.sh](https://brew.sh/))
|
|
186
|
+
- Ubuntu ๐ง : `sudo apt install ffmpeg`
|
|
187
|
+
- Windows ๐ช : grab a build from [ffmpeg.org/download.html](https://ffmpeg.org/download.html) and add it to `PATH`.
|
|
188
|
+
|
|
189
|
+
# Author
|
|
190
|
+
- [Warith HARCHAOUI](https://linkedin.com/in/warith-harchaoui)
|
|
191
|
+
|
|
192
|
+
# Acknowledgements
|
|
193
|
+
Special thanks to [Mohamed Chelali](https://mchelali.github.io) and [Bachir Zerroug](https://www.linkedin.com/in/bachirzerroug) for fruitful discussions.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Capture Helper
|
|
2
|
+
|
|
3
|
+
[๐ซ๐ท](https://github.com/warith-harchaoui/capture-helper/blob/main/LISEZMOI.md) ยท [๐ฌ๐ง](https://github.com/warith-harchaoui/capture-helper/blob/main/README.md)
|
|
4
|
+
|
|
5
|
+
[](https://github.com/warith-harchaoui/capture-helper/actions/workflows/ci.yml) [](https://github.com/warith-harchaoui/capture-helper/blob/main/LICENSE) [](#)
|
|
6
|
+
|
|
7
|
+
`Capture Helper` belongs to a collection of libraries called `AI Helpers` developed for building Artificial Intelligence.
|
|
8
|
+
|
|
9
|
+
**OBS-inspired** (no GUI) capture + processing + publishing layer for the AI Helpers stack. Library-shaped: cross-platform camera / microphone / screen / window / application-audio sources, composable filter chains, multi-source mixing, and emit-to-publish primitives for live YouTube / Twitch RTMP, HLS, and Icecast โ designed to plug into [video-helper](https://github.com/warith-harchaoui/video-helper) and [podcast-helper](https://github.com/warith-harchaoui/podcast-helper) for downstream frame / PCM contracts.
|
|
10
|
+
|
|
11
|
+
[๐ AI Helpers](https://harchaoui.org/warith/ai-helpers)
|
|
12
|
+
|
|
13
|
+
[](https://harchaoui.org/warith/ai-helpers)
|
|
14
|
+
|
|
15
|
+
## Status โ v0.1.0 INPUT layer
|
|
16
|
+
|
|
17
|
+
What ships today:
|
|
18
|
+
|
|
19
|
+
- `SourceKind` literal (`"camera"` | `"microphone"`)
|
|
20
|
+
- `Source` typed dict (kind, name, index, platform, driver)
|
|
21
|
+
- `MicFrame` typed dict (mirrors [`podcast_helper.PcmFrame`](https://github.com/warith-harchaoui/podcast-helper))
|
|
22
|
+
- `list_sources(kind=None)` โ cross-platform device enumeration via `ffmpeg -list_devices` (macOS avfoundation / Windows dshow / Linux v4l2 + pulse)
|
|
23
|
+
- `pick_source(kind, *, name_substring=..., index=...)` โ pick the first matching device, raises `ValueError` if nothing matches
|
|
24
|
+
- `iter_camera_frames(source, *, width=..., height=..., output_width=..., output_height=..., fps=..., max_frames=...)` โ yields **`(H, W, 3)` BGR uint8 numpy arrays**, same contract as `video_helper.extract_frames`
|
|
25
|
+
- `iter_mic_audio(source, *, target_sample_rate=16000, to_mono=True, frame_ms=20)` โ async iterator yielding `MicFrame`s, same contract as `podcast_helper.extract_audio_stream`
|
|
26
|
+
- `ffmpeg_input_args(source)` โ exposed low-level helper for users wiring their own ffmpeg pipelines
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import asyncio
|
|
30
|
+
import capture_helper as ch
|
|
31
|
+
|
|
32
|
+
# Enumerate available devices
|
|
33
|
+
for s in ch.list_sources():
|
|
34
|
+
print(f"{s['kind']:10s} [{s['index']}] {s['name']:40s} (driver={s['driver']})")
|
|
35
|
+
# camera [0] FaceTime HD Camera (driver=avfoundation)
|
|
36
|
+
# microphone [0] Built-in Microphone (driver=avfoundation)
|
|
37
|
+
|
|
38
|
+
# Camera โ numpy BGR frames (drop-in for video_helper.extract_frames)
|
|
39
|
+
cam = ch.pick_source("camera")
|
|
40
|
+
for frame in ch.iter_camera_frames(cam, output_width=640, output_height=360,
|
|
41
|
+
fps=30, max_frames=300):
|
|
42
|
+
# frame.shape == (360, 640, 3), dtype uint8, BGR.
|
|
43
|
+
do_something(frame)
|
|
44
|
+
|
|
45
|
+
# Microphone โ async PCM stream (drop-in for podcast_helper.extract_audio_stream)
|
|
46
|
+
async def listen():
|
|
47
|
+
mic = ch.pick_source("microphone")
|
|
48
|
+
async for f in ch.iter_mic_audio(mic, target_sample_rate=16000,
|
|
49
|
+
to_mono=True, frame_ms=20):
|
|
50
|
+
# f["pcm"].shape == (320,) โ 20ms @ 16kHz mono.
|
|
51
|
+
await asr.feed(f["pcm"])
|
|
52
|
+
asyncio.run(listen())
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Roadmap
|
|
56
|
+
|
|
57
|
+
| Version | Layer | Scope |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| v0.0.1 | INPUT scaffold | `list_sources` + types |
|
|
60
|
+
| **v0.1.0** (this release) | INPUT | `pick_source(...)` + `iter_camera_frames(source, ...)` + `iter_mic_audio(source, ...)` โ composes with video-helper / podcast-helper contracts |
|
|
61
|
+
| **v0.2.0** | INPUT extended | Screen / window capture; basic filter chain (noise gate, gain, scale) |
|
|
62
|
+
| **v0.3.0** | PROCESS | Scenes / mixer โ `mix_audio([sources], levels=[...])` + `compose_video([sources], layout=...)` |
|
|
63
|
+
| **v0.4.0** | PUBLISH | `emit_to_youtube_live(...)`, `emit_to_twitch_live(...)`, `emit_to_rtmp(...)`, `emit_to_hls(...)`, `emit_audio_to_icecast(...)` |
|
|
64
|
+
| **v0.5.0** | OUTPUT virtual | `output_to_virtual_camera(...)` (pyvirtualcam etc.), `output_to_virtual_mic(...)` |
|
|
65
|
+
| **v0.6.0** | OBS integration | OBS WebSocket client (react to scene / stream events) |
|
|
66
|
+
|
|
67
|
+
For a full cookbook (per-OS ffmpeg input strings, snapshot capture, live preview, ASR / VAD wiring), see [๐ EXAMPLES.md](https://github.com/warith-harchaoui/capture-helper/blob/main/EXAMPLES.md).
|
|
68
|
+
|
|
69
|
+
## Multi-surface exposure
|
|
70
|
+
|
|
71
|
+
`capture-helper` ships the same INPUT layer through **five surfaces**
|
|
72
|
+
so it plugs in wherever you already work โ no rewrite needed.
|
|
73
|
+
|
|
74
|
+
| Surface | Install | Entry point | Use case |
|
|
75
|
+
| --- | --- | --- | --- |
|
|
76
|
+
| **Python library** | `pip install โฆ@v0.2.0` | `import capture_helper as ch` | Notebooks, scripts, other AI Helpers |
|
|
77
|
+
| **argparse CLI** | *(no extra)* | `capture-helper โฆ` | Shells, cron, CI, container CMD |
|
|
78
|
+
| **click CLI** | `[cli]` extra | `capture-helper-click โฆ` | Users on a click-native stack (completion, colored `--help`) |
|
|
79
|
+
| **FastAPI HTTP** | `[api]` extra | `uvicorn capture_helper.api:app` | Reverse-proxied service, JSON / multipart clients |
|
|
80
|
+
| **MCP tools** | `[api,mcp]` extras | `capture-helper-mcp` | LLM agents (Claude Desktop, custom MCP clients) |
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# CLI (argparse โ always available)
|
|
84
|
+
capture-helper list-sources
|
|
85
|
+
capture-helper pick-source --kind camera --name FaceTime
|
|
86
|
+
capture-helper capture-mic --output mic.wav --seconds 3
|
|
87
|
+
|
|
88
|
+
# CLI (click twin โ same subcommands)
|
|
89
|
+
capture-helper-click list-sources
|
|
90
|
+
capture-helper-click capture-camera --output-dir frames/ \
|
|
91
|
+
--output-width 640 --output-height 360 --max-frames 30
|
|
92
|
+
|
|
93
|
+
# HTTP surface
|
|
94
|
+
uvicorn capture_helper.api:app --host 0.0.0.0 --port 8000
|
|
95
|
+
curl http://localhost:8000/sources
|
|
96
|
+
curl -o frames.zip \
|
|
97
|
+
'http://localhost:8000/capture/camera?output_width=320&output_height=240&max_frames=10'
|
|
98
|
+
|
|
99
|
+
# MCP surface (FastAPI + fastapi-mcp)
|
|
100
|
+
capture-helper-mcp # serves HTTP routes + MCP endpoint on :8000
|
|
101
|
+
|
|
102
|
+
# Docker (ships FastAPI + MCP by default)
|
|
103
|
+
docker build -t capture-helper .
|
|
104
|
+
docker run --rm -p 8000:8000 capture-helper
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For a GUI vision (device wall + PGM/PVW cueing, not a CLI mirror),
|
|
108
|
+
see [๐ GUI.md](https://github.com/warith-harchaoui/capture-helper/blob/main/GUI.md). For a competitive comparison against
|
|
109
|
+
OpenCV / PyAV / sounddevice / OBS / FFmpeg CLI / GStreamer, see
|
|
110
|
+
[๐ LANDSCAPE.md](https://github.com/warith-harchaoui/capture-helper/blob/main/LANDSCAPE.md).
|
|
111
|
+
|
|
112
|
+
## Installation
|
|
113
|
+
|
|
114
|
+
**Prerequisites** โ **Python 3.10โ3.13** and **git**, **ffmpeg**, **PortAudio**, cross-platform:
|
|
115
|
+
|
|
116
|
+
- ๐ **macOS** ([Homebrew](https://brew.sh)): `brew install python git ffmpeg portaudio`
|
|
117
|
+
- ๐ง **Ubuntu/Debian**: `sudo apt update && sudo apt install -y python3 python3-pip git ffmpeg portaudio19-dev`
|
|
118
|
+
- ๐ช **Windows** (PowerShell): `winget install Python.Python.3.12 Git.Git Gyan.FFmpeg` (PortAudio ships inside the Python wheels)
|
|
119
|
+
|
|
120
|
+
Then install the package:
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install --force-reinstall --no-cache-dir \
|
|
125
|
+
git+https://github.com/warith-harchaoui/capture-helper.git@v0.2.2
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Optional extras (pick what you need):
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pip install 'capture-helper[cli] @ git+โฆ@v0.2.0' # click CLI
|
|
132
|
+
pip install 'capture-helper[api] @ git+โฆ@v0.2.0' # FastAPI HTTP
|
|
133
|
+
pip install 'capture-helper[api,mcp] @ git+โฆ@v0.2.0' # MCP tools
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
You still need `ffmpeg` on PATH for device enumeration and live capture to return anything:
|
|
137
|
+
|
|
138
|
+
- macOS ๐ : `brew install ffmpeg`
|
|
139
|
+
|
|
140
|
+
(install `brew` thanks to [brew.sh](https://brew.sh/))
|
|
141
|
+
- Ubuntu ๐ง : `sudo apt install ffmpeg`
|
|
142
|
+
- Windows ๐ช : grab a build from [ffmpeg.org/download.html](https://ffmpeg.org/download.html) and add it to `PATH`.
|
|
143
|
+
|
|
144
|
+
# Author
|
|
145
|
+
- [Warith HARCHAOUI](https://linkedin.com/in/warith-harchaoui)
|
|
146
|
+
|
|
147
|
+
# Acknowledgements
|
|
148
|
+
Special thanks to [Mohamed Chelali](https://mchelali.github.io) and [Bachir Zerroug](https://www.linkedin.com/in/bachirzerroug) for fruitful discussions.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Capture Helper
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
OBS-inspired (no GUI) **capture + process + publish** library for the
|
|
6
|
+
AI Helpers stack. v0.1.0 ships the **INPUT layer**: cross-platform
|
|
7
|
+
device enumeration + selection (cameras / microphones) and a pair of
|
|
8
|
+
iterators that bridge those devices to the rest of the suite's
|
|
9
|
+
contracts.
|
|
10
|
+
|
|
11
|
+
What ships in v0.1.0
|
|
12
|
+
--------------------
|
|
13
|
+
- :class:`SourceKind` โ literal ``"camera"`` | ``"microphone"``.
|
|
14
|
+
- :class:`Source` โ typed dict describing one device.
|
|
15
|
+
- :class:`MicFrame` โ typed dict for one PCM frame (mirrors
|
|
16
|
+
:class:`podcast_helper.streaming.PcmFrame`).
|
|
17
|
+
- :func:`list_sources` โ best-effort cross-platform device enumeration
|
|
18
|
+
via ``ffmpeg -list_devices`` (avfoundation / v4l2 / dshow / pulse /
|
|
19
|
+
alsa). Returns ``[]`` rather than raising on unsupported platforms.
|
|
20
|
+
- :func:`pick_source` โ pick the first device of a given kind matching
|
|
21
|
+
optional ``name_substring`` / ``index`` filters.
|
|
22
|
+
- :func:`iter_camera_frames` โ synchronous generator yielding
|
|
23
|
+
``(H, W, 3)`` BGR uint8 numpy arrays โ same shape and dtype as
|
|
24
|
+
:func:`video_helper.extract_frames`.
|
|
25
|
+
- :func:`iter_mic_audio` โ async generator yielding
|
|
26
|
+
:class:`MicFrame` โ same shape as
|
|
27
|
+
:func:`podcast_helper.extract_audio_stream`.
|
|
28
|
+
|
|
29
|
+
What lands next
|
|
30
|
+
---------------
|
|
31
|
+
See :doc:`README` roadmap. v0.2.0 brings screen / window capture and a
|
|
32
|
+
basic filter chain; v0.3.0 brings multi-source mixing; v0.4.0 brings
|
|
33
|
+
RTMP / HLS / Icecast publish.
|
|
34
|
+
|
|
35
|
+
Usage example
|
|
36
|
+
-------------
|
|
37
|
+
>>> import asyncio, capture_helper as ch
|
|
38
|
+
>>> cam = ch.pick_source("camera")
|
|
39
|
+
>>> for frame in ch.iter_camera_frames(cam, output_width=640, output_height=360,
|
|
40
|
+
... fps=30, max_frames=300):
|
|
41
|
+
... # frame.shape == (360, 640, 3), dtype uint8, BGR.
|
|
42
|
+
... do_something(frame)
|
|
43
|
+
>>>
|
|
44
|
+
>>> async def listen():
|
|
45
|
+
... mic = ch.pick_source("microphone")
|
|
46
|
+
... async for f in ch.iter_mic_audio(mic, target_sample_rate=16000, frame_ms=20):
|
|
47
|
+
... # f["pcm"].shape == (320,) โ 20ms @ 16kHz mono.
|
|
48
|
+
... await asr.feed(f["pcm"])
|
|
49
|
+
>>> asyncio.run(listen())
|
|
50
|
+
|
|
51
|
+
Author
|
|
52
|
+
------
|
|
53
|
+
Warith Harchaoui, Ph.D. โ https://linkedin.com/in/warith-harchaoui/
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# Package-level attribution โ kept here so tooling that reads
|
|
57
|
+
# ``capture_helper.__author__`` gets the canonical value. Email lives
|
|
58
|
+
# ONLY here (never in module-level docstrings) to keep source files
|
|
59
|
+
# scrapeable without leaking the mailbox to search engines.
|
|
60
|
+
__author__ = "Warith Harchaoui, Ph.D."
|
|
61
|
+
__email__ = "warithmetics@deraison.ai"
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
# types
|
|
65
|
+
"SourceKind",
|
|
66
|
+
"Source",
|
|
67
|
+
"MicFrame",
|
|
68
|
+
# device enumeration + selection
|
|
69
|
+
"list_sources",
|
|
70
|
+
"pick_source",
|
|
71
|
+
# low-level helper exposed because some callers want to splice the
|
|
72
|
+
# per-OS ffmpeg input args into their own pipelines.
|
|
73
|
+
"ffmpeg_input_args",
|
|
74
|
+
# live capture iterators
|
|
75
|
+
"iter_camera_frames",
|
|
76
|
+
"iter_mic_audio",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
from .camera import iter_camera_frames
|
|
80
|
+
from .mic import MicFrame, iter_mic_audio
|
|
81
|
+
from .sources import Source, SourceKind, ffmpeg_input_args, list_sources, pick_source
|