Skip to content

Latest commit

 

History

History
178 lines (147 loc) · 11.1 KB

CODEMAP.md

File metadata and controls

178 lines (147 loc) · 11.1 KB

Deckboy Code Map

A structural map of the codebase: where things live, how data flows, and which threads touch what. Companion documents: CLAUDE.md (AI session quick-reference), private-notes/DEVNOTES.md (architectural decision log) and private-notes/AUDIT_ROADMAP.md (cleanup task map), both kept out of the public repository.

Line counts are approximate and drift between releases; re-measure rather than trusting them.


Top-level layout

deckboy/
├── VERSION              # single source of truth for the app version
├── CHANGES.md           # user-facing changelog (dated entry per version)
├── DEVNOTES.md          # internal architectural decision log
├── MANUAL.md            # operator manual
├── PORTABILITY.md       # cross-platform notes and env vars
├── CLAUDE.md            # AI session quick-reference
├── docs/                # specs, roadmap, this map, streamdeck profile
├── native/              # ALL native C++ source (see below)
├── data/                # fonts, themes, UI art, default/last project files
├── art/                 # icon sources + Windows .rc template
├── tools/               # package_windows.ps1 → dist zip
├── scripts/             # smoke.sh, demo show generators (Linux)
└── companion/           # Bitfocus Companion module bits

native/ — source tree

One executable target (Deckboy). main.cpp defines the App class and #includes every app/*.ipp file into it — the .ipp files are method bodies of App, not separate translation units. The whole app plus engine is ~49k lines across the files below.

App core (main.cpp + app/*.ipp — one class, one TU)

File ~Lines Owns
main.cpp 5,700 App class decl, settings action constants, palette/theme, fonts, save/load field helpers (escapeField), run() loop, WinMain, all member state
app/app_output_mgmt.ipp 4,200 Output runtime lifecycle: windows, fullscreen recovery, display selection/hot-plug, stream/NDI/DeckLink/Spout egress start/stop, OutputBackendRuntimeRoute
app/app_cue_transport.ipp 3,000 Take/go/stop/seek, deck transitions, auto-advance, overlay (PIP/lower-third) runtimes, panic profiles
app/app_render_main.ipp 2,800 Control-window Program/Transport panel, timeline, inspector host, loading animations
app/app_project_state.ipp 2,600 saveProject/loadProject (tab-delimited .deckboy), STATUS/STATE JSON snapshot builders, undo snapshots, workspace persistence
app/app_cue_mgmt.ipp 2,200 Import (async ffprobe), add/delete/reorder cues, cue editing actions, clipboard
app/app_remote_command.ipp 2,100 handleRemoteCommand() text-command dispatcher (Companion/OSC/HyperDeck all funnel here)
app/app_render_settings.ipp 2,100 Settings modal render + handleSettingsClick Part1/2/3
app/app_network.ipp 2,000 Companion TCP/UDP loop, OSC parse/feedback, OSC Query HTTP, HyperDeck server, ATEM/Art-Net/NMC/TSL bridges
app/app_render_inspector.ipp 1,600 Cue inspector sections (uses shared insp*() helpers)
app/app_render_control.ipp 1,400 Playlist column, cue rows, header/footer chrome
app/app_input.ipp 1,100 Keyboard + mouse dispatch, hotkeys
app/app_ui_widgets.ipp 1,000 Dropdowns, inline text editor, toasts, context menus
app/app_render_output.ipp 900 Per-output compositor: deck layers → output texture → window present + egress capture (SDL_RenderReadPixels → convert)
app/app_accessors.ipp 700 Focused deck/output/cue accessors, small state helpers
app/app_smoke.ipp 700 --smoke / --self-check harness
app/app_update.ipp 650 Per-tick update(): event pump, async future polling, display poll, output recovery poll
app/app_quick_action.ipp 550 QuickAction enum dispatch (inspector row actions)
app/app_overlays.ipp 470 Splash, startup dialog, dependency prompt + runtime detection
app/app_geometry.ipp 220 Cue geometry math (scale modes, crop, offsets)

Engine

File ~Lines Owns
core/sdl_compat.hpp ~200 SDL3 compat layer: int-rect draw overloads, SDL2-style display indices, nearest-scale texture creation, audio pause helper (v0.77.0)
engine/media_engine.cpp/.hpp 3,300 One instance per deck. Decode via in-process libav (v0.78.0, file cues) with ffmpeg-subprocess pipe fallback (live streams, break-glass); frames → frameQueue_, audio → SDL stream; stills/patterns/browser/source frames, transport, fades, transitions. Threading model documented in the .hpp header
engine/libav_decoder.cpp/.hpp 1,100 In-process libav* pipelines (DECKBOY_INPROC_DECODE, v0.78.0): VideoPipeline (d3d11va zero-copy on the output renderer's device, or CPU frames), AudioPipeline (s16/48k stereo, atempo speed), D3D11 interop helpers for the compositor's GPU bridge. Pimpl — header is libav-free
extras/terrarium_core.hpp 2,400 SDL-free Terrarium ecosystem sim (namespace terra, v0.78.4): world/step/species/weather, 8x8 glyph font, renderWorldRgba pixel renderer. Shared by the pattern source (pattern://terrarium via media_engine) and the companion exe
extras/terrarium.cpp 300 Standalone Terrarium app: windowing, input, SDL glyph-texture render on top of terrarium_core.hpp

Core (freestanding utilities)

File Owns
core/types.hpp Cue, Deck, OutputTarget, LayerAssignment, Project, DecodedFrame, enums (CueKind, TransportState, …)
core/constants.hpp Output raster constants, palette constants, mutable kLayout* metrics (rescaled by UI scale)
core/subprocess.hpp/.cpp spawnProcess() unified API — POSIX fork/exec + Windows CreateProcessW backends
core/io_utils.hpp readExact / readSome cross-platform pipe reads
core/paths.cpp Project-root/data/font resolution (see PORTABILITY.md)
core/utils.cpp trim/toLower/split string helpers
core/single_instance_guard.hpp flock (POSIX) / named mutex (Windows) instance lock
core/subtitle_parser.hpp, core/expression_parser.hpp, core/pixel_effects.hpp SRT parse, calculator-entry math, chroma key + color controls (CPU, RGBA path only)
core/system_browser.hpp openExternalUrl() for dependency prompts

Platform (backend seams)

File Owns
platform/output_backend.hpp/.cpp Output backend catalog + planOutputBackendRoute() (window/stream/NDI/DeckLink/Spout availability)
platform/capture_backend.hpp/.cpp Source-capture catalog + planSourceCapture() (x11grab/v4l2 on Linux, Windows.Graphics.Capture on Windows with a gdigrab fallback, scaffolds elsewhere)
platform/integration_backend.hpp/.cpp Integration adapter catalog (ATEM/NDI-trigger/NMC/MTC/LTC/Art-Net)
platform/ndi_api.hpp NDI send runtime (dynamic load)
platform/ndi_trigger_api.hpp NDI receive + find runtime (dynamic load)
platform/decklink.cpp Blackmagic DeckLink SDK 16.0 output (COM on Windows)
platform/siphon_spout.hpp/.cpp Spout (Windows) / Syphon (macOS) texture sharing
platform/browser.cpp Browser cue session lifecycle (WebView2 on Windows, external Chromium on Linux)
platform/midi.cpp RtMidi input (cross-platform) + MTC decode
platform/ltc_api.hpp libltc dynamic load
platform/network.hpp Socket helpers (winsock/BSD)

Render helpers

File Owns
render/layout.hpp VerticalLayout/HorizontalLayout/GridLayout/UITable, font-derived row spacing helpers
render/primitives.cpp drawFramedPanel beveling and low-level draws
render/texture_helpers.hpp syncFrameTexture() — RGBA32/NV12 texture create+upload

Data flow: cue → glass

Operator TAKE
  └─ app_cue_transport.ipp → MediaEngine::loadCue(&deck.cues[i])
       └─ startDecoderThreads(): 2 ffmpeg subprocesses (video pipe, audio pipe)
            video thread:  readExact → frameQueue_ (frameMutex_)
            audio thread:  readSome → gain/fade scale → SDL_PutAudioStreamData
  main loop each frame:
    MediaEngine::update()  — pops frames ≤ targetFrame (wall-clock × fps), uploads SDL_Texture
    app_render_output.ipp  — renderDeckLayerIntoOutput per layer assignment:
                             frame texture → bridge texture (alpha = deckOpacity × fadeGain)
                             → output compositor texture (AOI/warp/edge blend)
      ├─ presentOutputCompositorToWindow  (window outputs, vsync)
      └─ SDL_RenderReadPixels → sendOutputNdiFrame / DeckLink / Spout / ffmpeg stream stdin

Pipe pixel format is per-cue, frozen at TAKE: nv12 normally, rgba when chroma key/color controls are active (CPU effects path). See DEVNOTES "GPU Hardware Decode + NV12 Upload Path".

Clocking: video position runs on the wall clock but re-anchors to the audio device clock (frames queued − frames buffered) when they drift > 60 ms — audio is the master for cues that have it. The engine owns a snapshot of the loaded cue (activeCueSnapshot_); it never keeps pointers into Deck::cues. See DEVNOTES "Engine Cue Snapshot Ownership" / "Audio-Master A/V Clock".

Threading model

Main thread owns: all SDL windows/renderers/textures, project_ and all cue/deck/output state, the settings/UI, update()/render() loop (240 Hz anti-spin floor, vsync-governed normally).

Per-deck MediaEngine threads: video decode reader, audio decode reader, still-image decoder. Share frameQueue_ (via frameMutex_) and atomics (decoderStop_, decoderEof_, volume_) with the main thread.

Network threads (started per enabled integration): Companion TCP/UDP loop, OSC Query HTTP, HyperDeck TCP, ATEM/Art-Net/NMC/NDI-trigger/TSL/LTC listeners. They never touch app state directly: inbound commands are enqueued under remoteCommandMutex_ and drained on the main thread by processRemoteCommands(); outbound STATUS/STATE is served from a snapshot string rebuilt on the main thread under statusSnapshotMutex_.

std::async pools: ffprobe metadata (probeFutures_), waveform peaks (waveformMutex_), thumbnails/timeline strips (thumbnailMutex_, timelineStripMutex_). All polled non-blocking in update().

Serialization

.deckboy project files are tab-delimited text. The project header uses key–value lines (title\t...); Deck/Cue/OutputTarget records are positional with fields appended at the end and backward-compat guards (if (fields.size() >= N)). Special characters escaped via escapeField() / unescapeField() (main.cpp). Workspace layout persists separately in data/deckboy.workspace; last project path in data/last_project.txt.

Remote-control surface

All remote inputs normalize to text commands handled by handleRemoteCommand() (app_remote_command.ipp): Companion TCP/UDP (port 5510), OSC messages/bundles (same port, mapped via mapOscToRemoteCommand), HyperDeck protocol, Art-Net channel map, NDI metadata triggers, NMC sync. Feedback: OSC /deckboy/ack + /deckboy/state, STATUS/STATE (plain + JSON), TSL tally out, OSC Query HTTP.