AsciiCam/README.md
2026-06-19 13:07:18 +05:30

147 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AsciiCam
Real-time ASCII video from your webcam in the terminal - pure C99, no heavy runtime dependencies.
<img src="assets/demo.gif" width="325">
### Edge detection + threshold plugin
<img src="assets/demo-edgedetection.gif" width="325">
---
## Features
| Feature | Details |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **YUYV to grayscale** | SSE2 SIMD (`yuyv_to_gray_simd`) - 16 pixels per iteration on x86_64, NEON on ARM64 |
| **YUYV to RGB** | Fixedpoint BT.601 conversion for truecolor output |
| **Multiple render modes** | Braille (`⠿`), Blocks (`█ ░`), ASCII ramp, Halfblock, Dots - switch live with `m`/`M` |
| **Edge detection** | Sobel, Sobel with direction, Laplacian - toggle with `x`/`X` |
| **Hotreloadable charsets** | Load `.txt` ramps from a directory, switch with `n`/`N`, monitor with inotify |
| **Pseudo3D depth pop** | Parallax effect based on brightness - adjust with `+`/`-`/`v` |
| **FloydSteinberg dither** | Errordiffusion to reduce banding |
| **ANSI truecolor** | `\033[38;2;R;G;Bm` percell coloring |
| **Hotreload plugin system** | `inotify` + `dlopen` - rebuild a filter `.so`, it reloads live |
| **FPScapped render loop** | `CLOCK_MONOTONIC` + `nanosleep` frame pacing |
| **Producer/consumer threads** | Doublebuffered capture + render (stubbed in main loop, active in `thread_sharing.c`) |
| **Hardware camera controls** | V4L2 exposure, contrast, white-balance via `ioctl` — live keys `e`/`E`, `c`/`C`, `w`/`W` (Linux only; macOS/Windows display `n/a`) |
| **Crossplatform** | Linux (V4L2, nolibc), macOS (AVFoundation, system libc), Windows (Media Foundation) |
Linux: requires `gcc`, `linux/videodev2.h` (kernel headers), `libdl`, `libpthread`.
macOS: requires `Clang` and `AVFoundation` frameworks (linked automatically).
Windows: requires MinGW-w64; links `mfplat`, `mf`, `mfreadwrite`, `mfuuid`, `ole32` (Media Foundation). See Makefile `windows` branch.
No other external dependencies.
---
## Build
```bash
git clone https://github.com/Harshit-Dhanwalkar/AsciiCam.git
cd AsciiCam/C/
make
```
Requires: `gcc`, `linux/videodev2.h` (kernel headers), `libdl`, `libpthread`.
No other external dependencies.
```
build/webcam_ascii --help
```
---
## Run
```bash
# Basic (grayscale, 80×40, /dev/video0)
./build/webcam_ascii
# Truecolor output
./build/webcam_ascii -C
# With all three plugins
./build/webcam_ascii -p build/invert.so -p build/threshold.so -p build/edge_detect.so
# Edge detection mode, custom resolution
./build/webcam_ascii -e -w 320 -h 240 -W 120 -H 50
# Dithering + inverted charset
./build/webcam_ascii -D -i
```
---
## Plugin system
Plugins are shared objects (`.so`).
```bash
gcc -O2 -fPIC -shared -Iinclude filters/my_filter.c -o build/my_filter.so
./build/webcam_ascii -p build/my_filter.so
```
**Hot-reload:** the binary watches the `.so` with `inotify`. Recompile it while the viewer is running and it reloads automatically within $\approx$100 ms.
**Runtime controls:**
| Key | Action |
|---|---|
| `m` / `M` | cycle render mode forward / backward |
| `x` / `X` | cycle edge detection mode forward / backward |
| `n` / `N` | cycle loaded charset forward / backward |
| `p` / `o` | increase / decrease depth-pop strength |
| `e` / `E` | hw exposure down / up _(V4L2, Linux only)_ |
| `w` / `W` | hw white-balance down / up _(V4L2, Linux only)_ |
| `c` / `C` | hw contrast down / up _(V4L2, Linux only)_ |
| `↑` / `↓` | select plugin |
| `[` / `]` | param $\pm$1 |
| `{` / `}` | param $\pm$10 |
| `r` | reset param to 128 |
| `q` | quit |
---
## TODO
- [x] Adjustable capture resolution
- [x] Producer/consumer thread split (double-buffered)
- [x] Brightness / contrast adjustment
- [x] Invert brightness to charset mapping
- [x] ANSI truecolor output
- [x] Floyd-Steinberg dithering
- [x] Sobel edge detection
- [x] SIMD YUYV to grayscale (SSE2)
- [x] Hot-reload plugin system
- [x] nolibc - zero libc calls
- [x] Custom charset via config file
- [x] Hardware camera controls (V4L2 exposure / contrast / white-balance)
- [x] MacOS support
- [x] Windows support (Media Foundation capture backend)
- [ ] Windows console raw-mode and signal handling (`SetConsoleMode` / `SetConsoleCtrlHandler`)
- [ ] Hardware controls via `IAMCameraControl` / `IAMVideoProcAmp` (capture works, controls stubbed)
- [ ] macOS hardware controls via `AVCaptureDevice` exposure/white-balance APIs (capture works, controls stubbed)
- [ ] Cature frame resizing
- [ ] Record to `.mp4` / `.gif`
- [ ] Inter-frame delta compression
- [ ] LUT cache optimization
- [ ] Replace `pthread` with raw `futex` syscalls
- [ ] Replace `dlopen` with a minimal ELF loader
- [ ] Custom threading library using `clone()` + `futex` to eliminate `-lpthread` dependency
- [ ] Implement an ELF loader (or statically link plugins) to eliminate `-ldl` dependency
- [ ] Color support for MacOS
## Fixes
- [x] [Issue #2](https://github.com/Harshit-Dhanwalkar/AsciiCam/issues/2) MacOS support
- [x] Rewrite `capture.c` for MacOS port using [AVFoundation](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/04_MediaCapture.html).
- [x] `nl_calloc`: zero-fill loop was commented out (returned uninitialized memory) and the `SIZE_MAX` overflow guard ran _after_ the allocation, leaking the spurious block on overflow. Both fixed.
- [x] `nl_free`: backward coalescing was a `TODO` stub — non-LIFO frees (e.g. edge-detection scratch buffers) left permanently unmerged holes. Implemented by walking from arena start to find the preceding block.
- [x] `main.c` double-buffer bug: a second `out_buf` was `malloc`/`free`'d every frame inside the loop, shadowing the persistent pre-loop allocation. Frame-loop malloc removed; the single persistent buffer is reused for the program's lifetime.
- [x] `ascii.c` `RENDER_HALF_BLOCK` height rounding: `safe_dst_h` was rounded down to a multiple of 4 unconditionally, but half-block mode only needs multiples of 2 (it stacks 2 subpixel rows per glyph, not 4). Fixed to be mode-aware.
---
Project is under [PolyForm Noncommercial License BY-NC](LICENCE).
For commercial use contact *harshitpd1729@gmail.com*.