Compare commits
194 commits
feat/playo
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d138265245 | ||
|
|
b6545e61a9 | ||
|
|
9dc86aa3b6 | ||
|
|
07d1fc9e72 | ||
|
|
01211fef7a | ||
| 97d725537b | |||
| d654f7c8a1 | |||
| eeaa1c1b58 | |||
|
|
99723da00f | ||
|
|
b700902200 | ||
|
|
b2c63de2fa | ||
|
|
0d479d043d | ||
|
|
1573bf8954 | ||
| 2f1697b77b | |||
| c269468014 | |||
| 108390e823 | |||
| 7704988978 | |||
| a00e90ecc8 | |||
| c21260c9b0 | |||
| d16d19c26d | |||
| 63f05cd652 | |||
|
|
dbef15ae0a | ||
|
|
99bd6a8c9c | ||
|
|
4e6142f455 | ||
|
|
02d502baaf | ||
|
|
00a7af7c54 | ||
| cb9ef9c14e | |||
| f48a0b73ee | |||
| 463cc3694d | |||
| 30328e2871 | |||
| bd662f6917 | |||
| a04ef2de3a | |||
| 62b9a90291 | |||
| 600af4564e | |||
| d8df8755e2 | |||
| 5525041901 | |||
| 29238a339e | |||
| 9ae619357b | |||
| 1f342826bd | |||
| 167d9ad009 | |||
| b0f504ca69 | |||
| a8b59f087d | |||
| 228f68ab6d | |||
| d0d881c735 | |||
| bebfe7a43d | |||
| 20d913fbad | |||
| 3eacb35c1e | |||
| de3e09f39e | |||
| 52f039554c | |||
| ec1699907d | |||
| 1cef8c3bb3 | |||
| 82697d92de | |||
| 87fd3164c6 | |||
| 0aa010dacc | |||
| 9ac27ff194 | |||
| 190e608ac2 | |||
| fd8b2dc3f1 | |||
| 13feb0a6a2 | |||
| 702187c1dc | |||
| 1ff0f2d865 | |||
| bdebc3adac | |||
| a200cabad4 | |||
| 3ac685ed3b | |||
|
|
22853da023 | ||
|
|
b40f640fa1 | ||
|
|
a2790601c9 | ||
|
|
f8eda7fc37 | ||
| 4062be101d | |||
| e54b8403e7 | |||
|
|
f218650b85 | ||
|
|
08be8fea77 | ||
|
|
858c9f7b97 | ||
|
|
59294856a2 | ||
|
|
d509876444 | ||
|
|
e97a289722 | ||
|
|
6895fbc5af | ||
|
|
8a675992c2 | ||
| 6908801822 | |||
|
|
4d1b23959c | ||
|
|
d1d3033ed5 | ||
|
|
46dc17ffb1 | ||
|
|
c083d1006a | ||
|
|
9ec2997f53 | ||
|
|
4a3bf18f7f | ||
|
|
1c068b470e | ||
|
|
e4154ea83a | ||
|
|
cf928d1a46 | ||
|
|
f2f3a88308 | ||
|
|
b697d356b2 | ||
| ebeaf01a67 | |||
|
|
a61e385693 | ||
| bf7189218a | |||
| d66d4bea80 | |||
| cad1e52c38 | |||
|
|
6979f07307 | ||
|
|
eba8e94887 | ||
| a06b5ed304 | |||
| 7d704d3af3 | |||
| b324878db9 | |||
|
|
9809cdd13e | ||
| 75f265534e | |||
| 8b8a19c465 | |||
| 9adcae0329 | |||
| 63654ea0ed | |||
| 0eac34529b | |||
| d6e515e1a8 | |||
| 3d3c8c48de | |||
| b65ce5b0b7 | |||
| 323d482eab | |||
| 8f33cbfa86 | |||
| 12d76edc42 | |||
| 551377e4c9 | |||
| b875376887 | |||
| 252aa713d4 | |||
| 068e2eaa87 | |||
| e3be8745d3 | |||
| 7d408035ac | |||
| 884c8829a0 | |||
| 962c7c8f20 | |||
| b46abc9b1a | |||
| adfbeac217 | |||
| d4924b044f | |||
| 4da7bf8b41 | |||
| 6fec41aaf9 | |||
| de6e44b991 | |||
| c7e07df515 | |||
| 67c071a0ee | |||
| 3529590160 | |||
| 96f4f2dd3b | |||
| 1750298bb8 | |||
| 298cb18914 | |||
| e8a1f564b0 | |||
| bda33fedca | |||
|
|
66ff7065ec | ||
| c1512e29c5 | |||
| 06e480f2b4 | |||
|
|
9bcbac558c | ||
| 2cd20a0e72 | |||
| 35165e28a8 | |||
| 32a2d0329e | |||
| b92a5bc7f7 | |||
| 7b878d48c9 | |||
| 64bbb221f7 | |||
|
|
ef329399f1 | ||
| 984a73e8ec | |||
| 8a958046ef | |||
| 08499b93b2 | |||
| ca1eec0600 | |||
| 794b9d9929 | |||
| 1d642bd437 | |||
| fffff1c016 | |||
| 549ca6c73f | |||
| ea615c8c76 | |||
| f2d5f5aa16 | |||
| d908c0c056 | |||
| b597ffd58e | |||
| be8fd691a5 | |||
| dba3435e60 | |||
| 43011bd794 | |||
| 43656a5e88 | |||
| 68461af990 | |||
| 8bc460025d | |||
| 3578c7b4e9 | |||
| cddcc9a29e | |||
| 0e844c0fc3 | |||
| 551af09dc7 | |||
| 4d6a999665 | |||
| f971d57bb9 | |||
| 7ab70948a0 | |||
| 13bbd4216e | |||
| fcd8e8dd2e | |||
| 67ac007706 | |||
| b4f2fb12ff | |||
| aa7f836493 | |||
| c2409bd037 | |||
| 42064acefa | |||
| 2e2b091653 | |||
|
|
c502d4a16f | ||
|
|
9d098e9778 | ||
|
|
02631f7b96 | ||
|
|
9436434599 | ||
|
|
f837e57969 | ||
| 68c8f47c8f | |||
|
|
17bf086ef2 | ||
|
|
dac5213354 | ||
|
|
3f203f326e | ||
|
|
7e9f1277d4 | ||
|
|
9d8adbbbc1 | ||
|
|
3430ef823e | ||
|
|
08a0fb1b60 | ||
|
|
dd438b597a | ||
|
|
8746d71af1 | ||
|
|
6a161c7133 | ||
| 79369c378a |
147 changed files with 18810 additions and 1473 deletions
1
.claude/launch.json
Normal file
1
.claude/launch.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"version":"0.0.1","configurations":[{"name":"web-ui","runtimeExecutable":"npx","runtimeArgs":["serve","services/web-ui/public","--listen","47434","--no-clipboard"],"port":47434}]}
|
||||||
13
.env.example
13
.env.example
|
|
@ -22,6 +22,11 @@ SESSION_SECRET=changeme
|
||||||
# MAM API Configuration
|
# MAM API Configuration
|
||||||
MAM_API_URL=http://mam-api:3000
|
MAM_API_URL=http://mam-api:3000
|
||||||
|
|
||||||
|
# Node Agent Authentication
|
||||||
|
# Bearer token for node-agent to authenticate with mam-api /driver/* endpoints.
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
NODE_AGENT_TOKEN=changeme
|
||||||
|
|
||||||
# Auth — default to ON in production. Setting to 'false' is a dev-only escape
|
# Auth — default to ON in production. Setting to 'false' is a dev-only escape
|
||||||
# hatch that disables all auth checks and attaches a synthetic 'dev' user to
|
# hatch that disables all auth checks and attaches a synthetic 'dev' user to
|
||||||
# every request. Never run with AUTH_ENABLED=false on a network you don't control.
|
# every request. Never run with AUTH_ENABLED=false on a network you don't control.
|
||||||
|
|
@ -64,6 +69,14 @@ GOOGLE_ALLOWED_DOMAIN=
|
||||||
# the authenticator code (Google is treated as the first factor). Accounts without
|
# the authenticator code (Google is treated as the first factor). Accounts without
|
||||||
# TOTP complete sign-in in one Google step.
|
# TOTP complete sign-in in one Google step.
|
||||||
|
|
||||||
|
# Framecache — shared memory ring buffer for SDI + network ingest fan-out.
|
||||||
|
# Size in GB. Tune per node based on available RAM and number of SDI inputs.
|
||||||
|
# Each 1080p59.94 source uses ~494MB (120-frame ring at 4.1MB/frame).
|
||||||
|
# Baratheon (251GB RAM): 60
|
||||||
|
# zampp1 (93GB RAM): 40
|
||||||
|
# zampp2 (18GB RAM): 8 (increase node RAM before deploying capture)
|
||||||
|
FC_SHM_SIZE_GB=40
|
||||||
|
|
||||||
# Playout / Master Control (MCR)
|
# Playout / Master Control (MCR)
|
||||||
# Image tag the mam-api spawns when a channel starts. Build with:
|
# Image tag the mam-api spawns when a channel starts. Build with:
|
||||||
# docker compose --profile build-only build playout
|
# docker compose --profile build-only build playout
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -27,8 +27,8 @@ services/editor/**/node_modules
|
||||||
services/editor/**/dist
|
services/editor/**/dist
|
||||||
services/editor/.pnpm-store
|
services/editor/.pnpm-store
|
||||||
|
|
||||||
# Blackmagic DeckLink SDK + runtime libs (operator-supplied; see services/capture/build-with-decklink.sh)
|
# Blackmagic DeckLink SDK headers are now committed (private/internal repo) under services/capture/sdk/.
|
||||||
services/capture/sdk/
|
# Runtime .so libs (libDeckLinkAPI.so) come from the DesktopVideo driver install and are not committed.
|
||||||
services/capture/lib/
|
services/capture/lib/
|
||||||
|
|
||||||
# Editor backups
|
# Editor backups
|
||||||
|
|
|
||||||
325
deploy/install-driver.sh
Normal file
325
deploy/install-driver.sh
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# install-driver.sh <vendor>
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Idempotent HOST installer for capture-card runtime drivers / SDKs.
|
||||||
|
#
|
||||||
|
# Runs ON the cluster node's HOST kernel. The node-agent invokes it inside a
|
||||||
|
# one-shot PRIVILEGED ubuntu container that bind-mounts this repo plus the host
|
||||||
|
# paths needed to affect the host kernel (/lib/modules, /usr/src, /boot, /dev,
|
||||||
|
# and the host apt/dpkg via the mounted root). dkms / modprobe / ldconfig
|
||||||
|
# therefore operate against the running host kernel.
|
||||||
|
#
|
||||||
|
# Reads the proprietary vendor file(s) from sdk/<vendor>/ (in this repo).
|
||||||
|
# NO binaries are committed — if the expected file is missing the script exits
|
||||||
|
# non-zero with a clear message telling the operator what to drop in.
|
||||||
|
#
|
||||||
|
# Vendors (ALLOWLIST — nothing else is accepted):
|
||||||
|
# blackmagic Desktop Video .deb (DKMS kernel module)
|
||||||
|
# aja NTV2 driver source/SDK (built kernel module)
|
||||||
|
# deltacast VideoMaster installer (kernel module)
|
||||||
|
# ndi redistributable runtime libs (user-space only, no module)
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 installed (or already present / up to date)
|
||||||
|
# 2 bad usage / unknown vendor
|
||||||
|
# 3 expected vendor file missing in sdk/<vendor>/
|
||||||
|
# 4 missing kernel headers (cannot build DKMS / module)
|
||||||
|
# 5 build / install / module-load failure
|
||||||
|
#
|
||||||
|
# `bash -n` must pass. set -euo pipefail with `|| true` guarding every probe.
|
||||||
|
# ============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Resolve our own repo dir (deploy/ -> repo root), regardless of CWD.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||||
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd)"
|
||||||
|
SDK_ROOT="$REPO_DIR/sdk"
|
||||||
|
|
||||||
|
VENDOR="${1:-}"
|
||||||
|
KVER="$(uname -r 2>/dev/null || echo unknown)"
|
||||||
|
REBOOT_REQUIRED=0
|
||||||
|
|
||||||
|
log() { echo "[install-driver] $*"; }
|
||||||
|
warn() { echo "[install-driver] WARN: $*" >&2; }
|
||||||
|
die() { echo "[install-driver] ERROR: $*" >&2; exit "${2:-5}"; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: install-driver.sh <blackmagic|aja|deltacast|ndi>" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$VENDOR" ] || usage
|
||||||
|
case "$VENDOR" in
|
||||||
|
blackmagic|aja|deltacast|ndi) : ;;
|
||||||
|
*) echo "[install-driver] ERROR: unknown vendor '$VENDOR' (allowed: blackmagic aja deltacast ndi)" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
VENDOR_DIR="$SDK_ROOT/$VENDOR"
|
||||||
|
log "vendor=$VENDOR kernel=$KVER repo=$REPO_DIR"
|
||||||
|
log "reading vendor files from $VENDOR_DIR"
|
||||||
|
[ -d "$VENDOR_DIR" ] || die "vendor dir $VENDOR_DIR does not exist (repo not mounted?)" 3
|
||||||
|
|
||||||
|
# Pick the newest file matching a glob; echo its path or empty.
|
||||||
|
newest_match() {
|
||||||
|
# shellcheck disable=SC2012
|
||||||
|
ls -1t $1 2>/dev/null | head -n1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_headers() {
|
||||||
|
if [ -d "/lib/modules/$KVER/build" ] || dpkg -s "linux-headers-$KVER" >/dev/null 2>&1; then
|
||||||
|
log "kernel headers for $KVER present"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log "installing linux-headers-$KVER ..."
|
||||||
|
apt-get update -y >/dev/null 2>&1 || true
|
||||||
|
if ! apt-get install -y "linux-headers-$KVER" >/dev/null 2>&1; then
|
||||||
|
# Fall back to the generic meta-package; still may not match a custom kernel.
|
||||||
|
apt-get install -y linux-headers-generic >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
if [ -d "/lib/modules/$KVER/build" ] || dpkg -s "linux-headers-$KVER" >/dev/null 2>&1; then
|
||||||
|
log "kernel headers ready"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
die "kernel headers for $KVER unavailable — cannot build module. Install linux-headers-$KVER on the host." 4
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# blackmagic — Desktop Video .deb (DKMS) + modprobe + restart desktopvideo
|
||||||
|
# ===========================================================================
|
||||||
|
install_blackmagic() {
|
||||||
|
# Detect existing state first (idempotent skip).
|
||||||
|
if lsmod 2>/dev/null | grep -q '^blackmagic' && [ -e /dev/blackmagic ]; then
|
||||||
|
log "blackmagic module loaded and /dev/blackmagic present — already installed"
|
||||||
|
if command -v dkms >/dev/null 2>&1; then dkms status 2>/dev/null | grep -i blackmagic || true; fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local deb
|
||||||
|
deb="$(newest_match "$VENDOR_DIR/desktopvideo_*_amd64.deb")"
|
||||||
|
[ -n "$deb" ] && [ -f "$deb" ] || die \
|
||||||
|
"no desktopvideo_*_amd64.deb in $VENDOR_DIR — download Desktop Video for Ubuntu 22.04 and drop the .deb there (see sdk/blackmagic/README.md)." 3
|
||||||
|
log "using package: $(basename "$deb")"
|
||||||
|
|
||||||
|
ensure_headers
|
||||||
|
|
||||||
|
log "apt-get install (DKMS build) ..."
|
||||||
|
apt-get update -y >/dev/null 2>&1 || true
|
||||||
|
apt-get install -y "$deb" || die "apt-get install of $(basename "$deb") failed (DKMS build?)" 5
|
||||||
|
|
||||||
|
log "depmod + modprobe blackmagic ..."
|
||||||
|
depmod -a "$KVER" 2>/dev/null || true
|
||||||
|
if ! modprobe blackmagic 2>/dev/null; then
|
||||||
|
warn "modprobe blackmagic failed — a reboot may be required for the DKMS module to bind"
|
||||||
|
REBOOT_REQUIRED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Restart the DesktopVideoHelper daemon if present.
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl restart desktopvideo 2>/dev/null \
|
||||||
|
|| systemctl restart DesktopVideoHelper 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if lsmod 2>/dev/null | grep -q '^blackmagic' || [ -e /dev/blackmagic ]; then
|
||||||
|
log "blackmagic installed (module loaded / device present)"
|
||||||
|
else
|
||||||
|
warn "blackmagic installed but module not yet loaded — reboot required"
|
||||||
|
REBOOT_REQUIRED=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# aja — build ntv2 kernel module from source archive + modprobe
|
||||||
|
# ===========================================================================
|
||||||
|
install_aja() {
|
||||||
|
if lsmod 2>/dev/null | grep -q 'ajantv2'; then
|
||||||
|
log "ajantv2 module already loaded — already installed"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local src
|
||||||
|
src="$(newest_match "$VENDOR_DIR/ntv2sdk*.zip")"
|
||||||
|
[ -n "$src" ] || src="$(newest_match "$VENDOR_DIR/libajantv2*.tar.gz")"
|
||||||
|
[ -n "$src" ] && [ -f "$src" ] || die \
|
||||||
|
"no ntv2sdk*.zip / libajantv2*.tar.gz in $VENDOR_DIR — download the AJA NTV2 Linux SDK and drop it there (see sdk/aja/README.md)." 3
|
||||||
|
log "using source: $(basename "$src")"
|
||||||
|
|
||||||
|
ensure_headers
|
||||||
|
apt-get install -y build-essential unzip >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
local work; work="$(mktemp -d)"
|
||||||
|
log "extracting into $work ..."
|
||||||
|
case "$src" in
|
||||||
|
*.zip) unzip -q -o "$src" -d "$work" || die "failed to unzip $(basename "$src")" 5 ;;
|
||||||
|
*.tar.gz) tar -xzf "$src" -C "$work" || die "failed to untar $(basename "$src")" 5 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Find the linux driver dir (contains a Makefile producing ajantv2.ko).
|
||||||
|
local drvdir
|
||||||
|
drvdir="$(dirname "$(find "$work" -type d -path '*driver/linux' -print -quit 2>/dev/null || true)/." 2>/dev/null)"
|
||||||
|
[ -d "$drvdir" ] || drvdir="$(dirname "$(find "$work" -name 'ajantv2.c' -print -quit 2>/dev/null || true)" 2>/dev/null)"
|
||||||
|
[ -n "$drvdir" ] && [ -d "$drvdir" ] || die "could not locate AJA driver/linux source inside the archive" 5
|
||||||
|
log "building module in $drvdir ..."
|
||||||
|
make -C "$drvdir" >/dev/null 2>&1 || die "AJA module build failed" 5
|
||||||
|
|
||||||
|
local ko
|
||||||
|
ko="$(find "$drvdir" -name 'ajantv2.ko' -print -quit 2>/dev/null || true)"
|
||||||
|
if [ -n "$ko" ]; then
|
||||||
|
install -D -m 0644 "$ko" "/lib/modules/$KVER/extra/ajantv2.ko" 2>/dev/null || true
|
||||||
|
depmod -a "$KVER" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if ! modprobe ajantv2 2>/dev/null; then
|
||||||
|
# Fall back to the SDK's own load script if shipped.
|
||||||
|
local loader; loader="$(find "$work" -name 'load_ajantv2' -print -quit 2>/dev/null || true)"
|
||||||
|
if [ -n "$loader" ]; then bash "$loader" 2>/dev/null || true; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$work" 2>/dev/null || true
|
||||||
|
|
||||||
|
if lsmod 2>/dev/null | grep -q 'ajantv2'; then
|
||||||
|
log "ajantv2 installed and loaded"
|
||||||
|
else
|
||||||
|
warn "ajantv2 built but not loaded — a reboot may be required (old in-tree module wedged?)"
|
||||||
|
REBOOT_REQUIRED=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# deltacast — VideoMaster installer + module load
|
||||||
|
# ===========================================================================
|
||||||
|
install_deltacast() {
|
||||||
|
if lsmod 2>/dev/null | grep -q 'videomaster' || ls /dev/deltacast* >/dev/null 2>&1; then
|
||||||
|
log "Deltacast module loaded / device present — already installed"
|
||||||
|
install_deltacast_udev_rule
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pkg
|
||||||
|
pkg="$(newest_match "$VENDOR_DIR/VideoMaster*.run")"
|
||||||
|
[ -n "$pkg" ] || pkg="$(newest_match "$VENDOR_DIR/VideoMaster*.tar.gz")"
|
||||||
|
[ -n "$pkg" ] && [ -f "$pkg" ] || die \
|
||||||
|
"no VideoMaster*.run / VideoMaster*.tar.gz in $VENDOR_DIR — obtain the Deltacast VideoMaster Linux installer and drop it there (see sdk/deltacast/README.md)." 3
|
||||||
|
log "using installer: $(basename "$pkg")"
|
||||||
|
|
||||||
|
ensure_headers
|
||||||
|
apt-get install -y build-essential dkms >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
local work; work="$(mktemp -d)"
|
||||||
|
case "$pkg" in
|
||||||
|
*.run)
|
||||||
|
log "extracting self-extractor ..."
|
||||||
|
chmod +x "$pkg" 2>/dev/null || true
|
||||||
|
"$pkg" --noexec --target "$work" >/dev/null 2>&1 \
|
||||||
|
|| cp "$pkg" "$work/installer.run"
|
||||||
|
;;
|
||||||
|
*.tar.gz)
|
||||||
|
tar -xzf "$pkg" -C "$work" || die "failed to untar $(basename "$pkg")" 5
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local installer
|
||||||
|
installer="$(find "$work" -name 'install.sh' -print -quit 2>/dev/null || true)"
|
||||||
|
[ -n "$installer" ] || installer="$(find "$work" -name 'installer.run' -print -quit 2>/dev/null || true)"
|
||||||
|
[ -n "$installer" ] && [ -f "$installer" ] || die "Deltacast installer (install.sh) not found inside the package" 5
|
||||||
|
log "running vendor installer: $(basename "$installer") ..."
|
||||||
|
chmod +x "$installer" 2>/dev/null || true
|
||||||
|
( cd "$(dirname "$installer")" && bash "$installer" ) || die "Deltacast VideoMaster installer failed" 5
|
||||||
|
|
||||||
|
depmod -a "$KVER" 2>/dev/null || true
|
||||||
|
modprobe videomasterhd 2>/dev/null || modprobe videomaster 2>/dev/null || true
|
||||||
|
|
||||||
|
rm -rf "$work" 2>/dev/null || true
|
||||||
|
|
||||||
|
if lsmod 2>/dev/null | grep -q 'videomaster' || ls /dev/deltacast* >/dev/null 2>&1; then
|
||||||
|
log "Deltacast VideoMaster installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install our own udev rule that creates 8 /dev/deltacast symlinks (ports 0-7)
|
||||||
|
# pointing at the single real device node. Kept separate from the SDK's own
|
||||||
|
# rule so a driver reinstall won't clobber it.
|
||||||
|
install_deltacast_udev_rule
|
||||||
|
|
||||||
|
# First-time VideoMaster installs lay down udev rules + firmware that need a reboot.
|
||||||
|
warn "Deltacast: a REBOOT is recommended after a first-time VideoMaster install (udev + firmware)"
|
||||||
|
REBOOT_REQUIRED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy the repo's 99-wild-dragon-deltacast.rules into /etc/udev/rules.d/ and
|
||||||
|
# reload. Idempotent. Creates /dev/deltacast0..7 -> /dev/delta-x3700 so the
|
||||||
|
# node-agent advertises all 8 RX channels.
|
||||||
|
install_deltacast_udev_rule() {
|
||||||
|
local rule_src="$REPO_DIR/deploy/udev/99-wild-dragon-deltacast.rules"
|
||||||
|
local rule_dst="/etc/udev/rules.d/99-wild-dragon-deltacast.rules"
|
||||||
|
if [ ! -f "$rule_src" ]; then
|
||||||
|
warn "Deltacast: udev rule $rule_src not found in repo — skipping symlink rule install"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -f "$rule_dst" ] && cmp -s "$rule_src" "$rule_dst"; then
|
||||||
|
log "Deltacast: udev rule already up to date at $rule_dst"
|
||||||
|
else
|
||||||
|
log "installing Deltacast udev rule -> $rule_dst"
|
||||||
|
install -D -m 0644 "$rule_src" "$rule_dst" 2>/dev/null \
|
||||||
|
|| { warn "Deltacast: failed to install udev rule (continuing)"; return 0; }
|
||||||
|
udevadm control --reload-rules 2>/dev/null || true
|
||||||
|
udevadm trigger --action=add /dev/delta-x3700 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# ndi — copy redistributable runtime libs to /usr/local/lib + ldconfig
|
||||||
|
# ===========================================================================
|
||||||
|
install_ndi() {
|
||||||
|
local target="/opt/ndi-lib"
|
||||||
|
local found=0
|
||||||
|
# shellcheck disable=SC2231
|
||||||
|
for f in "$VENDOR_DIR"/libndi*.so*; do
|
||||||
|
[ -e "$f" ] || continue
|
||||||
|
found=1
|
||||||
|
break
|
||||||
|
done
|
||||||
|
[ "$found" = 1 ] || die \
|
||||||
|
"no libndi*.so* in $VENDOR_DIR — drop the NDI runtime redistributable libs there (see sdk/ndi/README.md)." 3
|
||||||
|
|
||||||
|
log "copying NDI runtime libs to $target ..."
|
||||||
|
mkdir -p "$target"
|
||||||
|
cp -av "$VENDOR_DIR"/libndi*.so* "$target"/ 2>/dev/null || die "failed copying NDI libs" 5
|
||||||
|
|
||||||
|
# Recreate the libndi.so dev symlink if only versioned libs were shipped.
|
||||||
|
if [ ! -e "$target/libndi.so" ]; then
|
||||||
|
local versioned
|
||||||
|
versioned="$(newest_match "$target/libndi.so.*")"
|
||||||
|
if [ -n "$versioned" ]; then
|
||||||
|
ln -sf "$(basename "$versioned")" "$target/libndi.so" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$target" > /etc/ld.so.conf.d/ndi.conf
|
||||||
|
ldconfig 2>/dev/null || true
|
||||||
|
|
||||||
|
if ldconfig -p 2>/dev/null | grep -q 'libndi'; then
|
||||||
|
log "NDI runtime registered with the dynamic linker"
|
||||||
|
else
|
||||||
|
die "NDI libs copied but ldconfig did not resolve libndi" 5
|
||||||
|
fi
|
||||||
|
log "NDI: no kernel module and no reboot required."
|
||||||
|
log "NDI: restart any process that already loaded an older libndi to pick up the new version."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
case "$VENDOR" in
|
||||||
|
blackmagic) install_blackmagic ;;
|
||||||
|
aja) install_aja ;;
|
||||||
|
deltacast) install_deltacast ;;
|
||||||
|
ndi) install_ndi ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$REBOOT_REQUIRED" = 1 ]; then
|
||||||
|
log "RESULT: $VENDOR install completed — REBOOT REQUIRED"
|
||||||
|
echo "[install-driver] REBOOT_REQUIRED=1"
|
||||||
|
else
|
||||||
|
log "RESULT: $VENDOR install completed — no reboot required"
|
||||||
|
echo "[install-driver] REBOOT_REQUIRED=0"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
@ -16,11 +16,12 @@
|
||||||
# Environment variables:
|
# Environment variables:
|
||||||
# MAM_API_URL REQUIRED Primary MAM API base URL
|
# MAM_API_URL REQUIRED Primary MAM API base URL
|
||||||
# NODE_TOKEN API bearer token (required if AUTH_ENABLED=true)
|
# NODE_TOKEN API bearer token (required if AUTH_ENABLED=true)
|
||||||
# NODE_ROLE Role tag reported to the cluster (default: worker)
|
# NODE_ROLE Role tag reported to the cluster (default: auto-detect)
|
||||||
# NODE_IP Override the LAN IP reported back (default: auto-detect)
|
# NODE_IP Override the LAN IP reported back (default: auto-detect)
|
||||||
# AGENT_PORT Host port for the node agent (default: 7436)
|
# AGENT_PORT Host port for the node agent (default: 7436)
|
||||||
# INSTALL_DIR Where to clone/find the repo (default: /opt/wild-dragon)
|
# INSTALL_DIR Where to clone/find the repo (default: /opt/wild-dragon)
|
||||||
# PROFILES Extra compose profiles, space-sep e.g. "worker capture"
|
# PROFILES Compose profiles, space-sep (default: auto-detect from hardware)
|
||||||
|
# Override only to force, e.g. "worker capture"
|
||||||
# BMD_MODEL DeckLink card model name (e.g. "DeckLink Duo 2")
|
# BMD_MODEL DeckLink card model name (e.g. "DeckLink Duo 2")
|
||||||
# REPO_URL Override the Forgejo clone URL
|
# REPO_URL Override the Forgejo clone URL
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
@ -32,8 +33,16 @@ REPO_URL="${REPO_URL:-https://forge.wilddragon.net/zgaetano/wild-dragon.git}"
|
||||||
INSTALL_DIR="${INSTALL_DIR:-/opt/wild-dragon}"
|
INSTALL_DIR="${INSTALL_DIR:-/opt/wild-dragon}"
|
||||||
MAM_API_URL="${MAM_API_URL:-}"
|
MAM_API_URL="${MAM_API_URL:-}"
|
||||||
NODE_TOKEN="${NODE_TOKEN:-}"
|
NODE_TOKEN="${NODE_TOKEN:-}"
|
||||||
|
# Track whether the caller pinned NODE_ROLE explicitly (manual override) vs.
|
||||||
|
# us defaulting it — so auto-detection only fills in an *unset* role.
|
||||||
|
[[ -n "${NODE_ROLE:-}" ]] && NODE_ROLE_EXPLICIT=1 || NODE_ROLE_EXPLICIT=""
|
||||||
NODE_ROLE="${NODE_ROLE:-worker}"
|
NODE_ROLE="${NODE_ROLE:-worker}"
|
||||||
NODE_IP="${NODE_IP:-}"
|
NODE_IP="${NODE_IP:-}"
|
||||||
|
# NODE_NAME pins this node's cluster identity (the heartbeat key). Default to the
|
||||||
|
# OS hostname, but ALWAYS write it explicitly so cloned VMs that share an
|
||||||
|
# /etc/hostname (e.g. two boxes both named "zampp1") don't collide on the same
|
||||||
|
# cluster_nodes row — which silently hides the capture node's DeckLink devices.
|
||||||
|
NODE_NAME="${NODE_NAME:-$(hostname)}"
|
||||||
AGENT_PORT="${AGENT_PORT:-7436}"
|
AGENT_PORT="${AGENT_PORT:-7436}"
|
||||||
PROFILES="${PROFILES:-}"
|
PROFILES="${PROFILES:-}"
|
||||||
BMD_MODEL="${BMD_MODEL:-}"
|
BMD_MODEL="${BMD_MODEL:-}"
|
||||||
|
|
@ -65,6 +74,37 @@ detect_lan_ip() {
|
||||||
echo "$ip"
|
echo "$ip"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Auto-detect hardware ─────────────────────────────────────────────────────
|
||||||
|
# Mirror detect_lan_ip's style: best-effort, guard every probe with `|| true`
|
||||||
|
# so a missing nvidia-smi/lspci never aborts under `set -euo pipefail`. The
|
||||||
|
# node self-describes its hardware here so the operator never has to pick a
|
||||||
|
# role — the right compose profiles are enabled automatically.
|
||||||
|
|
||||||
|
# GPU present? nvidia-smi is the strong signal; fall back to an lspci scan for
|
||||||
|
# NVIDIA or AMD VGA controllers (covers nodes where the driver isn't installed
|
||||||
|
# yet but the card is physically present).
|
||||||
|
detect_gpu() {
|
||||||
|
if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if command -v lspci &>/dev/null; then
|
||||||
|
if lspci 2>/dev/null | grep -iE 'nvidia|vga.*amd' &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# SDI capture card present? Blackmagic DeckLink or Deltacast, via lspci.
|
||||||
|
detect_sdi() {
|
||||||
|
if command -v lspci &>/dev/null; then
|
||||||
|
if lspci 2>/dev/null | grep -iE 'blackmagic|deltacast' &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# ── Preflight ────────────────────────────────────────────────────────────────
|
# ── Preflight ────────────────────────────────────────────────────────────────
|
||||||
echo -e "\n${BLD}${CYN}Wild Dragon MAM — Cluster Node Onboarding${NC}\n"
|
echo -e "\n${BLD}${CYN}Wild Dragon MAM — Cluster Node Onboarding${NC}\n"
|
||||||
|
|
||||||
|
|
@ -79,6 +119,36 @@ if [[ -z "$NODE_IP" ]]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Auto-assign compose profiles from detected hardware ──────────────────────
|
||||||
|
# Operator never picks a role: the worker profile always runs, and we add the
|
||||||
|
# gpu / capture profiles only when the matching hardware is present. Explicit
|
||||||
|
# PROFILES / NODE_ROLE env vars are honoured as a manual override escape hatch.
|
||||||
|
HAS_GPU=false; HAS_SDI=false
|
||||||
|
detect_gpu && HAS_GPU=true || true
|
||||||
|
detect_sdi && HAS_SDI=true || true
|
||||||
|
|
||||||
|
DETECTED_DESC="CPU"
|
||||||
|
[[ "$HAS_GPU" == true ]] && DETECTED_DESC="$DETECTED_DESC, GPU"
|
||||||
|
[[ "$HAS_SDI" == true ]] && DETECTED_DESC="$DETECTED_DESC, SDI capture card"
|
||||||
|
|
||||||
|
if [[ -z "$PROFILES" ]]; then
|
||||||
|
AUTO_PROFILES="worker"
|
||||||
|
[[ "$HAS_GPU" == true ]] && AUTO_PROFILES="$AUTO_PROFILES gpu"
|
||||||
|
[[ "$HAS_SDI" == true ]] && AUTO_PROFILES="$AUTO_PROFILES capture"
|
||||||
|
PROFILES="$AUTO_PROFILES"
|
||||||
|
info "Detected: $DETECTED_DESC → profiles: $PROFILES"
|
||||||
|
else
|
||||||
|
info "Detected: $DETECTED_DESC (profiles overridden by env: $PROFILES)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Derive a human-friendly role tag from detected hardware when not pinned.
|
||||||
|
# Capture cards win over GPU (an SDI ingest node is the more specific role).
|
||||||
|
if [[ -z "$NODE_ROLE_EXPLICIT" ]]; then
|
||||||
|
if [[ "$HAS_SDI" == true ]]; then NODE_ROLE="capture"
|
||||||
|
elif [[ "$HAS_GPU" == true ]]; then NODE_ROLE="gpu"
|
||||||
|
else NODE_ROLE="worker"; fi
|
||||||
|
fi
|
||||||
|
|
||||||
info "Primary API : $MAM_API_URL"
|
info "Primary API : $MAM_API_URL"
|
||||||
info "Role : $NODE_ROLE"
|
info "Role : $NODE_ROLE"
|
||||||
info "Agent port : $AGENT_PORT"
|
info "Agent port : $AGENT_PORT"
|
||||||
|
|
@ -135,6 +205,7 @@ info "Writing $ENV_FILE"
|
||||||
echo "MAM_API_URL=$MAM_API_URL"
|
echo "MAM_API_URL=$MAM_API_URL"
|
||||||
echo "NODE_TOKEN=$NODE_TOKEN"
|
echo "NODE_TOKEN=$NODE_TOKEN"
|
||||||
echo "NODE_ROLE=$NODE_ROLE"
|
echo "NODE_ROLE=$NODE_ROLE"
|
||||||
|
echo "NODE_NAME=$NODE_NAME"
|
||||||
echo "NODE_IP=$NODE_IP"
|
echo "NODE_IP=$NODE_IP"
|
||||||
echo "AGENT_PORT=$AGENT_PORT"
|
echo "AGENT_PORT=$AGENT_PORT"
|
||||||
echo "HEARTBEAT_MS=30000"
|
echo "HEARTBEAT_MS=30000"
|
||||||
|
|
|
||||||
1
deploy/udev/99-wild-dragon-deltacast.rules
Normal file
1
deploy/udev/99-wild-dragon-deltacast.rules
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
KERNEL=="delta-x3700", MODE="0666", RUN+="/bin/sh -c 'for i in 0 1 2 3 4 5 6 7; do ln -sf /dev/delta-x3700 /dev/deltacast$i; done'"
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
# - Sets NVENC_ENABLED=true so the worker prioritises h264_nvenc/hevc_nvenc
|
# - Sets NVENC_ENABLED=true so the worker prioritises h264_nvenc/hevc_nvenc
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
capture:
|
||||||
|
runtime: nvidia
|
||||||
|
environment:
|
||||||
|
NVIDIA_VISIBLE_DEVICES: all
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
worker:
|
worker:
|
||||||
build:
|
build:
|
||||||
context: ./services/worker
|
context: ./services/worker
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,15 @@ services:
|
||||||
build: ./services/node-agent
|
build: ./services/node-agent
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
pid: host
|
||||||
environment:
|
environment:
|
||||||
MAM_API_URL: ${MAM_API_URL}
|
MAM_API_URL: ${MAM_API_URL}
|
||||||
NODE_TOKEN: ${NODE_TOKEN:-}
|
NODE_TOKEN: ${NODE_TOKEN:-}
|
||||||
NODE_ROLE: ${NODE_ROLE:-worker}
|
NODE_ROLE: ${NODE_ROLE:-worker}
|
||||||
|
# NODE_NAME pins the cluster identity (heartbeat key). Set it per-node so
|
||||||
|
# cloned VMs that share /etc/hostname don't collide on the same
|
||||||
|
# cluster_nodes row. Falls back to the OS hostname when unset.
|
||||||
|
NODE_NAME: ${NODE_NAME:-}
|
||||||
NODE_IP: ${NODE_IP:-}
|
NODE_IP: ${NODE_IP:-}
|
||||||
AGENT_PORT: ${AGENT_PORT:-7436}
|
AGENT_PORT: ${AGENT_PORT:-7436}
|
||||||
HEARTBEAT_MS: ${HEARTBEAT_MS:-30000}
|
HEARTBEAT_MS: ${HEARTBEAT_MS:-30000}
|
||||||
|
|
@ -55,10 +60,31 @@ services:
|
||||||
BMD_MODEL: ${BMD_MODEL:-}
|
BMD_MODEL: ${BMD_MODEL:-}
|
||||||
BMD_DEVICE_PREFIX: ${BMD_DEVICE_PREFIX:-dv}
|
BMD_DEVICE_PREFIX: ${BMD_DEVICE_PREFIX:-dv}
|
||||||
LIVE_DIR: ${LIVE_DIR:-/mnt/NVME/MAM/wild-dragon-live}
|
LIVE_DIR: ${LIVE_DIR:-/mnt/NVME/MAM/wild-dragon-live}
|
||||||
|
# Framecache service URL (on the wild-dragon-worker network)
|
||||||
|
FC_URL: ${FC_URL:-http://framecache:7435}
|
||||||
|
# net_ingest binary — runs inside the framecache container via docker exec.
|
||||||
|
# node-agent has docker.sock so it can exec into the framecache container.
|
||||||
|
# Override with a host-installed path if preferred.
|
||||||
|
NET_INGEST_BIN: ${NET_INGEST_BIN:-docker exec framecache net_ingest}
|
||||||
|
# REPO_DIR: host path to the checked-out repo. The agent passes this to the
|
||||||
|
# one-shot driver-install container so install-driver.sh can read
|
||||||
|
# sdk/<vendor>/ and run deploy/install-driver.sh. Must match the host path
|
||||||
|
# bind-mounted below (onboard-node.sh clones to /opt/wild-dragon).
|
||||||
|
REPO_DIR: ${REPO_DIR:-/opt/wild-dragon}
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- /dev:/dev:ro
|
- /dev:/dev:ro
|
||||||
- /mnt/NVME/MAM/wild-dragon-live:/mnt/NVME/MAM/wild-dragon-live:ro
|
- /mnt/NVME/MAM/wild-dragon-live:/mnt/NVME/MAM/wild-dragon-live:ro
|
||||||
|
# Capture-driver deployment ("Capture Drivers / SDKs" in the Cluster admin
|
||||||
|
# screen): the agent itself does NOT run dkms/modprobe — it spawns a
|
||||||
|
# separate privileged ubuntu container that bind-mounts these host paths.
|
||||||
|
# The agent only needs to *see* the repo path so it can pass it through as
|
||||||
|
# a bind to that container; no extra privileges are granted to the agent.
|
||||||
|
# /opt/wild-dragon → repo (sdk/<vendor>/ + deploy/install-driver.sh)
|
||||||
|
# The install container additionally mounts /lib/modules,/usr/src,/boot,
|
||||||
|
# /dev and /opt from the host (handled in the agent, not here) so DKMS /
|
||||||
|
# modprobe / ldconfig affect the host kernel.
|
||||||
|
- ${REPO_DIR:-/opt/wild-dragon}:${REPO_DIR:-/opt/wild-dragon}:ro
|
||||||
devices:
|
devices:
|
||||||
- /dev/blackmagic:/dev/blackmagic
|
- /dev/blackmagic:/dev/blackmagic
|
||||||
|
|
||||||
|
|
@ -66,6 +92,7 @@ services:
|
||||||
build: ./services/worker
|
build: ./services/worker
|
||||||
profiles: [worker]
|
profiles: [worker]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
privileged: true
|
||||||
environment:
|
environment:
|
||||||
REDIS_URL: ${REDIS_URL}
|
REDIS_URL: ${REDIS_URL}
|
||||||
DATABASE_URL: ${DATABASE_URL}
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
|
@ -75,6 +102,7 @@ services:
|
||||||
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
S3_REGION: ${S3_REGION:-us-east-1}
|
S3_REGION: ${S3_REGION:-us-east-1}
|
||||||
NVENC_ENABLED: ${NVENC_ENABLED:-false}
|
NVENC_ENABLED: ${NVENC_ENABLED:-false}
|
||||||
|
GROWING_PATH: /growing
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon-worker
|
- wild-dragon-worker
|
||||||
|
|
||||||
|
|
@ -84,6 +112,7 @@ services:
|
||||||
build: ./services/capture
|
build: ./services/capture
|
||||||
profiles: [capture]
|
profiles: [capture]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
runtime: nvidia
|
||||||
environment:
|
environment:
|
||||||
REDIS_URL: ${REDIS_URL}
|
REDIS_URL: ${REDIS_URL}
|
||||||
DATABASE_URL: ${DATABASE_URL}
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
|
@ -92,6 +121,8 @@ services:
|
||||||
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||||
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
CAPTURE_PORT: 3001
|
CAPTURE_PORT: 3001
|
||||||
|
NVIDIA_VISIBLE_DEVICES: all
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
devices:
|
devices:
|
||||||
- ${BMD_DEVICE_0:-/dev/blackmagic/dv0}:/dev/blackmagic/dv0
|
- ${BMD_DEVICE_0:-/dev/blackmagic/dv0}:/dev/blackmagic/dv0
|
||||||
- ${BMD_DEVICE_1:-/dev/blackmagic/dv1}:/dev/blackmagic/dv1
|
- ${BMD_DEVICE_1:-/dev/blackmagic/dv1}:/dev/blackmagic/dv1
|
||||||
|
|
@ -126,6 +157,34 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon-worker
|
- wild-dragon-worker
|
||||||
|
|
||||||
|
# Framecache — shared memory ring buffer for SDI + network ingest fan-out.
|
||||||
|
# Runs on every worker node that has capture sources (Blackmagic, Deltacast).
|
||||||
|
# IPC host mode lets all capture sidecars share /dev/shm with this container.
|
||||||
|
# FC_SHM_SIZE can be tuned per node in .env.worker:
|
||||||
|
# Baratheon (251GB RAM): FC_SHM_SIZE=64424509440 (60GB)
|
||||||
|
# zampp1 (93GB RAM): FC_SHM_SIZE=42949672960 (40GB)
|
||||||
|
# zampp2 (18GB RAM): FC_SHM_SIZE=8589934592 (8GB — increase RAM first)
|
||||||
|
framecache:
|
||||||
|
build: ./services/framecache
|
||||||
|
profiles: [capture]
|
||||||
|
restart: unless-stopped
|
||||||
|
ipc: host
|
||||||
|
shm_size: '${FC_SHM_SIZE_GB:-40}gb'
|
||||||
|
environment:
|
||||||
|
FC_PORT: 7435
|
||||||
|
ports:
|
||||||
|
- "7435:7435"
|
||||||
|
volumes:
|
||||||
|
- /dev/shm:/dev/shm
|
||||||
|
networks:
|
||||||
|
- wild-dragon-worker
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:7435/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 3
|
||||||
|
start_period: 5s
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
wild-dragon-worker:
|
wild-dragon-worker:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,11 @@ services:
|
||||||
DOCKER_NETWORK: wild-dragon_wild-dragon
|
DOCKER_NETWORK: wild-dragon_wild-dragon
|
||||||
NODE_IP: ${NODE_IP}
|
NODE_IP: ${NODE_IP}
|
||||||
NODE_HOSTNAME: ${NODE_HOSTNAME:-}
|
NODE_HOSTNAME: ${NODE_HOSTNAME:-}
|
||||||
|
# Bearer mam-api forwards to a node-agent when installing capture drivers
|
||||||
|
# ("Capture Drivers / SDKs" panel). Set to the same value as the agents'
|
||||||
|
# NODE_TOKEN. If empty, agents with an empty NODE_TOKEN accept the call
|
||||||
|
# (dev); agents with a token will reject it (401).
|
||||||
|
NODE_AGENT_TOKEN: ${NODE_AGENT_TOKEN:-}
|
||||||
CAPTURE_TOKEN: ${CAPTURE_TOKEN}
|
CAPTURE_TOKEN: ${CAPTURE_TOKEN}
|
||||||
PLAYOUT_IMAGE: ${PLAYOUT_IMAGE:-wild-dragon-playout:latest}
|
PLAYOUT_IMAGE: ${PLAYOUT_IMAGE:-wild-dragon-playout:latest}
|
||||||
PLAYOUT_AMCP_BASE_PORT: ${PLAYOUT_AMCP_BASE_PORT:-5250}
|
PLAYOUT_AMCP_BASE_PORT: ${PLAYOUT_AMCP_BASE_PORT:-5250}
|
||||||
|
|
@ -107,6 +112,11 @@ services:
|
||||||
dockerfile: Dockerfile.gpu
|
dockerfile: Dockerfile.gpu
|
||||||
image: wild-dragon-worker-gpu:latest
|
image: wild-dragon-worker-gpu:latest
|
||||||
runtime: nvidia
|
runtime: nvidia
|
||||||
|
# Privileged so the promotion scanner can mount the growing-files CIFS share
|
||||||
|
# at /growing (same Approach A as the capture sidecar). Without the share
|
||||||
|
# mounted the scanner watches an empty local dir and never promotes growing
|
||||||
|
# captures to S3.
|
||||||
|
privileged: true
|
||||||
depends_on:
|
depends_on:
|
||||||
- queue
|
- queue
|
||||||
- db
|
- db
|
||||||
|
|
@ -123,7 +133,7 @@ services:
|
||||||
# after the capability-routing split, so import jobs sat unprocessed and
|
# after the capability-routing split, so import jobs sat unprocessed and
|
||||||
# assets stayed `ingesting` forever. import is concurrency-1 + network-
|
# assets stayed `ingesting` forever. import is concurrency-1 + network-
|
||||||
# bound, so one consumer (this heavy/primary worker) is sufficient.
|
# bound, so one consumer (this heavy/primary worker) is sufficient.
|
||||||
WORKER_QUEUES: proxy,conform,trim,import,playout-stage
|
WORKER_QUEUES: proxy,conform,trim,import,playout-stage,promotion
|
||||||
RUN_PROMOTION: "true"
|
RUN_PROMOTION: "true"
|
||||||
PROXY_CONCURRENCY: "2"
|
PROXY_CONCURRENCY: "2"
|
||||||
PLAYOUT_MEDIA_DIR: /media
|
PLAYOUT_MEDIA_DIR: /media
|
||||||
|
|
@ -131,7 +141,9 @@ services:
|
||||||
WORKER_LABEL: "zampp1 / Tesla P4"
|
WORKER_LABEL: "zampp1 / Tesla P4"
|
||||||
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/NVME/MAM/wild-dragon-growing:/growing
|
# NOTE: /growing is NOT a host bind anymore — the promotion scanner mounts
|
||||||
|
# the CIFS landing-zone share there itself (a bind would shadow it). The
|
||||||
|
# mount needs rshared propagation so the in-container CIFS mount is visible.
|
||||||
- /mnt/NVME/MAM/wild-dragon-media:/media
|
- /mnt/NVME/MAM/wild-dragon-media:/media
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon
|
- wild-dragon
|
||||||
|
|
|
||||||
221
docs/design/framecache/PLAN.md
Normal file
221
docs/design/framecache/PLAN.md
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
# Unified Framecache — Implementation Plan
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Replace the current named-FIFO-per-source architecture with a shared-memory
|
||||||
|
ring buffer (framecache) that fans raw video frames from any ingest source to
|
||||||
|
unlimited concurrent consumers with zero-copy reads.
|
||||||
|
|
||||||
|
**Approved design:** docs/design/framecache/DESIGN.md
|
||||||
|
**Branch:** feat/unified-framecache
|
||||||
|
**Roadmap (out of scope here):** RDMA cross-node, AJA, growing-file-while-recording browser playback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Strategy
|
||||||
|
|
||||||
|
Ship in 5 phases. Each phase is independently deployable and leaves the system
|
||||||
|
in a working state. Existing recording workflows are unaffected until Phase 5
|
||||||
|
cuts over.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Framecache Container (foundation)
|
||||||
|
|
||||||
|
**Goal:** Running framecache service with slot registry. No ingest writers yet.
|
||||||
|
|
||||||
|
### 1.1 — Create `services/framecache/` directory structure
|
||||||
|
|
||||||
|
```
|
||||||
|
services/framecache/
|
||||||
|
src/
|
||||||
|
framecache.c # main — slot manager + HTTP API
|
||||||
|
slot.c / slot.h # shm ring buffer lifecycle
|
||||||
|
registry.c # /dev/shm/framecache/registry.json writer
|
||||||
|
http.c # lightweight HTTP server (libmicrohttpd)
|
||||||
|
client/
|
||||||
|
fc_client.c / fc_client.h # consumer library
|
||||||
|
fc_client_node/
|
||||||
|
binding.cc # Node.js N-API addon
|
||||||
|
binding.gyp
|
||||||
|
Dockerfile
|
||||||
|
CMakeLists.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 — Shared memory layout (slot.h)
|
||||||
|
|
||||||
|
Each slot lives at `/dev/shm/framecache/<slot_id>`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define FC_MAGIC 0x46524D43 // "FRMC"
|
||||||
|
#define FC_RING_DEPTH 120 // ~2s at 59.94fps
|
||||||
|
#define FC_HEADER_SIZE 4096 // 4KB header block
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t version; // = 1
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t fps_num;
|
||||||
|
uint32_t fps_den;
|
||||||
|
uint32_t pixel_format; // FC_PIX_UYVY422 = 0
|
||||||
|
uint32_t frame_size; // width * height * 2
|
||||||
|
uint32_t ring_depth; // = FC_RING_DEPTH
|
||||||
|
_Atomic uint64_t write_cursor; // monotonically increasing frame index
|
||||||
|
_Atomic uint64_t dropped_frames;
|
||||||
|
uint8_t _pad[FC_HEADER_SIZE - 48];
|
||||||
|
} fc_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pts_us;
|
||||||
|
uint64_t wall_us;
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t data[]; // frame_size bytes
|
||||||
|
} fc_frame_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
Semaphore: `sem_open("/framecache-<slot_id>-write", ...)` — posted by writer
|
||||||
|
on each new frame, consumers `sem_timedwait` on it.
|
||||||
|
|
||||||
|
### 1.3 — HTTP API (port 7435)
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /slots body: {slot_id, width, height, fps_num, fps_den, source_type}
|
||||||
|
creates shm region, writes registry entry
|
||||||
|
201 {slot_id, shm_path, sem_name}
|
||||||
|
|
||||||
|
GET /slots 200 [{slot_id, width, height, fps_num, fps_den,
|
||||||
|
source_type, write_cursor, dropped_frames,
|
||||||
|
current_fps}]
|
||||||
|
|
||||||
|
GET /slots/:id 200 slot detail
|
||||||
|
DELETE /slots/:id destroys shm + semaphore, removes registry entry, 204
|
||||||
|
GET /health 200 {status: "ok"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 — Registry file
|
||||||
|
|
||||||
|
Written to `/dev/shm/framecache/registry.json` on every slot create/delete.
|
||||||
|
|
||||||
|
### 1.5 — Dockerfile
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM debian:bookworm
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential cmake libmicrohttpd-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY . /src
|
||||||
|
RUN cmake -S /src -B /build -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& cmake --build /build -j$(nproc)
|
||||||
|
EXPOSE 7435
|
||||||
|
CMD ["/build/framecache"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.6 — docker-compose.worker.yml addition
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
framecache:
|
||||||
|
build: ./services/framecache
|
||||||
|
ipc: host
|
||||||
|
shm_size: '60gb'
|
||||||
|
environment:
|
||||||
|
FC_SHM_SIZE: ${FC_SHM_SIZE:-64424509440}
|
||||||
|
FC_PORT: 7435
|
||||||
|
ports:
|
||||||
|
- "7435:7435"
|
||||||
|
volumes:
|
||||||
|
- /dev/shm:/dev/shm
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.7 — Consumer library (fc_client.c)
|
||||||
|
|
||||||
|
```c
|
||||||
|
fc_slot_t *fc_open(const char *slot_id);
|
||||||
|
int fc_read_frame(fc_slot_t *slot, fc_frame_t **out, uint64_t timeout_ms);
|
||||||
|
void fc_close(fc_slot_t *slot);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Commit:** `feat(framecache): phase 1 — framecache container + consumer library`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2 — Deltacast Bridge writes to framecache
|
||||||
|
|
||||||
|
**Goal:** deltacast-bridge writes frames to framecache shm instead of named FIFOs.
|
||||||
|
Legacy FIFO path kept as compile-time fallback (`-DLEGACY_FIFO=ON`) until Phase 5.
|
||||||
|
|
||||||
|
On signal lock:
|
||||||
|
1. POST /slots to framecache HTTP API
|
||||||
|
2. shm_open + mmap the slot
|
||||||
|
3. Video thread writes frame into ring, advances write_cursor atomically, sem_post
|
||||||
|
4. Audio: keeps writing to audio FIFO (unchanged)
|
||||||
|
5. On shutdown: DELETE /slots/:id
|
||||||
|
|
||||||
|
**Commit:** `feat(framecache): phase 2 — deltacast-bridge writes to shm`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3 — Blackmagic DeckLink Bridge
|
||||||
|
|
||||||
|
**Goal:** New decklink-bridge C program mirrors deltacast-bridge, replaces
|
||||||
|
ffmpeg -f decklink direct path.
|
||||||
|
|
||||||
|
- Uses IDeckLinkIterator to enumerate devices
|
||||||
|
- VideoInputFrameArrived callback calls fc_write_frame
|
||||||
|
- Registers slot on signal lock, deregisters on shutdown
|
||||||
|
- Audio stays in FIFO (same as deltacast)
|
||||||
|
|
||||||
|
**Commit:** `feat(framecache): phase 3 — decklink-bridge writes to shm`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4 — capture-manager reads from framecache
|
||||||
|
|
||||||
|
**Goal:** Enables simultaneous growing + proxy + HLS from one SDI input.
|
||||||
|
|
||||||
|
- Node.js N-API addon wrapping fc_open/fc_read_frame/fc_close
|
||||||
|
- capture-manager opens THREE fc_client handles per slot (own cursor each):
|
||||||
|
1. Growing/master ffmpeg feed
|
||||||
|
2. Proxy ffmpeg feed
|
||||||
|
3. HLS preview ffmpeg feed
|
||||||
|
- Each gets a separate rawvideo pipe to ffmpeg
|
||||||
|
- Growing MXF workflow (raw2bmx orchestrator) completely unchanged
|
||||||
|
|
||||||
|
**Commit:** `feat(framecache): phase 4 — capture-manager reads from framecache`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5 — Network ingest (RTMP/SRT) into framecache
|
||||||
|
|
||||||
|
**Goal:** RTMP and SRT sources decoded to raw UYVY422, written into framecache slots.
|
||||||
|
|
||||||
|
- net_ingest process per source: ffmpeg decodes to rawvideo, writes to slot
|
||||||
|
- capture-manager waits for slot, same fc_client consumer pattern
|
||||||
|
- Remove legacy FIFO code once all paths go through framecache
|
||||||
|
|
||||||
|
**Commit:** `feat(framecache): phase 5 — network ingest via framecache`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware / Deployment
|
||||||
|
|
||||||
|
| Node | RAM | /dev/shm | FC_SHM_SIZE |
|
||||||
|
|------|-----|----------|-------------|
|
||||||
|
| Baratheon | 251GB | 126GB | 60GB |
|
||||||
|
| zampp1 | 93GB | 47GB | 40GB |
|
||||||
|
| zampp2 | 18GB (upgrade) | 9.4GB | 8GB |
|
||||||
|
|
||||||
|
Ring buffer per 1080p59.94 source: ~494MB (120 frames × 4.1MB)
|
||||||
|
|
||||||
|
All recorder sidecars require `ipc: host`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Roadmap (not in this branch)
|
||||||
|
|
||||||
|
- Audio in framecache shm
|
||||||
|
- RDMA cross-node slot replication
|
||||||
|
- AJA hardware support
|
||||||
|
- Growing-file-while-recording browser HLS playback
|
||||||
|
- Mastercontrol/playout consumer
|
||||||
834
docs/superpowers/plans/2026-06-01-deltacast-sdi-capture-plan.md
Normal file
834
docs/superpowers/plans/2026-06-01-deltacast-sdi-capture-plan.md
Normal file
|
|
@ -0,0 +1,834 @@
|
||||||
|
# Deltacast SDI Capture — Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Wire up Deltacast VideoMaster SDI cards in the capture service using a C bridge binary that streams raw video to FFmpeg via pipe, with embedded audio via a named FIFO.
|
||||||
|
|
||||||
|
**Architecture:** A `deltacast-capture` C binary opens the VideoMaster board, waits for signal lock, emits a JSON format line to stderr, then streams raw UYVY video frames to stdout and 2-channel PCM audio to a named FIFO. `capture-manager.js` reads the JSON, spawns FFmpeg with `-f rawvideo -i pipe:0` for video and `-f s16le -i <fifo>` for audio, and pipes bridge stdout into FFmpeg stdin. Two concurrent SDK streams share the same board handle — `VHD_SDI_STPROC_DISJOINED_VIDEO` for video and `VHD_SDI_STPROC_DISJOINED_ANC` for audio.
|
||||||
|
|
||||||
|
**Tech Stack:** Deltacast VideoMaster C SDK 6.34.1 (`libvideomasterhd.so`, `libvideomasterhd_audio.so`), C17, CMake, Node.js ES modules, Docker multi-stage build.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
| Action | Path | Responsibility |
|
||||||
|
|---|---|---|
|
||||||
|
| Create | `services/capture/deltacast-bridge/CMakeLists.txt` | Build config for the bridge binary |
|
||||||
|
| Create | `services/capture/deltacast-bridge/main.c` | Bridge: board open, signal detect, video stream, audio thread |
|
||||||
|
| Modify | `services/capture/Dockerfile` | SDK extraction stage, bridge build stage, runtime .so install |
|
||||||
|
| Modify | `services/capture/src/capture-manager.js` | `readFirstStderrLine` helper, deltacast `_buildInputArgs`, bridge lifecycle in `start()`/`stop()` |
|
||||||
|
| Modify | `services/capture/src/routes/capture.js` | Accept `deltacast` as a valid `source_type` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: Bridge CMakeLists.txt
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `services/capture/deltacast-bridge/CMakeLists.txt`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the CMakeLists.txt**
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(deltacast-bridge C)
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
|
||||||
|
set(SDK_ROOT "/sdk" CACHE PATH "Path to extracted VideoMaster SDK")
|
||||||
|
|
||||||
|
add_executable(deltacast-capture main.c)
|
||||||
|
|
||||||
|
target_include_directories(deltacast-capture PRIVATE
|
||||||
|
${SDK_ROOT}/include/videomaster
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_directories(deltacast-capture PRIVATE
|
||||||
|
${SDK_ROOT}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(deltacast-capture PRIVATE
|
||||||
|
videomasterhd
|
||||||
|
videomasterhd_audio
|
||||||
|
pthread
|
||||||
|
)
|
||||||
|
|
||||||
|
# Embed the SDK RPATH so the binary finds the .so at runtime
|
||||||
|
set_target_properties(deltacast-capture PROPERTIES
|
||||||
|
INSTALL_RPATH "/usr/local/lib/deltacast"
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/deltacast-bridge/CMakeLists.txt
|
||||||
|
git commit -m "build(capture): add CMakeLists for deltacast-capture bridge binary"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: Bridge main.c
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `services/capture/deltacast-bridge/main.c`
|
||||||
|
|
||||||
|
The binary: parses CLI args, opens the board, waits for signal lock, emits one JSON line to stderr, then spawns an audio thread writing to a FIFO and runs a video capture loop writing raw UYVY frames to stdout.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create the bridge source file**
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* services/capture/deltacast-bridge/main.c
|
||||||
|
*
|
||||||
|
* Deltacast VideoMaster SDI capture bridge.
|
||||||
|
* Writes raw UYVY video to stdout and stereo PCM to a named FIFO.
|
||||||
|
* Emits one JSON line to stderr on signal lock before streaming starts.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* deltacast-capture --device <N> --port <N> --audio-pipe <path>
|
||||||
|
* [--signal-timeout <sec>]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "VideoMasterHD_Core.h"
|
||||||
|
#include "VideoMasterHD_Sdi.h"
|
||||||
|
#include "VideoMasterHD_Sdi_Audio.h"
|
||||||
|
|
||||||
|
/* ── Globals ─────────────────────────────────────────────────────────── */
|
||||||
|
static atomic_int g_stop = 0;
|
||||||
|
|
||||||
|
static void on_signal(int s) { (void)s; atomic_store(&g_stop, 1); }
|
||||||
|
|
||||||
|
/* ── Stream type by port index ───────────────────────────────────────── */
|
||||||
|
static ULONG rx_streamtype(unsigned port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return VHD_ST_RX0;
|
||||||
|
case 1: return VHD_ST_RX1;
|
||||||
|
case 2: return VHD_ST_RX2;
|
||||||
|
case 3: return VHD_ST_RX3;
|
||||||
|
default: return VHD_ST_RX0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Loopback board property by port index ───────────────────────────── */
|
||||||
|
static ULONG loopback_prop(unsigned port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return VHD_CORE_BP_PASSIVE_LOOPBACK_0;
|
||||||
|
case 1: return VHD_CORE_BP_PASSIVE_LOOPBACK_1;
|
||||||
|
case 2: return VHD_CORE_BP_PASSIVE_LOOPBACK_2;
|
||||||
|
case 3: return VHD_CORE_BP_PASSIVE_LOOPBACK_3;
|
||||||
|
default: return VHD_CORE_BP_PASSIVE_LOOPBACK_0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Video standard → width/height/fps/interlaced ───────────────────── */
|
||||||
|
typedef struct { int width, height, fps_num, fps_den; int interlaced; } VideoInfo;
|
||||||
|
|
||||||
|
static VideoInfo video_info(VHD_VIDEOSTANDARD std, VHD_CLOCKDIVISOR div) {
|
||||||
|
int ntsc = (div == VHD_CLOCKDIV_1001);
|
||||||
|
switch (std) {
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_25Hz: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_30Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_24Hz: return (VideoInfo){1920,1080,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_50Hz: return (VideoInfo){1920,1080,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_60Hz: return (VideoInfo){1920,1080,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_24Hz: return (VideoInfo){1920,1080,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_25Hz: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_30Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080i_50Hz: return (VideoInfo){1920,1080,25,1,1};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080i_60Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,1};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_50Hz: return (VideoInfo){1280,720,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_60Hz: return (VideoInfo){1280,720,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_25Hz: return (VideoInfo){1280,720,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_30Hz: return (VideoInfo){1280,720,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_24Hz: return (VideoInfo){1280,720,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_24Hz: return (VideoInfo){3840,2160,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_25Hz: return (VideoInfo){3840,2160,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_30Hz: return (VideoInfo){3840,2160,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_50Hz: return (VideoInfo){3840,2160,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_60Hz: return (VideoInfo){3840,2160,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S259M_NTSC_480: return (VideoInfo){720,480,ntsc?30000:30,ntsc?1001:1,1};
|
||||||
|
default: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Audio thread ────────────────────────────────────────────────────── */
|
||||||
|
typedef struct {
|
||||||
|
HANDLE board;
|
||||||
|
unsigned port;
|
||||||
|
ULONG video_std;
|
||||||
|
ULONG clock_div;
|
||||||
|
const char *fifo_path;
|
||||||
|
} AudioArgs;
|
||||||
|
|
||||||
|
static void *audio_thread(void *arg) {
|
||||||
|
AudioArgs *a = (AudioArgs *)arg;
|
||||||
|
|
||||||
|
HANDLE stream = NULL;
|
||||||
|
ULONG r = VHD_OpenStreamHandle(a->board, rx_streamtype(a->port),
|
||||||
|
VHD_SDI_STPROC_DISJOINED_ANC,
|
||||||
|
NULL, &stream, NULL);
|
||||||
|
if (r != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "[audio] VHD_OpenStreamHandle failed: %lu\n", r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VHD_SetStreamProperty(stream, VHD_SDI_SP_VIDEO_STANDARD, a->video_std);
|
||||||
|
VHD_SetStreamProperty(stream, VHD_SDI_SP_CLOCK_SYSTEM, a->clock_div);
|
||||||
|
VHD_SetStreamProperty(stream, VHD_CORE_SP_TRANSFER_SCHEME, VHD_TRANSFER_SLAVED);
|
||||||
|
|
||||||
|
/* Stereo pair, 16-bit, 48kHz on group 0 channel 0 */
|
||||||
|
ULONG max_samples = VHD_GetNbSamples((VHD_VIDEOSTANDARD)a->video_std,
|
||||||
|
(VHD_CLOCKDIVISOR)a->clock_div,
|
||||||
|
VHD_ASR_48000, 0);
|
||||||
|
ULONG block_size = VHD_GetBlockSize(VHD_AF_16, VHD_AM_STEREO);
|
||||||
|
ULONG buf_sz = (max_samples + 4) * block_size; /* +4 for 29.97 variation */
|
||||||
|
unsigned char *buf = calloc(1, buf_sz);
|
||||||
|
if (!buf) { VHD_CloseStreamHandle(stream); return NULL; }
|
||||||
|
|
||||||
|
VHD_AUDIOINFO ai;
|
||||||
|
memset(&ai, 0, sizeof(ai));
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].Mode = VHD_AM_STEREO;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].BufferFormat = VHD_AF_16;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].pData = buf;
|
||||||
|
|
||||||
|
if (VHD_StartStream(stream) != VHDERR_NOERROR) {
|
||||||
|
free(buf); VHD_CloseStreamHandle(stream); return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open FIFO for writing — blocks until FFmpeg opens the read end */
|
||||||
|
int fd = open(a->fifo_path, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "[audio] open FIFO failed: %s\n", strerror(errno));
|
||||||
|
VHD_StopStream(stream); VHD_CloseStreamHandle(stream); free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
while (!atomic_load(&g_stop)) {
|
||||||
|
r = VHD_LockSlotHandle(stream, &slot);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].DataSize = buf_sz;
|
||||||
|
if (VHD_SlotExtractAudio(slot, &ai) == VHDERR_NOERROR) {
|
||||||
|
ULONG sz = ai.pAudioGroups[0].pAudioChannels[0].DataSize;
|
||||||
|
if (sz > 0) write(fd, buf, sz);
|
||||||
|
}
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
} else if (r != VHDERR_TIMEOUT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
VHD_StopStream(stream);
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main ────────────────────────────────────────────────────────────── */
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
unsigned device_id = 0;
|
||||||
|
unsigned port_id = 0;
|
||||||
|
int sig_timeout = 30;
|
||||||
|
const char *audio_pipe = NULL;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--device") && i+1 < argc) device_id = (unsigned)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--port") && i+1 < argc) port_id = (unsigned)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--audio-pipe") && i+1 < argc) audio_pipe = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--signal-timeout") && i+1 < argc) sig_timeout = atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
|
||||||
|
/* ── Init API ─────────────────────────────────────────────────── */
|
||||||
|
ULONG dll_ver, nb_boards;
|
||||||
|
if (VHD_GetApiInfo(&dll_ver, &nb_boards) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_GetApiInfo failed\"}\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (device_id >= nb_boards) {
|
||||||
|
fprintf(stderr, "{\"error\":\"board %u not found (%lu detected)\"}\n", device_id, nb_boards);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Open board ───────────────────────────────────────────────── */
|
||||||
|
HANDLE board = NULL;
|
||||||
|
if (VHD_OpenBoardHandle(device_id, &board, NULL, 0) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_OpenBoardHandle failed for board %u\"}\n", device_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable passive (relay) loopback so RX is live */
|
||||||
|
VHD_SetBoardProperty(board, loopback_prop(port_id), FALSE);
|
||||||
|
|
||||||
|
/* ── Wait for signal lock ──────────────────────────────────────── */
|
||||||
|
ULONG video_std = (ULONG)NB_VHD_VIDEOSTANDARDS;
|
||||||
|
struct timespec deadline;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
||||||
|
deadline.tv_sec += sig_timeout;
|
||||||
|
|
||||||
|
while (!atomic_load(&g_stop)) {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
if (now.tv_sec > deadline.tv_sec ||
|
||||||
|
(now.tv_sec == deadline.tv_sec && now.tv_nsec >= deadline.tv_nsec)) break;
|
||||||
|
|
||||||
|
VHD_GetChannelProperty(board, VHD_RX_CHANNEL, port_id,
|
||||||
|
VHD_SDI_CP_VIDEO_STANDARD, &video_std);
|
||||||
|
if (video_std != (ULONG)NB_VHD_VIDEOSTANDARDS) break;
|
||||||
|
|
||||||
|
struct timespec ts = {0, 200000000L}; /* 200ms */
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atomic_load(&g_stop) || video_std == (ULONG)NB_VHD_VIDEOSTANDARDS) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"error\":\"no signal on board %u port %u within %ds\"}\n",
|
||||||
|
device_id, port_id, sig_timeout);
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG clock_div = VHD_CLOCKDIV_1;
|
||||||
|
VHD_GetChannelProperty(board, VHD_RX_CHANNEL, port_id,
|
||||||
|
VHD_SDI_CP_CLOCK_DIVISOR, &clock_div);
|
||||||
|
|
||||||
|
VideoInfo vi = video_info((VHD_VIDEOSTANDARD)video_std,
|
||||||
|
(VHD_CLOCKDIVISOR)clock_div);
|
||||||
|
|
||||||
|
/* ── Emit format JSON to stderr (one line, flushed) ─────────────── */
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"width\":%d,\"height\":%d,\"fps_num\":%d,\"fps_den\":%d,"
|
||||||
|
"\"interlaced\":%s,\"pix_fmt\":\"uyvy422\","
|
||||||
|
"\"audio_channels\":2,\"audio_rate\":48000,"
|
||||||
|
"\"device\":%u,\"port\":%u}\n",
|
||||||
|
vi.width, vi.height, vi.fps_num, vi.fps_den,
|
||||||
|
vi.interlaced ? "true" : "false",
|
||||||
|
device_id, port_id);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
/* ── Open video stream ───────────────────────────────────────────── */
|
||||||
|
HANDLE video_stream = NULL;
|
||||||
|
if (VHD_OpenStreamHandle(board, rx_streamtype(port_id),
|
||||||
|
VHD_SDI_STPROC_DISJOINED_VIDEO,
|
||||||
|
NULL, &video_stream, NULL) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_OpenStreamHandle (video) failed\"}\n");
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
VHD_SetStreamProperty(video_stream, VHD_SDI_SP_VIDEO_STANDARD, video_std);
|
||||||
|
VHD_SetStreamProperty(video_stream, VHD_SDI_SP_CLOCK_SYSTEM, clock_div);
|
||||||
|
VHD_SetStreamProperty(video_stream, VHD_CORE_SP_TRANSFER_SCHEME, VHD_TRANSFER_SLAVED);
|
||||||
|
VHD_SetStreamProperty(video_stream, VHD_CORE_SP_SLOTS_QUEUE_DEPTH, 8);
|
||||||
|
|
||||||
|
/* ── Launch audio thread (FIFO open blocks until FFmpeg connects) ── */
|
||||||
|
pthread_t audio_tid = 0;
|
||||||
|
AudioArgs audio_args = { board, port_id, video_std, clock_div, audio_pipe };
|
||||||
|
if (audio_pipe) {
|
||||||
|
pthread_create(&audio_tid, NULL, audio_thread, &audio_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Start video stream ──────────────────────────────────────────── */
|
||||||
|
if (VHD_StartStream(video_stream) != VHDERR_NOERROR) {
|
||||||
|
atomic_store(&g_stop, 1);
|
||||||
|
if (audio_tid) pthread_join(audio_tid, NULL);
|
||||||
|
VHD_CloseStreamHandle(video_stream);
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Video capture loop ──────────────────────────────────────────── */
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
while (!atomic_load(&g_stop)) {
|
||||||
|
ULONG r = VHD_LockSlotHandle(video_stream, &slot);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
BYTE *buf = NULL;
|
||||||
|
ULONG sz = 0;
|
||||||
|
if (VHD_GetSlotBuffer(slot, VHD_SDI_BT_VIDEO, &buf, &sz) == VHDERR_NOERROR) {
|
||||||
|
ULONG written = 0;
|
||||||
|
while (written < sz) {
|
||||||
|
ssize_t n = write(STDOUT_FILENO, buf + written, sz - written);
|
||||||
|
if (n <= 0) { atomic_store(&g_stop, 1); break; }
|
||||||
|
written += (ULONG)n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
} else if (r != VHDERR_TIMEOUT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cleanup ─────────────────────────────────────────────────────── */
|
||||||
|
VHD_StopStream(video_stream);
|
||||||
|
VHD_CloseStreamHandle(video_stream);
|
||||||
|
if (audio_tid) pthread_join(audio_tid, NULL);
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/deltacast-bridge/main.c
|
||||||
|
git commit -m "feat(capture): add deltacast-capture bridge binary source"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Dockerfile — SDK extraction + bridge build + runtime
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `services/capture/Dockerfile`
|
||||||
|
|
||||||
|
The existing Dockerfile has three logical sections: FFmpeg build, runtime. We add two new stages before FFmpeg and patch the runtime stage.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Read the current Dockerfile**
|
||||||
|
|
||||||
|
Read `services/capture/Dockerfile` and verify it starts with `FROM debian:bookworm AS ffmpeg-builder`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Prepend two new stages and patch runtime**
|
||||||
|
|
||||||
|
The full new Dockerfile:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ── Stage 0: Extract Deltacast VideoMaster SDK ───────────────────────────
|
||||||
|
FROM debian:bookworm AS sdk-extractor
|
||||||
|
COPY videomaster-linux.x64-6.34.1-dev.tar.gz /tmp/
|
||||||
|
RUN mkdir -p /sdk && tar -xzf /tmp/videomaster-linux.x64-6.34.1-dev.tar.gz -C /sdk
|
||||||
|
|
||||||
|
# ── Stage 1: Build deltacast-capture bridge binary ───────────────────────
|
||||||
|
FROM debian:bookworm AS bridge-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY --from=sdk-extractor /sdk /sdk
|
||||||
|
COPY deltacast-bridge/ /bridge/
|
||||||
|
RUN cmake -S /bridge -B /bridge/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DSDK_ROOT=/sdk \
|
||||||
|
&& cmake --build /bridge/build -j$(nproc)
|
||||||
|
|
||||||
|
# ── Stage 2: Build FFmpeg with DeckLink + NVENC (HEVC/H264) support ──────
|
||||||
|
# (unchanged — keep original content here)
|
||||||
|
FROM debian:bookworm AS ffmpeg-builder
|
||||||
|
# ... (rest of the existing ffmpeg-builder stage unchanged) ...
|
||||||
|
|
||||||
|
# ── Stage 3: Runtime image ───────────────────────────────────────────────
|
||||||
|
FROM node:20-bookworm
|
||||||
|
|
||||||
|
# Runtime deps for compiled ffmpeg libs (unchanged)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libx264-164 libx265-199 libvpx7 libopus0 libmp3lame0 \
|
||||||
|
libsrt1.5-openssl libzmq5 libstdc++6 libc++1 libc++abi1 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy compiled ffmpeg/ffprobe (unchanged)
|
||||||
|
COPY --from=ffmpeg-builder /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
|
||||||
|
COPY --from=ffmpeg-builder /usr/local/bin/ffprobe /usr/local/bin/ffprobe
|
||||||
|
COPY --from=ffmpeg-builder /usr/local/lib/ /usr/local/lib/
|
||||||
|
|
||||||
|
# DeckLink runtime .so (unchanged)
|
||||||
|
COPY lib/libDeckLinkAPI.so /usr/lib/libDeckLinkAPI.so
|
||||||
|
COPY lib/libDeckLinkPreviewAPI.so /usr/lib/libDeckLinkPreviewAPI.so
|
||||||
|
|
||||||
|
# Deltacast bridge binary + SDK runtime libs
|
||||||
|
COPY --from=bridge-builder /bridge/build/deltacast-capture /usr/local/bin/deltacast-capture
|
||||||
|
COPY --from=sdk-extractor /sdk/lib/libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/
|
||||||
|
COPY --from=sdk-extractor /sdk/lib/libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/
|
||||||
|
RUN ln -sf libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd.so.6 \
|
||||||
|
&& ln -sf libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd.so \
|
||||||
|
&& ln -sf libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd_audio.so.6 \
|
||||||
|
&& ln -sf libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd_audio.so \
|
||||||
|
&& ldconfig /usr/local/lib/deltacast \
|
||||||
|
&& ldconfig
|
||||||
|
|
||||||
|
RUN mkdir -p /live /growing
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3001
|
||||||
|
CMD ["node", "src/index.js"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation note:** Edit the existing Dockerfile. Prepend the two new FROM stages (sdk-extractor, bridge-builder) before the existing `FROM debian:bookworm AS ffmpeg-builder` line. Then in the final runtime stage, add the Deltacast `COPY` and `RUN` lines after the DeckLink `.so` lines (before the `RUN mkdir -p /live /growing` line).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/Dockerfile
|
||||||
|
git commit -m "build(capture): add Deltacast SDK extraction and bridge build stages to Dockerfile"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: capture-manager.js — `readFirstStderrLine` helper
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `services/capture/src/capture-manager.js` (add helper near top, after imports)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add the helper function after the existing imports (after line 6 `import { v4 as uuidv4 } from 'uuid';`)**
|
||||||
|
|
||||||
|
```js
|
||||||
|
/**
|
||||||
|
* Reads the first line from a spawned process's stderr stream.
|
||||||
|
* Resolves with the parsed JSON object when the first '\n' arrives.
|
||||||
|
* Rejects if the process exits with a non-zero code before emitting a line,
|
||||||
|
* or if timeoutMs elapses.
|
||||||
|
*/
|
||||||
|
function readFirstStderrLine(proc, timeoutMs = 35_000) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let buf = '';
|
||||||
|
let settled = false;
|
||||||
|
const settle = (fn) => { if (settled) return; settled = true; fn(); };
|
||||||
|
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
settle(() => reject(new Error(`deltacast-capture: timed out waiting for format JSON after ${timeoutMs}ms`)));
|
||||||
|
}, timeoutMs);
|
||||||
|
|
||||||
|
proc.stderr.setEncoding('utf8');
|
||||||
|
proc.stderr.on('data', (chunk) => {
|
||||||
|
buf += chunk;
|
||||||
|
const nl = buf.indexOf('\n');
|
||||||
|
if (nl === -1) return;
|
||||||
|
const line = buf.slice(0, nl).trim();
|
||||||
|
clearTimeout(timer);
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(line);
|
||||||
|
if (parsed.error) {
|
||||||
|
settle(() => reject(new Error(`deltacast-capture: ${parsed.error}`)));
|
||||||
|
} else {
|
||||||
|
settle(() => resolve(parsed));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
settle(() => reject(new Error(`deltacast-capture: invalid JSON on stderr: ${line}`)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
proc.on('exit', (code) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
settle(() => reject(new Error(`deltacast-capture: exited with code ${code} before emitting format JSON`)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/src/capture-manager.js
|
||||||
|
git commit -m "feat(capture): add readFirstStderrLine helper for deltacast bridge handshake"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: capture-manager.js — Deltacast `_buildInputArgs`
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `services/capture/src/capture-manager.js` — replace the `deltacast` branch of `_buildInputArgs` (currently lines 160–191)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Replace the existing deltacast branch**
|
||||||
|
|
||||||
|
Find the block starting with `// Deltacast SDI via VideoMaster SDK FFmpeg plugin.` and ending at the closing `}` of the `if (sourceType === 'deltacast')` block. Replace the entire `if (sourceType === 'deltacast') { ... }` block with:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (sourceType === 'deltacast') {
|
||||||
|
const idx = (typeof device === 'number' || /^\d+$/.test(String(device)))
|
||||||
|
? parseInt(device, 10) : 0;
|
||||||
|
const audioFifo = `/tmp/dc-audio-${this._sessionIdForBridge}`;
|
||||||
|
|
||||||
|
// Create the audio FIFO before spawning the bridge.
|
||||||
|
const { execSync: _exec } = await import('child_process');
|
||||||
|
try { _exec(`mkfifo ${audioFifo}`); } catch (_) { /* may already exist */ }
|
||||||
|
|
||||||
|
const bridge = spawn('deltacast-capture', [
|
||||||
|
'--device', String(idx),
|
||||||
|
'--port', String(idx),
|
||||||
|
'--audio-pipe', audioFifo,
|
||||||
|
'--signal-timeout', '30',
|
||||||
|
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
|
|
||||||
|
// Log bridge stderr after the first line (non-JSON diagnostic output)
|
||||||
|
let firstLineDone = false;
|
||||||
|
bridge.stderr.on('data', (d) => {
|
||||||
|
if (firstLineDone) console.error(`[deltacast-bridge] ${d}`);
|
||||||
|
else if (d.toString().includes('\n')) firstLineDone = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const fmt = await readFirstStderrLine(bridge, 35_000);
|
||||||
|
// fmt: { width, height, fps_num, fps_den, interlaced, pix_fmt,
|
||||||
|
// audio_channels, audio_rate, device, port }
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputArgs: [
|
||||||
|
'-f', 'rawvideo',
|
||||||
|
'-pix_fmt', fmt.pix_fmt,
|
||||||
|
'-video_size', `${fmt.width}x${fmt.height}`,
|
||||||
|
'-framerate', `${fmt.fps_num}/${fmt.fps_den}`,
|
||||||
|
'-i', 'pipe:0',
|
||||||
|
'-f', 's16le',
|
||||||
|
'-ar', String(fmt.audio_rate),
|
||||||
|
'-ac', String(fmt.audio_channels),
|
||||||
|
'-i', audioFifo,
|
||||||
|
],
|
||||||
|
isNetwork: false,
|
||||||
|
bridgeProcess: bridge,
|
||||||
|
audioFifo,
|
||||||
|
interlaced: !!fmt.interlaced,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/src/capture-manager.js
|
||||||
|
git commit -m "feat(capture): replace deltacast _buildInputArgs stub with real bridge spawn"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: capture-manager.js — `start()` bridge lifecycle + `stop()` cleanup
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `services/capture/src/capture-manager.js`
|
||||||
|
|
||||||
|
Four changes to `start()` and one to `stop()`.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Store session ID before `_buildInputArgs` call**
|
||||||
|
|
||||||
|
In `start()`, before the `const { inputArgs, isNetwork } = await this._buildInputArgs(...)` call (currently around line 307), add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
this._sessionIdForBridge = sessionId;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Store bridge state after `_buildInputArgs` returns**
|
||||||
|
|
||||||
|
After `const { inputArgs, isNetwork } = await this._buildInputArgs(...)`, change the destructuring to also capture `bridgeProcess` and `audioFifo`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { inputArgs, isNetwork, bridgeProcess = null, audioFifo = null, interlaced = false } = await this._buildInputArgs({
|
||||||
|
sourceType, device, sourceUrl, listen, listenPort, streamKey,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Pipe bridge stdout into FFmpeg stdin for deltacast**
|
||||||
|
|
||||||
|
After `const hiresProcess = spawn('ffmpeg', hiresArgs, { stdio: hiresStdio });`, add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// For deltacast, the bridge writes raw video to its stdout.
|
||||||
|
// Pipe it into FFmpeg's stdin so FFmpeg reads -i pipe:0.
|
||||||
|
if (bridgeProcess) {
|
||||||
|
bridgeProcess.stdout.pipe(hiresProcess.stdin);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Add bridge to `processes` map and `audioFifo` to `currentSession`**
|
||||||
|
|
||||||
|
Change the existing `const processes = { hires: hiresProcess };` line to:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const processes = { hires: hiresProcess };
|
||||||
|
if (bridgeProcess) processes.bridge = bridgeProcess;
|
||||||
|
```
|
||||||
|
|
||||||
|
And in the `this.state.currentSession = { ... }` object (near the end of `start()`), add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
audioFifo,
|
||||||
|
```
|
||||||
|
|
||||||
|
to the object literal (alongside `sourceType`, `device`, etc.).
|
||||||
|
|
||||||
|
- [ ] **Step 5: Fix deinterlace filter to include deltacast interlaced signals**
|
||||||
|
|
||||||
|
Find the line (currently ~321):
|
||||||
|
```js
|
||||||
|
const sdiFilterArgs = (sourceType === 'sdi') ? ['-vf', 'yadif=mode=1:deint=1'] : [];
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace with:
|
||||||
|
```js
|
||||||
|
const isInterlacedSource = sourceType === 'sdi' || (sourceType === 'deltacast' && interlaced);
|
||||||
|
const sdiFilterArgs = isInterlacedSource ? ['-vf', 'yadif=mode=1:deint=1'] : [];
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Include deltacast in the HLS split-output branch**
|
||||||
|
|
||||||
|
Find the line (currently ~334):
|
||||||
|
```js
|
||||||
|
if (sourceType === 'sdi' && this._assetIdForHls) {
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace with:
|
||||||
|
```js
|
||||||
|
if ((sourceType === 'sdi' || sourceType === 'deltacast') && this._assetIdForHls) {
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 7: Kill bridge in `stop()` and clean up FIFO**
|
||||||
|
|
||||||
|
In the `stop()` method, find the existing kill block:
|
||||||
|
```js
|
||||||
|
if (processes.hires) processes.hires.kill('SIGINT');
|
||||||
|
if (processes.proxy) processes.proxy.kill('SIGINT');
|
||||||
|
if (processes.hls) { try { processes.hls.kill('SIGINT'); } catch (_) {} }
|
||||||
|
```
|
||||||
|
|
||||||
|
Add a bridge kill and FIFO cleanup:
|
||||||
|
```js
|
||||||
|
if (processes.hires) processes.hires.kill('SIGINT');
|
||||||
|
if (processes.proxy) processes.proxy.kill('SIGINT');
|
||||||
|
if (processes.hls) { try { processes.hls.kill('SIGINT'); } catch (_) {} }
|
||||||
|
if (processes.bridge) { try { processes.bridge.kill('SIGINT'); } catch (_) {} }
|
||||||
|
```
|
||||||
|
|
||||||
|
Then after the existing `await Promise.all(uploadPromises);` block (around line 462), add FIFO cleanup:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (currentSession.audioFifo) {
|
||||||
|
try { (await import('node:fs')).unlinkSync(currentSession.audioFifo); } catch (_) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 8: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/src/capture-manager.js
|
||||||
|
git commit -m "feat(capture): wire bridge process lifecycle into start/stop for deltacast"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: routes/capture.js — Accept `deltacast` source_type
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `services/capture/src/routes/capture.js` (line 329)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Find the source_type validation block in `/start` handler (around line 318)**
|
||||||
|
|
||||||
|
Current code:
|
||||||
|
```js
|
||||||
|
} else {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: `Unknown source_type: ${source_type}. Must be sdi, srt, or rtmp`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This `else` branch fires when source_type isn't `sdi`, `srt`, or `rtmp`. Add `deltacast` to the accepted list.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add deltacast validation before the else block**
|
||||||
|
|
||||||
|
After the `} else if (source_type === 'srt' || source_type === 'rtmp') {` block, add:
|
||||||
|
|
||||||
|
```js
|
||||||
|
} else if (source_type === 'deltacast') {
|
||||||
|
if (device === undefined || device === null) {
|
||||||
|
return res.status(400).json({ error: 'deltacast source requires: device (board/port index)' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: `Unknown source_type: ${source_type}. Must be sdi, srt, rtmp, or deltacast`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add services/capture/src/routes/capture.js
|
||||||
|
git commit -m "feat(capture): accept deltacast as valid source_type in /start handler"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 8: Smoke test — verify the build and Node.js changes
|
||||||
|
|
||||||
|
**Files:** None created.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Verify the bridge compiles on the capture host (or in Docker)**
|
||||||
|
|
||||||
|
On the Deltacast machine (once it is available), run:
|
||||||
|
```bash
|
||||||
|
cd services/capture
|
||||||
|
tar -xzf ../../videomaster-linux.x64-6.34.1-dev.tar.gz -C /tmp/sdk
|
||||||
|
cmake -S deltacast-bridge -B /tmp/bridge-build -DSDK_ROOT=/tmp/sdk -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build /tmp/bridge-build -j$(nproc)
|
||||||
|
ls -lh /tmp/bridge-build/deltacast-capture
|
||||||
|
```
|
||||||
|
Expected: binary present, size ~50–200KB.
|
||||||
|
|
||||||
|
Until the hardware machine is available, verify the CMakeLists.txt syntax is correct by running the configure step only:
|
||||||
|
```bash
|
||||||
|
cmake -S services/capture/deltacast-bridge -B /tmp/bridge-test \
|
||||||
|
-DSDK_ROOT=C:/Users/zacga/Nextcloud/Claude/Projects/Dragonflight \
|
||||||
|
--check-system-vars 2>&1 | head -20
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify capture-manager.js has no syntax errors**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd services/capture
|
||||||
|
node --input-type=module < src/capture-manager.js 2>&1 | head -5
|
||||||
|
```
|
||||||
|
Expected: no output (file imports fine) or a module-not-found error for uuid (acceptable — the file is correct).
|
||||||
|
|
||||||
|
- [ ] **Step 3: Verify routes/capture.js has no syntax errors**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --input-type=module < services/capture/src/routes/capture.js 2>&1 | head -5
|
||||||
|
```
|
||||||
|
Expected: no output or dependency error only.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Confirm deltacast recorder creation is rejected correctly without device param**
|
||||||
|
|
||||||
|
Start the capture service locally (if possible) and POST:
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://localhost:3001/capture/start \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"project_id":"test","clip_name":"test","source_type":"deltacast"}' | jq .
|
||||||
|
```
|
||||||
|
Expected response:
|
||||||
|
```json
|
||||||
|
{"error":"deltacast source requires: device (board/port index)"}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: Final commit if any fixups were needed**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "fix(capture): deltacast smoke-test fixups"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hardware Validation Checklist (run on the Deltacast machine)
|
||||||
|
|
||||||
|
After the hardware machine is available:
|
||||||
|
|
||||||
|
1. Build the Docker image: `docker compose build capture`
|
||||||
|
2. Create a recorder with `source_type=deltacast`, `device=0`
|
||||||
|
3. Confirm capture container logs show the JSON format line within 5s of feed going live
|
||||||
|
4. Confirm recorder status shows `signal: "receiving"`
|
||||||
|
5. Record a 30s clip → verify asset created, proxy + HLS generated
|
||||||
|
6. Test stop mid-record → file finalized correctly
|
||||||
|
7. Test no-signal path → recorder stays idle, no asset created
|
||||||
|
8. Test container restart mid-record → existing asset finalized via `/finalize` endpoint
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
# Deltacast SDI Capture — Design Spec
|
||||||
|
**Date:** 2026-06-01
|
||||||
|
**Status:** Approved
|
||||||
|
**Approach:** Bridge binary (Option B2)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Dragonflight supports SDI ingest via Blackmagic DeckLink. Deltacast VideoMaster cards are a second hardware target. The VideoMaster SDK (v6.34.1) ships C++ headers and shared libraries but no FFmpeg demuxer plugin — there is no mainline FFmpeg `-f deltacast` input device. The `capture-manager.js` stub exists but falls back to a lavfi test card on all deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
|
||||||
|
Write a small C++ bridge binary (`deltacast-capture`) using the VideoMaster C++ Wrapper SDK. The bridge:
|
||||||
|
1. Detects signal format on startup, writes one JSON line to stderr
|
||||||
|
2. Streams raw YUV video frames to stdout
|
||||||
|
3. Streams raw PCM audio to a named FIFO
|
||||||
|
|
||||||
|
`capture-manager.js` reads the JSON handshake, then spawns FFmpeg with `-f rawvideo -i pipe:0` (video from bridge stdout) and `-f s16le -i <fifo>` (audio from FIFO). The existing HEVC NVENC / ProRes encode pipeline is unchanged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ capture container │
|
||||||
|
│ │
|
||||||
|
│ capture-manager.js │
|
||||||
|
│ │ │
|
||||||
|
│ ├─ spawn deltacast-capture --device 0 --port 0 │
|
||||||
|
│ │ --audio-pipe /tmp/dc-audio-{sessionId} │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ├─ stderr: JSON format line (one-time handshake) │
|
||||||
|
│ │ ├─ stdout: raw YUV frames (continuous) │
|
||||||
|
│ │ └─ FIFO: raw PCM audio (continuous) │
|
||||||
|
│ │ │
|
||||||
|
│ └─ spawn ffmpeg │
|
||||||
|
│ -f rawvideo -pix_fmt uyvy422 -s WxH -r FPS/1 │
|
||||||
|
│ -i pipe:0 ← piped from bridge stdout │
|
||||||
|
│ -f s16le -ar 48000 -ac <N> │
|
||||||
|
│ -i /tmp/dc-audio-{sessionId} │
|
||||||
|
│ <hevc_nvenc / prores / h264 encode args> │
|
||||||
|
│ <S3 pipe or growing-file output> │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### New files
|
||||||
|
- `services/capture/deltacast-bridge/CMakeLists.txt`
|
||||||
|
- `services/capture/deltacast-bridge/main.cpp`
|
||||||
|
|
||||||
|
### Modified files
|
||||||
|
- `services/capture/src/capture-manager.js` — `_buildInputArgs()` deltacast branch; `start()` and `stop()` bridge lifecycle
|
||||||
|
- `services/capture/Dockerfile` — SDK extraction stage, bridge build stage, runtime `.so` install
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The `deltacast-capture` Binary
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
```
|
||||||
|
deltacast-capture
|
||||||
|
--device <N> Board index (0-based)
|
||||||
|
--port <N> RX port index (0-based)
|
||||||
|
--audio-pipe <path> Named FIFO path for PCM audio output
|
||||||
|
[--signal-timeout <sec=30>]
|
||||||
|
[--audio-groups <N=2>] Number of SDI audio groups (2 groups = 8 channels)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Startup sequence
|
||||||
|
1. `Board::open(device, loopback_restore_cb)`
|
||||||
|
2. Disable loopback on `port`
|
||||||
|
3. `board.sdi().open_stream(rx_streamtype(port), VHD_SDI_STPROC_DISJOINED_VIDEO)`
|
||||||
|
4. Poll `wait_for_input()` up to `--signal-timeout` seconds
|
||||||
|
5. On timeout → write `{"error":"no signal","device":N,"port":N}` to stderr, exit 1
|
||||||
|
6. Detect `video_standard`, `clock_divisor`, `interface` → map to width/height/fps/pix_fmt/interlaced
|
||||||
|
7. Write one JSON line to stderr (flushed):
|
||||||
|
```json
|
||||||
|
{"width":1920,"height":1080,"fps_num":25,"fps_den":1,"pix_fmt":"uyvy422","interlaced":false,"audio_channels":8,"audio_rate":48000,"device":0,"port":0}
|
||||||
|
```
|
||||||
|
8. Set queue depth = 8, `rx_stream.start()`
|
||||||
|
9. Capture loop: `pop_slot()` → write video buffer to stdout → extract audio → write PCM to FIFO (background thread)
|
||||||
|
10. SIGTERM/SIGINT → set stop flag → flush, close FIFO, close stream/board, exit 0
|
||||||
|
|
||||||
|
### Pixel format
|
||||||
|
Default: `uyvy422` (4:2:2 8-bit, `VHD_SDI_BUFTYPE_VIDEO`). 10-bit (`v210`) is a future follow-up via `--pix-fmt v210`.
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
`sdi_slot.audio().extract(num_groups)` returns `std::vector<VHD_AUDIOGROUP>`. Samples are written to the FIFO as interleaved s16le PCM at 48000 Hz in a background thread so the video loop never blocks on audio consumers. Default `--audio-groups 2` yields 8 channels (standard embedded SDI stereo pairs 1–4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `capture-manager.js` Changes
|
||||||
|
|
||||||
|
### `_buildInputArgs()` — deltacast branch
|
||||||
|
|
||||||
|
Replace the existing lavfi-fallback stub with:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (sourceType === 'deltacast') {
|
||||||
|
const idx = parseInt(device, 10) || 0;
|
||||||
|
const audioFifo = `/tmp/dc-audio-${sessionId}`;
|
||||||
|
await execAsync(`mkfifo ${audioFifo}`);
|
||||||
|
|
||||||
|
const bridge = spawn('deltacast-capture', [
|
||||||
|
'--device', String(idx),
|
||||||
|
'--port', String(idx), // port == board index for single-port-per-recorder model
|
||||||
|
'--audio-pipe', audioFifo,
|
||||||
|
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||||
|
|
||||||
|
const fmt = await readFirstStderrLine(bridge, 35_000); // 35s timeout
|
||||||
|
// fmt: { width, height, fps_num, fps_den, pix_fmt, interlaced, audio_channels, audio_rate }
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputArgs: [
|
||||||
|
'-f', 'rawvideo',
|
||||||
|
'-pix_fmt', fmt.pix_fmt,
|
||||||
|
'-video_size', `${fmt.width}x${fmt.height}`,
|
||||||
|
'-framerate', `${fmt.fps_num}/${fmt.fps_den}`,
|
||||||
|
'-i', 'pipe:0',
|
||||||
|
'-f', 's16le',
|
||||||
|
'-ar', String(fmt.audio_rate),
|
||||||
|
'-ac', String(fmt.audio_channels),
|
||||||
|
'-i', audioFifo,
|
||||||
|
],
|
||||||
|
isNetwork: false,
|
||||||
|
bridgeProcess: bridge,
|
||||||
|
audioFifo,
|
||||||
|
interlaced: fmt.interlaced,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`readFirstStderrLine(proc, timeoutMs)` is a small helper that returns a parsed JSON object from the first line emitted on `proc.stderr`, or throws on timeout or non-zero exit.
|
||||||
|
|
||||||
|
### `start()` changes
|
||||||
|
- After `_buildInputArgs()` returns, store `bridgeProcess` and `audioFifo` on `this.state`
|
||||||
|
- Spawn FFmpeg with `stdio: ['pipe', ...]` for stdin
|
||||||
|
- `bridgeProcess.stdout.pipe(hiresProcess.stdin)`
|
||||||
|
- Deinterlace: if `interlaced === true`, add `-vf yadif=mode=1:deint=1` (already present for `sourceType === 'sdi'`; extend that check to include `deltacast`)
|
||||||
|
|
||||||
|
### `stop()` changes
|
||||||
|
- `if (processes.bridge) processes.bridge.kill('SIGINT')`
|
||||||
|
- After process cleanup: `if (this.state.audioFifo) { try { fs.unlinkSync(this.state.audioFifo); } catch (_) {} }`
|
||||||
|
|
||||||
|
### HLS preview
|
||||||
|
The existing `filter_complex split` SDI preview path works unchanged — the bridge→pipe is just a different `-i` source. Extend the `sourceType === 'sdi'` guard to `['sdi', 'deltacast'].includes(sourceType)`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dockerfile Changes
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ── Stage 0: Extract VideoMaster SDK ─────────────────────────────────────
|
||||||
|
FROM debian:bookworm AS sdk-extractor
|
||||||
|
COPY videomaster-linux.x64-6.34.1-dev.tar.gz /tmp/
|
||||||
|
RUN mkdir -p /sdk && tar -xzf /tmp/videomaster-linux.x64-6.34.1-dev.tar.gz -C /sdk
|
||||||
|
|
||||||
|
# ── Stage 1: Build deltacast-capture bridge ───────────────────────────────
|
||||||
|
FROM debian:bookworm AS bridge-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY --from=sdk-extractor /sdk /sdk
|
||||||
|
COPY deltacast-bridge/ /bridge/
|
||||||
|
RUN cmake -S /bridge -B /bridge/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DSDK_ROOT=/sdk \
|
||||||
|
&& cmake --build /bridge/build -j$(nproc)
|
||||||
|
|
||||||
|
# ── Stage 2: Build FFmpeg (unchanged) ─────────────────────────────────────
|
||||||
|
FROM debian:bookworm AS ffmpeg-builder
|
||||||
|
# ... existing content, no changes ...
|
||||||
|
|
||||||
|
# ── Stage 3: Runtime ──────────────────────────────────────────────────────
|
||||||
|
FROM node:20-bookworm
|
||||||
|
# ... existing runtime deps ...
|
||||||
|
COPY --from=bridge-builder /bridge/build/deltacast-capture /usr/local/bin/deltacast-capture
|
||||||
|
COPY --from=sdk-extractor /sdk/lib/ /usr/local/lib/deltacast/
|
||||||
|
RUN ldconfig /usr/local/lib/deltacast && ldconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
SDK `.so` files total ~4MB. The bridge binary adds ~200KB.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Bridge behavior | `capture-manager.js` response |
|
||||||
|
|---|---|---|
|
||||||
|
| No signal within timeout | Exit 1, `{"error":"no signal"}` on stderr | Throws — recorder stays idle, no asset created |
|
||||||
|
| Invalid board/port | Exit 1, `{"error":"board N not found"}` | Same as above |
|
||||||
|
| Bridge crash mid-capture | stdout closes → FFmpeg stdin EOF → FFmpeg exits cleanly | Existing stop handler fires; asset finalized with frames received so far |
|
||||||
|
| Audio FIFO open stall | Bridge blocks on FIFO write-open until FFmpeg opens read-end | Guarded by 10s watchdog on bridge spawn; if FFmpeg fails to start, bridge is SIGKILL'd |
|
||||||
|
| FIFO leftover on container crash | Stale file in `/tmp/` | Next `start()` uses a new `sessionId`-based path; harmless |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Without hardware (dev mode)
|
||||||
|
The lavfi fallback is **removed** from the deltacast branch — a missing `deltacast-capture` binary will throw at spawn time (clear error). Developers run the existing test card by using `sourceType = 'sdi'` with a DeckLink card or `sourceType = 'srt'` with a test stream.
|
||||||
|
|
||||||
|
The bridge binary can be tested standalone:
|
||||||
|
```bash
|
||||||
|
mkfifo /tmp/test-audio
|
||||||
|
deltacast-capture --device 0 --port 0 --audio-pipe /tmp/test-audio &
|
||||||
|
# watch stderr for JSON line, then:
|
||||||
|
cat /tmp/test-audio | ffprobe -f s16le -ar 48000 -ac 8 -i -
|
||||||
|
```
|
||||||
|
|
||||||
|
### With hardware (post-implementation)
|
||||||
|
1. Create recorder: `source_type=deltacast`, `device=0`, `port=0`
|
||||||
|
2. Verify JSON handshake in capture container logs within signal timeout
|
||||||
|
3. Verify `signal=receiving` in recorder status
|
||||||
|
4. Record 30s clip → asset created, proxy + HLS generated
|
||||||
|
5. Test stop mid-record → file finalized correctly
|
||||||
|
6. Test no-signal → recorder stays idle, no asset created
|
||||||
|
7. Test container restart mid-record → asset finalized on restart via existing `finalize` endpoint
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- 10-bit (`v210`) pixel format — follow-up
|
||||||
|
- `--audio-groups` UI control — follow-up
|
||||||
|
- GPU extension SDK (`gpuextension-linux.x64-2.2.0-dev.zip`) — covers GPU-accelerated colorspace conversion on the card; not needed for basic capture
|
||||||
|
- IP virtual card SDK (`ipvirtualcard`) — separate feature
|
||||||
|
- Promoting bridge to a native FFmpeg `libavdevice` input device — future v2
|
||||||
42
sdk/README.md
Normal file
42
sdk/README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Capture-card SDK / driver file store
|
||||||
|
|
||||||
|
This directory holds the **proprietary, non-redistributable** vendor SDKs and
|
||||||
|
drivers used to enable SDI / NDI capture cards on cluster nodes.
|
||||||
|
|
||||||
|
> **INTERNAL ONLY.** These files are licensed by their respective vendors and
|
||||||
|
> must **not** be published, redistributed, or committed to any public mirror.
|
||||||
|
> This repository is private. Do not change that.
|
||||||
|
|
||||||
|
## Why these live in the repo
|
||||||
|
|
||||||
|
The cluster admin screen lets an operator install/update capture-card drivers on
|
||||||
|
a node from the web UI (no SSH). The node-agent spawns a one-shot privileged
|
||||||
|
container that bind-mounts this repository and runs
|
||||||
|
[`deploy/install-driver.sh <vendor>`](../deploy/install-driver.sh), which reads
|
||||||
|
the vendor files from `sdk/<vendor>/`. Because the install must work offline on
|
||||||
|
an isolated broadcast LAN, the binaries ship in-repo rather than being fetched at
|
||||||
|
install time.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
sdk/
|
||||||
|
README.md ← this file
|
||||||
|
blackmagic/ ← Blackmagic Desktop Video (DeckLink) .deb
|
||||||
|
aja/ ← AJA ntv2 driver source / installer
|
||||||
|
deltacast/ ← Deltacast VideoMaster installer
|
||||||
|
ndi/ ← NDI redistributable runtime libs
|
||||||
|
```
|
||||||
|
|
||||||
|
Each vendor directory has its own `README.md` listing **exactly** which files an
|
||||||
|
admin must drop in. A `.gitkeep` keeps the empty directory committed.
|
||||||
|
|
||||||
|
## Important
|
||||||
|
|
||||||
|
- **No binaries are committed by default.** The directory structure + READMEs
|
||||||
|
are the deliverable. An admin downloads the proprietary files from the vendor
|
||||||
|
(per their licence) and drops them in the matching `sdk/<vendor>/` directory.
|
||||||
|
- The install script **fails gracefully** with a clear message if the expected
|
||||||
|
file is absent — it never fabricates or downloads binaries.
|
||||||
|
- Target host OS for all install paths is **Ubuntu 22.04 LTS (jammy), x86_64**,
|
||||||
|
matching the cluster worker nodes.
|
||||||
0
sdk/aja/.gitkeep
Normal file
0
sdk/aja/.gitkeep
Normal file
31
sdk/aja/README.md
Normal file
31
sdk/aja/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# AJA NTV2 driver
|
||||||
|
|
||||||
|
Drop the **AJA NTV2** driver source/SDK archive for Linux into this directory.
|
||||||
|
|
||||||
|
## Required file
|
||||||
|
|
||||||
|
| File | Notes |
|
||||||
|
|------|-------|
|
||||||
|
| `ntv2sdk*linux*.zip` **or** `libajantv2*.tar.gz` | The NTV2 SDK / open-source `libajantv2` source tree containing `driver/linux/` with the kernel-module `Makefile` and `load_ajantv2` / `unload_ajantv2` scripts. |
|
||||||
|
|
||||||
|
Example names: `ntv2sdklinux_17.0.1.zip`, `libajantv2-17.0.1.tar.gz`
|
||||||
|
|
||||||
|
The installer reads the **newest** matching archive.
|
||||||
|
|
||||||
|
## Where to get it
|
||||||
|
|
||||||
|
AJA → Support → Software & firmware → *NTV2 SDK* (Linux), or the public
|
||||||
|
`aja-video/libajantv2` source release. Download the Linux SDK zip / source
|
||||||
|
tarball and copy it here unmodified.
|
||||||
|
|
||||||
|
## What the install script does
|
||||||
|
|
||||||
|
1. Ensures `linux-headers-$(uname -r)`, `build-essential` are present.
|
||||||
|
2. Extracts the archive into a scratch build dir.
|
||||||
|
3. Builds the `ajantv2` kernel module from `driver/linux` (`make`).
|
||||||
|
4. Installs the module under `/lib/modules/$(uname -r)/extra`, runs `depmod`,
|
||||||
|
`modprobe ajantv2` (falls back to the SDK's `load_ajantv2` script).
|
||||||
|
5. Verifies the `ajantv2` module is loaded.
|
||||||
|
|
||||||
|
A **reboot is not normally required**; the module loads immediately after build.
|
||||||
|
The script reports if a reboot is needed (e.g. an old in-tree module is wedged).
|
||||||
0
sdk/blackmagic/.gitkeep
Normal file
0
sdk/blackmagic/.gitkeep
Normal file
35
sdk/blackmagic/README.md
Normal file
35
sdk/blackmagic/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Blackmagic Desktop Video (DeckLink) driver
|
||||||
|
|
||||||
|
Drop the **Blackmagic Desktop Video** Debian package for **Ubuntu 22.04 (x86_64)**
|
||||||
|
into this directory.
|
||||||
|
|
||||||
|
## Required file
|
||||||
|
|
||||||
|
| File | Notes |
|
||||||
|
|------|-------|
|
||||||
|
| `desktopvideo_*_amd64.deb` | The `desktopvideo` package from the Desktop Video installer archive. Provides the `blackmagic` kernel module (built via DKMS) and the `DesktopVideoHelper` daemon. |
|
||||||
|
|
||||||
|
Example name: `desktopvideo_14.4.1a4_amd64.deb`
|
||||||
|
|
||||||
|
The installer reads the **newest** matching `desktopvideo_*_amd64.deb` if more
|
||||||
|
than one is present.
|
||||||
|
|
||||||
|
## Where to get it
|
||||||
|
|
||||||
|
Blackmagic Design → Support → *Desktop Video* (Linux). Download the
|
||||||
|
"Desktop Video x.y.z Linux" tarball, extract it, and copy the
|
||||||
|
`deb/<arch>/desktopvideo_*_amd64.deb` file here.
|
||||||
|
|
||||||
|
> Optional: `desktopvideo-gui_*_amd64.deb` is **not** required for headless
|
||||||
|
> capture and is not installed.
|
||||||
|
|
||||||
|
## What the install script does
|
||||||
|
|
||||||
|
1. Ensures `linux-headers-$(uname -r)` is present (needed for the DKMS build).
|
||||||
|
2. `apt-get install -y ./desktopvideo_*_amd64.deb` (pulls DKMS deps).
|
||||||
|
3. Triggers the DKMS build, `depmod`, `modprobe blackmagic`.
|
||||||
|
4. Restarts the `DesktopVideoHelper` daemon.
|
||||||
|
5. Verifies `/dev/blackmagic` appears.
|
||||||
|
|
||||||
|
A **reboot is usually not required** but a DKMS rebuild against a freshly
|
||||||
|
installed kernel may need one — the script reports this.
|
||||||
0
sdk/deltacast/.gitkeep
Normal file
0
sdk/deltacast/.gitkeep
Normal file
31
sdk/deltacast/README.md
Normal file
31
sdk/deltacast/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Deltacast VideoMaster driver / SDK
|
||||||
|
|
||||||
|
Drop the **Deltacast VideoMaster** Linux installer into this directory.
|
||||||
|
|
||||||
|
## Required file
|
||||||
|
|
||||||
|
| File | Notes |
|
||||||
|
|------|-------|
|
||||||
|
| `VideoMaster*.run` **or** `VideoMaster*linux*.tar.gz` | The VideoMaster SDK + driver installer for Linux. Contains the `videomasterhd` kernel module sources and the `install.sh` driver installer. |
|
||||||
|
|
||||||
|
Example names: `VideoMaster-6.25.0.run`, `VideoMaster_6_25_Linux.tar.gz`
|
||||||
|
|
||||||
|
The installer reads the **newest** matching file.
|
||||||
|
|
||||||
|
## Where to get it
|
||||||
|
|
||||||
|
Deltacast → Products → *SDK* (<https://www.deltacast.tv/products/sdk>). Request
|
||||||
|
the VideoMaster Linux package (licence-gated) and copy the `.run` self-extractor
|
||||||
|
or the `.tar.gz` here unmodified.
|
||||||
|
|
||||||
|
## What the install script does
|
||||||
|
|
||||||
|
1. Ensures `linux-headers-$(uname -r)`, `build-essential`, `dkms` are present.
|
||||||
|
2. Runs the vendor installer:
|
||||||
|
- `.run` → executed with `--noexec --target <dir>` then its `install.sh`,
|
||||||
|
- `.tar.gz` → extracted, then its bundled `install.sh` is run.
|
||||||
|
3. Loads the Deltacast module (`modprobe videomasterhd` / vendor load script).
|
||||||
|
4. Verifies a `/dev/deltacast*` device node appears.
|
||||||
|
|
||||||
|
A **reboot may be required** after a first-time VideoMaster install (udev rules
|
||||||
|
+ firmware). The script reports this explicitly.
|
||||||
0
sdk/ndi/.gitkeep
Normal file
0
sdk/ndi/.gitkeep
Normal file
35
sdk/ndi/README.md
Normal file
35
sdk/ndi/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# NDI redistributable runtime
|
||||||
|
|
||||||
|
Drop the **NDI runtime redistributable** shared libraries into this directory.
|
||||||
|
NDI has **no kernel module** — it is purely user-space shared libraries, so this
|
||||||
|
is the lowest-risk install (no DKMS, no reboot).
|
||||||
|
|
||||||
|
## Required files
|
||||||
|
|
||||||
|
| File | Notes |
|
||||||
|
|------|-------|
|
||||||
|
| `libndi.so.*` | The versioned NDI runtime shared object, e.g. `libndi.so.6`. **Required.** |
|
||||||
|
| `libndi.so` *(optional)* | Dev symlink. The installer recreates it if absent. |
|
||||||
|
|
||||||
|
You may instead drop the whole **NDI SDK / Advanced SDK** `lib/x86_64-linux-gnu/`
|
||||||
|
directory contents here; the installer copies every `libndi*.so*` it finds.
|
||||||
|
|
||||||
|
Example name: `libndi.so.6.1.1`
|
||||||
|
|
||||||
|
## Where to get it
|
||||||
|
|
||||||
|
NDI → Tools / SDK download (NDI 6 SDK or NDI Advanced SDK for Linux). The
|
||||||
|
runtime libs live under `lib/x86_64-linux-gnu/` in the SDK. Per the NDI licence
|
||||||
|
the runtime is redistributable **within your own product** only — keep it in this
|
||||||
|
private repo, do not publish it.
|
||||||
|
|
||||||
|
## What the install script does
|
||||||
|
|
||||||
|
1. Copies every `libndi*.so*` from here into `/opt/ndi-lib`.
|
||||||
|
2. Writes `/etc/ld.so.conf.d/ndi.conf` pointing at `/opt/ndi-lib` and runs
|
||||||
|
`ldconfig`.
|
||||||
|
3. Recreates the `libndi.so` → `libndi.so.<N>` dev symlink if missing.
|
||||||
|
4. Verifies `ldconfig -p | grep libndi` resolves.
|
||||||
|
|
||||||
|
**No reboot required.** Running processes that already loaded an old `libndi`
|
||||||
|
must be restarted to pick up the new version — the script notes this.
|
||||||
7
services/capture/.dockerignore
Normal file
7
services/capture/.dockerignore
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Build artifacts that must never enter the Docker build context — a stale
|
||||||
|
# CMakeCache.txt from a native bridge build breaks the in-image cmake step
|
||||||
|
# ("CMakeCache.txt directory is different / source does not match").
|
||||||
|
deltacast-bridge/build/
|
||||||
|
node_modules/
|
||||||
|
*.bak
|
||||||
|
*.log
|
||||||
|
|
@ -1,4 +1,53 @@
|
||||||
# ── Stage 1: Build FFmpeg with DeckLink + NVENC (HEVC/H264) support ─────────
|
# ── Stage 0: Extract Deltacast VideoMaster SDK ───────────────────────────
|
||||||
|
FROM debian:bookworm AS sdk-extractor
|
||||||
|
COPY videomaster-linux.x64-6.34.1-dev.tar.gz /tmp/
|
||||||
|
RUN mkdir -p /sdk && tar -xzf /tmp/videomaster-linux.x64-6.34.1-dev.tar.gz -C /sdk
|
||||||
|
|
||||||
|
# ── Stage 1: Build deltacast-capture bridge binary ───────────────────────
|
||||||
|
FROM debian:bookworm AS bridge-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY --from=sdk-extractor /sdk /sdk
|
||||||
|
COPY deltacast-bridge/ /bridge/
|
||||||
|
RUN cmake -S /bridge -B /bridge/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DSDK_ROOT=/sdk \
|
||||||
|
&& cmake --build /bridge/build -j$(nproc)
|
||||||
|
|
||||||
|
# ── Stage 1d: Build fc_pipe (framecache slot → stdout adapter) ──────────
|
||||||
|
# Spawned by capture-manager.js to pipe raw frames from a framecache slot
|
||||||
|
# into ffmpeg as a rawvideo pipe input. Statically linked against fc_client
|
||||||
|
# (no runtime dependency on the framecache container — just shm + semaphores).
|
||||||
|
FROM debian:bookworm AS fc-pipe-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake libmicrohttpd-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY fc_client /fc-src/client
|
||||||
|
COPY deltacast-bridge /fc-src/src/slot.h # (copy from existing location)
|
||||||
|
RUN cmake -S /fc-src -B /fc-src/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& cmake --build /fc-src/build --target fc_pipe -j$(nproc)
|
||||||
|
|
||||||
|
# ── Stage 1c: Build decklink-bridge binary ───────────────────────────────
|
||||||
|
FROM debian:bookworm AS decklink-bridge-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake ca-certificates g++ \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
# DeckLink SDK headers (for IDeckLinkInput etc.)
|
||||||
|
COPY sdk/ /decklink-sdk/
|
||||||
|
# Shared fc_writer module from deltacast-bridge
|
||||||
|
COPY deltacast-bridge/fc_writer.h /fc_writer/fc_writer.h
|
||||||
|
COPY deltacast-bridge/fc_writer.c /fc_writer/fc_writer.c
|
||||||
|
# decklink-bridge source
|
||||||
|
COPY decklink-bridge/ /decklink-bridge/
|
||||||
|
RUN cmake -S /decklink-bridge -B /decklink-bridge/build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DDECKLINK_SDK_DIR=/decklink-sdk \
|
||||||
|
-DDELTACAST_BRIDGE_DIR=/fc_writer \
|
||||||
|
&& cmake --build /decklink-bridge/build -j$(nproc)
|
||||||
|
|
||||||
|
# ── Stage 2: Build FFmpeg with DeckLink + NVENC (HEVC/H264) support ─────────
|
||||||
# All-Intra HEVC NVENC is the master codec for growing-file ingest (see
|
# All-Intra HEVC NVENC is the master codec for growing-file ingest (see
|
||||||
# docs/design/2026-05-29-all-intra-hevc-ingest.md). This stage gets the
|
# docs/design/2026-05-29-all-intra-hevc-ingest.md). This stage gets the
|
||||||
# nv-codec-headers (header-only, no driver / no full CUDA toolkit needed)
|
# nv-codec-headers (header-only, no driver / no full CUDA toolkit needed)
|
||||||
|
|
@ -64,6 +113,34 @@ RUN ./configure \
|
||||||
RUN /usr/local/bin/ffmpeg -hide_banner -encoders 2>&1 | grep -E 'nvenc' \
|
RUN /usr/local/bin/ffmpeg -hide_banner -encoders 2>&1 | grep -E 'nvenc' \
|
||||||
|| (echo 'FATAL: nvenc encoders missing from ffmpeg build' && exit 1)
|
|| (echo 'FATAL: nvenc encoders missing from ffmpeg build' && exit 1)
|
||||||
|
|
||||||
|
# ── Stage 1b: Build bmx (raw2bmx / bmxtranswrap) from source ─────────────────
|
||||||
|
# bmx (bmxlib + libMXF + libMXF++) is the reference GROWING OP1a MXF writer. It
|
||||||
|
# writes a fresh IndexTableSegment (with an updated IndexDuration) into a new
|
||||||
|
# body partition at a periodic interval, so the recorded duration is readable —
|
||||||
|
# and INCREASES — from the header+index alone while the file is still being
|
||||||
|
# written (no footer needed). This is what makes the master a TRUE Premiere
|
||||||
|
# growing file. ffmpeg's MXF muxer cannot do this (its real duration/index lands
|
||||||
|
# only in the footer at av_write_trailer, so duration probes N/A until close).
|
||||||
|
#
|
||||||
|
# Debian/Ubuntu have no `bmxlib-tools` package (verified absent in bookworm), so
|
||||||
|
# we build from the BBC source. liburiparser/uuid/lzma/zlib/expat are the build
|
||||||
|
# deps; the runtime needs only libexpat1 + liburiparser1 + libuuid1 (added in
|
||||||
|
# the runtime stage below). Pinned to the bbc/bmx default branch (v1.6.x).
|
||||||
|
FROM debian:bookworm AS bmx-builder
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake git ca-certificates pkg-config \
|
||||||
|
liburiparser-dev uuid-dev liblzma-dev zlib1g-dev libexpat1-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
# Pin to a release tag so the produced soname (libMXF.so.1.6 etc.) stays stable
|
||||||
|
# for the COPY in the runtime stage. v1.6 is the BBC bmx series verified here.
|
||||||
|
RUN git clone --recursive --branch v1.6 https://github.com/bbc/bmx.git /bmx \
|
||||||
|
|| git clone --recursive https://github.com/bbc/bmx.git /bmx
|
||||||
|
WORKDIR /bmx/build
|
||||||
|
RUN cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local .. \
|
||||||
|
&& make -j"$(nproc)" && make install && ldconfig
|
||||||
|
# Sanity-check: raw2bmx must run, otherwise the growing-MXF pipeline is broken.
|
||||||
|
RUN /usr/local/bin/raw2bmx -h >/dev/null 2>&1 && echo 'raw2bmx OK'
|
||||||
|
|
||||||
# ── Stage 2: Runtime image ───────────────────────────────────────────────────
|
# ── Stage 2: Runtime image ───────────────────────────────────────────────────
|
||||||
FROM node:20-bookworm
|
FROM node:20-bookworm
|
||||||
|
|
||||||
|
|
@ -75,6 +152,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
libx264-164 libx265-199 libvpx7 libopus0 libmp3lame0 \
|
libx264-164 libx265-199 libvpx7 libopus0 libmp3lame0 \
|
||||||
libsrt1.5-openssl libzmq5 libstdc++6 libc++1 libc++abi1 \
|
libsrt1.5-openssl libzmq5 libstdc++6 libc++1 libc++abi1 \
|
||||||
cifs-utils util-linux \
|
cifs-utils util-linux \
|
||||||
|
libexpat1 liburiparser1 libuuid1 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy compiled ffmpeg/ffprobe
|
# Copy compiled ffmpeg/ffprobe
|
||||||
|
|
@ -85,7 +163,40 @@ COPY --from=ffmpeg-builder /usr/local/lib/ /usr/local/lib/
|
||||||
# DeckLink runtime .so
|
# DeckLink runtime .so
|
||||||
COPY lib/libDeckLinkAPI.so /usr/lib/libDeckLinkAPI.so
|
COPY lib/libDeckLinkAPI.so /usr/lib/libDeckLinkAPI.so
|
||||||
COPY lib/libDeckLinkPreviewAPI.so /usr/lib/libDeckLinkPreviewAPI.so
|
COPY lib/libDeckLinkPreviewAPI.so /usr/lib/libDeckLinkPreviewAPI.so
|
||||||
RUN ldconfig
|
|
||||||
|
# bmx (raw2bmx / bmxtranswrap / mxf2raw) — the growing OP1a MXF writer used for
|
||||||
|
# the edit-while-record master. Copy the built binaries + shared libs; runtime
|
||||||
|
# deps (libexpat1/liburiparser1/libuuid1) were installed above.
|
||||||
|
COPY --from=bmx-builder /usr/local/bin/raw2bmx /usr/local/bin/raw2bmx
|
||||||
|
COPY --from=bmx-builder /usr/local/bin/bmxtranswrap /usr/local/bin/bmxtranswrap
|
||||||
|
COPY --from=bmx-builder /usr/local/bin/mxf2raw /usr/local/bin/mxf2raw
|
||||||
|
COPY --from=bmx-builder /usr/local/lib/libMXF.so.1.6 /usr/local/lib/
|
||||||
|
COPY --from=bmx-builder /usr/local/lib/libMXF++.so.1.6 /usr/local/lib/
|
||||||
|
COPY --from=bmx-builder /usr/local/lib/libbmx.so.1.6 /usr/local/lib/
|
||||||
|
RUN cd /usr/local/lib \
|
||||||
|
&& ln -sf libMXF.so.1.6 libMXF.so.1 && ln -sf libMXF.so.1 libMXF.so \
|
||||||
|
&& ln -sf libMXF++.so.1.6 libMXF++.so.1 && ln -sf libMXF++.so.1 libMXF++.so \
|
||||||
|
&& ln -sf libbmx.so.1.6 libbmx.so.1 && ln -sf libbmx.so.1 libbmx.so \
|
||||||
|
&& ldconfig
|
||||||
|
# Verify raw2bmx resolves its libs and runs in the final image.
|
||||||
|
RUN raw2bmx -h >/dev/null 2>&1 && echo 'raw2bmx runtime OK'
|
||||||
|
|
||||||
|
# Deltacast bridge binary + SDK runtime libs
|
||||||
|
COPY --from=bridge-builder /bridge/build/deltacast-capture /usr/local/bin/deltacast-capture
|
||||||
|
|
||||||
|
# DeckLink bridge binary (no SDK runtime .so — uses dlopen at runtime)
|
||||||
|
COPY --from=decklink-bridge-builder /decklink-bridge/build/decklink-bridge /usr/local/bin/decklink-bridge
|
||||||
|
|
||||||
|
# fc_pipe — framecache slot → stdout, spawned by capture-manager.js
|
||||||
|
COPY --from=fc-pipe-builder /fc-src/build/fc_pipe /usr/local/bin/fc_pipe
|
||||||
|
COPY --from=sdk-extractor /sdk/lib/libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/
|
||||||
|
COPY --from=sdk-extractor /sdk/lib/libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/
|
||||||
|
RUN ln -sf libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd.so.6 \
|
||||||
|
&& ln -sf libvideomasterhd.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd.so \
|
||||||
|
&& ln -sf libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd_audio.so.6 \
|
||||||
|
&& ln -sf libvideomasterhd_audio.so.6.34.1 /usr/local/lib/deltacast/libvideomasterhd_audio.so \
|
||||||
|
&& ldconfig /usr/local/lib/deltacast \
|
||||||
|
&& ldconfig
|
||||||
|
|
||||||
# Mount points the recorder lifecycle expects to exist.
|
# Mount points the recorder lifecycle expects to exist.
|
||||||
# /live — HLS preview output (bound from host LIVE_DIR by node-agent)
|
# /live — HLS preview output (bound from host LIVE_DIR by node-agent)
|
||||||
|
|
|
||||||
51
services/capture/decklink-bridge/CMakeLists.txt
Normal file
51
services/capture/decklink-bridge/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(decklink-bridge CXX C)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -O2")
|
||||||
|
|
||||||
|
# Path to DeckLink SDK headers (services/capture/sdk/)
|
||||||
|
set(DECKLINK_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../sdk"
|
||||||
|
CACHE PATH "Path to Blackmagic DeckLink SDK headers")
|
||||||
|
|
||||||
|
# Path to Deltacast bridge (for fc_writer.h/c — shared writer module)
|
||||||
|
set(DELTACAST_BRIDGE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../deltacast-bridge"
|
||||||
|
CACHE PATH "Path to deltacast-bridge (contains fc_writer.h/c)")
|
||||||
|
|
||||||
|
# Legacy FIFO fallback option (mirrors deltacast-bridge option)
|
||||||
|
option(LEGACY_FIFO "Use named FIFOs instead of framecache shm" OFF)
|
||||||
|
|
||||||
|
# ── decklink-bridge executable ────────────────────────────────────────
|
||||||
|
add_executable(decklink-bridge
|
||||||
|
main.cpp
|
||||||
|
${DELTACAST_BRIDGE_DIR}/fc_writer.c # shared framecache writer
|
||||||
|
)
|
||||||
|
|
||||||
|
if(LEGACY_FIFO)
|
||||||
|
target_compile_definitions(decklink-bridge PRIVATE LEGACY_FIFO=1)
|
||||||
|
message(STATUS "decklink-bridge: LEGACY_FIFO mode enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "decklink-bridge: framecache shm mode enabled")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(decklink-bridge PRIVATE
|
||||||
|
${DECKLINK_SDK_DIR}
|
||||||
|
${DELTACAST_BRIDGE_DIR} # fc_writer.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(decklink-bridge PRIVATE
|
||||||
|
pthread
|
||||||
|
rt # shm_open, sem_open
|
||||||
|
dl # dlopen (used by DeckLinkAPIDispatch.cpp on Linux)
|
||||||
|
)
|
||||||
|
|
||||||
|
# DeckLink driver is linked at runtime via dlopen (no link-time .so needed).
|
||||||
|
# The SDK's DeckLinkAPIDispatch.cpp handles the dynamic loading.
|
||||||
|
|
||||||
|
set_target_properties(decklink-bridge PROPERTIES
|
||||||
|
INSTALL_RPATH "/usr/local/lib"
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS decklink-bridge DESTINATION bin)
|
||||||
557
services/capture/decklink-bridge/main.cpp
Normal file
557
services/capture/decklink-bridge/main.cpp
Normal file
|
|
@ -0,0 +1,557 @@
|
||||||
|
/**
|
||||||
|
* decklink-bridge/main.cpp
|
||||||
|
*
|
||||||
|
* Blackmagic DeckLink SDI shared multi-device bridge daemon.
|
||||||
|
*
|
||||||
|
* Opens one or more DeckLink devices and for each device:
|
||||||
|
* - Auto-detects the incoming signal format
|
||||||
|
* - Registers a framecache slot via HTTP API
|
||||||
|
* - Writes raw UYVY422 (bmdFormat8BitYUV) video frames into the shm ring
|
||||||
|
* - Writes PCM s16le audio to a named FIFO (audio-in-shm is roadmap)
|
||||||
|
*
|
||||||
|
* Slot ID format: "decklink-<node_id>-<device_index>"
|
||||||
|
* node_id comes from NODE_ID env var (set by node-agent), falls back to hostname.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* decklink-bridge --devices <csv> # device indices, e.g. "0,1"
|
||||||
|
* decklink-bridge --device <N> # single device compat alias
|
||||||
|
* [--fc-url http://framecache:7435]
|
||||||
|
* [--audio-pipe-dir /dev/shm/decklink]
|
||||||
|
* [--signal-timeout <sec>]
|
||||||
|
*
|
||||||
|
* For each device that acquires signal, emits one JSON line to stderr:
|
||||||
|
* {"device":N,"width":W,"height":H,"fps_num":N,"fps_den":D,
|
||||||
|
* "interlaced":false,"pix_fmt":"uyvy422",
|
||||||
|
* "audio_channels":2,"audio_rate":48000,
|
||||||
|
* "slot_id":"decklink-<node>-<N>"}
|
||||||
|
*
|
||||||
|
* Compile with -DLEGACY_FIFO=1 to fall back to writing a raw video FIFO
|
||||||
|
* instead of the framecache shm path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIDispatch.cpp"
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
extern "C" {
|
||||||
|
# include "../deltacast-bridge/fc_writer.h"
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef F_SETPIPE_SZ
|
||||||
|
# define F_SETPIPE_SZ 1031
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FC_URL_DEFAULT "http://localhost:7435"
|
||||||
|
#define AUDIO_PIPE_DIR "/dev/shm/decklink"
|
||||||
|
#define MAX_DEVICES 8
|
||||||
|
|
||||||
|
/* ── Global shutdown flag ──────────────────────────────────────────── */
|
||||||
|
static std::atomic<int> g_stop{0};
|
||||||
|
static void on_signal(int) { g_stop.store(1); }
|
||||||
|
|
||||||
|
/* ── Helpers ───────────────────────────────────────────────────────── */
|
||||||
|
static uint64_t now_us() {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
return (uint64_t)ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_all(int fd, const void *buf, size_t len) {
|
||||||
|
const uint8_t *p = static_cast<const uint8_t *>(buf);
|
||||||
|
size_t off = 0;
|
||||||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
while (off < len) {
|
||||||
|
ssize_t n = write(fd, p + off, len - off);
|
||||||
|
if (n > 0) { off += (size_t)n; continue; }
|
||||||
|
if (n < 0 && errno == EINTR) continue;
|
||||||
|
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||||
|
struct timespec ts{0, 1000000L};
|
||||||
|
nanosleep(&ts, nullptr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fcntl(fd, F_SETFL, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fcntl(fd, F_SETFL, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Per-device state ──────────────────────────────────────────────── */
|
||||||
|
struct DeviceState {
|
||||||
|
int device_idx = 0;
|
||||||
|
IDeckLink *decklink = nullptr;
|
||||||
|
IDeckLinkInput *input = nullptr;
|
||||||
|
|
||||||
|
/* Signal properties (filled on first frame or format-change) */
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
int fps_num = 0;
|
||||||
|
int fps_den = 1;
|
||||||
|
bool interlaced = false;
|
||||||
|
std::atomic<bool> signal_reported{false};
|
||||||
|
|
||||||
|
std::string slot_id;
|
||||||
|
std::string fc_url;
|
||||||
|
std::string audio_fifo;
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
fc_writer_t *fc_writer = nullptr;
|
||||||
|
/* Guards fc_writer + format fields (width/height/fps/signal_reported)
|
||||||
|
* against concurrent access from DeckLink SDK callback threads:
|
||||||
|
* VideoInputFormatChanged and VideoInputFrameArrived can fire on
|
||||||
|
* different threads without mutual exclusion, and reopen_slot() does
|
||||||
|
* close-then-open on fc_writer. Without this lock a frame callback could
|
||||||
|
* call fc_writer_write() on a freed writer (use-after-free), or two
|
||||||
|
* reopen_slot() calls could double-free. */
|
||||||
|
pthread_mutex_t fc_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#else
|
||||||
|
int video_fifo_fd = -1;
|
||||||
|
std::string video_fifo;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Audio FIFO fd — opened once, reopened on EPIPE */
|
||||||
|
int audio_fd = -1;
|
||||||
|
pthread_t audio_tid{};
|
||||||
|
std::atomic<int> audio_stop{0};
|
||||||
|
|
||||||
|
uint64_t frame_seq = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── Audio thread ──────────────────────────────────────────────────── */
|
||||||
|
/* DeckLink audio arrives via VideoInputFrameArrived callback, not a
|
||||||
|
* separate stream. We write it from the callback directly (see below).
|
||||||
|
* This thread exists only to keep the FIFO open and provide silence
|
||||||
|
* when no frames are arriving (e.g. signal lost). */
|
||||||
|
static void *audio_silence_thread(void *arg) {
|
||||||
|
DeviceState *ds = static_cast<DeviceState *>(arg);
|
||||||
|
|
||||||
|
const int RATE = 48000;
|
||||||
|
const int CH = 2;
|
||||||
|
const int FPS = ds->fps_num > 0 ? ds->fps_num : 30;
|
||||||
|
const int FPS_DEN = ds->fps_den > 0 ? ds->fps_den : 1;
|
||||||
|
long samples = ((long)RATE * FPS_DEN + FPS / 2) / FPS;
|
||||||
|
size_t tick = (size_t)samples * (size_t)CH * 2; /* s16le */
|
||||||
|
std::vector<uint8_t> silence(tick, 0);
|
||||||
|
|
||||||
|
while (!g_stop.load() && !ds->audio_stop.load()) {
|
||||||
|
int fd = open(ds->audio_fifo.c_str(), O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
struct timespec ts{0, 200000000L};
|
||||||
|
nanosleep(&ts, nullptr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fcntl(fd, F_SETPIPE_SZ, 1024 * 1024);
|
||||||
|
ds->audio_fd = fd;
|
||||||
|
|
||||||
|
long frame_ns = (long)(1000000000.0 * (double)FPS_DEN / (double)FPS);
|
||||||
|
struct timespec next;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &next);
|
||||||
|
|
||||||
|
while (!g_stop.load() && !ds->audio_stop.load()) {
|
||||||
|
/* Only write silence if no real audio arrived recently.
|
||||||
|
* Real audio is written by VideoInputFrameArrived directly. */
|
||||||
|
if (write_all(ds->audio_fd, silence.data(), tick) < 0) {
|
||||||
|
fprintf(stderr, "[audio:%d] EPIPE — reopening\n", ds->device_idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next.tv_nsec += frame_ns;
|
||||||
|
while (next.tv_nsec >= 1000000000L) { next.tv_nsec -= 1000000000L; next.tv_sec++; }
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
if (next.tv_sec > now.tv_sec ||
|
||||||
|
(next.tv_sec == now.tv_sec && next.tv_nsec > now.tv_nsec))
|
||||||
|
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, nullptr);
|
||||||
|
else
|
||||||
|
next = now;
|
||||||
|
}
|
||||||
|
ds->audio_fd = -1;
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── IDeckLinkInputCallback implementation ─────────────────────────── */
|
||||||
|
class CaptureCallback : public IDeckLinkInputCallback {
|
||||||
|
public:
|
||||||
|
explicit CaptureCallback(DeviceState *ds) : m_ds(ds), m_refcount(1) {}
|
||||||
|
|
||||||
|
/* IUnknown */
|
||||||
|
HRESULT QueryInterface(REFIID, void **) override { return E_NOINTERFACE; }
|
||||||
|
ULONG AddRef() override { return ++m_refcount; }
|
||||||
|
ULONG Release() override {
|
||||||
|
ULONG r = --m_refcount;
|
||||||
|
if (r == 0) delete this;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IDeckLinkInputCallback */
|
||||||
|
HRESULT VideoInputFormatChanged(
|
||||||
|
BMDVideoInputFormatChangedEvents events,
|
||||||
|
IDeckLinkDisplayMode *newMode,
|
||||||
|
BMDDetectedVideoInputFormatFlags detectedFlags) override
|
||||||
|
{
|
||||||
|
/* Re-enable input with new mode — required for auto-detect to work */
|
||||||
|
m_ds->input->PauseStreams();
|
||||||
|
|
||||||
|
BMDDisplayMode mode = newMode->GetDisplayMode();
|
||||||
|
|
||||||
|
/* Detect interlaced */
|
||||||
|
BMDFieldDominance fd = newMode->GetFieldDominance();
|
||||||
|
m_ds->interlaced = (fd == bmdUpperFieldFirst || fd == bmdLowerFieldFirst);
|
||||||
|
|
||||||
|
/* Get width/height */
|
||||||
|
m_ds->width = (int)newMode->GetWidth();
|
||||||
|
m_ds->height = (int)newMode->GetHeight();
|
||||||
|
|
||||||
|
/* Get frame rate */
|
||||||
|
BMDTimeValue frameDuration; BMDTimeScale timeScale;
|
||||||
|
newMode->GetFrameRate(&frameDuration, &timeScale);
|
||||||
|
m_ds->fps_num = (int)timeScale;
|
||||||
|
m_ds->fps_den = (int)frameDuration;
|
||||||
|
|
||||||
|
m_ds->input->EnableVideoInput(mode, bmdFormat8BitYUV,
|
||||||
|
bmdVideoInputEnableFormatDetection);
|
||||||
|
m_ds->input->FlushStreams();
|
||||||
|
m_ds->input->StartStreams();
|
||||||
|
|
||||||
|
fprintf(stderr, "[decklink:%d] format changed: %dx%d %.4ffps %s\n",
|
||||||
|
m_ds->device_idx,
|
||||||
|
m_ds->width, m_ds->height,
|
||||||
|
m_ds->fps_den ? (double)m_ds->fps_num / m_ds->fps_den : 0.0,
|
||||||
|
m_ds->interlaced ? "interlaced" : "progressive");
|
||||||
|
|
||||||
|
/* Re-open framecache slot with new format */
|
||||||
|
this->reopen_slot();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT VideoInputFrameArrived(
|
||||||
|
IDeckLinkVideoInputFrame *videoFrame,
|
||||||
|
IDeckLinkAudioInputPacket *audioPacket) override
|
||||||
|
{
|
||||||
|
if (g_stop.load()) return S_OK;
|
||||||
|
if (!videoFrame) return S_OK;
|
||||||
|
|
||||||
|
/* Detect format on first frame if format-change hasn't fired.
|
||||||
|
* Use atomic exchange so only ONE thread runs the first-frame init
|
||||||
|
* even if two frame callbacks race before signal_reported is set. */
|
||||||
|
bool expected = false;
|
||||||
|
if (m_ds->signal_reported.compare_exchange_strong(expected, true)) {
|
||||||
|
m_ds->width = (int)videoFrame->GetWidth();
|
||||||
|
m_ds->height = (int)videoFrame->GetHeight();
|
||||||
|
if (m_ds->fps_num == 0) {
|
||||||
|
m_ds->fps_num = 30000;
|
||||||
|
m_ds->fps_den = 1001;
|
||||||
|
}
|
||||||
|
this->reopen_slot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Write video frame ──────────────────────────────────────── */
|
||||||
|
void *bytes = nullptr;
|
||||||
|
videoFrame->GetBytes(&bytes);
|
||||||
|
uint32_t sz = (uint32_t)(videoFrame->GetRowBytes() * videoFrame->GetHeight());
|
||||||
|
|
||||||
|
uint32_t expected = (uint32_t)m_ds->width * (uint32_t)m_ds->height * 2;
|
||||||
|
if (sz != expected) {
|
||||||
|
fprintf(stderr, "[decklink:%d] WARN: frame sz=%u != expected %u — skipping\n",
|
||||||
|
m_ds->device_idx, sz, expected);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pts_us = 0;
|
||||||
|
if (m_ds->fps_num > 0) {
|
||||||
|
pts_us = m_ds->frame_seq * 1000000ULL
|
||||||
|
* (uint64_t)m_ds->fps_den
|
||||||
|
/ (uint64_t)m_ds->fps_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
/* Lock so a concurrent VideoInputFormatChanged → reopen_slot() cannot
|
||||||
|
* free fc_writer between our null-check and the write (use-after-free). */
|
||||||
|
pthread_mutex_lock(&m_ds->fc_lock);
|
||||||
|
if (m_ds->fc_writer) {
|
||||||
|
fc_writer_write(m_ds->fc_writer,
|
||||||
|
static_cast<const uint8_t *>(bytes), sz, pts_us);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m_ds->fc_lock);
|
||||||
|
#else
|
||||||
|
if (m_ds->video_fifo_fd >= 0) {
|
||||||
|
if (write_all(m_ds->video_fifo_fd,
|
||||||
|
static_cast<const uint8_t *>(bytes), sz) < 0) {
|
||||||
|
fprintf(stderr, "[decklink:%d] video FIFO EPIPE\n", m_ds->device_idx);
|
||||||
|
close(m_ds->video_fifo_fd);
|
||||||
|
m_ds->video_fifo_fd = open(m_ds->video_fifo.c_str(), O_WRONLY | O_NONBLOCK);
|
||||||
|
if (m_ds->video_fifo_fd >= 0)
|
||||||
|
fcntl(m_ds->video_fifo_fd, F_SETPIPE_SZ, 64 * 1024 * 1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_ds->frame_seq++;
|
||||||
|
|
||||||
|
/* ── Write audio ────────────────────────────────────────────── */
|
||||||
|
if (audioPacket && m_ds->audio_fd >= 0) {
|
||||||
|
void *abytes = nullptr;
|
||||||
|
audioPacket->GetBytes(&abytes);
|
||||||
|
uint32_t sample_count = (uint32_t)audioPacket->GetSampleFrameCount();
|
||||||
|
uint32_t audio_sz = sample_count * 2 /* ch */ * 2 /* s16le bytes */;
|
||||||
|
if (abytes && audio_sz > 0) {
|
||||||
|
/* Non-fatal if pipe is full — silence thread provides fallback */
|
||||||
|
write_all(m_ds->audio_fd,
|
||||||
|
static_cast<const uint8_t *>(abytes), audio_sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit signal JSON once per device on first frame */
|
||||||
|
if (m_ds->frame_seq == 1) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"device\":%d,\"width\":%d,\"height\":%d,"
|
||||||
|
"\"fps_num\":%d,\"fps_den\":%d,"
|
||||||
|
"\"interlaced\":%s,"
|
||||||
|
"\"pix_fmt\":\"uyvy422\","
|
||||||
|
"\"audio_channels\":2,\"audio_rate\":48000,"
|
||||||
|
"\"slot_id\":\"%s\"}\n",
|
||||||
|
m_ds->device_idx,
|
||||||
|
m_ds->width, m_ds->height,
|
||||||
|
m_ds->fps_num, m_ds->fps_den,
|
||||||
|
m_ds->interlaced ? "true" : "false",
|
||||||
|
m_ds->slot_id.c_str());
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeviceState *m_ds;
|
||||||
|
std::atomic<ULONG> m_refcount;
|
||||||
|
|
||||||
|
void reopen_slot() {
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
/* Serialize with frame writes and any concurrent reopen_slot() so we
|
||||||
|
* never double-free fc_writer or write to a half-closed one. */
|
||||||
|
pthread_mutex_lock(&m_ds->fc_lock);
|
||||||
|
if (m_ds->fc_writer) {
|
||||||
|
fc_writer_close(m_ds->fc_writer);
|
||||||
|
m_ds->fc_writer = nullptr;
|
||||||
|
}
|
||||||
|
if (m_ds->width > 0 && m_ds->height > 0 && m_ds->fps_num > 0) {
|
||||||
|
m_ds->fc_writer = fc_writer_open(
|
||||||
|
m_ds->fc_url.c_str(),
|
||||||
|
m_ds->slot_id.c_str(),
|
||||||
|
(uint32_t)m_ds->width, (uint32_t)m_ds->height,
|
||||||
|
(uint32_t)m_ds->fps_num, (uint32_t)m_ds->fps_den);
|
||||||
|
if (!m_ds->fc_writer) {
|
||||||
|
fprintf(stderr, "[decklink:%d] framecache unavailable\n",
|
||||||
|
m_ds->device_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&m_ds->fc_lock);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── Parse comma-separated device list ────────────────────────────── */
|
||||||
|
static std::vector<int> parse_devices(const char *csv) {
|
||||||
|
std::vector<int> out;
|
||||||
|
char buf[256];
|
||||||
|
strncpy(buf, csv, sizeof buf - 1);
|
||||||
|
char *tok = strtok(buf, ",");
|
||||||
|
while (tok) { out.push_back(atoi(tok)); tok = strtok(nullptr, ","); }
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main ──────────────────────────────────────────────────────────── */
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
std::vector<int> device_indices;
|
||||||
|
int sig_timeout = 30;
|
||||||
|
const char *fc_url = getenv("FC_URL") ? getenv("FC_URL") : FC_URL_DEFAULT;
|
||||||
|
const char *audio_dir = AUDIO_PIPE_DIR;
|
||||||
|
|
||||||
|
const char *node_id = getenv("NODE_ID");
|
||||||
|
char hostname[256] = "local";
|
||||||
|
if (!node_id) { gethostname(hostname, sizeof hostname); node_id = hostname; }
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--devices") && i+1 < argc)
|
||||||
|
device_indices = parse_devices(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--device") && i+1 < argc)
|
||||||
|
device_indices.push_back(atoi(argv[++i]));
|
||||||
|
else if (!strcmp(argv[i], "--fc-url") && i+1 < argc)
|
||||||
|
fc_url = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--audio-pipe-dir") && i+1 < argc)
|
||||||
|
audio_dir = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--signal-timeout") && i+1 < argc)
|
||||||
|
sig_timeout = atoi(argv[++i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_indices.empty()) {
|
||||||
|
fprintf(stderr, "{\"error\":\"no devices specified — use --devices 0,1 or --device 0\"}\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
/* Ensure audio pipe dir exists */
|
||||||
|
mkdir(audio_dir, 0755);
|
||||||
|
|
||||||
|
/* ── Enumerate DeckLink devices ─────────────────────────────────── */
|
||||||
|
IDeckLinkIterator *iterator = CreateDeckLinkIteratorInstance();
|
||||||
|
if (!iterator) {
|
||||||
|
fprintf(stderr, "{\"error\":\"CreateDeckLinkIteratorInstance failed — DeckLink driver not loaded?\"}\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IDeckLink *> all_devices;
|
||||||
|
IDeckLink *dl = nullptr;
|
||||||
|
while (iterator->Next(&dl) == S_OK) {
|
||||||
|
all_devices.push_back(dl);
|
||||||
|
}
|
||||||
|
iterator->Release();
|
||||||
|
|
||||||
|
fprintf(stderr, "[decklink] %zu device(s) detected\n", all_devices.size());
|
||||||
|
|
||||||
|
/* ── Set up per-device state ─────────────────────────────────────── */
|
||||||
|
std::vector<DeviceState> states(device_indices.size());
|
||||||
|
std::vector<CaptureCallback *> callbacks(device_indices.size(), nullptr);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < device_indices.size(); i++) {
|
||||||
|
int idx = device_indices[i];
|
||||||
|
if (idx < 0 || (size_t)idx >= all_devices.size()) {
|
||||||
|
fprintf(stderr, "{\"error\":\"device index %d out of range (%zu detected)\"}\n",
|
||||||
|
idx, all_devices.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceState &ds = states[i];
|
||||||
|
ds.device_idx = idx;
|
||||||
|
ds.fc_url = fc_url;
|
||||||
|
|
||||||
|
/* slot_id: "decklink-<node_id>-<device_idx>" */
|
||||||
|
char sid[128];
|
||||||
|
snprintf(sid, sizeof sid, "decklink-%s-%d", node_id, idx);
|
||||||
|
ds.slot_id = sid;
|
||||||
|
|
||||||
|
/* Audio FIFO path */
|
||||||
|
char apath[256];
|
||||||
|
snprintf(apath, sizeof apath, "%s/audio-%d.fifo", audio_dir, idx);
|
||||||
|
ds.audio_fifo = apath;
|
||||||
|
mkfifo(apath, 0666); /* ignore EEXIST */
|
||||||
|
|
||||||
|
#ifdef LEGACY_FIFO
|
||||||
|
/* Video FIFO (legacy path only) */
|
||||||
|
char vpath[256];
|
||||||
|
snprintf(vpath, sizeof vpath, "%s/video-%d.fifo", audio_dir, idx);
|
||||||
|
ds.video_fifo = vpath;
|
||||||
|
mkfifo(vpath, 0666);
|
||||||
|
int vfd = open(vpath, O_WRONLY | O_NONBLOCK);
|
||||||
|
if (vfd >= 0) fcntl(vfd, F_SETPIPE_SZ, 64 * 1024 * 1024);
|
||||||
|
ds.video_fifo_fd = vfd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IDeckLink *decklink = all_devices[(size_t)idx];
|
||||||
|
ds.decklink = decklink;
|
||||||
|
|
||||||
|
/* Get IDeckLinkInput */
|
||||||
|
IDeckLinkInput *input = nullptr;
|
||||||
|
if (decklink->QueryInterface(IID_IDeckLinkInput,
|
||||||
|
reinterpret_cast<void **>(&input)) != S_OK) {
|
||||||
|
fprintf(stderr, "[decklink:%d] QueryInterface IDeckLinkInput failed\n", idx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ds.input = input;
|
||||||
|
|
||||||
|
/* Install callback */
|
||||||
|
CaptureCallback *cb = new CaptureCallback(&ds);
|
||||||
|
callbacks[i] = cb;
|
||||||
|
input->SetCallback(cb);
|
||||||
|
|
||||||
|
/* Enable video with format detection — actual mode set on first
|
||||||
|
* VideoInputFormatChanged; use 1080i29.97 as a safe starting mode. */
|
||||||
|
HRESULT hr = input->EnableVideoInput(
|
||||||
|
bmdModeHD1080i5994,
|
||||||
|
bmdFormat8BitYUV,
|
||||||
|
bmdVideoInputEnableFormatDetection);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
fprintf(stderr, "[decklink:%d] EnableVideoInput failed (0x%08x)\n", idx, (unsigned)hr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable audio input — 48kHz stereo s16le */
|
||||||
|
input->EnableAudioInput(bmdAudioSampleRate48kHz,
|
||||||
|
bmdAudioSampleType16bitInteger, 2);
|
||||||
|
|
||||||
|
/* Start silence thread (keeps audio FIFO open) */
|
||||||
|
ds.fps_num = 30000; ds.fps_den = 1001; /* default until format detected */
|
||||||
|
pthread_create(&ds.audio_tid, nullptr, audio_silence_thread, &ds);
|
||||||
|
|
||||||
|
/* Start capture */
|
||||||
|
if (input->StartStreams() != S_OK) {
|
||||||
|
fprintf(stderr, "[decklink:%d] StartStreams failed\n", idx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[decklink:%d] capture started, waiting for signal...\n", idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Run until shutdown ─────────────────────────────────────────── */
|
||||||
|
while (!g_stop.load()) {
|
||||||
|
struct timespec ts{0, 100000000L}; /* 100ms */
|
||||||
|
nanosleep(&ts, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[decklink] shutdown signal received\n");
|
||||||
|
|
||||||
|
/* ── Cleanup ─────────────────────────────────────────────────────── */
|
||||||
|
for (size_t i = 0; i < states.size(); i++) {
|
||||||
|
DeviceState &ds = states[i];
|
||||||
|
|
||||||
|
if (ds.input) {
|
||||||
|
ds.input->StopStreams();
|
||||||
|
ds.input->DisableVideoInput();
|
||||||
|
ds.input->DisableAudioInput();
|
||||||
|
ds.input->SetCallback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ds.audio_stop.store(1);
|
||||||
|
if (ds.audio_tid) pthread_join(ds.audio_tid, nullptr);
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
if (ds.fc_writer) {
|
||||||
|
fc_writer_close(ds.fc_writer);
|
||||||
|
ds.fc_writer = nullptr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (ds.video_fifo_fd >= 0) close(ds.video_fifo_fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ds.input) { ds.input->Release(); ds.input = nullptr; }
|
||||||
|
if (callbacks[i]) { callbacks[i]->Release(); callbacks[i] = nullptr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto *d : all_devices) d->Release();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
49
services/capture/deltacast-bridge/CMakeLists.txt
Normal file
49
services/capture/deltacast-bridge/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(deltacast-bridge C)
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
|
||||||
|
set(SDK_ROOT "/sdk" CACHE PATH "Path to extracted VideoMaster SDK")
|
||||||
|
|
||||||
|
# Legacy FIFO mode — set LEGACY_FIFO=ON to disable framecache shm writes
|
||||||
|
# and fall back to the original named-FIFO path.
|
||||||
|
option(LEGACY_FIFO "Use named FIFOs instead of framecache shm" OFF)
|
||||||
|
|
||||||
|
# Primary binary: deltacast-bridge (shared multi-port daemon)
|
||||||
|
add_executable(deltacast-bridge main.c fc_writer.c)
|
||||||
|
|
||||||
|
if(LEGACY_FIFO)
|
||||||
|
target_compile_definitions(deltacast-bridge PRIVATE LEGACY_FIFO=1)
|
||||||
|
message(STATUS "deltacast-bridge: LEGACY_FIFO mode enabled (shm disabled)")
|
||||||
|
else()
|
||||||
|
message(STATUS "deltacast-bridge: framecache shm mode enabled")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(deltacast-bridge PRIVATE
|
||||||
|
${SDK_ROOT}/include/videomaster
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_directories(deltacast-bridge PRIVATE
|
||||||
|
${SDK_ROOT}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(deltacast-bridge PRIVATE
|
||||||
|
videomasterhd
|
||||||
|
videomasterhd_audio
|
||||||
|
pthread
|
||||||
|
rt # shm_open, sem_open
|
||||||
|
)
|
||||||
|
|
||||||
|
# Embed the SDK RPATH so the binary finds the .so at runtime
|
||||||
|
set_target_properties(deltacast-bridge PROPERTIES
|
||||||
|
INSTALL_RPATH "/usr/local/lib/deltacast"
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compat symlink: deltacast-capture -> deltacast-bridge
|
||||||
|
# (node-agent and any legacy scripts that reference the old name still work)
|
||||||
|
add_custom_command(TARGET deltacast-bridge POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||||
|
$<TARGET_FILE:deltacast-bridge>
|
||||||
|
$<TARGET_FILE_DIR:deltacast-bridge>/deltacast-capture
|
||||||
|
COMMENT "Creating deltacast-capture compat symlink"
|
||||||
|
)
|
||||||
300
services/capture/deltacast-bridge/fc_writer.c
Normal file
300
services/capture/deltacast-bridge/fc_writer.c
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
/**
|
||||||
|
* fc_writer.c — Framecache slot writer for deltacast-bridge.
|
||||||
|
*
|
||||||
|
* Uses only POSIX + libc — no external dependencies beyond what the bridge
|
||||||
|
* already links. HTTP calls are done with raw sockets (tiny GET/POST/DELETE)
|
||||||
|
* to avoid pulling in libcurl.
|
||||||
|
*/
|
||||||
|
#include "fc_writer.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
/* Re-use the shared memory layout from the framecache service */
|
||||||
|
#define FC_MAGIC 0x46524D43u
|
||||||
|
#define FC_VERSION 1u
|
||||||
|
#define FC_RING_DEPTH 120u
|
||||||
|
#define FC_HEADER_SIZE 4096u
|
||||||
|
#define FC_FRAME_HDR_SIZE 24u
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t fps_num;
|
||||||
|
uint32_t fps_den;
|
||||||
|
uint32_t pixel_format;
|
||||||
|
uint32_t frame_size;
|
||||||
|
uint32_t ring_depth;
|
||||||
|
uint32_t _reserved;
|
||||||
|
_Atomic uint64_t write_cursor;
|
||||||
|
_Atomic uint64_t dropped_frames;
|
||||||
|
char source_type[32];
|
||||||
|
char slot_id[64];
|
||||||
|
uint8_t _pad[FC_HEADER_SIZE - 112];
|
||||||
|
} fc_hdr_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pts_us;
|
||||||
|
uint64_t wall_us;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t _pad;
|
||||||
|
uint8_t data[];
|
||||||
|
} fc_frm_t;
|
||||||
|
|
||||||
|
struct fc_writer {
|
||||||
|
void *base;
|
||||||
|
size_t shm_size;
|
||||||
|
int shm_fd;
|
||||||
|
sem_t *sem;
|
||||||
|
char slot_id[64];
|
||||||
|
char fc_url[256]; /* base URL for DELETE on close */
|
||||||
|
char shm_path[128];
|
||||||
|
char sem_name[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── tiny HTTP helper ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static int http_request(const char *method,
|
||||||
|
const char *host, int port, const char *path,
|
||||||
|
const char *body, /* NULL for GET/DELETE */
|
||||||
|
char *resp_buf, size_t resp_len)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
memset(&sa, 0, sizeof sa);
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons((uint16_t)port);
|
||||||
|
|
||||||
|
struct hostent *he = gethostbyname(host);
|
||||||
|
if (!he) return -1;
|
||||||
|
memcpy(&sa.sin_addr, he->h_addr_list[0], (size_t)he->h_length);
|
||||||
|
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
|
||||||
|
struct timeval tv = { .tv_sec = 5 };
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) {
|
||||||
|
close(fd); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char req[4096];
|
||||||
|
int req_len;
|
||||||
|
if (body) {
|
||||||
|
req_len = snprintf(req, sizeof req,
|
||||||
|
"%s %s HTTP/1.0\r\n"
|
||||||
|
"Host: %s:%d\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: %zu\r\n"
|
||||||
|
"Connection: close\r\n\r\n"
|
||||||
|
"%s",
|
||||||
|
method, path, host, port, strlen(body), body);
|
||||||
|
} else {
|
||||||
|
req_len = snprintf(req, sizeof req,
|
||||||
|
"%s %s HTTP/1.0\r\n"
|
||||||
|
"Host: %s:%d\r\n"
|
||||||
|
"Connection: close\r\n\r\n",
|
||||||
|
method, path, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send(fd, req, (size_t)req_len, 0) < 0) { close(fd); return -1; }
|
||||||
|
|
||||||
|
int status = -1;
|
||||||
|
size_t got = 0;
|
||||||
|
char buf[8192];
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = recv(fd, buf + got, sizeof buf - got - 1, 0)) > 0)
|
||||||
|
got += (size_t)n;
|
||||||
|
buf[got] = '\0';
|
||||||
|
|
||||||
|
/* Parse status line */
|
||||||
|
if (sscanf(buf, "HTTP/%*s %d", &status) != 1) status = -1;
|
||||||
|
|
||||||
|
/* Copy body (after \r\n\r\n) into resp_buf */
|
||||||
|
if (resp_buf && resp_len > 0) {
|
||||||
|
const char *body_start = strstr(buf, "\r\n\r\n");
|
||||||
|
if (body_start) {
|
||||||
|
strncpy(resp_buf, body_start + 4, resp_len - 1);
|
||||||
|
resp_buf[resp_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse "host:port" or just "host" from a URL like "http://host:port" */
|
||||||
|
static void parse_url(const char *url, char *host, size_t hlen, int *port)
|
||||||
|
{
|
||||||
|
const char *p = url;
|
||||||
|
if (strncmp(p, "http://", 7) == 0) p += 7;
|
||||||
|
*port = 7435;
|
||||||
|
const char *colon = strchr(p, ':');
|
||||||
|
if (colon) {
|
||||||
|
size_t n = (size_t)(colon - p);
|
||||||
|
if (n >= hlen) n = hlen - 1;
|
||||||
|
strncpy(host, p, n);
|
||||||
|
host[n] = '\0';
|
||||||
|
*port = atoi(colon + 1);
|
||||||
|
} else {
|
||||||
|
strncpy(host, p, hlen - 1);
|
||||||
|
host[hlen - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int json_str(const char *json, const char *key, char *out, size_t len)
|
||||||
|
{
|
||||||
|
char pat[128];
|
||||||
|
snprintf(pat, sizeof pat, "\"%s\":", key);
|
||||||
|
const char *p = strstr(json, pat);
|
||||||
|
if (!p) return -1;
|
||||||
|
p += strlen(pat);
|
||||||
|
while (*p == ' ') p++;
|
||||||
|
if (*p != '"') return -1;
|
||||||
|
p++;
|
||||||
|
size_t i = 0;
|
||||||
|
while (*p && *p != '"' && i < len - 1) out[i++] = *p++;
|
||||||
|
out[i] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── public API ────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
fc_writer_t *fc_writer_open(const char *fc_url,
|
||||||
|
const char *slot_id,
|
||||||
|
uint32_t width, uint32_t height,
|
||||||
|
uint32_t fps_num, uint32_t fps_den)
|
||||||
|
{
|
||||||
|
char host[128]; int port;
|
||||||
|
parse_url(fc_url, host, sizeof host, &port);
|
||||||
|
|
||||||
|
/* POST /slots */
|
||||||
|
char body[512];
|
||||||
|
snprintf(body, sizeof body,
|
||||||
|
"{\"slot_id\":\"%s\","
|
||||||
|
"\"width\":%u,\"height\":%u,"
|
||||||
|
"\"fps_num\":%u,\"fps_den\":%u,"
|
||||||
|
"\"source_type\":\"deltacast\"}",
|
||||||
|
slot_id, width, height, fps_num, fps_den);
|
||||||
|
|
||||||
|
char resp[1024] = {0};
|
||||||
|
int status = http_request("POST", host, port, "/slots", body, resp, sizeof resp);
|
||||||
|
if (status != 201) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] POST /slots failed (HTTP %d): %s\n",
|
||||||
|
slot_id, status, resp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char shm_path[128] = {0}, sem_name[128] = {0};
|
||||||
|
json_str(resp, "shm_path", shm_path, sizeof shm_path);
|
||||||
|
json_str(resp, "sem_name", sem_name, sizeof sem_name);
|
||||||
|
|
||||||
|
if (!shm_path[0] || !sem_name[0]) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] bad response (missing shm_path/sem_name)\n", slot_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mmap the shm file */
|
||||||
|
int fd = open(shm_path, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] open %s: %s\n", slot_id, shm_path, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* Read header to get frame_size */
|
||||||
|
fc_hdr_t hdr;
|
||||||
|
if (pread(fd, &hdr, sizeof hdr, 0) != sizeof hdr || hdr.magic != FC_MAGIC) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] bad shm header\n", slot_id);
|
||||||
|
close(fd); return NULL;
|
||||||
|
}
|
||||||
|
size_t total = (size_t)FC_HEADER_SIZE
|
||||||
|
+ (size_t)FC_RING_DEPTH * ((size_t)FC_FRAME_HDR_SIZE + hdr.frame_size);
|
||||||
|
|
||||||
|
void *base = mmap(NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (base == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] mmap: %s\n", slot_id, strerror(errno));
|
||||||
|
close(fd); return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_t *sem = sem_open(sem_name, 0);
|
||||||
|
if (sem == SEM_FAILED) {
|
||||||
|
fprintf(stderr, "[fc_writer:%s] sem_open %s: %s\n", slot_id, sem_name, strerror(errno));
|
||||||
|
munmap(base, total); close(fd); return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc_writer_t *w = calloc(1, sizeof *w);
|
||||||
|
if (!w) { sem_close(sem); munmap(base, total); close(fd); return NULL; }
|
||||||
|
|
||||||
|
w->base = base;
|
||||||
|
w->shm_size = total;
|
||||||
|
w->shm_fd = fd;
|
||||||
|
w->sem = sem;
|
||||||
|
strncpy(w->slot_id, slot_id, sizeof w->slot_id - 1);
|
||||||
|
strncpy(w->fc_url, fc_url, sizeof w->fc_url - 1);
|
||||||
|
strncpy(w->shm_path, shm_path, sizeof w->shm_path - 1);
|
||||||
|
strncpy(w->sem_name, sem_name, sizeof w->sem_name - 1);
|
||||||
|
|
||||||
|
fprintf(stderr, "[fc_writer:%s] slot open (%ux%u %.2ffps shm=%s)\n",
|
||||||
|
slot_id, width, height,
|
||||||
|
fps_den ? (double)fps_num / fps_den : 0.0, shm_path);
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fc_writer_write(fc_writer_t *w,
|
||||||
|
const uint8_t *data, uint32_t size,
|
||||||
|
uint64_t pts_us)
|
||||||
|
{
|
||||||
|
fc_hdr_t *hdr = (fc_hdr_t *)w->base;
|
||||||
|
uint64_t cur = atomic_load_explicit(&hdr->write_cursor, memory_order_relaxed);
|
||||||
|
uint64_t idx = cur % FC_RING_DEPTH;
|
||||||
|
|
||||||
|
/* Locate frame in ring */
|
||||||
|
uint8_t *frames = (uint8_t *)w->base + FC_HEADER_SIZE;
|
||||||
|
fc_frm_t *frame = (fc_frm_t *)(frames + idx * ((size_t)FC_FRAME_HDR_SIZE + hdr->frame_size));
|
||||||
|
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
uint64_t wall = (uint64_t)ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
|
||||||
|
|
||||||
|
frame->pts_us = pts_us;
|
||||||
|
frame->wall_us = wall;
|
||||||
|
frame->size = size < hdr->frame_size ? size : hdr->frame_size;
|
||||||
|
memcpy(frame->data, data, frame->size);
|
||||||
|
|
||||||
|
atomic_store_explicit(&hdr->write_cursor, cur + 1, memory_order_release);
|
||||||
|
sem_post(w->sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fc_writer_close(fc_writer_t *w)
|
||||||
|
{
|
||||||
|
if (!w) return;
|
||||||
|
|
||||||
|
/* DELETE /slots/:id */
|
||||||
|
char host[128]; int port;
|
||||||
|
parse_url(w->fc_url, host, sizeof host, &port);
|
||||||
|
char path[192];
|
||||||
|
snprintf(path, sizeof path, "/slots/%s", w->slot_id);
|
||||||
|
http_request("DELETE", host, port, path, NULL, NULL, 0);
|
||||||
|
|
||||||
|
sem_close(w->sem);
|
||||||
|
munmap(w->base, w->shm_size);
|
||||||
|
close(w->shm_fd);
|
||||||
|
fprintf(stderr, "[fc_writer:%s] slot closed\n", w->slot_id);
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
50
services/capture/deltacast-bridge/fc_writer.h
Normal file
50
services/capture/deltacast-bridge/fc_writer.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* fc_writer.h — Lightweight framecache slot writer for deltacast-bridge.
|
||||||
|
*
|
||||||
|
* Registers a slot with the framecache HTTP API on signal lock, then writes
|
||||||
|
* raw UYVY422 frames directly into the shared memory ring buffer.
|
||||||
|
*
|
||||||
|
* Compile with -DLEGACY_FIFO to disable shm writes and fall back to the
|
||||||
|
* original named-FIFO path (useful during transition / on nodes without the
|
||||||
|
* framecache container running).
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct fc_writer fc_writer_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a slot with the framecache service and open the shm region for
|
||||||
|
* writing. fc_url is the HTTP base URL, e.g. "http://localhost:7435".
|
||||||
|
* slot_id must be unique per port, e.g. "deltacast-0-3" (device-port).
|
||||||
|
*
|
||||||
|
* Returns writer handle on success, NULL on failure (falls back to FIFO).
|
||||||
|
*/
|
||||||
|
fc_writer_t *fc_writer_open(const char *fc_url,
|
||||||
|
const char *slot_id,
|
||||||
|
uint32_t width, uint32_t height,
|
||||||
|
uint32_t fps_num, uint32_t fps_den);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write one raw UYVY422 frame into the ring buffer.
|
||||||
|
* Non-blocking — slow consumers are skipped, not waited on.
|
||||||
|
* pts_us: presentation timestamp in microseconds (0 if unknown).
|
||||||
|
*/
|
||||||
|
void fc_writer_write(fc_writer_t *w,
|
||||||
|
const uint8_t *data, uint32_t size,
|
||||||
|
uint64_t pts_us);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deregister slot from framecache service and unmap shm.
|
||||||
|
*/
|
||||||
|
void fc_writer_close(fc_writer_t *w);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
811
services/capture/deltacast-bridge/main.c
Normal file
811
services/capture/deltacast-bridge/main.c
Normal file
|
|
@ -0,0 +1,811 @@
|
||||||
|
/* services/capture/deltacast-bridge/main.c
|
||||||
|
*
|
||||||
|
* Deltacast VideoMaster SDI shared multi-port bridge daemon.
|
||||||
|
*
|
||||||
|
* Opens the board ONCE, opens RX streams for all requested ports, and
|
||||||
|
* writes each port's video frames into a shared-memory framecache slot
|
||||||
|
* (and audio to a named FIFO — audio-in-shm is a future roadmap item).
|
||||||
|
*
|
||||||
|
* Signal fan-out architecture:
|
||||||
|
* Board → video_thread → fc_writer → /dev/shm/framecache/<slot>
|
||||||
|
* └→ N consumers (recording, proxy,
|
||||||
|
* HLS preview) each read with
|
||||||
|
* their own cursor — zero-copy,
|
||||||
|
* no bandwidth splitting.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* deltacast-bridge --device <N> --ports <csv>
|
||||||
|
* [--video-pipe-dir /dev/shm/deltacast]
|
||||||
|
* [--audio-pipe-dir /dev/shm/deltacast]
|
||||||
|
* [--fc-url http://framecache:7435]
|
||||||
|
* [--signal-timeout <sec>]
|
||||||
|
*
|
||||||
|
* Compat alias: --port <N> treated as --ports <N> (single port).
|
||||||
|
*
|
||||||
|
* For each port that acquires signal, emits one JSON line to stderr:
|
||||||
|
* {"port":N,"width":W,"height":H,"fps_num":N,"fps_den":D,
|
||||||
|
* "pix_fmt":"uyvy422","audio_rate":48000,"audio_channels":2,
|
||||||
|
* "slot_id":"deltacast-<device>-<port>"}
|
||||||
|
*
|
||||||
|
* Compile with -DLEGACY_FIFO=1 to disable shm writes and fall back to
|
||||||
|
* the original named-FIFO path (for nodes without framecache running).
|
||||||
|
*
|
||||||
|
* Runs until SIGTERM/SIGINT, then closes all streams and the board.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "VideoMasterHD_Core.h"
|
||||||
|
#include "VideoMasterHD_Sdi.h"
|
||||||
|
#include "VideoMasterHD_Sdi_Audio.h"
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
# include "fc_writer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef F_SETPIPE_SZ
|
||||||
|
#define F_SETPIPE_SZ 1031
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Default framecache URL — overridden by FC_URL env var or --fc-url arg */
|
||||||
|
#define FC_URL_DEFAULT "http://localhost:7435"
|
||||||
|
|
||||||
|
/* ── Constants ────────────────────────────────────────────────────────── */
|
||||||
|
#define MAX_PORTS 8
|
||||||
|
|
||||||
|
/* ── Globals ──────────────────────────────────────────────────────────── */
|
||||||
|
static atomic_int g_stop = 0; /* global shutdown (SIGTERM/SIGINT only) */
|
||||||
|
|
||||||
|
static void on_signal(int s) { (void)s; atomic_store(&g_stop, 1); }
|
||||||
|
|
||||||
|
/* Per-port stop flag — set only when a fatal error occurs on that specific
|
||||||
|
* port (e.g. video lock lost). Audio EPIPE is handled by reopening the FIFO
|
||||||
|
* rather than stopping the port, so the thread survives ffmpeg restarts. */
|
||||||
|
static atomic_int g_port_stop[MAX_PORTS];
|
||||||
|
|
||||||
|
/* ── Stream type by port index (non-contiguous SDK enum) ────────────── */
|
||||||
|
static ULONG rx_streamtype(unsigned port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return VHD_ST_RX0;
|
||||||
|
case 1: return VHD_ST_RX1;
|
||||||
|
case 2: return VHD_ST_RX2;
|
||||||
|
case 3: return VHD_ST_RX3;
|
||||||
|
case 4: return VHD_ST_RX4;
|
||||||
|
case 5: return VHD_ST_RX5;
|
||||||
|
case 6: return VHD_ST_RX6;
|
||||||
|
case 7: return VHD_ST_RX7;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "{\"error\":\"port %u not supported (max 7)\"}\n", port);
|
||||||
|
return VHD_ST_RX0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Loopback board property by port index ───────────────────────────── */
|
||||||
|
static ULONG loopback_prop(unsigned port) {
|
||||||
|
switch (port) {
|
||||||
|
case 0: return VHD_CORE_BP_PASSIVE_LOOPBACK_0;
|
||||||
|
case 1: return VHD_CORE_BP_PASSIVE_LOOPBACK_1;
|
||||||
|
case 2: return VHD_CORE_BP_PASSIVE_LOOPBACK_2;
|
||||||
|
case 3: return VHD_CORE_BP_PASSIVE_LOOPBACK_3;
|
||||||
|
default: return -1; /* ports 4-7 have no passive loopback property; call site guards p < 4 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Video standard → width/height/fps/interlaced ───────────────────── */
|
||||||
|
typedef struct { int width, height, fps_num, fps_den; int interlaced; } VideoInfo;
|
||||||
|
|
||||||
|
static VideoInfo video_info(VHD_VIDEOSTANDARD std, VHD_CLOCKDIVISOR div) {
|
||||||
|
int ntsc = (div == VHD_CLOCKDIV_1001);
|
||||||
|
switch (std) {
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_25Hz: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_30Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_24Hz: return (VideoInfo){1920,1080,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_50Hz: return (VideoInfo){1920,1080,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080p_60Hz: return (VideoInfo){1920,1080,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_24Hz: return (VideoInfo){1920,1080,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_25Hz: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080psf_30Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080i_50Hz: return (VideoInfo){1920,1080,25,1,1};
|
||||||
|
case VHD_VIDEOSTD_S274M_1080i_60Hz: return (VideoInfo){1920,1080,ntsc?30000:30,ntsc?1001:1,1};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_50Hz: return (VideoInfo){1280,720,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_60Hz: return (VideoInfo){1280,720,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_25Hz: return (VideoInfo){1280,720,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_30Hz: return (VideoInfo){1280,720,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S296M_720p_24Hz: return (VideoInfo){1280,720,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_24Hz: return (VideoInfo){3840,2160,ntsc?24000:24,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_25Hz: return (VideoInfo){3840,2160,25,1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_30Hz: return (VideoInfo){3840,2160,ntsc?30000:30,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_50Hz: return (VideoInfo){3840,2160,50,1,0};
|
||||||
|
case VHD_VIDEOSTD_3840x2160p_60Hz: return (VideoInfo){3840,2160,ntsc?60000:60,ntsc?1001:1,0};
|
||||||
|
case VHD_VIDEOSTD_S259M_NTSC_480: return (VideoInfo){720,480,ntsc?30000:30,ntsc?1001:1,1};
|
||||||
|
default: return (VideoInfo){1920,1080,25,1,0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Write-all helper ─────────────────────────────────────────────────── */
|
||||||
|
/* Writes all bytes to fd. Uses non-blocking I/O so the bridge never stalls
|
||||||
|
* waiting for a slow reader. Returns 0 on success, -1 on fatal error (EPIPE
|
||||||
|
* = reader closed the FIFO). EAGAIN / EWOULDBLOCK on a full pipe is NOT fatal
|
||||||
|
* — the caller (video_thread) will retry on the next slot lock. */
|
||||||
|
static int write_all(int fd, const unsigned char *p, size_t len) {
|
||||||
|
/* Make the fd non-blocking for the duration of this write */
|
||||||
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
|
if (flags < 0) return -1;
|
||||||
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) return -1;
|
||||||
|
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < len) {
|
||||||
|
ssize_t n = write(fd, p + off, len - off);
|
||||||
|
if (n > 0) { off += (size_t)n; continue; }
|
||||||
|
if (n < 0 && errno == EINTR) continue;
|
||||||
|
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||||
|
/* Pipe full — brief yield then retry */
|
||||||
|
struct timespec ts = {0, 1000000L}; /* 1ms */
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* EPIPE or other fatal error — restore flags and return */
|
||||||
|
fcntl(fd, F_SETFL, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* Restore blocking mode */
|
||||||
|
fcntl(fd, F_SETFL, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Per-port state ───────────────────────────────────────────────────── */
|
||||||
|
typedef struct {
|
||||||
|
HANDLE board;
|
||||||
|
unsigned port;
|
||||||
|
unsigned device;
|
||||||
|
ULONG video_std;
|
||||||
|
ULONG clock_div;
|
||||||
|
VideoInfo vi;
|
||||||
|
char video_fifo[256];
|
||||||
|
char audio_fifo[256];
|
||||||
|
char slot_id[128]; /* framecache slot id: "deltacast-<dev>-<port>" */
|
||||||
|
char fc_url[256]; /* framecache HTTP base URL */
|
||||||
|
/* threads */
|
||||||
|
pthread_t video_tid;
|
||||||
|
pthread_t audio_tid;
|
||||||
|
/* streams (owned by threads, set before thread launch) */
|
||||||
|
HANDLE video_stream;
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
fc_writer_t *fc_writer; /* shm ring buffer writer (NULL = use FIFO fallback) */
|
||||||
|
#endif
|
||||||
|
} PortState;
|
||||||
|
|
||||||
|
/* ── Audio thread ──────────────────────────────────────────────────────
|
||||||
|
*
|
||||||
|
* - Opens FIFO writer (blocks until a reader connects — correct behaviour).
|
||||||
|
* - Feeds continuous wall-clock-paced s16le stereo (real or silence).
|
||||||
|
* - Best-effort VHD audio stream; silence fallback on any failure.
|
||||||
|
* - On EPIPE (ffmpeg reader died): closes and REOPENS the FIFO so the
|
||||||
|
* thread survives an ffmpeg restart without bringing down other ports.
|
||||||
|
* EPIPE never sets g_stop — only SIGTERM/SIGINT does that.
|
||||||
|
*/
|
||||||
|
static void *audio_thread(void *arg) {
|
||||||
|
PortState *ps = (PortState *)arg;
|
||||||
|
|
||||||
|
const int AUDIO_RATE = 48000;
|
||||||
|
const int CHANNELS = 2;
|
||||||
|
const size_t FRAME_BYTES = (size_t)CHANNELS * 2; /* s16le stereo */
|
||||||
|
int fps_num = ps->vi.fps_num > 0 ? ps->vi.fps_num : 25;
|
||||||
|
int fps_den = ps->vi.fps_den > 0 ? ps->vi.fps_den : 1;
|
||||||
|
long samples_per_frame = ((long)AUDIO_RATE * fps_den + fps_num / 2) / fps_num;
|
||||||
|
if (samples_per_frame < 1) samples_per_frame = 1;
|
||||||
|
size_t tick_bytes = (size_t)samples_per_frame * FRAME_BYTES;
|
||||||
|
|
||||||
|
ULONG max_samples = VHD_GetNbSamples((VHD_VIDEOSTANDARD)ps->video_std,
|
||||||
|
(VHD_CLOCKDIVISOR)ps->clock_div,
|
||||||
|
VHD_ASR_48000, 0);
|
||||||
|
ULONG block_size = VHD_GetBlockSize(VHD_AF_16, VHD_AM_STEREO);
|
||||||
|
size_t vhd_buf_sz = ((size_t)max_samples + 64) * (block_size ? block_size : FRAME_BYTES);
|
||||||
|
size_t buf_sz = vhd_buf_sz > tick_bytes ? vhd_buf_sz : tick_bytes;
|
||||||
|
unsigned char *buf = calloc(1, buf_sz);
|
||||||
|
if (!buf) return NULL;
|
||||||
|
|
||||||
|
/* Open the VHD audio stream once for the lifetime of the bridge.
|
||||||
|
* The stream stays open across reader reconnects — no need to reopen it. */
|
||||||
|
HANDLE stream = NULL;
|
||||||
|
int have_vhd_audio = 0;
|
||||||
|
VHD_AUDIOINFO ai;
|
||||||
|
memset(&ai, 0, sizeof(ai));
|
||||||
|
|
||||||
|
ULONG r = VHD_OpenStreamHandle(ps->board, rx_streamtype(ps->port),
|
||||||
|
VHD_SDI_STPROC_DISJOINED_ANC,
|
||||||
|
NULL, &stream, NULL);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
/* Per Deltacast SDK Sample_RXAudio.cpp: VHD_SDI_SP_INTERFACE must be
|
||||||
|
* propagated to the audio stream, otherwise VHD_SlotExtractAudio
|
||||||
|
* returns 0 samples (silent capture). */
|
||||||
|
ULONG iface = 0;
|
||||||
|
VHD_GetStreamProperty(stream, VHD_SDI_SP_INTERFACE, &iface);
|
||||||
|
|
||||||
|
VHD_SetStreamProperty(stream, VHD_SDI_SP_VIDEO_STANDARD, ps->video_std);
|
||||||
|
VHD_SetStreamProperty(stream, VHD_SDI_SP_CLOCK_SYSTEM, ps->clock_div);
|
||||||
|
VHD_SetStreamProperty(stream, VHD_CORE_SP_TRANSFER_SCHEME, VHD_TRANSFER_SLAVED);
|
||||||
|
VHD_SetStreamProperty(stream, VHD_SDI_SP_INTERFACE, iface);
|
||||||
|
|
||||||
|
/* Configure BOTH channels of the stereo pair (group 0). The actual PCM
|
||||||
|
* samples land in pAudioChannels[0].pData (packed L/R s16le). Channel
|
||||||
|
* [1] must declare Mode+BufferFormat so the SDK recognizes the pair. */
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].Mode = VHD_AM_STEREO;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].BufferFormat = VHD_AF_16;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].pData = buf;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[1].Mode = VHD_AM_STEREO;
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[1].BufferFormat = VHD_AF_16;
|
||||||
|
|
||||||
|
if (VHD_StartStream(stream) == VHDERR_NOERROR) {
|
||||||
|
have_vhd_audio = 1;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[audio:%u] VHD_StartStream failed — feeding silence\n", ps->port);
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
stream = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[audio:%u] VHD_OpenStreamHandle failed (%lu) — feeding silence\n",
|
||||||
|
ps->port, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
long frame_ns = (long)(1000000000.0 * (double)fps_den / (double)fps_num);
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
|
||||||
|
/* Outer loop: reopen the FIFO writer each time a reader connects.
|
||||||
|
* This allows the bridge to survive ffmpeg session stop/restart on a port
|
||||||
|
* without affecting any other port's threads. */
|
||||||
|
while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) {
|
||||||
|
|
||||||
|
int fd = open(ps->audio_fifo, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
/* Open failed (rare — FIFO was deleted?). Brief pause then retry. */
|
||||||
|
fprintf(stderr, "[audio:%u] open FIFO failed: %s\n", ps->port, strerror(errno));
|
||||||
|
struct timespec ts = {0, 200000000L};
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fcntl(fd, F_SETPIPE_SZ, 1024 * 1024);
|
||||||
|
|
||||||
|
/* Reset wall-clock baseline after potentially blocking on open().
|
||||||
|
* Only used for the SILENCE fallback path (no hardware audio). */
|
||||||
|
struct timespec next;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &next);
|
||||||
|
|
||||||
|
/* Inner loop: feed audio into the open FIFO until reader exits (EPIPE). */
|
||||||
|
while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) {
|
||||||
|
size_t out_bytes = 0;
|
||||||
|
|
||||||
|
if (have_vhd_audio) {
|
||||||
|
/* HARDWARE-PACED PATH (the normal case).
|
||||||
|
* VHD_LockSlotHandle blocks until the board has the next audio
|
||||||
|
* slot ready — this slot is generated from the SAME SDI signal
|
||||||
|
* as the video, so blocking here paces audio in lockstep with
|
||||||
|
* video at the TRUE hardware rate. We write ONLY the real
|
||||||
|
* samples the board gives us (no silence padding, no wall-clock
|
||||||
|
* sleep) so the audio timeline length exactly tracks video.
|
||||||
|
* This is the fix for progressive A/V drift: mixing wall-clock
|
||||||
|
* paced silence with variable-length real reads made the audio
|
||||||
|
* stream length diverge from the video stream length. */
|
||||||
|
r = VHD_LockSlotHandle(stream, &slot);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
ai.pAudioGroups[0].pAudioChannels[0].DataSize = (ULONG)buf_sz;
|
||||||
|
if (VHD_SlotExtractAudio(slot, &ai) == VHDERR_NOERROR) {
|
||||||
|
ULONG sz = ai.pAudioGroups[0].pAudioChannels[0].DataSize;
|
||||||
|
if (sz > 0 && (size_t)sz <= buf_sz) out_bytes = (size_t)sz;
|
||||||
|
}
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
|
||||||
|
if (out_bytes > 0) {
|
||||||
|
if (write_all(fd, buf, out_bytes) < 0) {
|
||||||
|
fprintf(stderr, "[audio:%u] EPIPE — waiting for next reader\n", ps->port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No wall-clock sleep — the board's slot cadence is the clock. */
|
||||||
|
continue;
|
||||||
|
} else if (r == VHDERR_TIMEOUT) {
|
||||||
|
/* No slot yet — loop and try again (do NOT inject silence,
|
||||||
|
* that would add extra samples and cause drift). */
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[audio:%u] lock error %lu — degrading to silence\n",
|
||||||
|
ps->port, r);
|
||||||
|
VHD_StopStream(stream);
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
stream = NULL;
|
||||||
|
have_vhd_audio = 0;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &next); /* rebase silence clock */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SILENCE FALLBACK PATH (no hardware audio available).
|
||||||
|
* Wall-clock paced one-frame-of-silence per video-frame interval so
|
||||||
|
* ffmpeg's input 1 never starves and audio length still tracks
|
||||||
|
* real time. */
|
||||||
|
memset(buf, 0, tick_bytes);
|
||||||
|
out_bytes = tick_bytes;
|
||||||
|
|
||||||
|
if (write_all(fd, buf, out_bytes) < 0) {
|
||||||
|
fprintf(stderr, "[audio:%u] EPIPE — waiting for next reader\n", ps->port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next.tv_nsec += frame_ns;
|
||||||
|
while (next.tv_nsec >= 1000000000L) { next.tv_nsec -= 1000000000L; next.tv_sec += 1; }
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
if (next.tv_sec > now.tv_sec ||
|
||||||
|
(next.tv_sec == now.tv_sec && next.tv_nsec > now.tv_nsec)) {
|
||||||
|
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
|
||||||
|
} else {
|
||||||
|
next = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
VHD_StopStream(stream);
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Video thread ─────────────────────────────────────────────────────── */
|
||||||
|
static void *video_thread(void *arg) {
|
||||||
|
PortState *ps = (PortState *)arg;
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
/* ── Framecache shm path (primary) ──────────────────────────────────
|
||||||
|
* Write frames directly into the shared memory ring buffer.
|
||||||
|
* Multiple consumers (growing recorder, proxy encoder, HLS preview)
|
||||||
|
* each hold their own read cursor and read independently — no FIFO
|
||||||
|
* splitting, no bandwidth halving.
|
||||||
|
*
|
||||||
|
* The fc_writer was opened by main() after signal lock. If it is
|
||||||
|
* NULL the framecache service was unavailable and we fall through to
|
||||||
|
* the legacy FIFO path automatically.
|
||||||
|
*/
|
||||||
|
if (ps->fc_writer) {
|
||||||
|
uint64_t frame_seq = 0;
|
||||||
|
while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) {
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
ULONG r = VHD_LockSlotHandle(ps->video_stream, &slot);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
BYTE *buf = NULL;
|
||||||
|
ULONG sz = 0;
|
||||||
|
if (VHD_GetSlotBuffer(slot, VHD_SDI_BT_VIDEO, &buf, &sz) == VHDERR_NOERROR) {
|
||||||
|
ULONG expected = (ULONG)ps->vi.width * (ULONG)ps->vi.height * 2;
|
||||||
|
if (sz != expected) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"[video:%u] WARN: sz=%lu != expected %lu — packing mismatch, skipping\n",
|
||||||
|
ps->port, (unsigned long)sz, (unsigned long)expected);
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* pts: frame index × frame duration in µs */
|
||||||
|
uint64_t pts_us = 0;
|
||||||
|
if (ps->vi.fps_num > 0) {
|
||||||
|
pts_us = frame_seq * 1000000ULL
|
||||||
|
* (uint64_t)ps->vi.fps_den
|
||||||
|
/ (uint64_t)ps->vi.fps_num;
|
||||||
|
}
|
||||||
|
fc_writer_write(ps->fc_writer, buf, (uint32_t)sz, pts_us);
|
||||||
|
frame_seq++;
|
||||||
|
}
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
} else if (r != VHDERR_TIMEOUT) {
|
||||||
|
fprintf(stderr, "[video:%u] VHD_LockSlotHandle error %lu — stopping port\n",
|
||||||
|
ps->port, (unsigned long)r);
|
||||||
|
atomic_store(&g_port_stop[ps->port], 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* fc_writer == NULL → fall through to FIFO path */
|
||||||
|
fprintf(stderr, "[video:%u] fc_writer unavailable — falling back to FIFO\n", ps->port);
|
||||||
|
#endif /* !LEGACY_FIFO */
|
||||||
|
|
||||||
|
/* ── Legacy FIFO path ────────────────────────────────────────────────
|
||||||
|
* Kept as compile-time fallback (-DLEGACY_FIFO=1) or when the
|
||||||
|
* framecache service is not reachable at startup.
|
||||||
|
*
|
||||||
|
* Outer loop: reopen the FIFO writer each time a reader connects.
|
||||||
|
* EPIPE means the ffmpeg sidecar for this port died (session
|
||||||
|
* stop/restart), NOT a hardware fault. Reopen and block until the
|
||||||
|
* next recorder start; other ports are unaffected.
|
||||||
|
*/
|
||||||
|
while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) {
|
||||||
|
|
||||||
|
int fd = open(ps->video_fifo, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "[video:%u] open FIFO failed: %s\n", ps->port, strerror(errno));
|
||||||
|
struct timespec ts = {0, 200000000L};
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int pipe_sz = 64 * 1024 * 1024; /* 64 MB — ~16 frames of 1080p UYVY */
|
||||||
|
if (fcntl(fd, F_SETPIPE_SZ, pipe_sz) < 0) {
|
||||||
|
fprintf(stderr, "[video:%u] fcntl F_SETPIPE_SZ failed: %s\n",
|
||||||
|
ps->port, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
int fatal = 0;
|
||||||
|
while (!atomic_load(&g_stop) && !atomic_load(&g_port_stop[ps->port])) {
|
||||||
|
ULONG r = VHD_LockSlotHandle(ps->video_stream, &slot);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
BYTE *buf = NULL;
|
||||||
|
ULONG sz = 0;
|
||||||
|
if (VHD_GetSlotBuffer(slot, VHD_SDI_BT_VIDEO, &buf, &sz) == VHDERR_NOERROR) {
|
||||||
|
ULONG expected = (ULONG)ps->vi.width * (ULONG)ps->vi.height * 2;
|
||||||
|
if (sz != expected) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"[video:%u] WARN: slot sz=%lu != expected %lu (w=%d h=%d) -- packing mismatch; skipping frame\n",
|
||||||
|
ps->port, (unsigned long)sz, (unsigned long)expected,
|
||||||
|
ps->vi.width, ps->vi.height);
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (write_all(fd, buf, sz) < 0) {
|
||||||
|
fprintf(stderr, "[video:%u] EPIPE — waiting for next reader\n", ps->port);
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VHD_UnlockSlotHandle(slot);
|
||||||
|
} else if (r != VHDERR_TIMEOUT) {
|
||||||
|
fprintf(stderr, "[video:%u] VHD_LockSlotHandle error %lu — stopping port\n",
|
||||||
|
ps->port, (unsigned long)r);
|
||||||
|
atomic_store(&g_port_stop[ps->port], 1);
|
||||||
|
fatal = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
if (fatal) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Parse comma-separated port list ─────────────────────────────────── */
|
||||||
|
static int parse_ports(const char *csv, unsigned *ports, int max) {
|
||||||
|
int count = 0;
|
||||||
|
char buf[256];
|
||||||
|
strncpy(buf, csv, sizeof(buf) - 1);
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
char *tok = strtok(buf, ",");
|
||||||
|
while (tok && count < max) {
|
||||||
|
ports[count++] = (unsigned)atoi(tok);
|
||||||
|
tok = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main ─────────────────────────────────────────────────────────────── */
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
unsigned device_id = 0;
|
||||||
|
unsigned ports[MAX_PORTS] = {0};
|
||||||
|
int port_count = 0;
|
||||||
|
int sig_timeout = 30;
|
||||||
|
const char *video_pipe_dir = "/dev/shm/deltacast";
|
||||||
|
const char *audio_pipe_dir = "/dev/shm/deltacast";
|
||||||
|
/* Framecache URL: CLI arg > FC_URL env var > default */
|
||||||
|
const char *fc_url_env = getenv("FC_URL");
|
||||||
|
const char *fc_url = fc_url_env ? fc_url_env : FC_URL_DEFAULT;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--device") && i+1 < argc) {
|
||||||
|
device_id = (unsigned)atoi(argv[++i]);
|
||||||
|
} else if (!strcmp(argv[i], "--ports") && i+1 < argc) {
|
||||||
|
port_count = parse_ports(argv[++i], ports, MAX_PORTS);
|
||||||
|
} else if (!strcmp(argv[i], "--port") && i+1 < argc) {
|
||||||
|
/* single-port compat alias */
|
||||||
|
ports[0] = (unsigned)atoi(argv[++i]);
|
||||||
|
port_count = 1;
|
||||||
|
} else if (!strcmp(argv[i], "--video-pipe-dir") && i+1 < argc) {
|
||||||
|
video_pipe_dir = argv[++i];
|
||||||
|
} else if (!strcmp(argv[i], "--audio-pipe-dir") && i+1 < argc) {
|
||||||
|
audio_pipe_dir = argv[++i];
|
||||||
|
} else if (!strcmp(argv[i], "--signal-timeout") && i+1 < argc) {
|
||||||
|
sig_timeout = atoi(argv[++i]);
|
||||||
|
} else if (!strcmp(argv[i], "--fc-url") && i+1 < argc) {
|
||||||
|
fc_url = argv[++i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port_count == 0) {
|
||||||
|
fprintf(stderr, "{\"error\":\"no ports specified — use --ports 0,1,2,...\"}\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
/* ── Init API ────────────────────────────────────────────────────── */
|
||||||
|
ULONG dll_ver, nb_boards;
|
||||||
|
if (VHD_GetApiInfo(&dll_ver, &nb_boards) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_GetApiInfo failed\"}\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (device_id >= nb_boards) {
|
||||||
|
fprintf(stderr, "{\"error\":\"board %u not found (%lu detected)\"}\n",
|
||||||
|
device_id, nb_boards);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Configure bi-directional channel mode before opening board ─────
|
||||||
|
*
|
||||||
|
* The DELTA-12G-e-h 8c is a bidirectional card. Unless we explicitly
|
||||||
|
* call VHD_SetBiDirCfg(BrdId, VHD_BIDIR_80) the board may default to
|
||||||
|
* a mixed RX/TX configuration (e.g. 4RX/4TX), which causes random RX
|
||||||
|
* stream opens to fail with VHDERR_RESOURCEUNAVAILABLE and produces the
|
||||||
|
* "connecting…" hang operators see when starting certain recorders.
|
||||||
|
*
|
||||||
|
* Per SDK sample Tools.cpp SetNbChannels(): open a temporary handle,
|
||||||
|
* check IS_BIDIR and channel counts, call VHD_SetBiDirCfg if needed,
|
||||||
|
* then close. The subsequent real board open will see all 8 as RX.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
HANDLE tmp = NULL;
|
||||||
|
if (VHD_OpenBoardHandle(device_id, &tmp, NULL, 0) == VHDERR_NOERROR) {
|
||||||
|
ULONG nb_rx = 0, nb_tx = 0, is_bidir = 0;
|
||||||
|
VHD_GetBoardProperty(tmp, VHD_CORE_BP_NB_RXCHANNELS, &nb_rx);
|
||||||
|
VHD_GetBoardProperty(tmp, VHD_CORE_BP_NB_TXCHANNELS, &nb_tx);
|
||||||
|
VHD_GetBoardProperty(tmp, VHD_CORE_BP_IS_BIDIR, &is_bidir);
|
||||||
|
VHD_CloseBoardHandle(tmp);
|
||||||
|
|
||||||
|
if (is_bidir) {
|
||||||
|
/* Set all channels to RX. For 8-channel bidir: VHD_BIDIR_80.
|
||||||
|
* VHD_SetBiDirCfg takes the board INDEX, not a handle. */
|
||||||
|
ULONG cfg = (nb_rx + nb_tx == 8) ? VHD_BIDIR_80 : VHD_BIDIR_40;
|
||||||
|
ULONG r = VHD_SetBiDirCfg(device_id, cfg);
|
||||||
|
if (r == VHDERR_NOERROR)
|
||||||
|
fprintf(stderr, "[board] SetBiDirCfg(%lu) OK — %lu+%lu ch bidir configured all-RX\n",
|
||||||
|
cfg, nb_rx, nb_tx);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "[board] SetBiDirCfg warn rc=%lu (non-fatal)\n", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Open board ONCE ─────────────────────────────────────────────── */
|
||||||
|
HANDLE board = NULL;
|
||||||
|
if (VHD_OpenBoardHandle(device_id, &board, NULL, 0) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_OpenBoardHandle failed for board %u\"}\n", device_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "[board] opened board %u with %d port(s)\n", device_id, port_count);
|
||||||
|
|
||||||
|
/* Per SDK samples: for 12G-ASI or 3G-ASI channel types the channel must be
|
||||||
|
* explicitly switched to SDI mode. Without this, VHD_SDI_CP_VIDEO_STANDARD
|
||||||
|
* polls return NB_VHD_VIDEOSTANDARDS (no signal) even when signal present.
|
||||||
|
* Also disable passive loopback for ports 0-3 so RX doesn't loop to TX. */
|
||||||
|
for (int pi = 0; pi < port_count; pi++) {
|
||||||
|
unsigned p = ports[pi];
|
||||||
|
ULONG chn_type = 0;
|
||||||
|
if (VHD_GetChannelProperty(board, VHD_RX_CHANNEL, p, VHD_CORE_CP_TYPE, &chn_type) == VHDERR_NOERROR) {
|
||||||
|
if (chn_type == VHD_CHNTYPE_3GSDI_ASI || chn_type == VHD_CHNTYPE_12GSDI_ASI)
|
||||||
|
VHD_SetChannelProperty(board, VHD_RX_CHANNEL, p, VHD_CORE_CP_MODE, VHD_CHANNEL_MODE_SDI);
|
||||||
|
}
|
||||||
|
if (p < 4) VHD_SetBoardProperty(board, loopback_prop(p), FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Wait for signal on all ports ───────────────────────────────── */
|
||||||
|
ULONG video_stds[MAX_PORTS] = {0};
|
||||||
|
ULONG clock_divs[MAX_PORTS] = {0};
|
||||||
|
int locked[MAX_PORTS] = {0};
|
||||||
|
|
||||||
|
for (int pi = 0; pi < port_count; pi++) {
|
||||||
|
video_stds[pi] = (ULONG)NB_VHD_VIDEOSTANDARDS;
|
||||||
|
clock_divs[pi] = VHD_CLOCKDIV_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec deadline;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
||||||
|
deadline.tv_sec += sig_timeout;
|
||||||
|
|
||||||
|
while (!atomic_load(&g_stop)) {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
if (now.tv_sec > deadline.tv_sec ||
|
||||||
|
(now.tv_sec == deadline.tv_sec && now.tv_nsec >= deadline.tv_nsec)) break;
|
||||||
|
|
||||||
|
int all_locked = 1;
|
||||||
|
for (int pi = 0; pi < port_count; pi++) {
|
||||||
|
if (locked[pi]) continue;
|
||||||
|
VHD_GetChannelProperty(board, VHD_RX_CHANNEL, ports[pi],
|
||||||
|
VHD_SDI_CP_VIDEO_STANDARD, &video_stds[pi]);
|
||||||
|
if (video_stds[pi] != (ULONG)NB_VHD_VIDEOSTANDARDS) {
|
||||||
|
VHD_GetChannelProperty(board, VHD_RX_CHANNEL, ports[pi],
|
||||||
|
VHD_SDI_CP_CLOCK_DIVISOR, &clock_divs[pi]);
|
||||||
|
locked[pi] = 1;
|
||||||
|
fprintf(stderr, "[board] port %u signal locked (std=%lu)\n",
|
||||||
|
ports[pi], video_stds[pi]);
|
||||||
|
} else {
|
||||||
|
all_locked = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (all_locked) break;
|
||||||
|
|
||||||
|
struct timespec ts = {0, 200000000L}; /* 200ms poll */
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Report results — continue with whatever locked, abort only if NONE locked. */
|
||||||
|
int any_locked = 0;
|
||||||
|
for (int pi = 0; pi < port_count; pi++) {
|
||||||
|
if (locked[pi]) { any_locked = 1; }
|
||||||
|
else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"error\":\"no signal on board %u port %u within %ds\"}\n",
|
||||||
|
device_id, ports[pi], sig_timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!any_locked || atomic_load(&g_stop)) {
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Create FIFOs and open streams for each locked port ─────────── */
|
||||||
|
PortState ps[MAX_PORTS];
|
||||||
|
memset(ps, 0, sizeof(ps));
|
||||||
|
int active_count = 0;
|
||||||
|
|
||||||
|
/* Initialise per-port stop flags. */
|
||||||
|
for (int pi = 0; pi < MAX_PORTS; pi++) atomic_store(&g_port_stop[pi], 0);
|
||||||
|
|
||||||
|
for (int pi = 0; pi < port_count; pi++) {
|
||||||
|
if (!locked[pi]) continue;
|
||||||
|
PortState *p = &ps[active_count];
|
||||||
|
p->board = board;
|
||||||
|
p->port = ports[pi];
|
||||||
|
p->device = device_id;
|
||||||
|
p->video_std = video_stds[pi];
|
||||||
|
p->clock_div = clock_divs[pi];
|
||||||
|
p->vi = video_info((VHD_VIDEOSTANDARD)video_stds[pi],
|
||||||
|
(VHD_CLOCKDIVISOR)clock_divs[pi]);
|
||||||
|
|
||||||
|
snprintf(p->video_fifo, sizeof(p->video_fifo),
|
||||||
|
"%s/video-%u.fifo", video_pipe_dir, ports[pi]);
|
||||||
|
snprintf(p->audio_fifo, sizeof(p->audio_fifo),
|
||||||
|
"%s/audio-%u.fifo", audio_pipe_dir, ports[pi]);
|
||||||
|
snprintf(p->slot_id, sizeof(p->slot_id),
|
||||||
|
"deltacast-%u-%u", device_id, ports[pi]);
|
||||||
|
strncpy(p->fc_url, fc_url, sizeof(p->fc_url) - 1);
|
||||||
|
|
||||||
|
/* Create audio FIFO (always needed — audio stays in FIFO for now). */
|
||||||
|
if (mkfifo(p->audio_fifo, 0666) != 0 && errno != EEXIST) {
|
||||||
|
fprintf(stderr, "[port:%u] mkfifo audio failed: %s\n", ports[pi], strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
/* Open framecache slot for video frames.
|
||||||
|
* Fall back to FIFO if framecache is unreachable. */
|
||||||
|
p->fc_writer = fc_writer_open(p->fc_url, p->slot_id,
|
||||||
|
(uint32_t)p->vi.width, (uint32_t)p->vi.height,
|
||||||
|
(uint32_t)p->vi.fps_num, (uint32_t)p->vi.fps_den);
|
||||||
|
if (!p->fc_writer) {
|
||||||
|
fprintf(stderr, "[port:%u] framecache unavailable — creating video FIFO fallback\n",
|
||||||
|
ports[pi]);
|
||||||
|
if (mkfifo(p->video_fifo, 0666) != 0 && errno != EEXIST) {
|
||||||
|
fprintf(stderr, "[port:%u] mkfifo video failed: %s\n", ports[pi], strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Legacy: always use video FIFO */
|
||||||
|
if (mkfifo(p->video_fifo, 0666) != 0 && errno != EEXIST) {
|
||||||
|
fprintf(stderr, "[port:%u] mkfifo video failed: %s\n", ports[pi], strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Open video stream. */
|
||||||
|
HANDLE vs = NULL;
|
||||||
|
ULONG r = VHD_OpenStreamHandle(board, rx_streamtype(ports[pi]),
|
||||||
|
VHD_SDI_STPROC_DISJOINED_VIDEO,
|
||||||
|
NULL, &vs, NULL);
|
||||||
|
if (r != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_OpenStreamHandle video failed port %u rc=%lu\"}\n",
|
||||||
|
ports[pi], r);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
VHD_SetStreamProperty(vs, VHD_SDI_SP_VIDEO_STANDARD, p->video_std);
|
||||||
|
VHD_SetStreamProperty(vs, VHD_SDI_SP_CLOCK_SYSTEM, p->clock_div);
|
||||||
|
VHD_SetStreamProperty(vs, VHD_CORE_SP_TRANSFER_SCHEME, VHD_TRANSFER_SLAVED);
|
||||||
|
VHD_SetStreamProperty(vs, VHD_CORE_SP_BUFFERQUEUE_DEPTH, 8);
|
||||||
|
ULONG iface = 0;
|
||||||
|
if (VHD_GetStreamProperty(vs, VHD_SDI_SP_INTERFACE, &iface) == VHDERR_NOERROR) {
|
||||||
|
VHD_SetStreamProperty(vs, VHD_SDI_SP_INTERFACE, iface);
|
||||||
|
fprintf(stderr, "[board] port %u explicitly set SDI Interface to %lu\n", ports[pi], iface);
|
||||||
|
}
|
||||||
|
/* Pin to tightly-packed 8-bit UYVY. Relying on SDK default caused
|
||||||
|
* the board to deliver frames whose size != width*height*2,
|
||||||
|
* producing rolled/sheared ("bouncing and bending") video. */
|
||||||
|
VHD_SetStreamProperty(vs, VHD_CORE_SP_BUFFER_PACKING, VHD_BUFPACK_VIDEO_YUV422_8);
|
||||||
|
p->video_stream = vs;
|
||||||
|
|
||||||
|
if (VHD_StartStream(vs) != VHDERR_NOERROR) {
|
||||||
|
fprintf(stderr, "{\"error\":\"VHD_StartStream video failed port %u\"}\n", ports[pi]);
|
||||||
|
VHD_CloseStreamHandle(vs);
|
||||||
|
p->video_stream = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit format JSON to stderr (one line per port on signal lock).
|
||||||
|
* Includes slot_id so node-agent / capture-manager can identify
|
||||||
|
* the framecache slot for this port. */
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"port\":%u,\"width\":%d,\"height\":%d,"
|
||||||
|
"\"fps_num\":%d,\"fps_den\":%d,"
|
||||||
|
"\"interlaced\":%s,"
|
||||||
|
"\"pix_fmt\":\"uyvy422\","
|
||||||
|
"\"audio_channels\":2,\"audio_rate\":48000,"
|
||||||
|
"\"device\":%u,"
|
||||||
|
"\"slot_id\":\"%s\"}\n",
|
||||||
|
ports[pi],
|
||||||
|
p->vi.width, p->vi.height,
|
||||||
|
p->vi.fps_num, p->vi.fps_den,
|
||||||
|
p->vi.interlaced ? "true" : "false",
|
||||||
|
device_id,
|
||||||
|
p->slot_id);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
/* Launch audio thread (blocks until reader connects to audio FIFO). */
|
||||||
|
pthread_create(&p->audio_tid, NULL, audio_thread, p);
|
||||||
|
|
||||||
|
/* Launch video thread (blocks until reader connects to video FIFO). */
|
||||||
|
pthread_create(&p->video_tid, NULL, video_thread, p);
|
||||||
|
|
||||||
|
active_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_count == 0) {
|
||||||
|
fprintf(stderr, "{\"error\":\"no ports successfully started\"}\n");
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Wait for all threads to finish ─────────────────────────────── */
|
||||||
|
for (int i = 0; i < active_count; i++) {
|
||||||
|
if (ps[i].video_tid) pthread_join(ps[i].video_tid, NULL);
|
||||||
|
if (ps[i].audio_tid) pthread_join(ps[i].audio_tid, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cleanup ─────────────────────────────────────────────────────── */
|
||||||
|
for (int i = 0; i < active_count; i++) {
|
||||||
|
if (ps[i].video_stream) {
|
||||||
|
VHD_StopStream(ps[i].video_stream);
|
||||||
|
VHD_CloseStreamHandle(ps[i].video_stream);
|
||||||
|
}
|
||||||
|
#ifndef LEGACY_FIFO
|
||||||
|
if (ps[i].fc_writer) {
|
||||||
|
fc_writer_close(ps[i].fc_writer);
|
||||||
|
ps[i].fc_writer = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
VHD_CloseBoardHandle(board);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1692
services/capture/sdk/DeckLinkAPI.h
Normal file
1692
services/capture/sdk/DeckLinkAPI.h
Normal file
File diff suppressed because it is too large
Load diff
338
services/capture/sdk/DeckLinkAPIConfiguration.h
Normal file
338
services/capture/sdk/DeckLinkAPIConfiguration.h
Normal file
|
|
@ -0,0 +1,338 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
** this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
** execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
** Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
** do so, all subject to the following:
|
||||||
|
**
|
||||||
|
** The copyright notices in the Software and this entire statement, including
|
||||||
|
** the above license grant, this restriction and the following disclaimer,
|
||||||
|
** must be included in all copies of the Software, in whole or in part, and
|
||||||
|
** all derivative works of the Software, unless such copies or derivative
|
||||||
|
** works are solely in the form of machine-executable object code generated by
|
||||||
|
** a source language processor.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -- AUTOMATICALLY GENERATED - DO NOT EDIT ---
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BMD_CONST
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BMD_CONST __declspec(selectany) static const
|
||||||
|
#else
|
||||||
|
#define BMD_CONST static const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration = /* 5A68FFD4-1C12-4EDE-A6D2-45451D385FC1 */ { 0x5A,0x68,0xFF,0xD4,0x1C,0x12,0x4E,0xDE,0xA6,0xD2,0x45,0x45,0x1D,0x38,0x5F,0xC1 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration = /* 138050E5-C60A-4552-BF3F-0F358049327E */ { 0x13,0x80,0x50,0xE5,0xC6,0x0A,0x45,0x52,0xBF,0x3F,0x0F,0x35,0x80,0x49,0x32,0x7E };
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID;
|
||||||
|
enum _BMDDeckLinkConfigurationID {
|
||||||
|
|
||||||
|
/* Serial port Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigSwapSerialRxTx = /* 'ssrt' */ 0x73737274,
|
||||||
|
|
||||||
|
/* Video Input/Output Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigHDMI3DPackingFormat = /* '3dpf' */ 0x33647066,
|
||||||
|
bmdDeckLinkConfigBypass = /* 'byps' */ 0x62797073,
|
||||||
|
bmdDeckLinkConfigClockTimingAdjustment = /* 'ctad' */ 0x63746164,
|
||||||
|
bmdDeckLinkConfigAudioMeterType = /* 'aumt' */ 0x61756D74,
|
||||||
|
|
||||||
|
/* Audio Input/Output Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigAnalogAudioConsumerLevels = /* 'aacl' */ 0x6161636C,
|
||||||
|
bmdDeckLinkConfigSwapHDMICh3AndCh4OnInput = /* 'hi34' */ 0x68693334,
|
||||||
|
bmdDeckLinkConfigSwapHDMICh3AndCh4OnOutput = /* 'ho34' */ 0x686F3334,
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputChannelsMutedByHeadphone = /* 'amhp' */ 0x616D6870,
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputChannelsMutedBySpeaker = /* 'amsp' */ 0x616D7370,
|
||||||
|
|
||||||
|
/* Video Output Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigFieldFlickerRemoval = /* 'fdfr' */ 0x66646672,
|
||||||
|
bmdDeckLinkConfigHD1080p24ToHD1080i5994Conversion = /* 'to59' */ 0x746F3539,
|
||||||
|
bmdDeckLinkConfig444SDIVideoOutput = /* '444o' */ 0x3434346F,
|
||||||
|
bmdDeckLinkConfigBlackVideoOutputDuringCapture = /* 'bvoc' */ 0x62766F63,
|
||||||
|
bmdDeckLinkConfigLowLatencyVideoOutput = /* 'llvo' */ 0x6C6C766F,
|
||||||
|
bmdDeckLinkConfigDownConversionOnAllAnalogOutput = /* 'caao' */ 0x6361616F,
|
||||||
|
bmdDeckLinkConfigSMPTELevelAOutput = /* 'smta' */ 0x736D7461,
|
||||||
|
bmdDeckLinkConfigRec2020Output = /* 'rec2' */ 0x72656332, // Ensure output is Rec.2020 colorspace
|
||||||
|
bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit = /* 'SDQS' */ 0x53445153,
|
||||||
|
bmdDeckLinkConfigOutput1080pAsPsF = /* 'pfpr' */ 0x70667072,
|
||||||
|
bmdDeckLinkConfigOutputValidateEDIDForDolbyVision = /* 'pred' */ 0x70726564,
|
||||||
|
bmdDeckLinkConfigExtendedDesktop = /* 'exdt' */ 0x65786474,
|
||||||
|
bmdDeckLinkConfigEthernetVideoOutputIP10 = /* 'IP10' */ 0x49503130,
|
||||||
|
|
||||||
|
/* Video Output Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigVideoOutputConnection = /* 'vocn' */ 0x766F636E,
|
||||||
|
bmdDeckLinkConfigVideoOutputConversionMode = /* 'vocm' */ 0x766F636D,
|
||||||
|
bmdDeckLinkConfigVideoOutputConversionColorspaceDestination = /* 'vccd' */ 0x76636364, // Parameter is of type BMDColorspace
|
||||||
|
bmdDeckLinkConfigVideoOutputConversionColorspaceSource = /* 'vccs' */ 0x76636373, // Parameter is of type BMDColorspace
|
||||||
|
bmdDeckLinkConfigAnalogVideoOutputFlags = /* 'avof' */ 0x61766F66,
|
||||||
|
bmdDeckLinkConfigReferenceInputTimingOffset = /* 'glot' */ 0x676C6F74,
|
||||||
|
bmdDeckLinkConfigReferenceOutputMode = /* 'glOm' */ 0x676C4F6D,
|
||||||
|
bmdDeckLinkConfigVideoOutputIdleOperation = /* 'voio' */ 0x766F696F,
|
||||||
|
bmdDeckLinkConfigDefaultVideoOutputMode = /* 'dvom' */ 0x64766F6D,
|
||||||
|
bmdDeckLinkConfigDefaultVideoOutputModeFlags = /* 'dvof' */ 0x64766F66,
|
||||||
|
bmdDeckLinkConfigSDIOutputLinkConfiguration = /* 'solc' */ 0x736F6C63,
|
||||||
|
bmdDeckLinkConfigHDMITimecodePacking = /* 'htpk' */ 0x6874706B,
|
||||||
|
bmdDeckLinkConfigPlaybackGroup = /* 'plgr' */ 0x706C6772,
|
||||||
|
|
||||||
|
/* Video Output Floats */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigVideoOutputComponentLumaGain = /* 'oclg' */ 0x6F636C67,
|
||||||
|
bmdDeckLinkConfigVideoOutputComponentChromaBlueGain = /* 'occb' */ 0x6F636362,
|
||||||
|
bmdDeckLinkConfigVideoOutputComponentChromaRedGain = /* 'occr' */ 0x6F636372,
|
||||||
|
bmdDeckLinkConfigVideoOutputCompositeLumaGain = /* 'oilg' */ 0x6F696C67,
|
||||||
|
bmdDeckLinkConfigVideoOutputCompositeChromaGain = /* 'oicg' */ 0x6F696367,
|
||||||
|
bmdDeckLinkConfigVideoOutputSVideoLumaGain = /* 'oslg' */ 0x6F736C67,
|
||||||
|
bmdDeckLinkConfigVideoOutputSVideoChromaGain = /* 'oscg' */ 0x6F736367,
|
||||||
|
bmdDeckLinkConfigDolbyVisionCMVersion = /* 'dvvr' */ 0x64767672,
|
||||||
|
bmdDeckLinkConfigDolbyVisionMasterMinimumNits = /* 'mnnt' */ 0x6D6E6E74,
|
||||||
|
bmdDeckLinkConfigDolbyVisionMasterMaximumNits = /* 'mxnt' */ 0x6D786E74,
|
||||||
|
|
||||||
|
/* Video Input Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigVideoInputScanning = /* 'visc' */ 0x76697363, // Applicable to H264 Pro Recorder only
|
||||||
|
bmdDeckLinkConfigUseDedicatedLTCInput = /* 'dltc' */ 0x646C7463, // Use timecode from LTC input instead of SDI stream
|
||||||
|
bmdDeckLinkConfigSDIInput3DPayloadOverride = /* '3dds' */ 0x33646473,
|
||||||
|
bmdDeckLinkConfigCapture1080pAsPsF = /* 'cfpr' */ 0x63667072,
|
||||||
|
|
||||||
|
/* Video Input Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigVideoInputConnection = /* 'vicn' */ 0x7669636E,
|
||||||
|
bmdDeckLinkConfigAnalogVideoInputFlags = /* 'avif' */ 0x61766966,
|
||||||
|
bmdDeckLinkConfigVideoInputConversionMode = /* 'vicm' */ 0x7669636D,
|
||||||
|
bmdDeckLinkConfig32PulldownSequenceInitialTimecodeFrame = /* 'pdif' */ 0x70646966,
|
||||||
|
bmdDeckLinkConfigVANCSourceLine1Mapping = /* 'vsl1' */ 0x76736C31,
|
||||||
|
bmdDeckLinkConfigVANCSourceLine2Mapping = /* 'vsl2' */ 0x76736C32,
|
||||||
|
bmdDeckLinkConfigVANCSourceLine3Mapping = /* 'vsl3' */ 0x76736C33,
|
||||||
|
bmdDeckLinkConfigCapturePassThroughMode = /* 'cptm' */ 0x6370746D,
|
||||||
|
bmdDeckLinkConfigCaptureGroup = /* 'cpgr' */ 0x63706772,
|
||||||
|
bmdDeckLinkConfigHANCInputFilter1 = /* 'hif1' */ 0x68696631,
|
||||||
|
bmdDeckLinkConfigHANCInputFilter2 = /* 'hif2' */ 0x68696632,
|
||||||
|
bmdDeckLinkConfigHANCInputFilter3 = /* 'hif3' */ 0x68696633,
|
||||||
|
bmdDeckLinkConfigHANCInputFilter4 = /* 'hif4' */ 0x68696634,
|
||||||
|
|
||||||
|
/* Video Input Floats */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigVideoInputComponentLumaGain = /* 'iclg' */ 0x69636C67,
|
||||||
|
bmdDeckLinkConfigVideoInputComponentChromaBlueGain = /* 'iccb' */ 0x69636362,
|
||||||
|
bmdDeckLinkConfigVideoInputComponentChromaRedGain = /* 'iccr' */ 0x69636372,
|
||||||
|
bmdDeckLinkConfigVideoInputCompositeLumaGain = /* 'iilg' */ 0x69696C67,
|
||||||
|
bmdDeckLinkConfigVideoInputCompositeChromaGain = /* 'iicg' */ 0x69696367,
|
||||||
|
bmdDeckLinkConfigVideoInputSVideoLumaGain = /* 'islg' */ 0x69736C67,
|
||||||
|
bmdDeckLinkConfigVideoInputSVideoChromaGain = /* 'iscg' */ 0x69736367,
|
||||||
|
|
||||||
|
/* Keying Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigInternalKeyingAncillaryDataSource = /* 'ikas' */ 0x696B6173,
|
||||||
|
|
||||||
|
/* Audio Input Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigMicrophonePhantomPower = /* 'mphp' */ 0x6D706870,
|
||||||
|
|
||||||
|
/* Audio Input Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigAudioInputConnection = /* 'aicn' */ 0x6169636E,
|
||||||
|
|
||||||
|
/* Audio Input Floats */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigAnalogAudioInputScaleChannel1 = /* 'ais1' */ 0x61697331,
|
||||||
|
bmdDeckLinkConfigAnalogAudioInputScaleChannel2 = /* 'ais2' */ 0x61697332,
|
||||||
|
bmdDeckLinkConfigAnalogAudioInputScaleChannel3 = /* 'ais3' */ 0x61697333,
|
||||||
|
bmdDeckLinkConfigAnalogAudioInputScaleChannel4 = /* 'ais4' */ 0x61697334,
|
||||||
|
bmdDeckLinkConfigDigitalAudioInputScale = /* 'dais' */ 0x64616973,
|
||||||
|
bmdDeckLinkConfigMicrophoneInputGain = /* 'micg' */ 0x6D696367,
|
||||||
|
bmdDeckLinkConfigAudioOutputXLRDelayFrames = /* 'xdfr' */ 0x78646672,
|
||||||
|
|
||||||
|
/* Audio Output Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigAudioOutputAESAnalogSwitch = /* 'aoaa' */ 0x616F6161,
|
||||||
|
bmdDeckLinkConfigAudioOutputXLRDelayTime = /* 'xdms' */ 0x78646D73,
|
||||||
|
bmdDeckLinkConfigAudioOutputXLRDelayType = /* 'xdty' */ 0x78647479,
|
||||||
|
|
||||||
|
/* Audio Output Floats */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputScaleChannel1 = /* 'aos1' */ 0x616F7331,
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputScaleChannel2 = /* 'aos2' */ 0x616F7332,
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputScaleChannel3 = /* 'aos3' */ 0x616F7333,
|
||||||
|
bmdDeckLinkConfigAnalogAudioOutputScaleChannel4 = /* 'aos4' */ 0x616F7334,
|
||||||
|
bmdDeckLinkConfigDigitalAudioOutputScale = /* 'daos' */ 0x64616F73,
|
||||||
|
bmdDeckLinkConfigHeadphoneVolume = /* 'hvol' */ 0x68766F6C,
|
||||||
|
bmdDeckLinkConfigSpeakerVolume = /* 'svol' */ 0x73766F6C,
|
||||||
|
|
||||||
|
/* Ethernet Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigEthernetPTPFollowerOnly = /* 'PTPf' */ 0x50545066,
|
||||||
|
bmdDeckLinkConfigEthernetPTPUseUDPEncapsulation = /* 'PTPU' */ 0x50545055,
|
||||||
|
bmdDeckLinkConfigEthernetUseManualNMOSRegistry = /* 'nmrp' */ 0x6E6D7270,
|
||||||
|
|
||||||
|
/* Ethernet Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigEthernetPTPPriority1 = /* 'PTP1' */ 0x50545031,
|
||||||
|
bmdDeckLinkConfigEthernetPTPPriority2 = /* 'PTP2' */ 0x50545032,
|
||||||
|
bmdDeckLinkConfigEthernetPTPDomain = /* 'PTPD' */ 0x50545044,
|
||||||
|
bmdDeckLinkConfigEthernetPTPLogAnnounceInterval = /* 'PTPA' */ 0x50545041,
|
||||||
|
|
||||||
|
/* Ethernet Strings */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigEthernetAudioOutputChannelOrder = /* 'caco' */ 0x6361636F,
|
||||||
|
bmdDeckLinkConfigEthernetNMOSRegistryAddress = /* 'nmre' */ 0x6E6D7265,
|
||||||
|
|
||||||
|
/* Parameterized Ethernet Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigParamEthernetUseDHCP = /* 'DHCP' */ 0x44484350,
|
||||||
|
|
||||||
|
/* Parameterized Ethernet Strings */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigParamEthernetStaticLocalIPAddress = /* 'nsip' */ 0x6E736970,
|
||||||
|
bmdDeckLinkConfigParamEthernetStaticSubnetMask = /* 'nssm' */ 0x6E73736D,
|
||||||
|
bmdDeckLinkConfigParamEthernetStaticGatewayIPAddress = /* 'nsgw' */ 0x6E736777,
|
||||||
|
bmdDeckLinkConfigParamEthernetStaticPrimaryDNS = /* 'nspd' */ 0x6E737064,
|
||||||
|
bmdDeckLinkConfigParamEthernetStaticSecondaryDNS = /* 'nssd' */ 0x6E737364,
|
||||||
|
bmdDeckLinkConfigParamEthernetVideoOutputAddress = /* 'noav' */ 0x6E6F6176,
|
||||||
|
bmdDeckLinkConfigParamEthernetAudioOutputAddress = /* 'noaa' */ 0x6E6F6161,
|
||||||
|
bmdDeckLinkConfigParamEthernetAncillaryOutputAddress = /* 'noaA' */ 0x6E6F6141,
|
||||||
|
|
||||||
|
/* Device Information Strings */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigDeviceInformationLabel = /* 'dila' */ 0x64696C61,
|
||||||
|
bmdDeckLinkConfigDeviceInformationSerialNumber = /* 'disn' */ 0x6469736E,
|
||||||
|
bmdDeckLinkConfigDeviceInformationCompany = /* 'dico' */ 0x6469636F,
|
||||||
|
bmdDeckLinkConfigDeviceInformationPhone = /* 'diph' */ 0x64697068,
|
||||||
|
bmdDeckLinkConfigDeviceInformationEmail = /* 'diem' */ 0x6469656D,
|
||||||
|
bmdDeckLinkConfigDeviceInformationDate = /* 'dida' */ 0x64696461,
|
||||||
|
|
||||||
|
/* Deck Control Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigDeckControlConnection = /* 'dcco' */ 0x6463636F,
|
||||||
|
|
||||||
|
/* UI/UX Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigDisplayLanguage = /* 'lang' */ 0x6C616E67
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkEncoderConfigurationID - DeckLink Encoder Configuration ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkEncoderConfigurationID;
|
||||||
|
enum _BMDDeckLinkEncoderConfigurationID {
|
||||||
|
|
||||||
|
/* Video Encoder Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkEncoderConfigPreferredBitDepth = /* 'epbr' */ 0x65706272,
|
||||||
|
bmdDeckLinkEncoderConfigFrameCodingMode = /* 'efcm' */ 0x6566636D,
|
||||||
|
|
||||||
|
/* HEVC/H.265 Encoder Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkEncoderConfigH265TargetBitrate = /* 'htbr' */ 0x68746272,
|
||||||
|
|
||||||
|
/* DNxHR/DNxHD Compression ID */
|
||||||
|
|
||||||
|
bmdDeckLinkEncoderConfigDNxHRCompressionID = /* 'dcid' */ 0x64636964,
|
||||||
|
|
||||||
|
/* DNxHR/DNxHD Level */
|
||||||
|
|
||||||
|
bmdDeckLinkEncoderConfigDNxHRLevel = /* 'dlev' */ 0x646C6576,
|
||||||
|
|
||||||
|
/* Encoded Sample Decriptions */
|
||||||
|
|
||||||
|
bmdDeckLinkEncoderConfigMPEG4SampleDescription = /* 'stsE' */ 0x73747345, // Full MPEG4 sample description (aka SampleEntry of an 'stsd' atom-box). Useful for MediaFoundation, QuickTime, MKV and more
|
||||||
|
bmdDeckLinkEncoderConfigMPEG4CodecSpecificDesc = /* 'esds' */ 0x65736473 // Sample description extensions only (atom stream, each with size and fourCC header). Useful for AVFoundation, VideoToolbox, MKV and more
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration;
|
||||||
|
class IDeckLinkEncoderConfiguration;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkConfiguration : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t* value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double* value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char** value) = 0;
|
||||||
|
virtual HRESULT SetFlagWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlagWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT SetIntWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetIntWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* out */ int64_t* value) = 0;
|
||||||
|
virtual HRESULT SetFloatWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloatWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* out */ double* value) = 0;
|
||||||
|
virtual HRESULT SetStringWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* in */ const char* value) = 0;
|
||||||
|
virtual HRESULT GetStringWithParam (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ uint64_t param, /* out */ const char** value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkEncoderConfiguration - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkEncoderConfiguration : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ int64_t* value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ double* value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ const char* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ const char** value) = 0;
|
||||||
|
virtual HRESULT GetBytes (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ void* buffer /* optional */, /* in, out */ uint32_t* bufferSize) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkEncoderConfiguration () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_H) */
|
||||||
84
services/capture/sdk/DeckLinkAPIConfiguration_v10_11.h
Normal file
84
services/capture/sdk/DeckLinkAPIConfiguration_v10_11.h
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_v10_11_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_v10_11_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_11 = /* EF90380B-4AE5-4346-9077-E288E149F129 */ {0xEF,0x90,0x38,0x0B,0x4A,0xE5,0x43,0x46,0x90,0x77,0xE2,0x88,0xE1,0x49,0xF1,0x29};
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkConfigurationID_v10_11 - DeckLink Configuration ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID_v10_11;
|
||||||
|
enum _BMDDeckLinkConfigurationID_v10_11 {
|
||||||
|
|
||||||
|
/* Video Input/Output Integers */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigDuplexMode_v10_11 = /* 'dupx' */ 0x64757078,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration_v10_11;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration_v10_11 - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkConfiguration_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration_v10_11 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_11_H) */
|
||||||
73
services/capture/sdk/DeckLinkAPIConfiguration_v10_2.h
Normal file
73
services/capture/sdk/DeckLinkAPIConfiguration_v10_2.h
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2014 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_v10_2_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_v10_2_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_2 = /* C679A35B-610C-4D09-B748-1D0478100FC0 */ {0xC6,0x79,0xA3,0x5B,0x61,0x0C,0x4D,0x09,0xB7,0x48,0x1D,0x04,0x78,0x10,0x0F,0xC0};
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration_v10_2;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration_v10_2 - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkConfiguration_v10_2 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration_v10_2 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_2_H) */
|
||||||
76
services/capture/sdk/DeckLinkAPIConfiguration_v10_4.h
Normal file
76
services/capture/sdk/DeckLinkAPIConfiguration_v10_4.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2015 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_v10_4_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_v10_4_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_4 = /* 1E69FCF6-4203-4936-8076-2A9F4CFD50CB */ {0x1E,0x69,0xFC,0xF6,0x42,0x03,0x49,0x36,0x80,0x76,0x2A,0x9F,0x4C,0xFD,0x50,0xCB};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration_v10_4;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration_v10_4 - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkConfiguration_v10_4 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration_v10_4 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_4_H) */
|
||||||
73
services/capture/sdk/DeckLinkAPIConfiguration_v10_5.h
Normal file
73
services/capture/sdk/DeckLinkAPIConfiguration_v10_5.h
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2015 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_v10_5_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_v10_5_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkEncoderConfiguration_v10_5 = /* 67455668-0848-45DF-8D8E-350A77C9A028 */ {0x67,0x45,0x56,0x68,0x08,0x48,0x45,0xDF,0x8D,0x8E,0x35,0x0A,0x77,0xC9,0xA0,0x28};
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkEncoderConfiguration_v10_5;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkEncoderConfiguration_v10_5 - DeckLink Encoder Configuration interface. Obtained from IDeckLinkEncoderInput */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkEncoderConfiguration_v10_5 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* in */ const char *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkEncoderConfigurationID cfgID, /* out */ const char **value) = 0;
|
||||||
|
virtual HRESULT GetDecoderConfigurationInfo (/* out */ void *buffer, /* in */ long bufferSize, /* out */ long *returnedSize) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkEncoderConfiguration_v10_5 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_5_H) */
|
||||||
75
services/capture/sdk/DeckLinkAPIConfiguration_v10_9.h
Normal file
75
services/capture/sdk/DeckLinkAPIConfiguration_v10_9.h
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPICONFIGURATION_v10_9_H
|
||||||
|
#define BMD_DECKLINKAPICONFIGURATION_v10_9_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration_v10_9 = /* CB71734A-FE37-4E8D-8E13-802133A1C3F2 */ {0xCB,0x71,0x73,0x4A,0xFE,0x37,0x4E,0x8D,0x8E,0x13,0x80,0x21,0x33,0xA1,0xC3,0xF2};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration_v10_9;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration_v10_9 - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkConfiguration_v10_9 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char **value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration_v10_9 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPICONFIGURATION_v10_9_H) */
|
||||||
84
services/capture/sdk/DeckLinkAPIConfiguration_v15_3_1.h
Normal file
84
services/capture/sdk/DeckLinkAPIConfiguration_v15_3_1.h
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPIConfiguration.h"
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkConfigurationID_v15_3_1 - DeckLink Configuration ID */
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID_v15_3_1;
|
||||||
|
enum _BMDDeckLinkConfigurationID_v15_3_1
|
||||||
|
{
|
||||||
|
/* Network Flags */
|
||||||
|
bmdDeckLinkConfigEthernetUseDHCP_v15_3_1 = /* 'DHCP' */ 0x44484350,
|
||||||
|
|
||||||
|
/* Network Strings */
|
||||||
|
bmdDeckLinkConfigEthernetStaticLocalIPAddress_v15_3_1 = /* 'nsip' */ 0x6E736970,
|
||||||
|
bmdDeckLinkConfigEthernetStaticSubnetMask_v15_3_1 = /* 'nssm' */ 0x6E73736D,
|
||||||
|
bmdDeckLinkConfigEthernetStaticGatewayIPAddress_v15_3_1 = /* 'nsgw' */ 0x6E736777,
|
||||||
|
bmdDeckLinkConfigEthernetStaticPrimaryDNS_v15_3_1 = /* 'nspd' */ 0x6E737064,
|
||||||
|
bmdDeckLinkConfigEthernetStaticSecondaryDNS_v15_3_1 = /* 'nssd' */ 0x6E737364,
|
||||||
|
bmdDeckLinkConfigEthernetVideoOutputAddress_v15_3_1 = /* 'noav' */ 0x6E6F6176,
|
||||||
|
bmdDeckLinkConfigEthernetAudioOutputAddress_v15_3_1 = /* 'noaa' */ 0x6E6F6161,
|
||||||
|
bmdDeckLinkConfigEthernetAncillaryOutputAddress_v15_3_1 = /* 'noaA' */ 0x6E6F6141,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkConfiguration_v15_3_1 = /* 912F634B-2D4E-40A4-8AAB-8D80B73F1289 */ {0x91,0x2F,0x63,0x4B,0x2D,0x4E,0x40,0xA4,0x8A,0xAB,0x8D,0x80,0xB7,0x3F,0x12,0x89};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkConfiguration_v15_3_1 - DeckLink Configuration interface */
|
||||||
|
|
||||||
|
class IDeckLinkConfiguration_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ bool value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT SetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ int64_t value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT SetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ double value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT SetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* in */ const char* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkConfigurationID cfgID, /* out */ const char* *value) = 0;
|
||||||
|
virtual HRESULT WriteConfigurationToPreferences (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkConfiguration_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
227
services/capture/sdk/DeckLinkAPIDeckControl.h
Normal file
227
services/capture/sdk/DeckLinkAPIDeckControl.h
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
** this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
** execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
** Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
** do so, all subject to the following:
|
||||||
|
**
|
||||||
|
** The copyright notices in the Software and this entire statement, including
|
||||||
|
** the above license grant, this restriction and the following disclaimer,
|
||||||
|
** must be included in all copies of the Software, in whole or in part, and
|
||||||
|
** all derivative works of the Software, unless such copies or derivative
|
||||||
|
** works are solely in the form of machine-executable object code generated by
|
||||||
|
** a source language processor.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -- AUTOMATICALLY GENERATED - DO NOT EDIT ---
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIDECKCONTROL_H
|
||||||
|
#define BMD_DECKLINKAPIDECKCONTROL_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BMD_CONST
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BMD_CONST __declspec(selectany) static const
|
||||||
|
#else
|
||||||
|
#define BMD_CONST static const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkDeckControlStatusCallback = /* 53436FFB-B434-4906-BADC-AE3060FFE8EF */ { 0x53,0x43,0x6F,0xFB,0xB4,0x34,0x49,0x06,0xBA,0xDC,0xAE,0x30,0x60,0xFF,0xE8,0xEF };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkDeckControl = /* 8E1C3ACE-19C7-4E00-8B92-D80431D958BE */ { 0x8E,0x1C,0x3A,0xCE,0x19,0xC7,0x4E,0x00,0x8B,0x92,0xD8,0x04,0x31,0xD9,0x58,0xBE };
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlMode - DeckControl mode */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlMode;
|
||||||
|
enum _BMDDeckControlMode {
|
||||||
|
bmdDeckControlNotOpened = /* 'ntop' */ 0x6E746F70,
|
||||||
|
bmdDeckControlVTRControlMode = /* 'vtrc' */ 0x76747263,
|
||||||
|
bmdDeckControlExportMode = /* 'expm' */ 0x6578706D,
|
||||||
|
bmdDeckControlCaptureMode = /* 'capm' */ 0x6361706D
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlEvent - DeckControl event */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlEvent;
|
||||||
|
enum _BMDDeckControlEvent {
|
||||||
|
bmdDeckControlAbortedEvent = /* 'abte' */ 0x61627465, // This event is triggered when a capture or edit-to-tape operation is aborted.
|
||||||
|
|
||||||
|
/* Export-To-Tape events */
|
||||||
|
|
||||||
|
bmdDeckControlPrepareForExportEvent = /* 'pfee' */ 0x70666565, // This event is triggered a few frames before reaching the in-point. IDeckLinkInput::StartScheduledPlayback should be called at this point.
|
||||||
|
bmdDeckControlExportCompleteEvent = /* 'exce' */ 0x65786365, // This event is triggered a few frames after reaching the out-point. At this point, it is safe to stop playback. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode.
|
||||||
|
|
||||||
|
/* Capture events */
|
||||||
|
|
||||||
|
bmdDeckControlPrepareForCaptureEvent = /* 'pfce' */ 0x70666365, // This event is triggered a few frames before reaching the in-point. The serial timecode attached to IDeckLinkVideoInputFrames is now valid.
|
||||||
|
bmdDeckControlCaptureCompleteEvent = /* 'ccev' */ 0x63636576 // This event is triggered a few frames after reaching the out-point. Upon reception of this event the deck's control mode is set back to bmdDeckControlVTRControlMode.
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlVTRControlState - VTR Control state */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlVTRControlState;
|
||||||
|
enum _BMDDeckControlVTRControlState {
|
||||||
|
bmdDeckControlNotInVTRControlMode = /* 'nvcm' */ 0x6E76636D,
|
||||||
|
bmdDeckControlVTRControlPlaying = /* 'vtrp' */ 0x76747270,
|
||||||
|
bmdDeckControlVTRControlRecording = /* 'vtrr' */ 0x76747272,
|
||||||
|
bmdDeckControlVTRControlStill = /* 'vtra' */ 0x76747261,
|
||||||
|
bmdDeckControlVTRControlShuttleForward = /* 'vtsf' */ 0x76747366,
|
||||||
|
bmdDeckControlVTRControlShuttleReverse = /* 'vtsr' */ 0x76747372,
|
||||||
|
bmdDeckControlVTRControlJogForward = /* 'vtjf' */ 0x76746A66,
|
||||||
|
bmdDeckControlVTRControlJogReverse = /* 'vtjr' */ 0x76746A72,
|
||||||
|
bmdDeckControlVTRControlStopped = /* 'vtro' */ 0x7674726F
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlStatusFlags - Deck Control status flags */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlStatusFlags;
|
||||||
|
enum _BMDDeckControlStatusFlags {
|
||||||
|
bmdDeckControlStatusDeckConnected = 1 << 0,
|
||||||
|
bmdDeckControlStatusRemoteMode = 1 << 1,
|
||||||
|
bmdDeckControlStatusRecordInhibited = 1 << 2,
|
||||||
|
bmdDeckControlStatusCassetteOut = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlExportModeOpsFlags - Export mode flags */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlExportModeOpsFlags;
|
||||||
|
enum _BMDDeckControlExportModeOpsFlags {
|
||||||
|
bmdDeckControlExportModeInsertVideo = 1 << 0,
|
||||||
|
bmdDeckControlExportModeInsertAudio1 = 1 << 1,
|
||||||
|
bmdDeckControlExportModeInsertAudio2 = 1 << 2,
|
||||||
|
bmdDeckControlExportModeInsertAudio3 = 1 << 3,
|
||||||
|
bmdDeckControlExportModeInsertAudio4 = 1 << 4,
|
||||||
|
bmdDeckControlExportModeInsertAudio5 = 1 << 5,
|
||||||
|
bmdDeckControlExportModeInsertAudio6 = 1 << 6,
|
||||||
|
bmdDeckControlExportModeInsertAudio7 = 1 << 7,
|
||||||
|
bmdDeckControlExportModeInsertAudio8 = 1 << 8,
|
||||||
|
bmdDeckControlExportModeInsertAudio9 = 1 << 9,
|
||||||
|
bmdDeckControlExportModeInsertAudio10 = 1 << 10,
|
||||||
|
bmdDeckControlExportModeInsertAudio11 = 1 << 11,
|
||||||
|
bmdDeckControlExportModeInsertAudio12 = 1 << 12,
|
||||||
|
bmdDeckControlExportModeInsertTimeCode = 1 << 13,
|
||||||
|
bmdDeckControlExportModeInsertAssemble = 1 << 14,
|
||||||
|
bmdDeckControlExportModeInsertPreview = 1 << 15,
|
||||||
|
bmdDeckControlUseManualExport = 1 << 16
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlError - Deck Control error */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlError;
|
||||||
|
enum _BMDDeckControlError {
|
||||||
|
bmdDeckControlNoError = /* 'noer' */ 0x6E6F6572,
|
||||||
|
bmdDeckControlModeError = /* 'moer' */ 0x6D6F6572,
|
||||||
|
bmdDeckControlMissedInPointError = /* 'mier' */ 0x6D696572,
|
||||||
|
bmdDeckControlDeckTimeoutError = /* 'dter' */ 0x64746572,
|
||||||
|
bmdDeckControlCommandFailedError = /* 'cfer' */ 0x63666572,
|
||||||
|
bmdDeckControlDeviceAlreadyOpenedError = /* 'dalo' */ 0x64616C6F,
|
||||||
|
bmdDeckControlFailedToOpenDeviceError = /* 'fder' */ 0x66646572,
|
||||||
|
bmdDeckControlInLocalModeError = /* 'lmer' */ 0x6C6D6572,
|
||||||
|
bmdDeckControlEndOfTapeError = /* 'eter' */ 0x65746572,
|
||||||
|
bmdDeckControlUserAbortError = /* 'uaer' */ 0x75616572,
|
||||||
|
bmdDeckControlNoTapeInDeckError = /* 'nter' */ 0x6E746572,
|
||||||
|
bmdDeckControlNoVideoFromCardError = /* 'nvfc' */ 0x6E766663,
|
||||||
|
bmdDeckControlNoCommunicationError = /* 'ncom' */ 0x6E636F6D,
|
||||||
|
bmdDeckControlBufferTooSmallError = /* 'btsm' */ 0x6274736D,
|
||||||
|
bmdDeckControlBadChecksumError = /* 'chks' */ 0x63686B73,
|
||||||
|
bmdDeckControlUnknownError = /* 'uner' */ 0x756E6572
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkDeckControlStatusCallback;
|
||||||
|
class IDeckLinkDeckControl;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkDeckControlStatusCallback - Deck control state change callback. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkDeckControlStatusCallback : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT TimecodeUpdate (/* in */ BMDTimecodeBCD currentTimecode) = 0;
|
||||||
|
virtual HRESULT VTRControlStateChanged (/* in */ BMDDeckControlVTRControlState newState, /* in */ BMDDeckControlError error) = 0;
|
||||||
|
virtual HRESULT DeckControlEventReceived (/* in */ BMDDeckControlEvent event, /* in */ BMDDeckControlError error) = 0;
|
||||||
|
virtual HRESULT DeckControlStatusChanged (/* in */ BMDDeckControlStatusFlags flags, /* in */ uint32_t mask) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkDeckControlStatusCallback () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkDeckControl - Deck Control main interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkDeckControl : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Open (/* in */ BMDTimeScale timeScale, /* in */ BMDTimeValue timeValue, /* in */ bool timecodeIsDropFrame, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Close (/* in */ bool standbyOn) = 0;
|
||||||
|
virtual HRESULT GetCurrentState (/* out */ BMDDeckControlMode* mode, /* out */ BMDDeckControlVTRControlState* vtrControlState, /* out */ BMDDeckControlStatusFlags* flags) = 0;
|
||||||
|
virtual HRESULT SetStandby (/* in */ bool standbyOn) = 0;
|
||||||
|
virtual HRESULT SendCommand (/* in */ uint8_t* inBuffer, /* in */ uint32_t inBufferSize, /* out */ uint8_t* outBuffer, /* out */ uint32_t* outDataSize, /* in */ uint32_t outBufferSize, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Play (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Stop (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT TogglePlayStop (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Eject (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT GoToTimecode (/* in */ BMDTimecodeBCD timecode, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT FastForward (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Rewind (/* in */ bool viewTape, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT StepForward (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT StepBack (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Jog (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Shuttle (/* in */ double rate, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT GetTimecodeString (/* out */ const char** currentTimeCode, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT GetTimecode (/* out */ IDeckLinkTimecode** currentTimecode, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT GetTimecodeBCD (/* out */ BMDTimecodeBCD* currentTimecode, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT SetPreroll (/* in */ uint32_t prerollSeconds) = 0;
|
||||||
|
virtual HRESULT GetPreroll (/* out */ uint32_t* prerollSeconds) = 0;
|
||||||
|
virtual HRESULT SetExportOffset (/* in */ int32_t exportOffsetFields) = 0;
|
||||||
|
virtual HRESULT GetExportOffset (/* out */ int32_t* exportOffsetFields) = 0;
|
||||||
|
virtual HRESULT GetManualExportOffset (/* out */ int32_t* deckManualExportOffsetFields) = 0;
|
||||||
|
virtual HRESULT SetCaptureOffset (/* in */ int32_t captureOffsetFields) = 0;
|
||||||
|
virtual HRESULT GetCaptureOffset (/* out */ int32_t* captureOffsetFields) = 0;
|
||||||
|
virtual HRESULT StartExport (/* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* in */ BMDDeckControlExportModeOpsFlags exportModeOps, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT StartCapture (/* in */ bool useVITC, /* in */ BMDTimecodeBCD inTimecode, /* in */ BMDTimecodeBCD outTimecode, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT GetDeviceID (/* out */ uint16_t* deviceId, /* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT Abort (void) = 0;
|
||||||
|
virtual HRESULT CrashRecordStart (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT CrashRecordStop (/* out */ BMDDeckControlError* error) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkDeckControlStatusCallback* callback) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkDeckControl () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIDECKCONTROL_H) */
|
||||||
83
services/capture/sdk/DeckLinkAPIDiscovery.h
Normal file
83
services/capture/sdk/DeckLinkAPIDiscovery.h
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
** this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
** execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
** Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
** do so, all subject to the following:
|
||||||
|
**
|
||||||
|
** The copyright notices in the Software and this entire statement, including
|
||||||
|
** the above license grant, this restriction and the following disclaimer,
|
||||||
|
** must be included in all copies of the Software, in whole or in part, and
|
||||||
|
** all derivative works of the Software, unless such copies or derivative
|
||||||
|
** works are solely in the form of machine-executable object code generated by
|
||||||
|
** a source language processor.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -- AUTOMATICALLY GENERATED - DO NOT EDIT ---
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIDISCOVERY_H
|
||||||
|
#define BMD_DECKLINKAPIDISCOVERY_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BMD_CONST
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BMD_CONST __declspec(selectany) static const
|
||||||
|
#else
|
||||||
|
#define BMD_CONST static const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLink = /* C418FBDD-0587-48ED-8FE5-640F0A14AF91 */ { 0xC4,0x18,0xFB,0xDD,0x05,0x87,0x48,0xED,0x8F,0xE5,0x64,0x0F,0x0A,0x14,0xAF,0x91 };
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLink;
|
||||||
|
|
||||||
|
/* Interface IDeckLink - Represents a DeckLink device */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLink : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetModelName (/* out */ const char** modelName) = 0;
|
||||||
|
virtual HRESULT GetDisplayName (/* out */ const char** displayName) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLink () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIDISCOVERY_H) */
|
||||||
188
services/capture/sdk/DeckLinkAPIDispatch.cpp
Normal file
188
services/capture/sdk/DeckLinkAPIDispatch.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGL3ScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0004");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0003");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0003");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0002");
|
||||||
|
if (!gCreateVideoFrameAncillaryPacketsFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGL3ScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGL3PreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent (void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGL3PreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGL3PreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion* CreateVideoConversionInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoFrameAncillaryPacketsFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoFrameAncillaryPacketsFunc();
|
||||||
|
}
|
||||||
173
services/capture/sdk/DeckLinkAPIDispatch_v10_11.cpp
Normal file
173
services/capture/sdk/DeckLinkAPIDispatch_v10_11.cpp
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2019 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v10_11.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0003");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0002");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001");
|
||||||
|
if (!gCreateVideoFrameAncillaryPacketsFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent_v10_11 (void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion* CreateVideoConversionInstance_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v10_11 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoFrameAncillaryPacketsFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoFrameAncillaryPacketsFunc();
|
||||||
|
}
|
||||||
159
services/capture/sdk/DeckLinkAPIDispatch_v10_8.cpp
Normal file
159
services/capture/sdk/DeckLinkAPIDispatch_v10_8.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI(void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0002");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0001");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI(void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent(void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance(void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance(void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper(void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion* CreateVideoConversionInstance(void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance(void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
188
services/capture/sdk/DeckLinkAPIDispatch_v14_2_1.cpp
Normal file
188
services/capture/sdk/DeckLinkAPIDispatch_v14_2_1.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v14_2_1.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper_v14_2_1* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper_v14_2_1* (*CreateOpenGL3ScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion_v14_2_1* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0004");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0001");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0003");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001");
|
||||||
|
if (!gCreateVideoFrameAncillaryPacketsFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0001");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGL3ScreenPreviewHelper_0001");
|
||||||
|
if (!gCreateOpenGL3PreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper_v14_2_1* CreateOpenGLScreenPreviewHelper_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper_v14_2_1* CreateOpenGL3ScreenPreviewHelper_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGL3PreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGL3PreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion_v14_2_1* CreateVideoConversionInstance_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v14_2_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoFrameAncillaryPacketsFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoFrameAncillaryPacketsFunc();
|
||||||
|
}
|
||||||
188
services/capture/sdk/DeckLinkAPIDispatch_v15_2.cpp
Normal file
188
services/capture/sdk/DeckLinkAPIDispatch_v15_2.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v15_2.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGL3ScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkVideoFrameAncillaryPackets_v15_2* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0004");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0002");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0003");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001");
|
||||||
|
if (!gCreateVideoFrameAncillaryPacketsFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGL3ScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGL3PreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent_v15_2 (void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGL3PreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGL3PreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion* CreateVideoConversionInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoFrameAncillaryPackets_v15_2* CreateVideoFrameAncillaryPacketsInstance_v15_2 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoFrameAncillaryPacketsFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoFrameAncillaryPacketsFunc();
|
||||||
|
}
|
||||||
188
services/capture/sdk/DeckLinkAPIDispatch_v15_3_1.cpp
Normal file
188
services/capture/sdk/DeckLinkAPIDispatch_v15_3_1.cpp
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v15_3_1.h"
|
||||||
|
|
||||||
|
#define kDeckLinkAPI_Name "libDeckLinkAPI.so"
|
||||||
|
#define KDeckLinkPreviewAPI_Name "libDeckLinkPreviewAPI.so"
|
||||||
|
|
||||||
|
typedef IDeckLinkIterator* (*CreateIteratorFunc)(void);
|
||||||
|
typedef IDeckLinkAPIInformation* (*CreateAPIInformationFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGLScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkGLScreenPreviewHelper* (*CreateOpenGL3ScreenPreviewHelperFunc)(void);
|
||||||
|
typedef IDeckLinkVideoConversion_v15_3_1* (*CreateVideoConversionInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkDiscovery* (*CreateDeckLinkDiscoveryInstanceFunc)(void);
|
||||||
|
typedef IDeckLinkVideoFrameAncillaryPackets* (*CreateVideoFrameAncillaryPacketsInstanceFunc)(void);
|
||||||
|
|
||||||
|
static pthread_once_t gDeckLinkOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
static pthread_once_t gPreviewOnceControl = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
static bool gLoadedDeckLinkAPI = false;
|
||||||
|
|
||||||
|
static CreateIteratorFunc gCreateIteratorFunc = NULL;
|
||||||
|
static CreateAPIInformationFunc gCreateAPIInformationFunc = NULL;
|
||||||
|
static CreateOpenGLScreenPreviewHelperFunc gCreateOpenGLPreviewFunc = NULL;
|
||||||
|
static CreateOpenGL3ScreenPreviewHelperFunc gCreateOpenGL3PreviewFunc = NULL;
|
||||||
|
static CreateVideoConversionInstanceFunc gCreateVideoConversionFunc = NULL;
|
||||||
|
static CreateDeckLinkDiscoveryInstanceFunc gCreateDeckLinkDiscoveryFunc = NULL;
|
||||||
|
static CreateVideoFrameAncillaryPacketsInstanceFunc gCreateVideoFrameAncillaryPacketsFunc = NULL;
|
||||||
|
|
||||||
|
static void InitDeckLinkAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(kDeckLinkAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gLoadedDeckLinkAPI = true;
|
||||||
|
|
||||||
|
gCreateIteratorFunc = (CreateIteratorFunc)dlsym(libraryHandle, "CreateDeckLinkIteratorInstance_0004");
|
||||||
|
if (!gCreateIteratorFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateAPIInformationFunc = (CreateAPIInformationFunc)dlsym(libraryHandle, "CreateDeckLinkAPIInformationInstance_0001");
|
||||||
|
if (!gCreateAPIInformationFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)dlsym(libraryHandle, "CreateVideoConversionInstance_0002");
|
||||||
|
if (!gCreateVideoConversionFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateDeckLinkDiscoveryFunc = (CreateDeckLinkDiscoveryInstanceFunc)dlsym(libraryHandle, "CreateDeckLinkDiscoveryInstance_0003");
|
||||||
|
if (!gCreateDeckLinkDiscoveryFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateVideoFrameAncillaryPacketsFunc = (CreateVideoFrameAncillaryPacketsInstanceFunc)dlsym(libraryHandle, "CreateVideoFrameAncillaryPacketsInstance_0001");
|
||||||
|
if (!gCreateVideoFrameAncillaryPacketsFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDeckLinkPreviewAPI (void)
|
||||||
|
{
|
||||||
|
void *libraryHandle;
|
||||||
|
|
||||||
|
libraryHandle = dlopen(KDeckLinkPreviewAPI_Name, RTLD_NOW|RTLD_GLOBAL);
|
||||||
|
if (!libraryHandle)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGLScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGLPreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
gCreateOpenGL3PreviewFunc = (CreateOpenGL3ScreenPreviewHelperFunc)dlsym(libraryHandle, "CreateOpenGL3ScreenPreviewHelper_0002");
|
||||||
|
if (!gCreateOpenGL3PreviewFunc)
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeckLinkAPIPresent (void)
|
||||||
|
{
|
||||||
|
// If the DeckLink API dynamic library was successfully loaded, return this knowledge to the caller
|
||||||
|
return gLoadedDeckLinkAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkIterator* CreateDeckLinkIteratorInstance_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateIteratorFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateIteratorFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateAPIInformationFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateAPIInformationFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGLPreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGLPreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
pthread_once(&gPreviewOnceControl, InitDeckLinkPreviewAPI);
|
||||||
|
|
||||||
|
if (gCreateOpenGL3PreviewFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateOpenGL3PreviewFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoConversion_v15_3_1* CreateVideoConversionInstance_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoConversionFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoConversionFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateDeckLinkDiscoveryFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateDeckLinkDiscoveryFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v15_3_1 (void)
|
||||||
|
{
|
||||||
|
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
|
||||||
|
|
||||||
|
if (gCreateVideoFrameAncillaryPacketsFunc == NULL)
|
||||||
|
return NULL;
|
||||||
|
return gCreateVideoFrameAncillaryPacketsFunc();
|
||||||
|
}
|
||||||
68
services/capture/sdk/DeckLinkAPIGLScreenPreview_v14_2_1.h
Normal file
68
services/capture/sdk/DeckLinkAPIGLScreenPreview_v14_2_1.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIGLSCREENPREVIEW_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIGLSCREENPREVIEW_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkGLScreenPreviewHelper_v14_2_1 = /* 504E2209-CAC7-4C1A-9FB4-C5BB6274D22F */ { 0x50, 0x4E, 0x22, 0x09, 0xCA, 0xC7, 0x4C, 0x1A, 0x9F, 0xB4, 0xC5, 0xBB, 0x62, 0x74, 0xD2, 0x2F };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkGLScreenPreviewHelper - Created with CoCreateInstance on platforms with native COM support or from CreateOpenGLScreenPreviewHelper/CreateOpenGL3ScreenPreviewHelper on other platforms. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkGLScreenPreviewHelper_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* Methods must be called with OpenGL context set */
|
||||||
|
|
||||||
|
virtual HRESULT InitializeGL (void) = 0;
|
||||||
|
virtual HRESULT PaintGL (void) = 0;
|
||||||
|
virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame) = 0;
|
||||||
|
virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkGLScreenPreviewHelper_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
61
services/capture/sdk/DeckLinkAPIMemoryAllocator_v14_2_1.h
Normal file
61
services/capture/sdk/DeckLinkAPIMemoryAllocator_v14_2_1.h
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIMEMORYALLOCATOR_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIMEMORYALLOCATOR_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkMemoryAllocator_v14_2_1 = /* B36EB6E7-9D29-4AA8-92EF-843B87A289E8 */ { 0xB3, 0x6E, 0xB6, 0xE7, 0x9D, 0x29, 0x4A, 0xA8, 0x92, 0xEF, 0x84, 0x3B, 0x87, 0xA2, 0x89, 0xE8 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkMemoryAllocator_v14_2_1 - Created with CoCreateInstance. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkMemoryAllocator_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT AllocateBuffer (/* in */ uint32_t bufferSize, /* out */ void** allocatedBuffer) = 0;
|
||||||
|
virtual HRESULT ReleaseBuffer (/* in */ void* buffer) = 0;
|
||||||
|
virtual HRESULT Commit (void) = 0;
|
||||||
|
virtual HRESULT Decommit (void) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
65
services/capture/sdk/DeckLinkAPIMetalScreenPreview_v14_2_1.h
Normal file
65
services/capture/sdk/DeckLinkAPIMetalScreenPreview_v14_2_1.h
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIMETALSCREENPREVIEW_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIMETALSCREENPREVIEW_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkMetalScreenPreviewHelper_v14_2_1 = /* 1AB252C5-DACB-4AE8-A58B-5320DE9CE373 */ { 0x1A, 0xB2, 0x52, 0xC5, 0xDA, 0xCB, 0x4A, 0xE8, 0xA5, 0x8B, 0x53, 0x20, 0xDE, 0x9C, 0xE3, 0x73 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkMetalScreenPreviewHelper - Created with CreateMetalScreenPreviewHelper(). */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkMetalScreenPreviewHelper_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Initialize (/* in */ void* device) = 0;
|
||||||
|
virtual HRESULT Draw (/* in */ void* cmdBuffer, /* in */ void* renderPassDescriptor, /* in */ void* viewport) = 0;
|
||||||
|
virtual HRESULT SetFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame) = 0;
|
||||||
|
virtual HRESULT Set3DPreviewFormat (/* in */ BMD3DPreviewFormat previewFormat) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkMetalScreenPreviewHelper_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
291
services/capture/sdk/DeckLinkAPIModes.h
Normal file
291
services/capture/sdk/DeckLinkAPIModes.h
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
** this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
** execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
** Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
** do so, all subject to the following:
|
||||||
|
**
|
||||||
|
** The copyright notices in the Software and this entire statement, including
|
||||||
|
** the above license grant, this restriction and the following disclaimer,
|
||||||
|
** must be included in all copies of the Software, in whole or in part, and
|
||||||
|
** all derivative works of the Software, unless such copies or derivative
|
||||||
|
** works are solely in the form of machine-executable object code generated by
|
||||||
|
** a source language processor.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -- AUTOMATICALLY GENERATED - DO NOT EDIT ---
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIMODES_H
|
||||||
|
#define BMD_DECKLINKAPIMODES_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BMD_CONST
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BMD_CONST __declspec(selectany) static const
|
||||||
|
#else
|
||||||
|
#define BMD_CONST static const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkDisplayModeIterator = /* 9C88499F-F601-4021-B80B-032E4EB41C35 */ { 0x9C,0x88,0x49,0x9F,0xF6,0x01,0x40,0x21,0xB8,0x0B,0x03,0x2E,0x4E,0xB4,0x1C,0x35 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkDisplayMode = /* 3EB2C1AB-0A3D-4523-A3AD-F40D7FB14E78 */ { 0x3E,0xB2,0xC1,0xAB,0x0A,0x3D,0x45,0x23,0xA3,0xAD,0xF4,0x0D,0x7F,0xB1,0x4E,0x78 };
|
||||||
|
|
||||||
|
/* Enum BMDDisplayMode - BMDDisplayMode enumerates the video modes supported. */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDisplayMode;
|
||||||
|
enum _BMDDisplayMode {
|
||||||
|
|
||||||
|
/* SD Modes */
|
||||||
|
|
||||||
|
bmdModeNTSC = /* 'ntsc' */ 0x6E747363,
|
||||||
|
bmdModeNTSC2398 = /* 'nt23' */ 0x6E743233, // 3:2 pulldown
|
||||||
|
bmdModePAL = /* 'pal ' */ 0x70616C20,
|
||||||
|
bmdModeNTSCp = /* 'ntsp' */ 0x6E747370,
|
||||||
|
bmdModePALp = /* 'palp' */ 0x70616C70,
|
||||||
|
|
||||||
|
/* HD 1080 Modes */
|
||||||
|
|
||||||
|
bmdModeHD1080p2398 = /* '23ps' */ 0x32337073,
|
||||||
|
bmdModeHD1080p24 = /* '24ps' */ 0x32347073,
|
||||||
|
bmdModeHD1080p25 = /* 'Hp25' */ 0x48703235,
|
||||||
|
bmdModeHD1080p2997 = /* 'Hp29' */ 0x48703239,
|
||||||
|
bmdModeHD1080p30 = /* 'Hp30' */ 0x48703330,
|
||||||
|
bmdModeHD1080p4795 = /* 'Hp47' */ 0x48703437,
|
||||||
|
bmdModeHD1080p48 = /* 'Hp48' */ 0x48703438,
|
||||||
|
bmdModeHD1080p50 = /* 'Hp50' */ 0x48703530,
|
||||||
|
bmdModeHD1080p5994 = /* 'Hp59' */ 0x48703539,
|
||||||
|
bmdModeHD1080p6000 = /* 'Hp60' */ 0x48703630, // N.B. This _really_ is 60.00 Hz.
|
||||||
|
bmdModeHD1080p9590 = /* 'Hp95' */ 0x48703935,
|
||||||
|
bmdModeHD1080p96 = /* 'Hp96' */ 0x48703936,
|
||||||
|
bmdModeHD1080p100 = /* 'Hp10' */ 0x48703130,
|
||||||
|
bmdModeHD1080p11988 = /* 'Hp11' */ 0x48703131,
|
||||||
|
bmdModeHD1080p120 = /* 'Hp12' */ 0x48703132,
|
||||||
|
bmdModeHD1080i50 = /* 'Hi50' */ 0x48693530,
|
||||||
|
bmdModeHD1080i5994 = /* 'Hi59' */ 0x48693539,
|
||||||
|
bmdModeHD1080i6000 = /* 'Hi60' */ 0x48693630, // N.B. This _really_ is 60.00 Hz.
|
||||||
|
|
||||||
|
/* HD 720 Modes */
|
||||||
|
|
||||||
|
bmdModeHD720p50 = /* 'hp50' */ 0x68703530,
|
||||||
|
bmdModeHD720p5994 = /* 'hp59' */ 0x68703539,
|
||||||
|
bmdModeHD720p60 = /* 'hp60' */ 0x68703630,
|
||||||
|
|
||||||
|
/* 2K Modes */
|
||||||
|
|
||||||
|
bmdMode2k2398 = /* '2k23' */ 0x326B3233,
|
||||||
|
bmdMode2k24 = /* '2k24' */ 0x326B3234,
|
||||||
|
bmdMode2k25 = /* '2k25' */ 0x326B3235,
|
||||||
|
|
||||||
|
/* 2K DCI Modes */
|
||||||
|
|
||||||
|
bmdMode2kDCI2398 = /* '2d23' */ 0x32643233,
|
||||||
|
bmdMode2kDCI24 = /* '2d24' */ 0x32643234,
|
||||||
|
bmdMode2kDCI25 = /* '2d25' */ 0x32643235,
|
||||||
|
bmdMode2kDCI2997 = /* '2d29' */ 0x32643239,
|
||||||
|
bmdMode2kDCI30 = /* '2d30' */ 0x32643330,
|
||||||
|
bmdMode2kDCI4795 = /* '2d47' */ 0x32643437,
|
||||||
|
bmdMode2kDCI48 = /* '2d48' */ 0x32643438,
|
||||||
|
bmdMode2kDCI50 = /* '2d50' */ 0x32643530,
|
||||||
|
bmdMode2kDCI5994 = /* '2d59' */ 0x32643539,
|
||||||
|
bmdMode2kDCI60 = /* '2d60' */ 0x32643630,
|
||||||
|
bmdMode2kDCI9590 = /* '2d95' */ 0x32643935,
|
||||||
|
bmdMode2kDCI96 = /* '2d96' */ 0x32643936,
|
||||||
|
bmdMode2kDCI100 = /* '2d10' */ 0x32643130,
|
||||||
|
bmdMode2kDCI11988 = /* '2d11' */ 0x32643131,
|
||||||
|
bmdMode2kDCI120 = /* '2d12' */ 0x32643132,
|
||||||
|
|
||||||
|
/* 4K UHD Modes */
|
||||||
|
|
||||||
|
bmdMode4K2160p2398 = /* '4k23' */ 0x346B3233,
|
||||||
|
bmdMode4K2160p24 = /* '4k24' */ 0x346B3234,
|
||||||
|
bmdMode4K2160p25 = /* '4k25' */ 0x346B3235,
|
||||||
|
bmdMode4K2160p2997 = /* '4k29' */ 0x346B3239,
|
||||||
|
bmdMode4K2160p30 = /* '4k30' */ 0x346B3330,
|
||||||
|
bmdMode4K2160p4795 = /* '4k47' */ 0x346B3437,
|
||||||
|
bmdMode4K2160p48 = /* '4k48' */ 0x346B3438,
|
||||||
|
bmdMode4K2160p50 = /* '4k50' */ 0x346B3530,
|
||||||
|
bmdMode4K2160p5994 = /* '4k59' */ 0x346B3539,
|
||||||
|
bmdMode4K2160p60 = /* '4k60' */ 0x346B3630,
|
||||||
|
bmdMode4K2160p9590 = /* '4k95' */ 0x346B3935,
|
||||||
|
bmdMode4K2160p96 = /* '4k96' */ 0x346B3936,
|
||||||
|
bmdMode4K2160p100 = /* '4k10' */ 0x346B3130,
|
||||||
|
bmdMode4K2160p11988 = /* '4k11' */ 0x346B3131,
|
||||||
|
bmdMode4K2160p120 = /* '4k12' */ 0x346B3132,
|
||||||
|
|
||||||
|
/* 4K DCI Modes */
|
||||||
|
|
||||||
|
bmdMode4kDCI2398 = /* '4d23' */ 0x34643233,
|
||||||
|
bmdMode4kDCI24 = /* '4d24' */ 0x34643234,
|
||||||
|
bmdMode4kDCI25 = /* '4d25' */ 0x34643235,
|
||||||
|
bmdMode4kDCI2997 = /* '4d29' */ 0x34643239,
|
||||||
|
bmdMode4kDCI30 = /* '4d30' */ 0x34643330,
|
||||||
|
bmdMode4kDCI4795 = /* '4d47' */ 0x34643437,
|
||||||
|
bmdMode4kDCI48 = /* '4d48' */ 0x34643438,
|
||||||
|
bmdMode4kDCI50 = /* '4d50' */ 0x34643530,
|
||||||
|
bmdMode4kDCI5994 = /* '4d59' */ 0x34643539,
|
||||||
|
bmdMode4kDCI60 = /* '4d60' */ 0x34643630,
|
||||||
|
bmdMode4kDCI9590 = /* '4d95' */ 0x34643935,
|
||||||
|
bmdMode4kDCI96 = /* '4d96' */ 0x34643936,
|
||||||
|
bmdMode4kDCI100 = /* '4d10' */ 0x34643130,
|
||||||
|
bmdMode4kDCI11988 = /* '4d11' */ 0x34643131,
|
||||||
|
bmdMode4kDCI120 = /* '4d12' */ 0x34643132,
|
||||||
|
|
||||||
|
/* 8K UHD Modes */
|
||||||
|
|
||||||
|
bmdMode8K4320p2398 = /* '8k23' */ 0x386B3233,
|
||||||
|
bmdMode8K4320p24 = /* '8k24' */ 0x386B3234,
|
||||||
|
bmdMode8K4320p25 = /* '8k25' */ 0x386B3235,
|
||||||
|
bmdMode8K4320p2997 = /* '8k29' */ 0x386B3239,
|
||||||
|
bmdMode8K4320p30 = /* '8k30' */ 0x386B3330,
|
||||||
|
bmdMode8K4320p4795 = /* '8k47' */ 0x386B3437,
|
||||||
|
bmdMode8K4320p48 = /* '8k48' */ 0x386B3438,
|
||||||
|
bmdMode8K4320p50 = /* '8k50' */ 0x386B3530,
|
||||||
|
bmdMode8K4320p5994 = /* '8k59' */ 0x386B3539,
|
||||||
|
bmdMode8K4320p60 = /* '8k60' */ 0x386B3630,
|
||||||
|
|
||||||
|
/* 8K DCI Modes */
|
||||||
|
|
||||||
|
bmdMode8kDCI2398 = /* '8d23' */ 0x38643233,
|
||||||
|
bmdMode8kDCI24 = /* '8d24' */ 0x38643234,
|
||||||
|
bmdMode8kDCI25 = /* '8d25' */ 0x38643235,
|
||||||
|
bmdMode8kDCI2997 = /* '8d29' */ 0x38643239,
|
||||||
|
bmdMode8kDCI30 = /* '8d30' */ 0x38643330,
|
||||||
|
bmdMode8kDCI4795 = /* '8d47' */ 0x38643437,
|
||||||
|
bmdMode8kDCI48 = /* '8d48' */ 0x38643438,
|
||||||
|
bmdMode8kDCI50 = /* '8d50' */ 0x38643530,
|
||||||
|
bmdMode8kDCI5994 = /* '8d59' */ 0x38643539,
|
||||||
|
bmdMode8kDCI60 = /* '8d60' */ 0x38643630,
|
||||||
|
|
||||||
|
/* PC Modes */
|
||||||
|
|
||||||
|
bmdMode640x480p60 = /* 'vga6' */ 0x76676136,
|
||||||
|
bmdMode800x600p60 = /* 'svg6' */ 0x73766736,
|
||||||
|
bmdMode1440x900p50 = /* 'wxg5' */ 0x77786735,
|
||||||
|
bmdMode1440x900p60 = /* 'wxg6' */ 0x77786736,
|
||||||
|
bmdMode1440x1080p50 = /* 'sxg5' */ 0x73786735,
|
||||||
|
bmdMode1440x1080p60 = /* 'sxg6' */ 0x73786736,
|
||||||
|
bmdMode1600x1200p50 = /* 'uxg5' */ 0x75786735,
|
||||||
|
bmdMode1600x1200p60 = /* 'uxg6' */ 0x75786736,
|
||||||
|
bmdMode1920x1200p50 = /* 'wux5' */ 0x77757835,
|
||||||
|
bmdMode1920x1200p60 = /* 'wux6' */ 0x77757836,
|
||||||
|
bmdMode1920x1440p50 = /* '1945' */ 0x31393435,
|
||||||
|
bmdMode1920x1440p60 = /* '1946' */ 0x31393436,
|
||||||
|
bmdMode2560x1440p50 = /* 'wqh5' */ 0x77716835,
|
||||||
|
bmdMode2560x1440p60 = /* 'wqh6' */ 0x77716836,
|
||||||
|
bmdMode2560x1600p50 = /* 'wqx5' */ 0x77717835,
|
||||||
|
bmdMode2560x1600p60 = /* 'wqx6' */ 0x77717836,
|
||||||
|
bmdModeUnknown = /* 'iunk' */ 0x69756E6B
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDFieldDominance - BMDFieldDominance enumerates settings applicable to video fields. */
|
||||||
|
|
||||||
|
typedef uint32_t BMDFieldDominance;
|
||||||
|
enum _BMDFieldDominance {
|
||||||
|
bmdUnknownFieldDominance = 0,
|
||||||
|
bmdLowerFieldFirst = /* 'lowr' */ 0x6C6F7772,
|
||||||
|
bmdUpperFieldFirst = /* 'uppr' */ 0x75707072,
|
||||||
|
bmdProgressiveFrame = /* 'prog' */ 0x70726F67,
|
||||||
|
bmdProgressiveSegmentedFrame = /* 'psf ' */ 0x70736620
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDPixelFormat - Video pixel formats supported for output/input */
|
||||||
|
|
||||||
|
typedef uint32_t BMDPixelFormat;
|
||||||
|
enum _BMDPixelFormat {
|
||||||
|
bmdFormatUnspecified = 0,
|
||||||
|
bmdFormat8BitYUV = /* '2vuy' */ 0x32767579,
|
||||||
|
bmdFormat10BitYUV = /* 'v210' */ 0x76323130,
|
||||||
|
bmdFormat10BitYUVA = /* 'Ay10' */ 0x41793130, // Big-endian YUVA 10 bit per component with SMPTE video levels (64-940) for YUV but full range alpha
|
||||||
|
bmdFormat8BitARGB = 32,
|
||||||
|
bmdFormat8BitBGRA = /* 'BGRA' */ 0x42475241,
|
||||||
|
bmdFormat10BitRGB = /* 'r210' */ 0x72323130, // Big-endian RGB 10-bit per component with SMPTE video levels (64-940). Packed as 2:10:10:10
|
||||||
|
bmdFormat12BitRGB = /* 'R12B' */ 0x52313242, // Big-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component
|
||||||
|
bmdFormat12BitRGBLE = /* 'R12L' */ 0x5231324C, // Little-endian RGB 12-bit per component with full range (0-4095). Packed as 12-bit per component
|
||||||
|
bmdFormat10BitRGBXLE = /* 'R10l' */ 0x5231306C, // Little-endian 10-bit RGB with SMPTE video levels (64-940)
|
||||||
|
bmdFormat10BitRGBX = /* 'R10b' */ 0x52313062, // Big-endian 10-bit RGB with SMPTE video levels (64-940)
|
||||||
|
|
||||||
|
/* Formats supported only by devices that can be queried for an IDeckLinkEncoderInput */
|
||||||
|
|
||||||
|
bmdFormatH265 = /* 'hev1' */ 0x68657631,
|
||||||
|
bmdFormatDNxHR = /* 'AVdh' */ 0x41566468
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDisplayModeFlags - Flags to describe the characteristics of an IDeckLinkDisplayMode. */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDisplayModeFlags;
|
||||||
|
enum _BMDDisplayModeFlags {
|
||||||
|
bmdDisplayModeSupports3D = 1 << 0,
|
||||||
|
bmdDisplayModeColorspaceRec601 = 1 << 1,
|
||||||
|
bmdDisplayModeColorspaceRec709 = 1 << 2,
|
||||||
|
bmdDisplayModeColorspaceRec2020 = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkDisplayModeIterator;
|
||||||
|
class IDeckLinkDisplayMode;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkDisplayModeIterator - Enumerates over supported input/output display modes. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkDisplayModeIterator : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Next (/* out */ IDeckLinkDisplayMode** deckLinkDisplayMode) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkDisplayModeIterator () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkDisplayMode - Represents a display mode */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkDisplayMode : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetName (/* out */ const char** name) = 0;
|
||||||
|
virtual BMDDisplayMode GetDisplayMode (void) = 0;
|
||||||
|
virtual long GetWidth (void) = 0;
|
||||||
|
virtual long GetHeight (void) = 0;
|
||||||
|
virtual HRESULT GetFrameRate (/* out */ BMDTimeValue* frameDuration, /* out */ BMDTimeScale* timeScale) = 0;
|
||||||
|
virtual BMDFieldDominance GetFieldDominance (void) = 0;
|
||||||
|
virtual BMDDisplayModeFlags GetFlags (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkDisplayMode () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIMODES_H) */
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPISCREENPREVIEWCALLBACK_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPISCREENPREVIEWCALLBACK_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkScreenPreviewCallback_v14_2_1 = /* B1D3F49A-85FE-4C5D-95C8-0B5D5DCCD438 */ { 0xB1, 0xD3, 0xF4, 0x9A, 0x85, 0xFE, 0x4C, 0x5D, 0x95, 0xC8, 0x0B, 0x5D, 0x5D, 0xCC, 0xD4, 0x38 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkScreenPreviewCallback_v14_2_1 - Screen preview callback */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkScreenPreviewCallback_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DrawFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkScreenPreviewCallback_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPISCREENPREVIEWCALLBACK_v14_2_1_H) */
|
||||||
140
services/capture/sdk/DeckLinkAPITypes.h
Normal file
140
services/capture/sdk/DeckLinkAPITypes.h
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2026 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
** this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
** execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
** Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
** do so, all subject to the following:
|
||||||
|
**
|
||||||
|
** The copyright notices in the Software and this entire statement, including
|
||||||
|
** the above license grant, this restriction and the following disclaimer,
|
||||||
|
** must be included in all copies of the Software, in whole or in part, and
|
||||||
|
** all derivative works of the Software, unless such copies or derivative
|
||||||
|
** works are solely in the form of machine-executable object code generated by
|
||||||
|
** a source language processor.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -- AUTOMATICALLY GENERATED - DO NOT EDIT ---
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPITYPES_H
|
||||||
|
#define BMD_DECKLINKAPITYPES_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BMD_CONST
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BMD_CONST __declspec(selectany) static const
|
||||||
|
#else
|
||||||
|
#define BMD_CONST static const
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
typedef int64_t BMDTimeValue;
|
||||||
|
typedef int64_t BMDTimeScale;
|
||||||
|
typedef uint32_t BMDTimecodeBCD;
|
||||||
|
typedef uint32_t BMDTimecodeUserBits;
|
||||||
|
typedef int64_t BMDIPFlowID;
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkTimecode = /* BC6CFBD3-8317-4325-AC1C-1216391E9340 */ { 0xBC,0x6C,0xFB,0xD3,0x83,0x17,0x43,0x25,0xAC,0x1C,0x12,0x16,0x39,0x1E,0x93,0x40 };
|
||||||
|
|
||||||
|
/* Enum BMDTimecodeFlags - Timecode flags */
|
||||||
|
|
||||||
|
typedef uint32_t BMDTimecodeFlags;
|
||||||
|
enum _BMDTimecodeFlags {
|
||||||
|
bmdTimecodeFlagDefault = 0,
|
||||||
|
bmdTimecodeIsDropFrame = 1 << 0,
|
||||||
|
bmdTimecodeFieldMark = 1 << 1,
|
||||||
|
bmdTimecodeColorFrame = 1 << 2,
|
||||||
|
bmdTimecodeEmbedRecordingTrigger = 1 << 3, // On SDI recording trigger utilises a user-bit.
|
||||||
|
bmdTimecodeRecordingTriggered = 1 << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDVideoConnection - Video connection types */
|
||||||
|
|
||||||
|
typedef uint32_t BMDVideoConnection;
|
||||||
|
enum _BMDVideoConnection {
|
||||||
|
bmdVideoConnectionUnspecified = 0,
|
||||||
|
bmdVideoConnectionSDI = 1 << 0,
|
||||||
|
bmdVideoConnectionHDMI = 1 << 1,
|
||||||
|
bmdVideoConnectionOpticalSDI = 1 << 2,
|
||||||
|
bmdVideoConnectionComponent = 1 << 3,
|
||||||
|
bmdVideoConnectionComposite = 1 << 4,
|
||||||
|
bmdVideoConnectionSVideo = 1 << 5,
|
||||||
|
bmdVideoConnectionEthernet = 1 << 6,
|
||||||
|
bmdVideoConnectionOpticalEthernet = 1 << 7,
|
||||||
|
bmdVideoConnectionInternal = 1 << 8
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDAudioConnection - Audio connection types */
|
||||||
|
|
||||||
|
typedef uint32_t BMDAudioConnection;
|
||||||
|
enum _BMDAudioConnection {
|
||||||
|
bmdAudioConnectionEmbedded = 1 << 0,
|
||||||
|
bmdAudioConnectionAESEBU = 1 << 1,
|
||||||
|
bmdAudioConnectionAnalog = 1 << 2,
|
||||||
|
bmdAudioConnectionAnalogXLR = 1 << 3,
|
||||||
|
bmdAudioConnectionAnalogRCA = 1 << 4,
|
||||||
|
bmdAudioConnectionMicrophone = 1 << 5,
|
||||||
|
bmdAudioConnectionHeadphones = 1 << 6
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckControlConnection - Deck control connections */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckControlConnection;
|
||||||
|
enum _BMDDeckControlConnection {
|
||||||
|
bmdDeckControlConnectionRS422Remote1 = 1 << 0,
|
||||||
|
bmdDeckControlConnectionRS422Remote2 = 1 << 1
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
|
||||||
|
class IDeckLinkTimecode;
|
||||||
|
|
||||||
|
/* Interface IDeckLinkTimecode - Used for video frame timecode representation. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkTimecode : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual BMDTimecodeBCD GetBCD (void) = 0;
|
||||||
|
virtual HRESULT GetComponents (/* out */ uint8_t* hours, /* out */ uint8_t* minutes, /* out */ uint8_t* seconds, /* out */ uint8_t* frames) = 0;
|
||||||
|
virtual HRESULT GetString (/* out */ const char** timecode) = 0;
|
||||||
|
virtual BMDTimecodeFlags GetFlags (void) = 0;
|
||||||
|
virtual HRESULT GetTimecodeUserBits (/* out */ BMDTimecodeUserBits* userBits) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkTimecode () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPITYPES_H) */
|
||||||
50
services/capture/sdk/DeckLinkAPIVersion.h
Normal file
50
services/capture/sdk/DeckLinkAPIVersion.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
* ** Copyright (c) 2014 Blackmagic Design
|
||||||
|
* **
|
||||||
|
* ** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
* ** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
* ** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
* ** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
* ** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
* ** accordance with:
|
||||||
|
* **
|
||||||
|
* ** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
* ** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
* ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
* **
|
||||||
|
* ** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
* ** as notified by that third party,
|
||||||
|
* **
|
||||||
|
* ** and all subject to the following:
|
||||||
|
* **
|
||||||
|
* ** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
* ** including the above license grant, this restriction and the following
|
||||||
|
* ** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
* ** part, and all derivative works of the Software, unless such copies or
|
||||||
|
* ** derivative works are solely in the form of machine-executable object code
|
||||||
|
* ** generated by a source language processor.
|
||||||
|
* **
|
||||||
|
* ** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* ** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
* ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
* ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* ** DEALINGS IN THE SOFTWARE.
|
||||||
|
* **
|
||||||
|
* ** A copy of the Software is available free of charge at
|
||||||
|
* ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
* **
|
||||||
|
* ** -LICENSE-END-
|
||||||
|
* */
|
||||||
|
|
||||||
|
/* DeckLinkAPIVersion.h */
|
||||||
|
|
||||||
|
#ifndef __DeckLink_API_Version_h__
|
||||||
|
#define __DeckLink_API_Version_h__
|
||||||
|
|
||||||
|
#define BLACKMAGIC_DECKLINK_API_VERSION 0x10000000
|
||||||
|
#define BLACKMAGIC_DECKLINK_API_VERSION_STRING "16.0"
|
||||||
|
|
||||||
|
#endif // __DeckLink_API_Version_h__
|
||||||
|
|
||||||
62
services/capture/sdk/DeckLinkAPIVideoConversion_v14_2_1.h
Normal file
62
services/capture/sdk/DeckLinkAPIVideoConversion_v14_2_1.h
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOCONVERSION_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOCONVERSION_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoConversion_v14_2_1 = /* 3BBCB8A2-DA2C-42D9-B5D8-88083644E99A */ { 0x3B, 0xBC, 0xB8, 0xA2, 0xDA, 0x2C, 0x42, 0xD9, 0xB5, 0xD8, 0x88, 0x08, 0x36, 0x44, 0xE9, 0x9A };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoConversion - Created with CoCreateInstance. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoConversion_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* srcFrame, /* in */ IDeckLinkVideoFrame_v14_2_1* dstFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoConversion_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
88
services/capture/sdk/DeckLinkAPIVideoEncoderInput_v10_11.h
Normal file
88
services/capture/sdk/DeckLinkAPIVideoEncoderInput_v10_11.h
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOENCODERINPUT_v10_11_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOENCODERINPUT_v10_11_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPI_v10_11.h"
|
||||||
|
#include "DeckLinkAPIMemoryAllocator_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkEncoderInput_v10_11 = /* 270587DA-6B7D-42E7-A1F0-6D853F581185 */ {0x27,0x05,0x87,0xDA,0x6B,0x7D,0x42,0xE7,0xA1,0xF0,0x6D,0x85,0x3F,0x58,0x11,0x85};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkEncoderInput_v10_11 - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class IDeckLinkEncoderInput_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport_v10_11 *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailablePacketsCount (/* out */ uint32_t *availablePacketsCount) = 0;
|
||||||
|
virtual HRESULT SetMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioFormat audioFormat, /* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t *availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkEncoderInputCallback *theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkEncoderInput_v10_11 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOENCODERINPUT_v10_11_H) */
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOFRAME3DEXTENSIONS_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOFRAME3DEXTENSIONS_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoFrame3DExtensions_v14_2_1 = /* DA0F7E4A-EDC7-48A8-9CDD-2DB51C729CD7 */ { 0xDA, 0x0F, 0x7E, 0x4A, 0xED, 0xC7, 0x48, 0xA8, 0x9C, 0xDD, 0x2D, 0xB5, 0x1C, 0x72, 0x9C, 0xD7 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoFrame3DExtensions - Optional interface implemented on IDeckLinkVideoFrame to support 3D frames */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoFrame3DExtensions_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual BMDVideo3DPackingFormat Get3DPackingFormat (void) = 0;
|
||||||
|
virtual HRESULT GetFrameForRightEye (/* out */ IDeckLinkVideoFrame_v14_2_1** rightEyeFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoFrame3DExtensions_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOFRAME3DEXTENSIONS_v14_2_1_H) */
|
||||||
68
services/capture/sdk/DeckLinkAPIVideoFrame_v14_2_1.h
Normal file
68
services/capture/sdk/DeckLinkAPIVideoFrame_v14_2_1.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOFRAME_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOFRAME_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoFrame_v14_2_1 = /* 3F716FE0-F023-4111-BE5D-EF4414C05B17 */ { 0x3F, 0x71, 0x6F, 0xE0, 0xF0, 0x23, 0x41, 0x11, 0xBE, 0x5D, 0xEF, 0x44, 0x14, 0xC0, 0x5B, 0x17 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoFrame - Interface to encapsulate a video frame; can be caller-implemented. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoFrame_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual long GetWidth (void) = 0;
|
||||||
|
virtual long GetHeight (void) = 0;
|
||||||
|
virtual long GetRowBytes (void) = 0;
|
||||||
|
virtual BMDPixelFormat GetPixelFormat (void) = 0;
|
||||||
|
virtual BMDFrameFlags GetFlags (void) = 0;
|
||||||
|
virtual HRESULT GetBytes (/* out */ void** buffer) = 0;
|
||||||
|
virtual HRESULT GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode** timecode) = 0;
|
||||||
|
virtual HRESULT GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary** ancillary) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoFrame_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOFRAME_v14_2_1_H) */
|
||||||
91
services/capture/sdk/DeckLinkAPIVideoInput_v10_11.h
Normal file
91
services/capture/sdk/DeckLinkAPIVideoInput_v10_11.h
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOINPUT_v10_11_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOINPUT_v10_11_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPI_v10_11.h"
|
||||||
|
#include "DeckLinkAPIMemoryAllocator_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoInput_v11_5_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInput_v10_11 = /* AF22762B-DFAC-4846-AA79-FA8883560995 */ {0xAF,0x22,0x76,0x2B,0xDF,0xAC,0x48,0x46,0xAA,0x79,0xFA,0x88,0x83,0x56,0x09,0x95};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInput_v10_11 - DeckLink input interface. */
|
||||||
|
|
||||||
|
class IDeckLinkInput_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* out */ BMDDisplayModeSupport_v10_11 *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t *availableFrameCount) = 0;
|
||||||
|
virtual HRESULT SetVideoInputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t *availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback_v11_5_1 *theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInput_v10_11 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOINPUT_v10_11_H) */
|
||||||
90
services/capture/sdk/DeckLinkAPIVideoInput_v11_4.h
Normal file
90
services/capture/sdk/DeckLinkAPIVideoInput_v11_4.h
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2019 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOINPUT_v11_4_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOINPUT_v11_4_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIMemoryAllocator_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoInput_v11_5_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInput_v11_4 = /* 2A88CF76-F494-4216-A7EF-DC74EEB83882 */ { 0x2A,0x88,0xCF,0x76,0xF4,0x94,0x42,0x16,0xA7,0xEF,0xDC,0x74,0xEE,0xB8,0x38,0x82 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInput_v11_4 - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInput_v11_4 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of 0 is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDSupportedVideoModeFlags flags, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0;
|
||||||
|
virtual HRESULT SetVideoInputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback_v11_5_1* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInput_v11_4 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOINPUT_v11_4_H) */
|
||||||
103
services/capture/sdk/DeckLinkAPIVideoInput_v11_5_1.h
Normal file
103
services/capture/sdk/DeckLinkAPIVideoInput_v11_5_1.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2020 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOINPUT_v11_5_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOINPUT_v11_5_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoInput_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInputCallback_v11_5_1 = /* DD04E5EC-7415-42AB-AE4A-E80C4DFC044A */ { 0xDD, 0x04, 0xE5, 0xEC, 0x74, 0x15, 0x42, 0xAB, 0xAE, 0x4A, 0xE8, 0x0C, 0x4D, 0xFC, 0x04, 0x4A };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInput_v11_5_1 = /* 9434C6E4-B15D-4B1C-979E-661E3DDCB4B9 */ { 0x94, 0x34, 0xC6, 0xE4, 0xB1, 0x5D, 0x4B, 0x1C, 0x97, 0x9E, 0x66, 0x1E, 0x3D, 0xDC, 0xB4, 0xB9 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInputCallback_v11_5_1 - Frame arrival callback. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInputCallback_v11_5_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0;
|
||||||
|
virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame_v14_2_1* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInputCallback_v11_5_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInput_v11_5_1 - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInput_v11_5_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoInputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0;
|
||||||
|
virtual HRESULT SetVideoInputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback_v11_5_1* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInput_v11_5_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOINPUT_v11_5_1_H) */
|
||||||
118
services/capture/sdk/DeckLinkAPIVideoInput_v14_2_1.h
Normal file
118
services/capture/sdk/DeckLinkAPIVideoInput_v14_2_1.h
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOINPUT_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOINPUT_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIMemoryAllocator_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIScreenPreviewCallback_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoInputFrame_v14_2_1 = /* 05CFE374-537C-4094-9A57-680525118F44 */ { 0x05, 0xCF, 0xE3, 0x74, 0x53, 0x7C, 0x40, 0x94, 0x9A, 0x57, 0x68, 0x05, 0x25, 0x11, 0x8F, 0x44 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInputCallback_v14_2_1 = /* C6FCE4C9-C4E4-4047-82FB-5D238232A902 */ { 0xC6, 0xFC, 0xE4, 0xC9, 0xC4, 0xE4, 0x40, 0x47, 0x82, 0xFB, 0x5D, 0x23, 0x82, 0x32, 0xA9, 0x02 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInput_v14_2_1 = /* C21CDB6E-F414-46E4-A636-80A566E0ED37 */ { 0xC2, 0x1C, 0xDB, 0x6E, 0xF4, 0x14, 0x46, 0xE4, 0xA6, 0x36, 0x80, 0xA5, 0x66, 0xE0, 0xED, 0x37 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoInputFrame - Provided by the IDeckLinkVideoInput frame arrival callback. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoInputFrame_v14_2_1 : public IDeckLinkVideoFrame_v14_2_1
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetStreamTime (/* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT GetHardwareReferenceTimestamp (/* in */ BMDTimeScale timeScale, /* out */ BMDTimeValue* frameTime, /* out */ BMDTimeValue* frameDuration) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoInputFrame_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInputCallback_v14_2_1 - Frame arrival callback. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInputCallback_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT VideoInputFormatChanged (/* in */ BMDVideoInputFormatChangedEvents notificationEvents, /* in */ IDeckLinkDisplayMode* newDisplayMode, /* in */ BMDDetectedVideoInputFormatFlags detectedSignalFlags) = 0;
|
||||||
|
virtual HRESULT VideoInputFrameArrived (/* in */ IDeckLinkVideoInputFrame_v14_2_1* videoFrame, /* in */ IDeckLinkAudioInputPacket* audioPacket) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInputCallback_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInput - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInput_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoInputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0;
|
||||||
|
virtual HRESULT SetVideoInputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback_v14_2_1* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInput_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOINPUT_v14_2_1_H) */
|
||||||
86
services/capture/sdk/DeckLinkAPIVideoInput_v15_3_1.h
Normal file
86
services/capture/sdk/DeckLinkAPIVideoInput_v15_3_1.h
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2025 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v15_3_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkInput_v15_3_1 = /* 4095DB82-E294-4B8C-AAA8-3B9E80C49336 */ { 0x40,0x95,0xDB,0x82,0xE2,0x94,0x4B,0x8C,0xAA,0xA8,0x3B,0x9E,0x80,0xC4,0x93,0x36 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkInput_v15_3_1 - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkInput_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoInputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT EnableVideoInputWithAllocatorProvider (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags, /* in */ IDeckLinkVideoBufferAllocatorProvider_v15_3_1* allocatorProvider) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableVideoFrameCount (/* out */ uint32_t* availableFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkInputCallback* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkInput_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
109
services/capture/sdk/DeckLinkAPIVideoOutput_v10_11.h
Normal file
109
services/capture/sdk/DeckLinkAPIVideoOutput_v10_11.h
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOOUTPUT_v10_11_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOOUTPUT_v10_11_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPI_v10_11.h"
|
||||||
|
#include "DeckLinkAPIVideoInput_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoOutput_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkOutput_v10_11 = /* CC5C8A6E-3F2F-4B3A-87EA-FD78AF300564 */ {0xCC,0x5C,0x8A,0x6E,0x3F,0x2F,0x4B,0x3A,0x87,0xEA,0xFD,0x78,0xAF,0x30,0x05,0x64};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkOutput_v10_11 - DeckLink output interface. */
|
||||||
|
|
||||||
|
class IDeckLinkOutput_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoOutputFlags flags, /* out */ BMDDisplayModeSupport_v10_11 *result, /* out */ IDeckLinkDisplayMode **resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator **iterator) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1 *previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoOutput (void) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1 *theAllocator) = 0;
|
||||||
|
virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame_v14_2_1 **outFrame) = 0;
|
||||||
|
virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary **outBuffer) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame_v14_2_1 *theFrame) = 0;
|
||||||
|
virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback_v14_2_1 *theCallback) = 0;
|
||||||
|
virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t *bufferedFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Audio Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0;
|
||||||
|
virtual HRESULT DisableAudioOutput (void) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT WriteAudioSamplesSync (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t *sampleFramesWritten) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT BeginAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT EndAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT ScheduleAudioSamples (/* in */ void *buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t *sampleFramesWritten) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t *bufferedSampleFrameCount) = 0;
|
||||||
|
virtual HRESULT FlushBufferedAudioSamples (void) = 0;
|
||||||
|
|
||||||
|
virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback *theCallback) = 0;
|
||||||
|
|
||||||
|
/* Output Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0;
|
||||||
|
virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue *actualStopTime, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool *active) = 0;
|
||||||
|
virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *streamTime, /* out */ double *playbackSpeed) = 0;
|
||||||
|
virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus *referenceStatus) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *hardwareTime, /* out */ BMDTimeValue *timeInFrame, /* out */ BMDTimeValue *ticksPerFrame) = 0;
|
||||||
|
virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame_v14_2_1 *theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue *frameCompletionTimestamp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkOutput_v10_11 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOOUTPUT_v10_11_H) */
|
||||||
101
services/capture/sdk/DeckLinkAPIVideoOutput_v11_4.h
Normal file
101
services/capture/sdk/DeckLinkAPIVideoOutput_v11_4.h
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2019 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOOUTPUT_v11_4_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOOUTPUT_v11_4_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoOutput_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkOutput_v11_4 = /* 065A0F6C-C508-4D0D-B919-F5EB0EBFC96B */ { 0x06,0x5A,0x0F,0x6C,0xC5,0x08,0x4D,0x0D,0xB9,0x19,0xF5,0xEB,0x0E,0xBF,0xC9,0x6B };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkOutput_v11_4 - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkOutput_v11_4 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of 0 is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoOutput (void) = 0;
|
||||||
|
virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame_v14_2_1** outFrame) = 0;
|
||||||
|
virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary** outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred
|
||||||
|
virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame) = 0;
|
||||||
|
virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback_v14_2_1* theCallback) = 0;
|
||||||
|
virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t* bufferedFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Audio Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0;
|
||||||
|
virtual HRESULT DisableAudioOutput (void) = 0;
|
||||||
|
virtual HRESULT WriteAudioSamplesSync (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT BeginAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT EndAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT ScheduleAudioSamples (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t* bufferedSampleFrameCount) = 0;
|
||||||
|
virtual HRESULT FlushBufferedAudioSamples (void) = 0;
|
||||||
|
virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Output Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0;
|
||||||
|
virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue* actualStopTime, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool* active) = 0;
|
||||||
|
virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* streamTime, /* out */ double* playbackSpeed) = 0;
|
||||||
|
virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus* referenceStatus) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* frameCompletionTimestamp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkOutput_v11_4 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPIVIDEOOUTPUT_v11_4_H) */
|
||||||
133
services/capture/sdk/DeckLinkAPIVideoOutput_v14_2_1.h
Normal file
133
services/capture/sdk/DeckLinkAPIVideoOutput_v14_2_1.h
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2022 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPIVIDEOOUTPUT_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPIVIDEOOUTPUT_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIMemoryAllocator_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIScreenPreviewCallback_v14_2_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkMutableVideoFrame_v14_2_1 = /* 69E2639F-40DA-4E19-B6F2-20ACE815C390 */ { 0x69, 0xE2, 0x63, 0x9F, 0x40, 0xDA, 0x4E, 0x19, 0xB6, 0xF2, 0x20, 0xAC, 0xE8, 0x15, 0xC3, 0x90 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoOutputCallback_v14_2_1 = /* 20AA5225-1958-47CB-820B-80A8D521A6EE */ { 0x20, 0xAA, 0x52, 0x25, 0x19, 0x58, 0x47, 0xCB, 0x82, 0x0B, 0x80, 0xA8, 0xD5, 0x21, 0xA6, 0xEE };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkOutput_v14_2_1 = /* BE2D9020-461E-442F-84B7-E949CB953B9D */ { 0xBE, 0x2D, 0x90, 0x20, 0x46, 0x1E, 0x44, 0x2F, 0x84, 0xB7, 0xE9, 0x49, 0xCB, 0x95, 0x3B, 0x9D };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkMutableVideoFrame - Created by IDeckLinkOutput::CreateVideoFrame. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkMutableVideoFrame_v14_2_1 : public IDeckLinkVideoFrame_v14_2_1
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT SetFlags (/* in */ BMDFrameFlags newFlags) = 0;
|
||||||
|
virtual HRESULT SetTimecode (/* in */ BMDTimecodeFormat format, /* in */ IDeckLinkTimecode* timecode) = 0;
|
||||||
|
virtual HRESULT SetTimecodeFromComponents (/* in */ BMDTimecodeFormat format, /* in */ uint8_t hours, /* in */ uint8_t minutes, /* in */ uint8_t seconds, /* in */ uint8_t frames, /* in */ BMDTimecodeFlags flags) = 0;
|
||||||
|
virtual HRESULT SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary* ancillary) = 0;
|
||||||
|
virtual HRESULT SetTimecodeUserBits (/* in */ BMDTimecodeFormat format, /* in */ BMDTimecodeUserBits userBits) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkMutableVideoFrame_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoOutputCallback - Frame completion callback. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoOutputCallback_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT ScheduledFrameCompleted (/* in */ IDeckLinkVideoFrame_v14_2_1* completedFrame, /* in */ BMDOutputFrameCompletionResult result) = 0;
|
||||||
|
virtual HRESULT ScheduledPlaybackHasStopped (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoOutputCallback_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkOutput_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoOutputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback_v14_2_1* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoOutput (void) = 0;
|
||||||
|
virtual HRESULT SetVideoOutputFrameMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame_v14_2_1** outFrame) = 0;
|
||||||
|
virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary** outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred
|
||||||
|
virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame) = 0;
|
||||||
|
virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback_v14_2_1* theCallback) = 0;
|
||||||
|
virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t* bufferedFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Audio Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0;
|
||||||
|
virtual HRESULT DisableAudioOutput (void) = 0;
|
||||||
|
virtual HRESULT WriteAudioSamplesSync (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT BeginAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT EndAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT ScheduleAudioSamples (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t* bufferedSampleFrameCount) = 0;
|
||||||
|
virtual HRESULT FlushBufferedAudioSamples (void) = 0;
|
||||||
|
virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Output Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0;
|
||||||
|
virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue* actualStopTime, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool* active) = 0;
|
||||||
|
virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* streamTime, /* out */ double* playbackSpeed) = 0;
|
||||||
|
virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus* referenceStatus) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame_v14_2_1* theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* frameCompletionTimestamp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkOutput_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
103
services/capture/sdk/DeckLinkAPIVideoOutput_v15_3_1.h
Normal file
103
services/capture/sdk/DeckLinkAPIVideoOutput_v15_3_1.h
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2025 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPI_v15_3_1.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkOutput_v15_3_1 = /* 1A8077F1-9FE2-4533-8147-2294305E253F */ { 0x1A,0x80,0x77,0xF1,0x9F,0xE2,0x45,0x33,0x81,0x47,0x22,0x94,0x30,0x5E,0x25,0x3F };
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
/* Interface IDeckLinkOutput - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkOutput_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedPixelFormat, /* in */ BMDVideoOutputConversionMode conversionMode, /* in */ BMDSupportedVideoModeFlags flags, /* out */ BMDDisplayMode* actualMode, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
virtual HRESULT SetScreenPreviewCallback (/* in */ IDeckLinkScreenPreviewCallback* previewCallback) = 0;
|
||||||
|
|
||||||
|
/* Video Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoOutput (/* in */ BMDDisplayMode displayMode, /* in */ BMDVideoOutputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoOutput (void) = 0;
|
||||||
|
virtual HRESULT CreateVideoFrame (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0;
|
||||||
|
virtual HRESULT CreateVideoFrameWithBuffer (/* in */ int32_t width, /* in */ int32_t height, /* in */ int32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDFrameFlags flags, /* in */ IDeckLinkVideoBuffer_v15_3_1* buffer, /* out */ IDeckLinkMutableVideoFrame** outFrame) = 0;
|
||||||
|
virtual HRESULT RowBytesForPixelFormat (/* in */ BMDPixelFormat pixelFormat, /* in */ int32_t width, /* out */ int32_t* rowBytes) = 0;
|
||||||
|
virtual HRESULT CreateAncillaryData (/* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoFrameAncillary** outBuffer) = 0; // Use of IDeckLinkVideoFrameAncillaryPackets is preferred
|
||||||
|
virtual HRESULT DisplayVideoFrameSync (/* in */ IDeckLinkVideoFrame* theFrame) = 0;
|
||||||
|
virtual HRESULT ScheduleVideoFrame (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeValue displayTime, /* in */ BMDTimeValue displayDuration, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT SetScheduledFrameCompletionCallback (/* in */ IDeckLinkVideoOutputCallback* theCallback) = 0;
|
||||||
|
virtual HRESULT GetBufferedVideoFrameCount (/* out */ uint32_t* bufferedFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Audio Output */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioOutput (/* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount, /* in */ BMDAudioOutputStreamType streamType) = 0;
|
||||||
|
virtual HRESULT DisableAudioOutput (void) = 0;
|
||||||
|
virtual HRESULT WriteAudioSamplesSync (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT BeginAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT EndAudioPreroll (void) = 0;
|
||||||
|
virtual HRESULT ScheduleAudioSamples (/* in */ void* buffer, /* in */ uint32_t sampleFrameCount, /* in */ BMDTimeValue streamTime, /* in */ BMDTimeScale timeScale, /* out */ uint32_t* sampleFramesWritten) = 0;
|
||||||
|
virtual HRESULT GetBufferedAudioSampleFrameCount (/* out */ uint32_t* bufferedSampleFrameCount) = 0;
|
||||||
|
virtual HRESULT FlushBufferedAudioSamples (void) = 0;
|
||||||
|
virtual HRESULT SetAudioCallback (/* in */ IDeckLinkAudioOutputCallback* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Output Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartScheduledPlayback (/* in */ BMDTimeValue playbackStartTime, /* in */ BMDTimeScale timeScale, /* in */ double playbackSpeed) = 0;
|
||||||
|
virtual HRESULT StopScheduledPlayback (/* in */ BMDTimeValue stopPlaybackAtTime, /* out */ BMDTimeValue* actualStopTime, /* in */ BMDTimeScale timeScale) = 0;
|
||||||
|
virtual HRESULT IsScheduledPlaybackRunning (/* out */ bool* active) = 0;
|
||||||
|
virtual HRESULT GetScheduledStreamTime (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* streamTime, /* out */ double* playbackSpeed) = 0;
|
||||||
|
virtual HRESULT GetReferenceStatus (/* out */ BMDReferenceStatus* referenceStatus) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
virtual HRESULT GetFrameCompletionReferenceTimestamp (/* in */ IDeckLinkVideoFrame* theFrame, /* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* frameCompletionTimestamp) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkOutput_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // defined(__cplusplus)
|
||||||
134
services/capture/sdk/DeckLinkAPI_v10_11.h
Normal file
134
services/capture/sdk/DeckLinkAPI_v10_11.h
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2018 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_11_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_11_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkAttributes_v10_11 = /* ABC11843-D966-44CB-96E2-A1CB5D3135C4 */ {0xAB,0xC1,0x18,0x43,0xD9,0x66,0x44,0xCB,0x96,0xE2,0xA1,0xCB,0x5D,0x31,0x35,0xC4};
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkNotification_v10_11 = /* 0A1FB207-E215-441B-9B19-6FA1575946C5 */ {0x0A,0x1F,0xB2,0x07,0xE2,0x15,0x44,0x1B,0x9B,0x19,0x6F,0xA1,0x57,0x59,0x46,0xC5};
|
||||||
|
|
||||||
|
/* Enum BMDDisplayModeSupport_v10_11 - Output mode supported flags */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDisplayModeSupport_v10_11;
|
||||||
|
enum _BMDDisplayModeSupport_v10_11 {
|
||||||
|
bmdDisplayModeNotSupported_v10_11 = 0,
|
||||||
|
bmdDisplayModeSupported_v10_11,
|
||||||
|
bmdDisplayModeSupportedWithConversion_v10_11
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDuplexMode_v10_11 - Duplex for configurable ports */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDuplexMode_v10_11;
|
||||||
|
enum _BMDDuplexMode_v10_11 {
|
||||||
|
bmdDuplexModeFull_v10_11 = /* 'fdup' */ 0x66647570,
|
||||||
|
bmdDuplexModeHalf_v10_11 = /* 'hdup' */ 0x68647570
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkAttributeID_v10_11 - DeckLink Attribute ID */
|
||||||
|
|
||||||
|
enum _BMDDeckLinkAttributeID_v10_11 {
|
||||||
|
|
||||||
|
/* Flags */
|
||||||
|
|
||||||
|
BMDDeckLinkSupportsDuplexModeConfiguration_v10_11 = 'dupx',
|
||||||
|
BMDDeckLinkSupportsHDKeying_v10_11 = 'keyh',
|
||||||
|
|
||||||
|
/* Integers */
|
||||||
|
|
||||||
|
BMDDeckLinkPairedDevicePersistentID_v10_11 = 'ppid',
|
||||||
|
BMDDeckLinkSupportsFullDuplex_v10_11 = 'fdup',
|
||||||
|
};
|
||||||
|
|
||||||
|
enum _BMDDeckLinkStatusID_v10_11 {
|
||||||
|
bmdDeckLinkStatusDuplexMode_v10_11 = 'dupx',
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint32_t BMDDuplexStatus_v10_11;
|
||||||
|
enum _BMDDuplexStatus_v10_11 {
|
||||||
|
bmdDuplexFullDuplex_v10_11 = 'fdup',
|
||||||
|
bmdDuplexHalfDuplex_v10_11 = 'hdup',
|
||||||
|
bmdDuplexSimplex_v10_11 = 'splx',
|
||||||
|
bmdDuplexInactive_v10_11 = 'inac',
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
/* Interface IDeckLinkAttributes_v10_11 - DeckLink Attribute interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkAttributes_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool *value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ const char **value) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkAttributes_v10_11 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkNotification_v10_11 - DeckLink Notification interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkNotification_v10_11 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0;
|
||||||
|
virtual HRESULT Unsubscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
BMD_PUBLIC IDeckLinkIterator* CreateDeckLinkIteratorInstance_v10_11 (void);
|
||||||
|
BMD_PUBLIC IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v10_11 (void);
|
||||||
|
BMD_PUBLIC IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v10_11 (void);
|
||||||
|
BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper_v10_11 (void);
|
||||||
|
BMD_PUBLIC IDeckLinkVideoConversion* CreateVideoConversionInstance_v10_11 (void);
|
||||||
|
BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v10_11 (void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(__cplusplus)
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_11_H) */
|
||||||
68
services/capture/sdk/DeckLinkAPI_v10_2.h
Normal file
68
services/capture/sdk/DeckLinkAPI_v10_2.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2014 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_2_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_2_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID_v10_2;
|
||||||
|
enum _BMDDeckLinkConfigurationID_v10_2 {
|
||||||
|
/* Video output flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfig3GBpsVideoOutput_v10_2 = '3gbs',
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDAudioConnection_v10_2 - Audio connection types */
|
||||||
|
|
||||||
|
typedef uint32_t BMDAudioConnection_v10_2;
|
||||||
|
enum _BMDAudioConnection_v10_2 {
|
||||||
|
bmdAudioConnectionEmbedded_v10_2 = /* 'embd' */ 0x656D6264,
|
||||||
|
bmdAudioConnectionAESEBU_v10_2 = /* 'aes ' */ 0x61657320,
|
||||||
|
bmdAudioConnectionAnalog_v10_2 = /* 'anlg' */ 0x616E6C67,
|
||||||
|
bmdAudioConnectionAnalogXLR_v10_2 = /* 'axlr' */ 0x61786C72,
|
||||||
|
bmdAudioConnectionAnalogRCA_v10_2 = /* 'arca' */ 0x61726361
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_2_H) */
|
||||||
58
services/capture/sdk/DeckLinkAPI_v10_4.h
Normal file
58
services/capture/sdk/DeckLinkAPI_v10_4.h
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2015 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_4_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_4_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkConfigurationID - DeckLink Configuration ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID_v10_4;
|
||||||
|
enum _BMDDeckLinkConfigurationID_v10_4 {
|
||||||
|
|
||||||
|
/* Video output flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfigSingleLinkVideoOutput_v10_4 = /* 'sglo' */ 0x73676C6F,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_4_H) */
|
||||||
59
services/capture/sdk/DeckLinkAPI_v10_5.h
Normal file
59
services/capture/sdk/DeckLinkAPI_v10_5.h
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2015 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_5_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_5_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkAttributeID_v10_5;
|
||||||
|
enum _BMDDeckLinkAttributeID_v10_5 {
|
||||||
|
|
||||||
|
/* Integers */
|
||||||
|
|
||||||
|
BMDDeckLinkDeviceBusyState_v10_5 = /* 'dbst' */ 0x64627374,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_5_H) */
|
||||||
|
|
||||||
63
services/capture/sdk/DeckLinkAPI_v10_6.h
Normal file
63
services/capture/sdk/DeckLinkAPI_v10_6.h
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2016 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_6_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_6_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkAttributeID_c10_6;
|
||||||
|
enum _BMDDeckLinkAttributeID_v10_6 {
|
||||||
|
|
||||||
|
/* Flags */
|
||||||
|
|
||||||
|
BMDDeckLinkSupportsDesktopDisplay_v10_6 = /* 'extd' */ 0x65787464,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint32_t BMDIdleVideoOutputOperation_v10_6;
|
||||||
|
enum _BMDIdleVideoOutputOperation_v10_6 {
|
||||||
|
bmdIdleVideoOutputDesktop_v10_6 = /* 'desk' */ 0x6465736B
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_6_H) */
|
||||||
58
services/capture/sdk/DeckLinkAPI_v10_9.h
Normal file
58
services/capture/sdk/DeckLinkAPI_v10_9.h
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2017 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v10_9_H
|
||||||
|
#define BMD_DECKLINKAPI_v10_9_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Type Declarations
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkConfigurationID_v10_9;
|
||||||
|
enum _BMDDeckLinkConfigurationID_v10_9 {
|
||||||
|
|
||||||
|
/* Flags */
|
||||||
|
|
||||||
|
bmdDeckLinkConfig1080pNotPsF_v10_9 = 'fpro',
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v10_9_H) */
|
||||||
113
services/capture/sdk/DeckLinkAPI_v11_5.h
Normal file
113
services/capture/sdk/DeckLinkAPI_v11_5.h
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2020 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v11_5_H
|
||||||
|
#define BMD_DECKLINKAPI_v11_5_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoFrameMetadataExtensions_v11_5 = /* D5973DC9-6432-46D0-8F0B-2496F8A1238F */ {0xD5,0x97,0x3D,0xC9,0x64,0x32,0x46,0xD0,0x8F,0x0B,0x24,0x96,0xF8,0xA1,0x23,0x8F};
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkFrameMetadataID - DeckLink Frame Metadata ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkFrameMetadataID_v11_5;
|
||||||
|
enum _BMDDeckLinkFrameMetadataID_v11_5 {
|
||||||
|
bmdDeckLinkFrameMetadataCintelFilmType_v11_5 = /* 'cfty' */ 0x63667479, // Current film type
|
||||||
|
bmdDeckLinkFrameMetadataCintelFilmGauge_v11_5 = /* 'cfga' */ 0x63666761, // Current film gauge
|
||||||
|
bmdDeckLinkFrameMetadataCintelKeykodeLow_v11_5 = /* 'ckkl' */ 0x636B6B6C, // Raw keykode value - low 64 bits
|
||||||
|
bmdDeckLinkFrameMetadataCintelKeykodeHigh_v11_5 = /* 'ckkh' */ 0x636B6B68, // Raw keykode value - high 64 bits
|
||||||
|
bmdDeckLinkFrameMetadataCintelTile1Size_v11_5 = /* 'ct1s' */ 0x63743173, // Size in bytes of compressed raw tile 1
|
||||||
|
bmdDeckLinkFrameMetadataCintelTile2Size_v11_5 = /* 'ct2s' */ 0x63743273, // Size in bytes of compressed raw tile 2
|
||||||
|
bmdDeckLinkFrameMetadataCintelTile3Size_v11_5 = /* 'ct3s' */ 0x63743373, // Size in bytes of compressed raw tile 3
|
||||||
|
bmdDeckLinkFrameMetadataCintelTile4Size_v11_5 = /* 'ct4s' */ 0x63743473, // Size in bytes of compressed raw tile 4
|
||||||
|
bmdDeckLinkFrameMetadataCintelImageWidth_v11_5 = /* 'IWPx' */ 0x49575078, // Width in pixels of image
|
||||||
|
bmdDeckLinkFrameMetadataCintelImageHeight_v11_5 = /* 'IHPx' */ 0x49485078, // Height in pixels of image
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingRedInRed_v11_5 = /* 'mrir' */ 0x6D726972, // Red in red linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInRed_v11_5 = /* 'mgir' */ 0x6D676972, // Green in red linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInRed_v11_5 = /* 'mbir' */ 0x6D626972, // Blue in red linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingRedInGreen_v11_5 = /* 'mrig' */ 0x6D726967, // Red in green linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInGreen_v11_5 = /* 'mgig' */ 0x6D676967, // Green in green linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInGreen_v11_5 = /* 'mbig' */ 0x6D626967, // Blue in green linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingRedInBlue_v11_5 = /* 'mrib' */ 0x6D726962, // Red in blue linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingGreenInBlue_v11_5 = /* 'mgib' */ 0x6D676962, // Green in blue linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLinearMaskingBlueInBlue_v11_5 = /* 'mbib' */ 0x6D626962, // Blue in blue linear masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingRedInRed_v11_5 = /* 'mlrr' */ 0x6D6C7272, // Red in red log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingGreenInRed_v11_5 = /* 'mlgr' */ 0x6D6C6772, // Green in red log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingBlueInRed_v11_5 = /* 'mlbr' */ 0x6D6C6272, // Blue in red log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingRedInGreen_v11_5 = /* 'mlrg' */ 0x6D6C7267, // Red in green log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingGreenInGreen_v11_5 = /* 'mlgg' */ 0x6D6C6767, // Green in green log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingBlueInGreen_v11_5 = /* 'mlbg' */ 0x6D6C6267, // Blue in green log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingRedInBlue_v11_5 = /* 'mlrb' */ 0x6D6C7262, // Red in blue log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingGreenInBlue_v11_5 = /* 'mlgb' */ 0x6D6C6762, // Green in blue log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelLogMaskingBlueInBlue_v11_5 = /* 'mlbb' */ 0x6D6C6262, // Blue in blue log masking parameter
|
||||||
|
bmdDeckLinkFrameMetadataCintelFilmFrameRate_v11_5 = /* 'cffr' */ 0x63666672, // Film frame rate
|
||||||
|
bmdDeckLinkFrameMetadataCintelOffsetToApplyHorizontal_v11_5 = /* 'otah' */ 0x6F746168, // Horizontal offset (pixels) to be applied to image
|
||||||
|
bmdDeckLinkFrameMetadataCintelOffsetToApplyVertical_v11_5 = /* 'otav' */ 0x6F746176, // Vertical offset (pixels) to be applied to image
|
||||||
|
bmdDeckLinkFrameMetadataCintelGainRed_v11_5 = /* 'LfRd' */ 0x4C665264, // Red gain parameter to apply after log
|
||||||
|
bmdDeckLinkFrameMetadataCintelGainGreen_v11_5 = /* 'LfGr' */ 0x4C664772, // Green gain parameter to apply after log
|
||||||
|
bmdDeckLinkFrameMetadataCintelGainBlue_v11_5 = /* 'LfBl' */ 0x4C66426C, // Blue gain parameter to apply after log
|
||||||
|
bmdDeckLinkFrameMetadataCintelLiftRed_v11_5 = /* 'GnRd' */ 0x476E5264, // Red lift parameter to apply after log and gain
|
||||||
|
bmdDeckLinkFrameMetadataCintelLiftGreen_v11_5 = /* 'GnGr' */ 0x476E4772, // Green lift parameter to apply after log and gain
|
||||||
|
bmdDeckLinkFrameMetadataCintelLiftBlue_v11_5 = /* 'GnBl' */ 0x476E426C, // Blue lift parameter to apply after log and gain
|
||||||
|
bmdDeckLinkFrameMetadataCintelHDRGainRed_v11_5 = /* 'HGRd' */ 0x48475264, // Red gain parameter to apply to linear data for HDR Combination
|
||||||
|
bmdDeckLinkFrameMetadataCintelHDRGainGreen_v11_5 = /* 'HGGr' */ 0x48474772, // Green gain parameter to apply to linear data for HDR Combination
|
||||||
|
bmdDeckLinkFrameMetadataCintelHDRGainBlue_v11_5 = /* 'HGBl' */ 0x4847426C, // Blue gain parameter to apply to linear data for HDR Combination
|
||||||
|
bmdDeckLinkFrameMetadataCintel16mmCropRequired_v11_5 = /* 'c16c' */ 0x63313663, // The image should be cropped to 16mm size
|
||||||
|
bmdDeckLinkFrameMetadataCintelInversionRequired_v11_5 = /* 'cinv' */ 0x63696E76, // The image should be colour inverted
|
||||||
|
bmdDeckLinkFrameMetadataCintelFlipRequired_v11_5 = /* 'cflr' */ 0x63666C72, // The image should be flipped horizontally
|
||||||
|
bmdDeckLinkFrameMetadataCintelFocusAssistEnabled_v11_5 = /* 'cfae' */ 0x63666165, // Focus Assist is currently enabled
|
||||||
|
bmdDeckLinkFrameMetadataCintelKeykodeIsInterpolated_v11_5 = /* 'kkii' */ 0x6B6B6969 // The keykode for this frame is interpolated from nearby keykodes
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoFrameMetadataExtensions - Optional interface implemented on IDeckLinkVideoFrame to support frame metadata such as HDMI HDR information */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoFrameMetadataExtensions_v11_5 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* out */ int64_t *value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* out */ double *value) = 0;
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkFrameMetadataID_v11_5 metadataID, /* out */ const char **value) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoFrameMetadataExtensions_v11_5 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v11_5_H) */
|
||||||
57
services/capture/sdk/DeckLinkAPI_v11_5_1.h
Normal file
57
services/capture/sdk/DeckLinkAPI_v11_5_1.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2020 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v11_5_1_H
|
||||||
|
#define BMD_DECKLINKAPI_v11_5_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkStatusID - DeckLink Status ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkStatusID_v11_5_1;
|
||||||
|
enum _BMDDeckLinkStatusID_v11_5_1 {
|
||||||
|
|
||||||
|
/* Video output flags */
|
||||||
|
|
||||||
|
bmdDeckLinkStatusDetectedVideoInputFlags_v11_5_1 = /* 'dvif' */ 0x64766966,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v11_5_1_H) */
|
||||||
110
services/capture/sdk/DeckLinkAPI_v14_2_1.h
Normal file
110
services/capture/sdk/DeckLinkAPI_v14_2_1.h
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2018 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v14_2_1_H
|
||||||
|
#define BMD_DECKLINKAPI_v14_2_1_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
#include "DeckLinkAPIVideoConversion_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIGLScreenPreview_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIMetalScreenPreview_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIScreenPreviewCallback_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoOutput_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoInput_v14_2_1.h"
|
||||||
|
#include "DeckLinkAPIVideoFrame3DExtensions_v14_2_1.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkEncoderInput_v14_2_1 = /* F222551D-13DF-4FD8-B587-9D4F19EC12C9 */ { 0xF2,0x22,0x55,0x1D,0x13,0xDF,0x4F,0xD8,0xB5,0x87,0x9D,0x4F,0x19,0xEC,0x12,0xC9 };
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
/* Interface IDeckLinkEncoderInput - Created by QueryInterface from IDeckLink. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkEncoderInput_v14_2_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT DoesSupportVideoMode (/* in */ BMDVideoConnection connection /* If a value of bmdVideoConnectionUnspecified is specified, the caller does not care about the connection */, /* in */ BMDDisplayMode requestedMode, /* in */ BMDPixelFormat requestedCodec, /* in */ uint32_t requestedCodecProfile, /* in */ BMDSupportedVideoModeFlags flags, /* out */ bool* supported) = 0;
|
||||||
|
virtual HRESULT GetDisplayMode (/* in */ BMDDisplayMode displayMode, /* out */ IDeckLinkDisplayMode** resultDisplayMode) = 0;
|
||||||
|
virtual HRESULT GetDisplayModeIterator (/* out */ IDeckLinkDisplayModeIterator** iterator) = 0;
|
||||||
|
|
||||||
|
/* Video Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableVideoInput (/* in */ BMDDisplayMode displayMode, /* in */ BMDPixelFormat pixelFormat, /* in */ BMDVideoInputFlags flags) = 0;
|
||||||
|
virtual HRESULT DisableVideoInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailablePacketsCount (/* out */ uint32_t* availablePacketsCount) = 0;
|
||||||
|
virtual HRESULT SetMemoryAllocator (/* in */ IDeckLinkMemoryAllocator_v14_2_1* theAllocator) = 0;
|
||||||
|
|
||||||
|
/* Audio Input */
|
||||||
|
|
||||||
|
virtual HRESULT EnableAudioInput (/* in */ BMDAudioFormat audioFormat, /* in */ BMDAudioSampleRate sampleRate, /* in */ BMDAudioSampleType sampleType, /* in */ uint32_t channelCount) = 0;
|
||||||
|
virtual HRESULT DisableAudioInput (void) = 0;
|
||||||
|
virtual HRESULT GetAvailableAudioSampleFrameCount (/* out */ uint32_t* availableSampleFrameCount) = 0;
|
||||||
|
|
||||||
|
/* Input Control */
|
||||||
|
|
||||||
|
virtual HRESULT StartStreams (void) = 0;
|
||||||
|
virtual HRESULT StopStreams (void) = 0;
|
||||||
|
virtual HRESULT PauseStreams (void) = 0;
|
||||||
|
virtual HRESULT FlushStreams (void) = 0;
|
||||||
|
virtual HRESULT SetCallback (/* in */ IDeckLinkEncoderInputCallback* theCallback) = 0;
|
||||||
|
|
||||||
|
/* Hardware Timing */
|
||||||
|
|
||||||
|
virtual HRESULT GetHardwareReferenceClock (/* in */ BMDTimeScale desiredTimeScale, /* out */ BMDTimeValue* hardwareTime, /* out */ BMDTimeValue* timeInFrame, /* out */ BMDTimeValue* ticksPerFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkEncoderInput_v14_2_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
BMD_PUBLIC IDeckLinkIterator* CreateDeckLinkIteratorInstance_v14_2_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v14_2_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v14_2_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkGLScreenPreviewHelper_v14_2_1* CreateOpenGLScreenPreviewHelper_v14_2_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkGLScreenPreviewHelper_v14_2_1* CreateOpenGL3ScreenPreviewHelper_v14_2_1(void); // Requires OpenGL 3.2 support and provides improved performance and color handling
|
||||||
|
BMD_PUBLIC IDeckLinkVideoConversion_v14_2_1* CreateVideoConversionInstance_v14_2_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v14_2_1(void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(__cplusplus)
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_v14_2_1_H) */
|
||||||
96
services/capture/sdk/DeckLinkAPI_v15_2.h
Normal file
96
services/capture/sdk/DeckLinkAPI_v15_2.h
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2025 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BMD_DECKLINKAPI_v15_2_H
|
||||||
|
#define BMD_DECKLINKAPI_v15_2_H
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkAncillaryPacket_v15_2 = /* CC5BBF7E-029C-4D3B-9158-6000EF5E3670 */ { 0xCC,0x5B,0xBF,0x7E,0x02,0x9C,0x4D,0x3B,0x91,0x58,0x60,0x00,0xEF,0x5E,0x36,0x70 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkAncillaryPacketIterator_v15_2 = /* 3FC8994B-88FB-4C17-968F-9AAB69D964A7 */ { 0x3F,0xC8,0x99,0x4B,0x88,0xFB,0x4C,0x17,0x96,0x8F,0x9A,0xAB,0x69,0xD9,0x64,0xA7 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoFrameAncillaryPackets_v15_2 = /* 6C186C0F-459E-41D8-AEE2-4812D81AEE68 */ { 0x6C,0x18,0x6C,0x0F,0x45,0x9E,0x41,0xD8,0xAE,0xE2,0x48,0x12,0xD8,0x1A,0xEE,0x68 };
|
||||||
|
|
||||||
|
/* Interface IDeckLinkAncillaryPacket - On output, user needs to implement this interface */
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkAncillaryPacket_v15_2 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetBytes (/* in */ BMDAncillaryPacketFormat format /* For output, only one format need be offered */, /* out */ const void** data /* Optional */, /* out */ uint32_t* size /* Optional */) = 0;
|
||||||
|
virtual uint8_t GetDID (void) = 0;
|
||||||
|
virtual uint8_t GetSDID (void) = 0;
|
||||||
|
virtual uint32_t GetLineNumber (void) = 0; // On output, zero is auto
|
||||||
|
virtual uint8_t GetDataStreamIndex (void) = 0; // Usually zero. Can only be 1 if non-SD and the first data stream is completely full
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkAncillaryPacket_v15_2 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkAncillaryPacketIterator - Enumerates ancillary packets */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkAncillaryPacketIterator_v15_2 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Next (/* out */ IDeckLinkAncillaryPacket_v15_2** packet) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkAncillaryPacketIterator_v15_2 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoFrameAncillaryPackets - Obtained through QueryInterface on an IDeckLinkVideoFrame object. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets_v15_2 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetPacketIterator (/* out */ IDeckLinkAncillaryPacketIterator_v15_2** iterator) = 0;
|
||||||
|
virtual HRESULT GetFirstPacketByID (/* in */ uint8_t DID, /* in */ uint8_t SDID, /* out */ IDeckLinkAncillaryPacket_v15_2** packet) = 0;
|
||||||
|
virtual HRESULT AttachPacket (/* in */ IDeckLinkAncillaryPacket_v15_2* packet) = 0; // Implement IDeckLinkAncillaryPacket to output your own
|
||||||
|
virtual HRESULT DetachPacket (/* in */ IDeckLinkAncillaryPacket_v15_2* packet) = 0;
|
||||||
|
virtual HRESULT DetachAllPackets (void) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoFrameAncillaryPackets_v15_2 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__cplusplus) */
|
||||||
|
#endif /* defined(BMD_DECKLINKAPI_H) */
|
||||||
189
services/capture/sdk/DeckLinkAPI_v15_3_1.h
Normal file
189
services/capture/sdk/DeckLinkAPI_v15_3_1.h
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2025 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit (“EULA”) available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DeckLinkAPI.h"
|
||||||
|
|
||||||
|
// Interface ID Declarations
|
||||||
|
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkStatus_v15_3_1 = /* 5F558200-4028-49BC-BEAC-DB3FA4A96E46 */ { 0x5F,0x55,0x82,0x00,0x40,0x28,0x49,0xBC,0xBE,0xAC,0xDB,0x3F,0xA4,0xA9,0x6E,0x46 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoBuffer_v15_3_1 = /* CCB4B64A-5C86-4E02-B778-885D352709FE */ { 0xCC,0xB4,0xB6,0x4A,0x5C,0x86,0x4E,0x02,0xB7,0x78,0x88,0x5D,0x35,0x27,0x09,0xFE };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocator_v15_3_1 = /* 3481A4DF-2B11-4E55-AC61-836B87985E9A */ { 0x34,0x81,0xA4,0xDF,0x2B,0x11,0x4E,0x55,0xAC,0x61,0x83,0x6B,0x87,0x98,0x5E,0x9A };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoBufferAllocatorProvider_v15_3_1 = /* 08B80403-BFF2-49D0-B448-8C908B9E9FC9 */ { 0x08,0xB8,0x04,0x03,0xBF,0xF2,0x49,0xD0,0xB4,0x48,0x8C,0x90,0x8B,0x9E,0x9F,0xC9 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkVideoConversion_v15_3_1 = /* A48755D9-8BD5-4727-A1E9-069FDEDBA6E9 */ { 0xA4,0x87,0x55,0xD9,0x8B,0xD5,0x47,0x27,0xA1,0xE9,0x06,0x9F,0xDE,0xDB,0xA6,0xE9 };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkProfileAttributes_v15_3_1 = /* 17D4BF8E-4911-473A-80A0-731CF6FF345B */ { 0x17,0xD4,0xBF,0x8E,0x49,0x11,0x47,0x3A,0x80,0xA0,0x73,0x1C,0xF6,0xFF,0x34,0x5B };
|
||||||
|
BMD_CONST REFIID IID_IDeckLinkNotification_v15_3_1 = /* B85DF4C8-BDF5-47C1-8064-28162EBDD4EB */ { 0xB8,0x5D,0xF4,0xC8,0xBD,0xF5,0x47,0xC1,0x80,0x64,0x28,0x16,0x2E,0xBD,0xD4,0xEB };
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkStatusID_v15_3_1 - DeckLink Status ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkStatusID_v15_3_1;
|
||||||
|
enum _BMDDeckLinkStatusID_v15_3_1
|
||||||
|
{
|
||||||
|
/* Integers */
|
||||||
|
bmdDeckLinkStatusDeviceTemperature_v15_3_1 = /* 'dtmp' */ 0x64746D70,
|
||||||
|
|
||||||
|
bmdDeckLinkStatusEthernetLink_v15_3_1 = /* 'sels' */ 0x73656C73,
|
||||||
|
bmdDeckLinkStatusEthernetLinkMbps_v15_3_1 = /* 'sesp' */ 0x73657370,
|
||||||
|
|
||||||
|
/* Strings */
|
||||||
|
bmdDeckLinkStatusEthernetLocalIPAddress_v15_3_1 = /* 'seip' */ 0x73656970,
|
||||||
|
bmdDeckLinkStatusEthernetSubnetMask_v15_3_1 = /* 'sesm' */ 0x7365736D,
|
||||||
|
bmdDeckLinkStatusEthernetGatewayIPAddress_v15_3_1 = /* 'segw' */ 0x73656777,
|
||||||
|
bmdDeckLinkStatusEthernetPrimaryDNS_v15_3_1 = /* 'sepd' */ 0x73657064,
|
||||||
|
bmdDeckLinkStatusEthernetSecondaryDNS_v15_3_1 = /* 'sesd' */ 0x73657364,
|
||||||
|
bmdDeckLinkStatusEthernetVideoOutputAddress_v15_3_1 = /* 'soav' */ 0x736F6176,
|
||||||
|
bmdDeckLinkStatusEthernetAudioOutputAddress_v15_3_1 = /* 'soaa' */ 0x736F6161,
|
||||||
|
bmdDeckLinkStatusEthernetAncillaryOutputAddress_v15_3_1 = /* 'soaA' */ 0x736F6141,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enum BMDDeckLinkAttributeID - DeckLink Attribute ID */
|
||||||
|
|
||||||
|
typedef uint32_t BMDDeckLinkAttributeID_v15_3_1;
|
||||||
|
enum _BMDDeckLinkAttributeID_v15_3_1
|
||||||
|
{
|
||||||
|
/* Strings */
|
||||||
|
BMDDeckLinkEthernetMACAddress_v15_3_1 = /* 'eMAC' */ 0x654D4143,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkStatus_v15_3_1 - DeckLink Status interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkStatus_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkStatusID statusID, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkStatusID statusID, /* out */ int64_t* value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkStatusID statusID, /* out */ double* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkStatusID statusID, /* out */ const char** value) = 0;
|
||||||
|
virtual HRESULT GetBytes (/* in */ BMDDeckLinkStatusID statusID, /* out */ void* buffer, /* in, out */ uint32_t* bufferSize) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkStatus_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoBuffer_v15_3_1 - Interface to encapsulate a video frame buffer; can be caller-implemented. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoBuffer_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetBytes (/* out */ void** buffer) = 0;
|
||||||
|
virtual HRESULT StartAccess (/* in */ BMDBufferAccessFlags flags) = 0;
|
||||||
|
virtual HRESULT EndAccess (/* in */ BMDBufferAccessFlags flags) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoBuffer_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoBufferAllocator_v15_3_1 - Buffer allocator for video. */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoBufferAllocator_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT AllocateVideoBuffer (/* out */ IDeckLinkVideoBuffer_v15_3_1** allocatedBuffer) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoBufferAllocator_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoBufferAllocatorProvider_v15_3_1 - Allows EnableVideoInputWithAllocatorProvider to obtain allocators */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoBufferAllocatorProvider_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetVideoBufferAllocator (/* in */ uint32_t bufferSize, /* in */ uint32_t width, /* in */ uint32_t height, /* in */ uint32_t rowBytes, /* in */ BMDPixelFormat pixelFormat, /* out */ IDeckLinkVideoBufferAllocator_v15_3_1** allocator) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoBufferAllocatorProvider_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkVideoConversion_v15_3_1 */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkVideoConversion_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT ConvertFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ IDeckLinkVideoFrame* dstFrame) = 0;
|
||||||
|
virtual HRESULT ConvertNewFrame (/* in */ IDeckLinkVideoFrame* srcFrame, /* in */ BMDPixelFormat dstPixelFormat, /* in */ BMDColorspace dstColorspace, /* in */ IDeckLinkVideoBuffer_v15_3_1* dstBuffer, /* out */ IDeckLinkVideoFrame** dstFrame) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkVideoConversion_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkProfileAttributes_v15_3_1 - Created by QueryInterface from an IDeckLinkProfile, or from IDeckLink. When queried from IDeckLink, interrogates the active profile */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkProfileAttributes_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT GetFlag (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ bool* value) = 0;
|
||||||
|
virtual HRESULT GetInt (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ int64_t* value) = 0;
|
||||||
|
virtual HRESULT GetFloat (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ double* value) = 0;
|
||||||
|
virtual HRESULT GetString (/* in */ BMDDeckLinkAttributeID cfgID, /* out */ const char** value) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkProfileAttributes_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interface IDeckLinkNotification_v15_3_1 - DeckLink Notification interface */
|
||||||
|
|
||||||
|
class BMD_PUBLIC IDeckLinkNotification_v15_3_1 : public IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT Subscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0;
|
||||||
|
virtual HRESULT Unsubscribe (/* in */ BMDNotifications topic, /* in */ IDeckLinkNotificationCallback *theCallback) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IDeckLinkNotification_v15_3_1 () {} // call Release method to drop reference count
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Functions */
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
BMD_PUBLIC IDeckLinkIterator* CreateDeckLinkIteratorInstance_v15_3_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkDiscovery* CreateDeckLinkDiscoveryInstance_v15_3_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkAPIInformation* CreateDeckLinkAPIInformationInstance_v15_3_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGLScreenPreviewHelper_v15_3_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkGLScreenPreviewHelper* CreateOpenGL3ScreenPreviewHelper_v15_3_1(void); // Requires OpenGL 3.2 support and provides improved performance and color handling
|
||||||
|
BMD_PUBLIC IDeckLinkVideoConversion_v15_3_1* CreateVideoConversionInstance_v15_3_1(void);
|
||||||
|
BMD_PUBLIC IDeckLinkVideoFrameAncillaryPackets* CreateVideoFrameAncillaryPacketsInstance_v15_3_1(void); // For use when creating a custom IDeckLinkVideoFrame without wrapping IDeckLinkOutput::CreateVideoFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(__cplusplus)
|
||||||
116
services/capture/sdk/LinuxCOM.h
Normal file
116
services/capture/sdk/LinuxCOM.h
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* -LICENSE-START-
|
||||||
|
** Copyright (c) 2009 Blackmagic Design
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
** obtaining a copy of the software and accompanying documentation (the
|
||||||
|
** "Software") to use, reproduce, display, distribute, sub-license, execute,
|
||||||
|
** and transmit the Software, and to prepare derivative works of the Software,
|
||||||
|
** and to permit third-parties to whom the Software is furnished to do so, in
|
||||||
|
** accordance with:
|
||||||
|
**
|
||||||
|
** (1) if the Software is obtained from Blackmagic Design, the End User License
|
||||||
|
** Agreement for the Software Development Kit ("EULA") available at
|
||||||
|
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
|
||||||
|
**
|
||||||
|
** (2) if the Software is obtained from any third party, such licensing terms
|
||||||
|
** as notified by that third party,
|
||||||
|
**
|
||||||
|
** and all subject to the following:
|
||||||
|
**
|
||||||
|
** (3) the copyright notices in the Software and this entire statement,
|
||||||
|
** including the above license grant, this restriction and the following
|
||||||
|
** disclaimer, must be included in all copies of the Software, in whole or in
|
||||||
|
** part, and all derivative works of the Software, unless such copies or
|
||||||
|
** derivative works are solely in the form of machine-executable object code
|
||||||
|
** generated by a source language processor.
|
||||||
|
**
|
||||||
|
** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
** DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
** A copy of the Software is available free of charge at
|
||||||
|
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
|
||||||
|
**
|
||||||
|
** -LICENSE-END-
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LINUX_COM_H_
|
||||||
|
#define __LINUX_COM_H_
|
||||||
|
|
||||||
|
struct REFIID
|
||||||
|
{
|
||||||
|
unsigned char byte0;
|
||||||
|
unsigned char byte1;
|
||||||
|
unsigned char byte2;
|
||||||
|
unsigned char byte3;
|
||||||
|
unsigned char byte4;
|
||||||
|
unsigned char byte5;
|
||||||
|
unsigned char byte6;
|
||||||
|
unsigned char byte7;
|
||||||
|
unsigned char byte8;
|
||||||
|
unsigned char byte9;
|
||||||
|
unsigned char byte10;
|
||||||
|
unsigned char byte11;
|
||||||
|
unsigned char byte12;
|
||||||
|
unsigned char byte13;
|
||||||
|
unsigned char byte14;
|
||||||
|
unsigned char byte15;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef REFIID CFUUIDBytes;
|
||||||
|
#define CFUUIDGetUUIDBytes(x) x
|
||||||
|
|
||||||
|
typedef int HRESULT;
|
||||||
|
typedef unsigned long ULONG;
|
||||||
|
typedef void *LPVOID;
|
||||||
|
|
||||||
|
#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)
|
||||||
|
#define FAILED(Status) ((HRESULT)(Status)<0)
|
||||||
|
|
||||||
|
#define IS_ERROR(Status) ((unsigned long)(Status) >> 31 == SEVERITY_ERROR)
|
||||||
|
#define HRESULT_CODE(hr) ((hr) & 0xFFFF)
|
||||||
|
#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff)
|
||||||
|
#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1)
|
||||||
|
#define SEVERITY_SUCCESS 0
|
||||||
|
#define SEVERITY_ERROR 1
|
||||||
|
|
||||||
|
#define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
|
||||||
|
|
||||||
|
#define S_OK ((HRESULT)0x00000000L)
|
||||||
|
#define S_FALSE ((HRESULT)0x00000001L)
|
||||||
|
#define E_UNEXPECTED ((HRESULT)0x8000FFFFL)
|
||||||
|
#define E_NOTIMPL ((HRESULT)0x80000001L)
|
||||||
|
#define E_OUTOFMEMORY ((HRESULT)0x80000002L)
|
||||||
|
#define E_INVALIDARG ((HRESULT)0x80000003L)
|
||||||
|
#define E_NOINTERFACE ((HRESULT)0x80000004L)
|
||||||
|
#define E_POINTER ((HRESULT)0x80000005L)
|
||||||
|
#define E_HANDLE ((HRESULT)0x80000006L)
|
||||||
|
#define E_ABORT ((HRESULT)0x80000007L)
|
||||||
|
#define E_FAIL ((HRESULT)0x80000008L)
|
||||||
|
#define E_ACCESSDENIED ((HRESULT)0x80000009L)
|
||||||
|
|
||||||
|
#define STDMETHODCALLTYPE
|
||||||
|
|
||||||
|
#define IID_IUnknown (REFIID){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}
|
||||||
|
#define IUnknownUUID IID_IUnknown
|
||||||
|
|
||||||
|
#ifndef BMD_PUBLIC
|
||||||
|
#define BMD_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
class BMD_PUBLIC IUnknown
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0;
|
||||||
|
virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
|
||||||
|
virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -23,6 +23,12 @@ app.use('/capture', captureRoutes);
|
||||||
const server = app.listen(PORT, () => {
|
const server = app.listen(PORT, () => {
|
||||||
console.log(`Wild Dragon Capture Service listening on port ${PORT}`);
|
console.log(`Wild Dragon Capture Service listening on port ${PORT}`);
|
||||||
bootstrapAutoStart();
|
bootstrapAutoStart();
|
||||||
|
// Auto-start idle signal preview for deltacast/sdi sidecars.
|
||||||
|
// 3s delay lets the deltacast bridge FIFOs come up first.
|
||||||
|
const _srcType = process.env.SOURCE_TYPE;
|
||||||
|
if (process.env.RECORDER_ID && (_srcType === 'deltacast' || _srcType === 'sdi')) {
|
||||||
|
setTimeout(() => captureManager.startIdlePreview(), 3000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mapped from the env vars routes/recorders.js writes into the container.
|
// Mapped from the env vars routes/recorders.js writes into the container.
|
||||||
|
|
@ -63,6 +69,13 @@ async function bootstrapAutoStart() {
|
||||||
const streamKey = envOpt('STREAM_KEY');
|
const streamKey = envOpt('STREAM_KEY');
|
||||||
const sourceUrl = envOpt('SOURCE_URL');
|
const sourceUrl = envOpt('SOURCE_URL');
|
||||||
const device = envInt('DEVICE_INDEX');
|
const device = envInt('DEVICE_INDEX');
|
||||||
|
// SOURCE_CONFIG is the recorder's source_config JSON (set by recorders.js).
|
||||||
|
// For deltacast it carries the capture channel (`port`) and optional `board`.
|
||||||
|
let sourceConfig = {};
|
||||||
|
try { sourceConfig = JSON.parse(process.env.SOURCE_CONFIG || '{}') || {}; }
|
||||||
|
catch (e) { console.warn('[bootstrap] bad SOURCE_CONFIG JSON:', e.message); }
|
||||||
|
const port = Number.isInteger(sourceConfig.port) ? sourceConfig.port : undefined;
|
||||||
|
const board = Number.isInteger(sourceConfig.board) ? sourceConfig.board : undefined;
|
||||||
|
|
||||||
console.log(`[bootstrap] starting ${sourceType} ingest (listen=${listen} port=${listenPort || 'n/a'})...`);
|
console.log(`[bootstrap] starting ${sourceType} ingest (listen=${listen} port=${listenPort || 'n/a'})...`);
|
||||||
try {
|
try {
|
||||||
|
|
@ -72,6 +85,8 @@ async function bootstrapAutoStart() {
|
||||||
binId: envOpt('BIN_ID') || null,
|
binId: envOpt('BIN_ID') || null,
|
||||||
clipName,
|
clipName,
|
||||||
device,
|
device,
|
||||||
|
port,
|
||||||
|
board,
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceUrl,
|
sourceUrl,
|
||||||
listen,
|
listen,
|
||||||
|
|
@ -135,6 +150,25 @@ async function gracefulShutdown(signal) {
|
||||||
console.error('[shutdown] failed to flag empty asset:', e.message);
|
console.error('[shutdown] failed to flag empty asset:', e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (completed.growingPath) {
|
||||||
|
// Growing-files recorder: the master lives on the SMB share. Mark the asset
|
||||||
|
// as pending_migration so the UI shows it is on SMB and provides a manual
|
||||||
|
// right-click option to promote it to S3.
|
||||||
|
console.log(`[shutdown] growing capture finalized on share (${completed.growingPath}); flagging pending_migration`);
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${MAM_API_URL}/api/v1/assets/${liveAssetId}/pending-migration`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', ...(MAM_API_TOKEN ? { 'Authorization': `Bearer ${MAM_API_TOKEN}` } : {}) },
|
||||||
|
body: JSON.stringify({ duration: completed.duration }),
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
console.warn(`[shutdown] mam-api pending-migration returned ${res.status}: ${await res.text()}`);
|
||||||
|
} else {
|
||||||
|
console.log('[shutdown] live asset flagged pending_migration with mam-api');
|
||||||
|
}
|
||||||
|
} catch (mamErr) {
|
||||||
|
console.error('[shutdown] failed to flag pending_migration:', mamErr.message);
|
||||||
|
}
|
||||||
} else if (liveAssetId) {
|
} else if (liveAssetId) {
|
||||||
// Finalise the pre-created live asset by id (avoids POST / 409 collision).
|
// Finalise the pre-created live asset by id (avoids POST / 409 collision).
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ function classifyProbeError(raw, sourceType) {
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
const MAM_API_URL = process.env.MAM_API_URL || 'http://mam-api:3000';
|
const MAM_API_URL = process.env.MAM_API_URL || 'http://mam-api:3000';
|
||||||
|
const MAM_API_TOKEN = process.env.MAM_API_TOKEN || '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /devices
|
* GET /devices
|
||||||
|
|
@ -325,12 +326,43 @@ router.post('/start', async (req, res) => {
|
||||||
error: `${source_type.toUpperCase()} caller mode requires: source_url`,
|
error: `${source_type.toUpperCase()} caller mode requires: source_url`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (source_type === 'deltacast') {
|
||||||
|
if (device === undefined || device === null) {
|
||||||
|
return res.status(400).json({ error: 'deltacast source requires: device (board/port index)' });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: `Unknown source_type: ${source_type}. Must be sdi, srt, or rtmp`,
|
error: `Unknown source_type: ${source_type}. Must be sdi, srt, rtmp, or deltacast`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create live asset in MAM API before starting capture
|
||||||
|
let assetId;
|
||||||
|
try {
|
||||||
|
const mamResponse = await fetch(`${MAM_API_URL}/api/v1/assets`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', ...(MAM_API_TOKEN ? { Authorization: `Bearer ${MAM_API_TOKEN}` } : { 'X-Requested-With': 'dragonflight-ui' }) },
|
||||||
|
body: JSON.stringify({
|
||||||
|
projectId: project_id,
|
||||||
|
binId: bin_id,
|
||||||
|
clipName: clip_name,
|
||||||
|
sourceType: source_type,
|
||||||
|
status: 'live',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mamResponse.ok) {
|
||||||
|
const errText = await mamResponse.text();
|
||||||
|
throw new Error(`MAM API returned ${mamResponse.status}: ${errText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset = await mamResponse.json();
|
||||||
|
assetId = asset.id;
|
||||||
|
} catch (mamError) {
|
||||||
|
console.error('Failed to create live asset:', mamError.message);
|
||||||
|
return res.status(500).json({ error: `Could not create live asset: ${mamError.message}` });
|
||||||
|
}
|
||||||
|
|
||||||
const session = await captureManager.start({
|
const session = await captureManager.start({
|
||||||
projectId: project_id,
|
projectId: project_id,
|
||||||
binId: bin_id || null,
|
binId: bin_id || null,
|
||||||
|
|
@ -341,6 +373,7 @@ router.post('/start', async (req, res) => {
|
||||||
listen,
|
listen,
|
||||||
listenPort: listen_port,
|
listenPort: listen_port,
|
||||||
streamKey: stream_key,
|
streamKey: stream_key,
|
||||||
|
assetId,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json(session);
|
res.json(session);
|
||||||
|
|
@ -365,33 +398,34 @@ router.post('/stop', async (req, res) => {
|
||||||
|
|
||||||
const completedSession = await captureManager.stop(session_id);
|
const completedSession = await captureManager.stop(session_id);
|
||||||
|
|
||||||
// Register asset with mam-api.
|
// Finalize the pre-created live asset.
|
||||||
// If proxyKey is null (SRT/RTMP source), set needsProxy=true so the
|
// If it was a growing-file session, we call /pending-migration to flip status
|
||||||
// worker generates a proxy from the hires file asynchronously.
|
// to 'pending_migration' (on SMB, not S3). Otherwise, we call /finalize to
|
||||||
|
// kick off the proxy/thumbnail job chain.
|
||||||
|
if (completedSession.assetId) {
|
||||||
try {
|
try {
|
||||||
const mamResponse = await fetch(`${MAM_API_URL}/api/v1/assets`, {
|
const path = completedSession.growingPath ? 'pending-migration' : 'finalize';
|
||||||
method: 'POST',
|
const body = completedSession.growingPath
|
||||||
headers: { 'Content-Type': 'application/json' },
|
? { duration: completedSession.duration }
|
||||||
body: JSON.stringify({
|
: {
|
||||||
projectId: completedSession.projectId,
|
|
||||||
binId: completedSession.binId,
|
|
||||||
clipName: completedSession.clipName,
|
|
||||||
sourceType: completedSession.sourceType,
|
|
||||||
hiresKey: completedSession.hiresKey,
|
hiresKey: completedSession.hiresKey,
|
||||||
proxyKey: completedSession.proxyKey,
|
proxyKey: completedSession.proxyKey,
|
||||||
needsProxy: completedSession.proxyKey === null,
|
needsProxy: completedSession.proxyKey === null,
|
||||||
duration: completedSession.duration,
|
duration: completedSession.duration,
|
||||||
capturedAt: completedSession.startedAt,
|
capturedAt: completedSession.startedAt,
|
||||||
}),
|
};
|
||||||
});
|
|
||||||
|
|
||||||
|
const mamResponse = await fetch(`${MAM_API_URL}/api/v1/assets/${completedSession.assetId}/${path}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
});
|
||||||
if (!mamResponse.ok) {
|
if (!mamResponse.ok) {
|
||||||
console.warn(
|
console.warn(`MAM API ${path} returned ${mamResponse.status}: ${await mamResponse.text()}`);
|
||||||
`MAM API registration returned ${mamResponse.status}: ${await mamResponse.text()}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (mamError) {
|
} catch (mamError) {
|
||||||
console.warn('Failed to register asset with MAM API:', mamError.message);
|
console.warn('Failed to finalize/pending-migrate asset with MAM API:', mamError.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(completedSession);
|
res.json(completedSession);
|
||||||
|
|
|
||||||
63
services/framecache/CMakeLists.txt
Normal file
63
services/framecache/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(framecache C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -O2")
|
||||||
|
|
||||||
|
# ── libmicrohttpd ────────────────────────────────────────────────────
|
||||||
|
find_library(MHD_LIB microhttpd REQUIRED)
|
||||||
|
find_path(MHD_INCLUDE microhttpd.h REQUIRED)
|
||||||
|
include_directories(${MHD_INCLUDE})
|
||||||
|
|
||||||
|
# ── framecache server ────────────────────────────────────────────────
|
||||||
|
add_executable(framecache
|
||||||
|
src/framecache.c
|
||||||
|
src/slot.c
|
||||||
|
src/registry.c
|
||||||
|
)
|
||||||
|
target_link_libraries(framecache ${MHD_LIB} rt pthread)
|
||||||
|
|
||||||
|
# ── fc_client static library (used by bridges + test) ───────────────
|
||||||
|
add_library(fc_client STATIC
|
||||||
|
client/fc_client.c
|
||||||
|
src/slot.c # client needs fc_slot_shm_size / fc_frame_at
|
||||||
|
)
|
||||||
|
target_include_directories(fc_client PUBLIC src client)
|
||||||
|
target_link_libraries(fc_client rt pthread)
|
||||||
|
|
||||||
|
# ── net_ingest — network source (RTMP/SRT) → framecache slot ─────────
|
||||||
|
# Spawned by node-agent when a network recorder starts.
|
||||||
|
# Decodes the network stream to raw UYVY422 via ffmpeg and writes frames
|
||||||
|
# into a framecache slot, giving capture-manager the same fc_pipe consumer
|
||||||
|
# interface as SDI sources.
|
||||||
|
add_executable(net_ingest
|
||||||
|
src/net_ingest.c
|
||||||
|
src/slot.c
|
||||||
|
)
|
||||||
|
target_include_directories(net_ingest PRIVATE src)
|
||||||
|
target_link_libraries(net_ingest rt pthread)
|
||||||
|
install(TARGETS net_ingest DESTINATION bin)
|
||||||
|
|
||||||
|
# ── fc_pipe — slot → stdout adapter (used by capture-manager.js) ─────
|
||||||
|
# Spawned by capture-manager as a child process; writes raw UYVY422
|
||||||
|
# frames from a framecache slot to stdout so ffmpeg reads them as
|
||||||
|
# rawvideo pipe input. Multiple fc_pipe instances on the same slot
|
||||||
|
# each get an independent cursor — zero-copy fan-out.
|
||||||
|
add_executable(fc_pipe
|
||||||
|
client/fc_pipe.c
|
||||||
|
)
|
||||||
|
target_link_libraries(fc_pipe fc_client)
|
||||||
|
target_include_directories(fc_pipe PRIVATE src client)
|
||||||
|
|
||||||
|
# ── test consumer (dev utility) ──────────────────────────────────────
|
||||||
|
if(BUILD_TESTS)
|
||||||
|
add_executable(fc_test_consumer
|
||||||
|
client/fc_test_consumer.c
|
||||||
|
)
|
||||||
|
target_link_libraries(fc_test_consumer fc_client)
|
||||||
|
target_include_directories(fc_test_consumer PRIVATE src client)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS framecache fc_pipe DESTINATION bin)
|
||||||
|
install(FILES client/fc_client.h src/slot.h DESTINATION include/framecache)
|
||||||
|
install(TARGETS fc_client DESTINATION lib)
|
||||||
33
services/framecache/Dockerfile
Normal file
33
services/framecache/Dockerfile
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# ── Build stage ─────────────────────────────────────────────────────
|
||||||
|
FROM debian:bookworm AS builder
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential cmake libmicrohttpd-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY . /src
|
||||||
|
RUN cmake -S /src -B /build \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
&& cmake --build /build -j"$(nproc)"
|
||||||
|
|
||||||
|
# ── Runtime stage ────────────────────────────────────────────────────
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libmicrohttpd12 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /build/framecache /usr/local/bin/framecache
|
||||||
|
COPY --from=builder /build/fc_pipe /usr/local/bin/fc_pipe
|
||||||
|
COPY --from=builder /build/net_ingest /usr/local/bin/net_ingest
|
||||||
|
COPY --from=builder /build/fc_test_consumer /usr/local/bin/fc_test_consumer 2>/dev/null || true
|
||||||
|
|
||||||
|
# /dev/shm/framecache is created at runtime (tmpfs)
|
||||||
|
RUN mkdir -p /dev/shm/framecache
|
||||||
|
|
||||||
|
EXPOSE 7435
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s \
|
||||||
|
CMD wget -qO- http://localhost:7435/health || exit 1
|
||||||
|
|
||||||
|
CMD ["/usr/local/bin/framecache"]
|
||||||
210
services/framecache/client/fc_client.c
Normal file
210
services/framecache/client/fc_client.c
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
/**
|
||||||
|
* fc_client.c — Consumer-side framecache client implementation.
|
||||||
|
*/
|
||||||
|
#include "fc_client.h"
|
||||||
|
#include "../src/slot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define SHM_DIR "/dev/shm/framecache"
|
||||||
|
#define SEM_PREFIX "/framecache-"
|
||||||
|
#define SEM_SUFFIX "-write"
|
||||||
|
|
||||||
|
struct fc_consumer {
|
||||||
|
int shm_fd;
|
||||||
|
void *base;
|
||||||
|
size_t shm_size;
|
||||||
|
sem_t *sem;
|
||||||
|
uint64_t read_cursor; /* consumer's own position in the ring */
|
||||||
|
uint64_t local_dropped; /* frames skipped by this consumer */
|
||||||
|
uint8_t *copy_buf; /* consumer-owned frame copy buffer (frame_size bytes) */
|
||||||
|
uint32_t frame_size; /* cached from header */
|
||||||
|
char slot_id[FC_MAX_SLOT_ID];
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint64_t now_us(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
return (uint64_t)ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc_consumer_t *fc_consumer_open(const char *slot_id, uint64_t wait_ms)
|
||||||
|
{
|
||||||
|
char shm_path[128], sem_name[128];
|
||||||
|
snprintf(shm_path, sizeof shm_path, "%s/%s", SHM_DIR, slot_id);
|
||||||
|
snprintf(sem_name, sizeof sem_name, "%s%s%s", SEM_PREFIX, slot_id, SEM_SUFFIX);
|
||||||
|
|
||||||
|
uint64_t deadline = now_us() + wait_ms * 1000ULL;
|
||||||
|
int fd = -1;
|
||||||
|
while (1) {
|
||||||
|
fd = open(shm_path, O_RDONLY);
|
||||||
|
if (fd >= 0) break;
|
||||||
|
if (now_us() >= deadline) return NULL;
|
||||||
|
struct timespec ts = { .tv_nsec = 100000000 }; /* 100ms */
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read header to get frame_size */
|
||||||
|
fc_header_t hdr;
|
||||||
|
if (pread(fd, &hdr, sizeof hdr, 0) != sizeof hdr || hdr.magic != FC_MAGIC) {
|
||||||
|
close(fd); return NULL;
|
||||||
|
}
|
||||||
|
size_t total = fc_slot_shm_size(hdr.frame_size);
|
||||||
|
|
||||||
|
void *base = mmap(NULL, total, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (base == MAP_FAILED) { close(fd); return NULL; }
|
||||||
|
|
||||||
|
sem_t *sem = sem_open(sem_name, 0);
|
||||||
|
if (sem == SEM_FAILED) { munmap(base, total); close(fd); return NULL; }
|
||||||
|
|
||||||
|
fc_consumer_t *c = calloc(1, sizeof *c);
|
||||||
|
if (!c) { sem_close(sem); munmap(base, total); close(fd); return NULL; }
|
||||||
|
|
||||||
|
/* Consumer-owned copy buffer — fc_consumer_read copies the frame here and
|
||||||
|
* re-validates the cursor afterward, so a writer lapping a slow consumer
|
||||||
|
* cannot corrupt the frame the caller is using. */
|
||||||
|
c->copy_buf = malloc(hdr.frame_size);
|
||||||
|
if (!c->copy_buf) {
|
||||||
|
free(c); sem_close(sem); munmap(base, total); close(fd); return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->shm_fd = fd;
|
||||||
|
c->base = base;
|
||||||
|
c->shm_size = total;
|
||||||
|
c->sem = sem;
|
||||||
|
c->frame_size = hdr.frame_size;
|
||||||
|
/* Start reading from the current write position so we don't replay old frames */
|
||||||
|
c->read_cursor = atomic_load_explicit(
|
||||||
|
&((fc_header_t *)base)->write_cursor, memory_order_acquire);
|
||||||
|
c->local_dropped = 0;
|
||||||
|
strncpy(c->slot_id, slot_id, FC_MAX_SLOT_ID - 1);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fc_consumer_read(fc_consumer_t *c, fc_frame_ref_t *ref, uint64_t timeout_ms)
|
||||||
|
{
|
||||||
|
fc_header_t *hdr = (fc_header_t *)c->base;
|
||||||
|
int dropped = 0; /* set when this call skipped one or more frames */
|
||||||
|
|
||||||
|
/* ── Wait for new data ──────────────────────────────────────────────
|
||||||
|
* The semaphore is used ONLY as an edge-wakeup hint, never as a frame
|
||||||
|
* counter. The writer posts once per frame, but a consumer that skips
|
||||||
|
* frames (lap) or reads less often than the writer posts would otherwise
|
||||||
|
* leave the count climbing unbounded — causing sem_timedwait to never
|
||||||
|
* block (100% CPU busy-spin) and eventually EOVERFLOW. So:
|
||||||
|
* - cursor-diff (write_cursor - read_cursor) is the SOURCE OF TRUTH for
|
||||||
|
* whether a frame is available.
|
||||||
|
* - we drain the semaphore to zero (sem_trywait loop) so the count never
|
||||||
|
* accumulates.
|
||||||
|
* - if no frame is available we block on ONE sem_timedwait for wakeup. */
|
||||||
|
for (;;) {
|
||||||
|
uint64_t write_cur = atomic_load_explicit(&hdr->write_cursor,
|
||||||
|
memory_order_acquire);
|
||||||
|
|
||||||
|
/* Lap detection: if the writer is more than ring_depth ahead, the
|
||||||
|
* oldest unread frames have been overwritten — skip to the oldest
|
||||||
|
* still-valid frame. */
|
||||||
|
if (write_cur > c->read_cursor + hdr->ring_depth) {
|
||||||
|
uint64_t skipped = write_cur - c->read_cursor - hdr->ring_depth;
|
||||||
|
c->read_cursor = write_cur - hdr->ring_depth;
|
||||||
|
c->local_dropped += skipped;
|
||||||
|
/* NOTE: do NOT write hdr->dropped_frames here — the consumer maps
|
||||||
|
* the shm PROT_READ (read-only), so an atomic write would SIGSEGV.
|
||||||
|
* Per-consumer drops are tracked in c->local_dropped and exposed
|
||||||
|
* via fc_consumer_dropped(). The writer owns hdr->dropped_frames. */
|
||||||
|
dropped = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->read_cursor < write_cur) {
|
||||||
|
/* A frame is available — drain the semaphore so its count never
|
||||||
|
* accumulates, then read+copy below. */
|
||||||
|
while (sem_trywait(c->sem) == 0) { /* drain */ }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No frame yet — drain stale posts, then block for a wakeup. */
|
||||||
|
while (sem_trywait(c->sem) == 0) { /* drain */ }
|
||||||
|
|
||||||
|
struct timespec abs_ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &abs_ts);
|
||||||
|
abs_ts.tv_sec += (time_t)(timeout_ms / 1000);
|
||||||
|
abs_ts.tv_nsec += (long)((timeout_ms % 1000) * 1000000L);
|
||||||
|
if (abs_ts.tv_nsec >= 1000000000L) { abs_ts.tv_sec++; abs_ts.tv_nsec -= 1000000000L; }
|
||||||
|
|
||||||
|
int w = sem_timedwait(c->sem, &abs_ts);
|
||||||
|
if (w != 0) {
|
||||||
|
if (errno == ETIMEDOUT) {
|
||||||
|
/* Re-check the cursor once more before giving up — the writer
|
||||||
|
* may have advanced between our check and the wait. */
|
||||||
|
uint64_t wc2 = atomic_load_explicit(&hdr->write_cursor,
|
||||||
|
memory_order_acquire);
|
||||||
|
if (c->read_cursor < wc2) continue;
|
||||||
|
return FC_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
return FC_ERROR;
|
||||||
|
}
|
||||||
|
/* Woken — loop to re-evaluate cursor-diff. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Copy the frame into the consumer-owned buffer ──────────────────── */
|
||||||
|
fc_frame_t *frame = fc_frame_at(c->base, hdr->frame_size, c->read_cursor);
|
||||||
|
uint32_t fsz = frame->size;
|
||||||
|
if (fsz > hdr->frame_size) fsz = hdr->frame_size;
|
||||||
|
uint64_t pts = frame->pts_us;
|
||||||
|
uint64_t wall = frame->wall_us;
|
||||||
|
memcpy(c->copy_buf, frame->data, fsz);
|
||||||
|
|
||||||
|
/* ── Re-validate AFTER the copy ─────────────────────────────────────
|
||||||
|
* If the writer lapped us during the copy (overwrote this slot), the copy
|
||||||
|
* may be torn — discard it and signal DROPPED so the caller reads again. */
|
||||||
|
uint64_t write_after = atomic_load_explicit(&hdr->write_cursor,
|
||||||
|
memory_order_acquire);
|
||||||
|
if (write_after > c->read_cursor + hdr->ring_depth) {
|
||||||
|
uint64_t skipped = write_after - c->read_cursor - hdr->ring_depth;
|
||||||
|
c->read_cursor = write_after - hdr->ring_depth;
|
||||||
|
c->local_dropped += skipped;
|
||||||
|
return FC_LAPPED; /* copy torn — ref not valid, caller reads again */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy is valid. */
|
||||||
|
ref->data = c->copy_buf;
|
||||||
|
ref->size = fsz;
|
||||||
|
ref->pts_us = pts;
|
||||||
|
ref->wall_us = wall;
|
||||||
|
ref->seq = c->read_cursor;
|
||||||
|
|
||||||
|
c->read_cursor++;
|
||||||
|
return dropped ? FC_DROPPED : FC_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fc_consumer_close(fc_consumer_t *c)
|
||||||
|
{
|
||||||
|
if (!c) return;
|
||||||
|
if (c->copy_buf) free(c->copy_buf);
|
||||||
|
sem_close(c->sem);
|
||||||
|
munmap(c->base, c->shm_size);
|
||||||
|
close(c->shm_fd);
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fc_consumer_write_cursor(fc_consumer_t *c)
|
||||||
|
{
|
||||||
|
fc_header_t *hdr = (fc_header_t *)c->base;
|
||||||
|
return atomic_load(&hdr->write_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fc_consumer_dropped(fc_consumer_t *c)
|
||||||
|
{
|
||||||
|
return c->local_dropped;
|
||||||
|
}
|
||||||
82
services/framecache/client/fc_client.h
Normal file
82
services/framecache/client/fc_client.h
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* fc_client.h — Consumer-side framecache client library.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* fc_consumer_t *c = fc_consumer_open("deltacast-zampp3-0");
|
||||||
|
* fc_frame_ref_t ref;
|
||||||
|
* while (fc_consumer_read(c, &ref, 2000) == FC_OK) {
|
||||||
|
* // ref.data valid until next fc_consumer_read call
|
||||||
|
* process_frame(ref.data, ref.size, ref.pts_us);
|
||||||
|
* }
|
||||||
|
* fc_consumer_close(c);
|
||||||
|
*
|
||||||
|
* Each consumer tracks its own read_cursor — multiple consumers on the same
|
||||||
|
* slot are fully independent and never block each other or the writer.
|
||||||
|
*
|
||||||
|
* If a consumer falls more than ring_depth frames behind the writer its cursor
|
||||||
|
* is snapped to the latest frame and FC_DROPPED is returned once.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Return codes */
|
||||||
|
#define FC_OK 0 /* valid frame returned in ref */
|
||||||
|
#define FC_TIMEOUT 1 /* no new frame within timeout_ms — ref not populated */
|
||||||
|
#define FC_DROPPED 2 /* valid frame returned in ref, BUT one or more older
|
||||||
|
* frames were skipped first (consumer fell behind).
|
||||||
|
* ref IS populated — caller should USE the frame. */
|
||||||
|
#define FC_LAPPED 3 /* the copy was overwritten mid-read (writer lapped the
|
||||||
|
* consumer during memcpy). ref NOT populated — caller
|
||||||
|
* should call fc_consumer_read again. */
|
||||||
|
#define FC_ERROR -1
|
||||||
|
|
||||||
|
typedef struct fc_consumer fc_consumer_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t *data; /* pointer to a CONSUMER-OWNED copy of the frame —
|
||||||
|
* stable until the next fc_consumer_read() call.
|
||||||
|
* (Previously a zero-copy pointer into the shm ring,
|
||||||
|
* which the writer could overwrite mid-use when it
|
||||||
|
* lapped a slow consumer. We now copy into the
|
||||||
|
* consumer's own buffer and re-validate the cursor
|
||||||
|
* AFTER the copy, so a lapped frame is discarded
|
||||||
|
* rather than streamed corrupt.) */
|
||||||
|
uint32_t size; /* bytes */
|
||||||
|
uint64_t pts_us; /* presentation timestamp (microseconds) */
|
||||||
|
uint64_t wall_us; /* wall clock at write time (microseconds) */
|
||||||
|
uint64_t seq; /* write_cursor value for this frame */
|
||||||
|
} fc_frame_ref_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a consumer handle for the named slot.
|
||||||
|
* Polls the slot shm file until it appears (up to wait_ms milliseconds).
|
||||||
|
* Returns NULL if slot not found within wait_ms or on error.
|
||||||
|
*/
|
||||||
|
fc_consumer_t *fc_consumer_open(const char *slot_id, uint64_t wait_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the next frame.
|
||||||
|
* Blocks up to timeout_ms waiting for a new frame (via semaphore).
|
||||||
|
* Returns FC_OK, FC_TIMEOUT, FC_DROPPED, or FC_ERROR.
|
||||||
|
* On FC_OK or FC_DROPPED the ref fields are populated.
|
||||||
|
*/
|
||||||
|
int fc_consumer_read(fc_consumer_t *c, fc_frame_ref_t *ref, uint64_t timeout_ms);
|
||||||
|
|
||||||
|
/** Close the consumer handle. Does NOT destroy the slot. */
|
||||||
|
void fc_consumer_close(fc_consumer_t *c);
|
||||||
|
|
||||||
|
/** Current write_cursor of the slot (approximate — no lock). */
|
||||||
|
uint64_t fc_consumer_write_cursor(fc_consumer_t *c);
|
||||||
|
|
||||||
|
/** Frames dropped by this consumer since open. */
|
||||||
|
uint64_t fc_consumer_dropped(fc_consumer_t *c);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
133
services/framecache/client/fc_pipe.c
Normal file
133
services/framecache/client/fc_pipe.c
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
/**
|
||||||
|
* fc_pipe.c — Framecache slot → stdout pipe adapter.
|
||||||
|
*
|
||||||
|
* Opens a framecache slot as a consumer and writes raw video frames to
|
||||||
|
* stdout in a continuous stream. capture-manager.js spawns this process
|
||||||
|
* and feeds its stdout to ffmpeg as a rawvideo pipe input — identical to
|
||||||
|
* the way DeckLink bridges currently pipe raw frames.
|
||||||
|
*
|
||||||
|
* Each consumer instance has its own independent read cursor, so multiple
|
||||||
|
* fc_pipe processes reading from the same slot never interfere with each
|
||||||
|
* other. This is how growing + proxy + HLS all read the same SDI signal
|
||||||
|
* simultaneously.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* fc_pipe <slot_id> [wait_ms]
|
||||||
|
*
|
||||||
|
* Writes raw UYVY422 frame data to stdout. Terminates on:
|
||||||
|
* - SIGTERM / SIGINT (clean stop from capture-manager)
|
||||||
|
* - stdout EPIPE (ffmpeg exited)
|
||||||
|
* - Slot disappears (bridge stopped)
|
||||||
|
*
|
||||||
|
* Exit codes:
|
||||||
|
* 0 clean stop (SIGTERM)
|
||||||
|
* 1 slot not found within wait_ms
|
||||||
|
* 2 stdout write error (EPIPE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../src/slot.h"
|
||||||
|
#include "fc_client.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
static volatile int g_stop = 0;
|
||||||
|
static void on_signal(int s) { (void)s; g_stop = 1; }
|
||||||
|
|
||||||
|
/* Write all bytes to fd. Returns 0 on success, -1 on EPIPE/error. */
|
||||||
|
static int write_all_fd(int fd, const void *buf, size_t len) {
|
||||||
|
const uint8_t *p = (const uint8_t *)buf;
|
||||||
|
size_t off = 0;
|
||||||
|
while (off < len) {
|
||||||
|
ssize_t n = write(fd, p + off, len - off);
|
||||||
|
if (n > 0) { off += (size_t)n; continue; }
|
||||||
|
if (n < 0 && errno == EINTR) continue;
|
||||||
|
return -1; /* EPIPE or other fatal error */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <slot_id> [wait_ms]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const char *slot_id = argv[1];
|
||||||
|
uint64_t wait_ms = argc >= 3 ? (uint64_t)atoll(argv[2]) : 30000;
|
||||||
|
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGPIPE, SIG_IGN); /* detect EPIPE via write() return value */
|
||||||
|
|
||||||
|
/* Set stdout to binary mode — no newline translation */
|
||||||
|
fcntl(STDOUT_FILENO, F_SETFL,
|
||||||
|
fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
|
||||||
|
|
||||||
|
fprintf(stderr, "[fc_pipe] opening slot '%s' (wait %llums)\n",
|
||||||
|
slot_id, (unsigned long long)wait_ms);
|
||||||
|
|
||||||
|
fc_consumer_t *c = fc_consumer_open(slot_id, wait_ms);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "[fc_pipe] slot '%s' not found within %llums\n",
|
||||||
|
slot_id, (unsigned long long)wait_ms);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[fc_pipe] slot open, streaming to stdout\n");
|
||||||
|
|
||||||
|
uint64_t frames_out = 0;
|
||||||
|
uint64_t total_dropped = 0;
|
||||||
|
|
||||||
|
while (!g_stop) {
|
||||||
|
fc_frame_ref_t ref;
|
||||||
|
int rc = fc_consumer_read(c, &ref, 2000 /* 2s timeout */);
|
||||||
|
|
||||||
|
if (rc == FC_TIMEOUT) continue;
|
||||||
|
if (rc == FC_ERROR) break;
|
||||||
|
|
||||||
|
if (rc == FC_LAPPED) {
|
||||||
|
/* Copy was torn (writer lapped us mid-read). No valid frame to
|
||||||
|
* write — log and read again. */
|
||||||
|
total_dropped = fc_consumer_dropped(c);
|
||||||
|
fprintf(stderr, "[fc_pipe] WARNING: frame lapped mid-read — total dropped: %llu\n",
|
||||||
|
(unsigned long long)total_dropped);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == FC_DROPPED) {
|
||||||
|
/* Skipped one or more older frames, but THIS frame is valid — log
|
||||||
|
* and write it (do NOT continue). */
|
||||||
|
total_dropped = fc_consumer_dropped(c);
|
||||||
|
fprintf(stderr, "[fc_pipe] WARNING: consumer fell behind — total dropped: %llu\n",
|
||||||
|
(unsigned long long)total_dropped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write frame data to stdout (ref.data is a stable consumer-owned copy) */
|
||||||
|
if (write_all_fd(STDOUT_FILENO, ref.data, ref.size) < 0) {
|
||||||
|
if (!g_stop)
|
||||||
|
fprintf(stderr, "[fc_pipe] stdout EPIPE — ffmpeg exited\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
frames_out++;
|
||||||
|
|
||||||
|
/* Periodic stats to stderr (every 300 frames ≈ 5s at 60fps) */
|
||||||
|
if (frames_out % 300 == 0) {
|
||||||
|
fprintf(stderr, "[fc_pipe] frames=%llu dropped=%llu\n",
|
||||||
|
(unsigned long long)frames_out,
|
||||||
|
(unsigned long long)total_dropped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fc_consumer_close(c);
|
||||||
|
fprintf(stderr, "[fc_pipe] done frames=%llu dropped=%llu\n",
|
||||||
|
(unsigned long long)frames_out,
|
||||||
|
(unsigned long long)total_dropped);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
74
services/framecache/client/fc_test_consumer.c
Normal file
74
services/framecache/client/fc_test_consumer.c
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* fc_test_consumer.c — Dev utility: attach to a framecache slot and print stats.
|
||||||
|
*
|
||||||
|
* Usage: fc_test_consumer <slot_id> [wait_ms]
|
||||||
|
*/
|
||||||
|
#include "fc_client.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static volatile int g_run = 1;
|
||||||
|
static void on_sig(int s) { (void)s; g_run = 0; }
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <slot_id> [wait_ms]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
const char *slot_id = argv[1];
|
||||||
|
uint64_t wait_ms = argc >= 3 ? (uint64_t)atoi(argv[2]) : 30000;
|
||||||
|
|
||||||
|
signal(SIGINT, on_sig);
|
||||||
|
signal(SIGTERM, on_sig);
|
||||||
|
|
||||||
|
fprintf(stderr, "Opening slot '%s' (wait up to %llums)...\n",
|
||||||
|
slot_id, (unsigned long long)wait_ms);
|
||||||
|
|
||||||
|
fc_consumer_t *c = fc_consumer_open(slot_id, wait_ms);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "Failed to open slot '%s'\n", slot_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Slot opened. Reading frames (Ctrl+C to stop)...\n");
|
||||||
|
|
||||||
|
uint64_t total = 0, dropped = 0;
|
||||||
|
struct timespec t0;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &t0);
|
||||||
|
|
||||||
|
while (g_run) {
|
||||||
|
fc_frame_ref_t ref;
|
||||||
|
int rc = fc_consumer_read(c, &ref, 2000);
|
||||||
|
if (rc == FC_TIMEOUT) continue;
|
||||||
|
if (rc == FC_ERROR) { fprintf(stderr, "read error\n"); break; }
|
||||||
|
if (rc == FC_LAPPED) { /* torn copy — no valid frame, read again */ continue; }
|
||||||
|
if (rc == FC_DROPPED) {
|
||||||
|
dropped = fc_consumer_dropped(c);
|
||||||
|
fprintf(stderr, "[WARN] consumer fell behind — total dropped: %llu\n",
|
||||||
|
(unsigned long long)dropped);
|
||||||
|
}
|
||||||
|
total++;
|
||||||
|
|
||||||
|
/* Print stats every 100 frames */
|
||||||
|
if (total % 100 == 0) {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
double elapsed = (now.tv_sec - t0.tv_sec)
|
||||||
|
+ (now.tv_nsec - t0.tv_nsec) * 1e-9;
|
||||||
|
fprintf(stdout, "frames=%llu dropped=%llu fps=%.2f pts_us=%llu\n",
|
||||||
|
(unsigned long long)total,
|
||||||
|
(unsigned long long)fc_consumer_dropped(c),
|
||||||
|
total / elapsed,
|
||||||
|
(unsigned long long)ref.pts_us);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Done. total=%llu dropped=%llu\n",
|
||||||
|
(unsigned long long)total,
|
||||||
|
(unsigned long long)fc_consumer_dropped(c));
|
||||||
|
fc_consumer_close(c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
366
services/framecache/src/framecache.c
Normal file
366
services/framecache/src/framecache.c
Normal file
|
|
@ -0,0 +1,366 @@
|
||||||
|
/**
|
||||||
|
* framecache.c — Main entry point. HTTP API server + slot manager.
|
||||||
|
*
|
||||||
|
* Endpoints:
|
||||||
|
* POST /slots Create slot
|
||||||
|
* GET /slots List slots
|
||||||
|
* GET /slots/:id Get slot detail
|
||||||
|
* DELETE /slots/:id Destroy slot
|
||||||
|
* GET /health Health check
|
||||||
|
*
|
||||||
|
* Uses libmicrohttpd for the HTTP layer (single-threaded, poll-based).
|
||||||
|
*/
|
||||||
|
#include "slot.h"
|
||||||
|
#include "registry.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <microhttpd.h>
|
||||||
|
|
||||||
|
#ifndef FC_PORT_DEFAULT
|
||||||
|
#define FC_PORT_DEFAULT 7435
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ── tiny JSON helpers ─────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static int json_get_uint(const char *json, const char *key, uint32_t *out)
|
||||||
|
{
|
||||||
|
char pat[128];
|
||||||
|
snprintf(pat, sizeof pat, "\"%s\":", key);
|
||||||
|
const char *p = strstr(json, pat);
|
||||||
|
if (!p) return -1;
|
||||||
|
p += strlen(pat);
|
||||||
|
while (*p == ' ' || *p == '\t') p++;
|
||||||
|
*out = (uint32_t)strtoul(p, NULL, 10);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int json_get_str(const char *json, const char *key,
|
||||||
|
char *out, size_t out_len)
|
||||||
|
{
|
||||||
|
char pat[128];
|
||||||
|
snprintf(pat, sizeof pat, "\"%s\":", key);
|
||||||
|
const char *p = strstr(json, pat);
|
||||||
|
if (!p) return -1;
|
||||||
|
p += strlen(pat);
|
||||||
|
while (*p == ' ' || *p == '\t') p++;
|
||||||
|
if (*p != '"') return -1;
|
||||||
|
p++;
|
||||||
|
size_t i = 0;
|
||||||
|
while (*p && *p != '"' && i < out_len - 1)
|
||||||
|
out[i++] = *p++;
|
||||||
|
out[i] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── HTTP request accumulator ──────────────────────────────────────── */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
size_t cap;
|
||||||
|
} req_body_t;
|
||||||
|
|
||||||
|
static void req_body_free(req_body_t *r)
|
||||||
|
{
|
||||||
|
free(r->buf);
|
||||||
|
r->buf = NULL; r->len = 0; r->cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── response helpers ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static enum MHD_Result respond(struct MHD_Connection *conn,
|
||||||
|
unsigned int status,
|
||||||
|
const char *body)
|
||||||
|
{
|
||||||
|
struct MHD_Response *r = MHD_create_response_from_buffer(
|
||||||
|
strlen(body), (void *)body, MHD_RESPMEM_MUST_COPY);
|
||||||
|
MHD_add_response_header(r, "Content-Type", "application/json");
|
||||||
|
MHD_add_response_header(r, "Access-Control-Allow-Origin", "*");
|
||||||
|
enum MHD_Result rc = MHD_queue_response(conn, status, r);
|
||||||
|
MHD_destroy_response(r);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── slot → JSON ───────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static void slot_to_json(struct fc_slot *s, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
fc_header_t *hdr = fc_slot_header(s);
|
||||||
|
uint64_t wc = atomic_load(&hdr->write_cursor);
|
||||||
|
uint64_t df = atomic_load(&hdr->dropped_frames);
|
||||||
|
/* simple fps estimate — not perfect but good enough for status */
|
||||||
|
snprintf(buf, len,
|
||||||
|
"{"
|
||||||
|
"\"slot_id\":\"%s\","
|
||||||
|
"\"shm_path\":\"%s\","
|
||||||
|
"\"sem_name\":\"%s\","
|
||||||
|
"\"width\":%u,"
|
||||||
|
"\"height\":%u,"
|
||||||
|
"\"fps_num\":%u,"
|
||||||
|
"\"fps_den\":%u,"
|
||||||
|
"\"pixel_format\":\"UYVY422\","
|
||||||
|
"\"source_type\":\"%s\","
|
||||||
|
"\"frame_size\":%u,"
|
||||||
|
"\"ring_depth\":%u,"
|
||||||
|
"\"write_cursor\":%llu,"
|
||||||
|
"\"dropped_frames\":%llu"
|
||||||
|
"}",
|
||||||
|
fc_slot_id(s),
|
||||||
|
fc_slot_shm_path(s),
|
||||||
|
fc_slot_sem_name(s),
|
||||||
|
hdr->width, hdr->height,
|
||||||
|
hdr->fps_num, hdr->fps_den,
|
||||||
|
hdr->source_type,
|
||||||
|
hdr->frame_size,
|
||||||
|
hdr->ring_depth,
|
||||||
|
(unsigned long long)wc,
|
||||||
|
(unsigned long long)df
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── request handler ───────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static enum MHD_Result handle_request(
|
||||||
|
void *cls,
|
||||||
|
struct MHD_Connection *conn,
|
||||||
|
const char *url,
|
||||||
|
const char *method,
|
||||||
|
const char *version,
|
||||||
|
const char *upload_data,
|
||||||
|
size_t *upload_data_size,
|
||||||
|
void **con_cls)
|
||||||
|
{
|
||||||
|
(void)cls; (void)version;
|
||||||
|
|
||||||
|
/* First call: allocate body accumulator */
|
||||||
|
if (*con_cls == NULL) {
|
||||||
|
req_body_t *rb = calloc(1, sizeof *rb);
|
||||||
|
if (!rb) return MHD_NO;
|
||||||
|
*con_cls = rb;
|
||||||
|
return MHD_YES;
|
||||||
|
}
|
||||||
|
req_body_t *rb = (req_body_t *)*con_cls;
|
||||||
|
|
||||||
|
/* Accumulate POST body */
|
||||||
|
if (*upload_data_size > 0) {
|
||||||
|
size_t need = rb->len + *upload_data_size + 1;
|
||||||
|
if (need > rb->cap) {
|
||||||
|
rb->buf = realloc(rb->buf, need);
|
||||||
|
rb->cap = need;
|
||||||
|
}
|
||||||
|
memcpy(rb->buf + rb->len, upload_data, *upload_data_size);
|
||||||
|
rb->len += *upload_data_size;
|
||||||
|
rb->buf[rb->len] = '\0';
|
||||||
|
*upload_data_size = 0;
|
||||||
|
return MHD_YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MHD_Result rc;
|
||||||
|
char resp[4096];
|
||||||
|
|
||||||
|
/* GET /health */
|
||||||
|
if (strcmp(method, "GET") == 0 && strcmp(url, "/health") == 0) {
|
||||||
|
rc = respond(conn, MHD_HTTP_OK, "{\"status\":\"ok\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GET /slots
|
||||||
|
* Worst case: FC_MAX_SLOTS (256) × ~2KB/entry ≈ 512KB. A 64KB stack buffer
|
||||||
|
* would overflow at ~32 slots (and `pos` could pass `sizeof big`, making
|
||||||
|
* `sizeof big - pos` underflow to a huge size_t). Heap-allocate a buffer
|
||||||
|
* sized for the worst case and bound-check every append. */
|
||||||
|
if (strcmp(method, "GET") == 0 && strcmp(url, "/slots") == 0) {
|
||||||
|
size_t cap = (size_t)FC_MAX_SLOTS * 2100 + 64; /* worst case + brackets */
|
||||||
|
char *big = malloc(cap);
|
||||||
|
if (!big) {
|
||||||
|
rc = respond(conn, MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
"{\"error\":\"out of memory\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
size_t pos = 0;
|
||||||
|
if (pos < cap) big[pos++] = '[';
|
||||||
|
int first = 1;
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (!g_registry[i].active) continue;
|
||||||
|
char entry[2100];
|
||||||
|
slot_to_json(g_registry[i].slot, entry, sizeof entry);
|
||||||
|
size_t elen = strlen(entry);
|
||||||
|
/* +2 for possible comma + closing bracket, +1 for NUL */
|
||||||
|
if (pos + elen + 3 >= cap) break; /* never overflow */
|
||||||
|
if (!first) big[pos++] = ',';
|
||||||
|
first = 0;
|
||||||
|
memcpy(big + pos, entry, elen);
|
||||||
|
pos += elen;
|
||||||
|
}
|
||||||
|
if (pos + 2 < cap) big[pos++] = ']';
|
||||||
|
big[pos] = '\0';
|
||||||
|
rc = respond(conn, MHD_HTTP_OK, big);
|
||||||
|
free(big);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GET /slots/:id */
|
||||||
|
if (strcmp(method, "GET") == 0 &&
|
||||||
|
strncmp(url, "/slots/", 7) == 0 && strlen(url) > 7)
|
||||||
|
{
|
||||||
|
const char *id = url + 7;
|
||||||
|
struct fc_slot *s = registry_find(id);
|
||||||
|
if (!s) {
|
||||||
|
rc = respond(conn, MHD_HTTP_NOT_FOUND,
|
||||||
|
"{\"error\":\"slot not found\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
slot_to_json(s, resp, sizeof resp);
|
||||||
|
rc = respond(conn, MHD_HTTP_OK, resp);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* POST /slots */
|
||||||
|
if (strcmp(method, "POST") == 0 && strcmp(url, "/slots") == 0) {
|
||||||
|
if (!rb->buf || rb->len == 0) {
|
||||||
|
rc = respond(conn, MHD_HTTP_BAD_REQUEST,
|
||||||
|
"{\"error\":\"empty body\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
char slot_id[FC_MAX_SLOT_ID] = {0};
|
||||||
|
char source_type[32] = "unknown";
|
||||||
|
uint32_t width = 0, height = 0, fps_num = 0, fps_den = 0;
|
||||||
|
|
||||||
|
json_get_str(rb->buf, "slot_id", slot_id, sizeof slot_id);
|
||||||
|
json_get_str(rb->buf, "source_type", source_type, sizeof source_type);
|
||||||
|
json_get_uint(rb->buf, "width", &width);
|
||||||
|
json_get_uint(rb->buf, "height", &height);
|
||||||
|
json_get_uint(rb->buf, "fps_num", &fps_num);
|
||||||
|
json_get_uint(rb->buf, "fps_den", &fps_den);
|
||||||
|
|
||||||
|
if (!slot_id[0] || !width || !height || !fps_num || !fps_den) {
|
||||||
|
rc = respond(conn, MHD_HTTP_BAD_REQUEST,
|
||||||
|
"{\"error\":\"missing required fields: "
|
||||||
|
"slot_id, width, height, fps_num, fps_den\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (registry_find(slot_id)) {
|
||||||
|
rc = respond(conn, MHD_HTTP_CONFLICT,
|
||||||
|
"{\"error\":\"slot already exists\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fc_slot *s = fc_slot_create(slot_id, width, height,
|
||||||
|
fps_num, fps_den,
|
||||||
|
FC_PIX_UYVY422, source_type);
|
||||||
|
if (!s) {
|
||||||
|
rc = respond(conn, MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
"{\"error\":\"failed to create slot\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
registry_add(s);
|
||||||
|
|
||||||
|
snprintf(resp, sizeof resp,
|
||||||
|
"{\"slot_id\":\"%s\","
|
||||||
|
"\"shm_path\":\"%s\","
|
||||||
|
"\"sem_name\":\"%s\"}",
|
||||||
|
fc_slot_id(s),
|
||||||
|
fc_slot_shm_path(s),
|
||||||
|
fc_slot_sem_name(s));
|
||||||
|
rc = respond(conn, MHD_HTTP_CREATED, resp);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DELETE /slots/:id */
|
||||||
|
if (strcmp(method, "DELETE") == 0 &&
|
||||||
|
strncmp(url, "/slots/", 7) == 0 && strlen(url) > 7)
|
||||||
|
{
|
||||||
|
const char *id = url + 7;
|
||||||
|
struct fc_slot *s = registry_find(id);
|
||||||
|
if (!s) {
|
||||||
|
rc = respond(conn, MHD_HTTP_NOT_FOUND,
|
||||||
|
"{\"error\":\"slot not found\"}");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
registry_remove(id);
|
||||||
|
fc_slot_destroy(s);
|
||||||
|
rc = respond(conn, MHD_HTTP_NO_CONTENT, "");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = respond(conn, MHD_HTTP_NOT_FOUND, "{\"error\":\"not found\"}");
|
||||||
|
|
||||||
|
done:
|
||||||
|
req_body_free(rb);
|
||||||
|
free(rb);
|
||||||
|
*con_cls = NULL;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void request_completed(void *cls,
|
||||||
|
struct MHD_Connection *conn,
|
||||||
|
void **con_cls,
|
||||||
|
enum MHD_RequestTerminationCode toe)
|
||||||
|
{
|
||||||
|
(void)cls; (void)conn; (void)toe;
|
||||||
|
if (*con_cls) {
|
||||||
|
req_body_free((req_body_t *)*con_cls);
|
||||||
|
free(*con_cls);
|
||||||
|
*con_cls = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── main ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static volatile int g_running = 1;
|
||||||
|
|
||||||
|
static void on_signal(int sig) { (void)sig; g_running = 0; }
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
|
||||||
|
/* Ensure /dev/shm/framecache exists */
|
||||||
|
mkdir("/dev/shm/framecache", 0755);
|
||||||
|
|
||||||
|
/* Write empty registry */
|
||||||
|
registry_write_json();
|
||||||
|
|
||||||
|
const char *port_str = getenv("FC_PORT");
|
||||||
|
uint16_t port = port_str ? (uint16_t)atoi(port_str) : FC_PORT_DEFAULT;
|
||||||
|
|
||||||
|
struct MHD_Daemon *daemon = MHD_start_daemon(
|
||||||
|
MHD_USE_SELECT_INTERNALLY,
|
||||||
|
port,
|
||||||
|
NULL, NULL,
|
||||||
|
handle_request, NULL,
|
||||||
|
MHD_OPTION_NOTIFY_COMPLETED, request_completed, NULL,
|
||||||
|
MHD_OPTION_END);
|
||||||
|
|
||||||
|
if (!daemon) {
|
||||||
|
fprintf(stderr, "[framecache] failed to start HTTP server on port %u\n", port);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[framecache] listening on port %u\n", port);
|
||||||
|
|
||||||
|
while (g_running) {
|
||||||
|
struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; /* 100ms */
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "[framecache] shutting down\n");
|
||||||
|
|
||||||
|
/* Destroy all active slots */
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (g_registry[i].active) {
|
||||||
|
registry_remove(g_registry[i].slot_id);
|
||||||
|
fc_slot_destroy(g_registry[i].slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MHD_stop_daemon(daemon);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
422
services/framecache/src/net_ingest.c
Normal file
422
services/framecache/src/net_ingest.c
Normal file
|
|
@ -0,0 +1,422 @@
|
||||||
|
/**
|
||||||
|
* net_ingest.c — Network source (RTMP/SRT) → framecache slot ingest.
|
||||||
|
*
|
||||||
|
* Spawns ffmpeg to decode a network stream to raw UYVY422 on stdout, then
|
||||||
|
* reads those frames and writes them into a framecache slot via the shm
|
||||||
|
* ring buffer. Registers the slot with the framecache HTTP API on startup
|
||||||
|
* and deregisters on clean exit.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* net_ingest --url <srt://...|rtmp://...>
|
||||||
|
* --slot-id <recorder-uuid>
|
||||||
|
* --fc-url http://framecache:7435
|
||||||
|
* --width <W> --height <H>
|
||||||
|
* --fps-num <N> --fps-den <D>
|
||||||
|
* [--source-type srt|rtmp]
|
||||||
|
* [--listen] # SRT/RTMP listener mode
|
||||||
|
* [--listen-port <N>] # listener port (SRT default 9000, RTMP 1935)
|
||||||
|
* [--stream-key <k>] # RTMP stream key (default "stream")
|
||||||
|
*
|
||||||
|
* Emits one JSON line to stderr on first frame:
|
||||||
|
* {"slot_id":"<id>","width":W,"height":H,"fps_num":N,"fps_den":D,
|
||||||
|
* "source_type":"srt","pix_fmt":"uyvy422"}
|
||||||
|
*
|
||||||
|
* Exits 0 on clean stop (SIGTERM), 1 on error.
|
||||||
|
*
|
||||||
|
* The framecache slot stays alive between ffmpeg reconnects (listener mode):
|
||||||
|
* net_ingest keeps the slot open and restarts ffmpeg on disconnect.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "slot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
/* Re-use fc_writer helpers inline (no external dep) */
|
||||||
|
#define FC_URL_DEFAULT "http://localhost:7435"
|
||||||
|
|
||||||
|
static volatile int g_stop = 0;
|
||||||
|
static void on_signal(int s) { (void)s; g_stop = 1; }
|
||||||
|
|
||||||
|
/* ── Tiny HTTP POST/DELETE (same approach as fc_writer.c) ─────────── */
|
||||||
|
static int http_req(const char *method, const char *host, int port,
|
||||||
|
const char *path, const char *body,
|
||||||
|
char *resp, size_t resp_len)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
memset(&sa, 0, sizeof sa);
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons((uint16_t)port);
|
||||||
|
struct hostent *he = gethostbyname(host);
|
||||||
|
if (!he) return -1;
|
||||||
|
memcpy(&sa.sin_addr, he->h_addr_list[0], (size_t)he->h_length);
|
||||||
|
|
||||||
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
struct timeval tv = { .tv_sec = 5 };
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||||||
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
|
||||||
|
if (connect(fd, (struct sockaddr *)&sa, sizeof sa) < 0) { close(fd); return -1; }
|
||||||
|
|
||||||
|
char req[4096];
|
||||||
|
int rlen;
|
||||||
|
if (body)
|
||||||
|
rlen = snprintf(req, sizeof req,
|
||||||
|
"%s %s HTTP/1.0\r\nHost: %s:%d\r\n"
|
||||||
|
"Content-Type: application/json\r\nContent-Length: %zu\r\n"
|
||||||
|
"Connection: close\r\n\r\n%s",
|
||||||
|
method, path, host, port, strlen(body), body);
|
||||||
|
else
|
||||||
|
rlen = snprintf(req, sizeof req,
|
||||||
|
"%s %s HTTP/1.0\r\nHost: %s:%d\r\nConnection: close\r\n\r\n",
|
||||||
|
method, path, host, port);
|
||||||
|
|
||||||
|
send(fd, req, (size_t)rlen, 0);
|
||||||
|
|
||||||
|
int status = -1;
|
||||||
|
size_t got = 0;
|
||||||
|
char buf[8192];
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = recv(fd, buf + got, sizeof buf - got - 1, 0)) > 0) got += (size_t)n;
|
||||||
|
buf[got] = '\0';
|
||||||
|
sscanf(buf, "HTTP/%*s %d", &status);
|
||||||
|
if (resp && resp_len) {
|
||||||
|
const char *b = strstr(buf, "\r\n\r\n");
|
||||||
|
if (b) { strncpy(resp, b + 4, resp_len - 1); resp[resp_len-1] = '\0'; }
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_url(const char *url, char *host, size_t hl, int *port) {
|
||||||
|
const char *p = url;
|
||||||
|
if (!strncmp(p, "http://", 7)) p += 7;
|
||||||
|
*port = 7435;
|
||||||
|
const char *colon = strchr(p, ':');
|
||||||
|
if (colon) {
|
||||||
|
size_t n = (size_t)(colon - p) < hl ? (size_t)(colon - p) : hl - 1;
|
||||||
|
strncpy(host, p, n); host[n] = '\0';
|
||||||
|
*port = atoi(colon + 1);
|
||||||
|
} else { strncpy(host, p, hl - 1); host[hl-1] = '\0'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static int json_str(const char *j, const char *k, char *out, size_t len) {
|
||||||
|
char pat[128]; snprintf(pat, sizeof pat, "\"%s\":", k);
|
||||||
|
const char *p = strstr(j, pat); if (!p) return -1;
|
||||||
|
p += strlen(pat); while (*p == ' ') p++;
|
||||||
|
if (*p != '"') return -1; p++;
|
||||||
|
size_t i = 0;
|
||||||
|
while (*p && *p != '"' && i < len - 1) out[i++] = *p++;
|
||||||
|
out[i] = '\0'; return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Frame size helpers ────────────────────────────────────────────── */
|
||||||
|
static inline size_t frame_bytes(uint32_t w, uint32_t h) {
|
||||||
|
return (size_t)w * h * 2; /* UYVY422 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Register slot with framecache ────────────────────────────────── */
|
||||||
|
static int register_slot(const char *fc_url, const char *slot_id,
|
||||||
|
uint32_t w, uint32_t h,
|
||||||
|
uint32_t fps_num, uint32_t fps_den,
|
||||||
|
const char *source_type,
|
||||||
|
char *shm_path, size_t sp_len,
|
||||||
|
char *sem_name, size_t sn_len)
|
||||||
|
{
|
||||||
|
char host[128]; int port;
|
||||||
|
parse_url(fc_url, host, sizeof host, &port);
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
snprintf(body, sizeof body,
|
||||||
|
"{\"slot_id\":\"%s\",\"width\":%u,\"height\":%u,"
|
||||||
|
"\"fps_num\":%u,\"fps_den\":%u,\"source_type\":\"%s\"}",
|
||||||
|
slot_id, w, h, fps_num, fps_den, source_type);
|
||||||
|
|
||||||
|
char resp[1024] = {0};
|
||||||
|
int st = http_req("POST", host, port, "/slots", body, resp, sizeof resp);
|
||||||
|
if (st != 201) {
|
||||||
|
fprintf(stderr, "[net_ingest] POST /slots failed HTTP %d: %s\n", st, resp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
json_str(resp, "shm_path", shm_path, sp_len);
|
||||||
|
json_str(resp, "sem_name", sem_name, sn_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deregister_slot(const char *fc_url, const char *slot_id) {
|
||||||
|
char host[128]; int port;
|
||||||
|
parse_url(fc_url, host, sizeof host, &port);
|
||||||
|
char path[192]; snprintf(path, sizeof path, "/slots/%s", slot_id);
|
||||||
|
http_req("DELETE", host, port, path, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Open shm + semaphore for writing ─────────────────────────────── */
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *base;
|
||||||
|
size_t size;
|
||||||
|
int fd;
|
||||||
|
sem_t *sem;
|
||||||
|
} ShmWriter;
|
||||||
|
|
||||||
|
static int shm_writer_open(const char *shm_path, const char *sem_name,
|
||||||
|
ShmWriter *sw)
|
||||||
|
{
|
||||||
|
sw->fd = open(shm_path, O_RDWR);
|
||||||
|
if (sw->fd < 0) return -1;
|
||||||
|
fc_header_t hdr;
|
||||||
|
if (pread(sw->fd, &hdr, sizeof hdr, 0) != sizeof hdr || hdr.magic != FC_MAGIC) {
|
||||||
|
close(sw->fd); return -1;
|
||||||
|
}
|
||||||
|
sw->size = fc_slot_shm_size(hdr.frame_size);
|
||||||
|
sw->base = mmap(NULL, sw->size, PROT_READ | PROT_WRITE, MAP_SHARED, sw->fd, 0);
|
||||||
|
if (sw->base == MAP_FAILED) { close(sw->fd); return -1; }
|
||||||
|
sw->sem = sem_open(sem_name, 0);
|
||||||
|
if (sw->sem == SEM_FAILED) { munmap(sw->base, sw->size); close(sw->fd); return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shm_write_frame(ShmWriter *sw, const uint8_t *data,
|
||||||
|
uint32_t size, uint64_t pts_us)
|
||||||
|
{
|
||||||
|
fc_header_t *hdr = (fc_header_t *)sw->base;
|
||||||
|
uint64_t cur = atomic_load_explicit(&hdr->write_cursor, memory_order_relaxed);
|
||||||
|
fc_frame_t *frame = fc_frame_at(sw->base, hdr->frame_size, cur);
|
||||||
|
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
frame->pts_us = pts_us;
|
||||||
|
frame->wall_us = (uint64_t)ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
|
||||||
|
frame->size = size < hdr->frame_size ? size : hdr->frame_size;
|
||||||
|
memcpy(frame->data, data, frame->size);
|
||||||
|
atomic_store_explicit(&hdr->write_cursor, cur + 1, memory_order_release);
|
||||||
|
sem_post(sw->sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shm_writer_close(ShmWriter *sw) {
|
||||||
|
if (sw->sem) { sem_close(sw->sem); sw->sem = NULL; }
|
||||||
|
if (sw->base) { munmap(sw->base, sw->size); sw->base = NULL; }
|
||||||
|
if (sw->fd >= 0) { close(sw->fd); sw->fd = -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Build ffmpeg args for network decode → rawvideo stdout ──────────
|
||||||
|
* All dynamic strings are written into CALLER-OWNED buffers (passed in) so
|
||||||
|
* there is no per-call strdup leak across listener reconnects. The video
|
||||||
|
* filter forces the EXACT target W:H (scale=W:H, not iw:ih) so a mid-stream
|
||||||
|
* source resolution change cannot desync the fixed-size frame reassembly —
|
||||||
|
* ffmpeg's scaler always emits width*height*2 bytes per frame.
|
||||||
|
*
|
||||||
|
* Caller must provide:
|
||||||
|
* url_buf — at least 320 bytes (built listener URL, or copied caller URL)
|
||||||
|
* vf_buf — at least 64 bytes (scale/format filter)
|
||||||
|
*/
|
||||||
|
static int build_ffmpeg_args(
|
||||||
|
char **argv, int max_args,
|
||||||
|
const char *url, const char *source_type,
|
||||||
|
int listen, int listen_port, const char *stream_key,
|
||||||
|
uint32_t w, uint32_t h,
|
||||||
|
char *url_buf, size_t url_buf_len,
|
||||||
|
char *vf_buf, size_t vf_buf_len)
|
||||||
|
{
|
||||||
|
(void)max_args;
|
||||||
|
char port_str[16];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
argv[i++] = "ffmpeg";
|
||||||
|
argv[i++] = "-hide_banner";
|
||||||
|
argv[i++] = "-loglevel"; argv[i++] = "warning";
|
||||||
|
|
||||||
|
/* Input */
|
||||||
|
argv[i++] = "-probesize"; argv[i++] = "32M";
|
||||||
|
argv[i++] = "-analyzeduration"; argv[i++] = "10M";
|
||||||
|
argv[i++] = "-fflags"; argv[i++] = "+genpts";
|
||||||
|
|
||||||
|
if (!strcmp(source_type, "srt") && listen) {
|
||||||
|
snprintf(port_str, sizeof port_str, "%d", listen_port ? listen_port : 9000);
|
||||||
|
snprintf(url_buf, url_buf_len, "srt://0.0.0.0:%s?mode=listener", port_str);
|
||||||
|
argv[i++] = "-i"; argv[i++] = url_buf;
|
||||||
|
} else if (!strcmp(source_type, "rtmp") && listen) {
|
||||||
|
snprintf(port_str, sizeof port_str, "%d", listen_port ? listen_port : 1935);
|
||||||
|
snprintf(url_buf, url_buf_len, "rtmp://0.0.0.0:%s/live/%s",
|
||||||
|
port_str, stream_key ? stream_key : "stream");
|
||||||
|
argv[i++] = "-listen"; argv[i++] = "1";
|
||||||
|
argv[i++] = "-i"; argv[i++] = url_buf;
|
||||||
|
} else {
|
||||||
|
argv[i++] = "-i"; argv[i++] = (char *)url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force EXACT output dimensions so every frame is exactly w*h*2 bytes,
|
||||||
|
* even if the source resolution changes mid-stream (SRT/RTMP reconnect to
|
||||||
|
* a different encoder). This is the resync guarantee for the fixed-size
|
||||||
|
* frame reassembly loop in main(). */
|
||||||
|
snprintf(vf_buf, vf_buf_len, "scale=%u:%u,format=uyvy422", w, h);
|
||||||
|
|
||||||
|
/* Video output: raw UYVY422 to stdout */
|
||||||
|
argv[i++] = "-map"; argv[i++] = "0:v:0";
|
||||||
|
argv[i++] = "-vf"; argv[i++] = vf_buf;
|
||||||
|
argv[i++] = "-f"; argv[i++] = "rawvideo";
|
||||||
|
argv[i++] = "-pix_fmt"; argv[i++] = "uyvy422";
|
||||||
|
argv[i++] = "pipe:1";
|
||||||
|
|
||||||
|
argv[i] = NULL;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Main ──────────────────────────────────────────────────────────── */
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
const char *url = NULL;
|
||||||
|
const char *slot_id = NULL;
|
||||||
|
const char *fc_url = getenv("FC_URL") ? getenv("FC_URL") : FC_URL_DEFAULT;
|
||||||
|
const char *source_type = "srt";
|
||||||
|
uint32_t width = 1920, height = 1080;
|
||||||
|
uint32_t fps_num = 30000, fps_den = 1001;
|
||||||
|
int listen = 0, listen_port = 0;
|
||||||
|
const char *stream_key = "stream";
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--url") && i+1 < argc) url = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--slot-id") && i+1 < argc) slot_id = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--fc-url") && i+1 < argc) fc_url = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--source-type") && i+1 < argc) source_type = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--width") && i+1 < argc) width = (uint32_t)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--height") && i+1 < argc) height = (uint32_t)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--fps-num") && i+1 < argc) fps_num = (uint32_t)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--fps-den") && i+1 < argc) fps_den = (uint32_t)atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--listen")) listen = 1;
|
||||||
|
else if (!strcmp(argv[i], "--listen-port") && i+1 < argc) listen_port = atoi(argv[++i]);
|
||||||
|
else if (!strcmp(argv[i], "--stream-key") && i+1 < argc) stream_key = argv[++i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slot_id) {
|
||||||
|
fprintf(stderr, "[net_ingest] --slot-id required\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!url && !listen) {
|
||||||
|
fprintf(stderr, "[net_ingest] --url or --listen required\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGTERM, on_signal);
|
||||||
|
signal(SIGINT, on_signal);
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
signal(SIGCHLD, SIG_DFL);
|
||||||
|
|
||||||
|
/* ── Register slot ──────────────────────────────────────────────── */
|
||||||
|
char shm_path[128] = {0}, sem_name[128] = {0};
|
||||||
|
if (register_slot(fc_url, slot_id, width, height, fps_num, fps_den,
|
||||||
|
source_type, shm_path, sizeof shm_path,
|
||||||
|
sem_name, sizeof sem_name) < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShmWriter sw = { .fd = -1 };
|
||||||
|
if (shm_writer_open(shm_path, sem_name, &sw) < 0) {
|
||||||
|
fprintf(stderr, "[net_ingest] failed to open shm %s\n", shm_path);
|
||||||
|
deregister_slot(fc_url, slot_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fsz = frame_bytes(width, height);
|
||||||
|
uint8_t *frame_buf = malloc(fsz);
|
||||||
|
if (!frame_buf) { shm_writer_close(&sw); deregister_slot(fc_url, slot_id); return 1; }
|
||||||
|
|
||||||
|
uint64_t frame_seq = 0;
|
||||||
|
int reported = 0;
|
||||||
|
|
||||||
|
fprintf(stderr, "[net_ingest] slot=%s %ux%u %.2ffps source=%s%s\n",
|
||||||
|
slot_id, width, height,
|
||||||
|
fps_den ? (double)fps_num / fps_den : 0.0,
|
||||||
|
source_type, listen ? " (listener)" : "");
|
||||||
|
|
||||||
|
/* Caller-owned arg buffers — reused each reconnect, no per-loop leak. */
|
||||||
|
char ff_url_buf[320];
|
||||||
|
char ff_vf_buf[64];
|
||||||
|
|
||||||
|
/* ── Outer reconnect loop (listener mode stays alive between sessions) */
|
||||||
|
while (!g_stop) {
|
||||||
|
/* Build ffmpeg argv (writes into ff_url_buf / ff_vf_buf, no strdup) */
|
||||||
|
char *ff_argv[64];
|
||||||
|
build_ffmpeg_args(ff_argv, 64, url, source_type,
|
||||||
|
listen, listen_port, stream_key, width, height,
|
||||||
|
ff_url_buf, sizeof ff_url_buf,
|
||||||
|
ff_vf_buf, sizeof ff_vf_buf);
|
||||||
|
|
||||||
|
/* Spawn ffmpeg with stdout pipe */
|
||||||
|
int pfd[2];
|
||||||
|
if (pipe(pfd) < 0) break;
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) { close(pfd[0]); close(pfd[1]); break; }
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
/* Child: redirect stdout to pipe write end */
|
||||||
|
dup2(pfd[1], STDOUT_FILENO);
|
||||||
|
close(pfd[0]); close(pfd[1]);
|
||||||
|
execvp("ffmpeg", ff_argv);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parent: read from pipe read end */
|
||||||
|
close(pfd[1]);
|
||||||
|
int rfd = pfd[0];
|
||||||
|
|
||||||
|
size_t buf_off = 0;
|
||||||
|
while (!g_stop) {
|
||||||
|
ssize_t n = read(rfd, frame_buf + buf_off, fsz - buf_off);
|
||||||
|
if (n <= 0) break; /* ffmpeg exited or pipe closed */
|
||||||
|
buf_off += (size_t)n;
|
||||||
|
if (buf_off < fsz) continue; /* incomplete frame — keep reading */
|
||||||
|
|
||||||
|
/* Full frame assembled */
|
||||||
|
uint64_t pts_us = fps_num > 0
|
||||||
|
? frame_seq * 1000000ULL * fps_den / fps_num
|
||||||
|
: 0;
|
||||||
|
shm_write_frame(&sw, frame_buf, (uint32_t)fsz, pts_us);
|
||||||
|
frame_seq++;
|
||||||
|
buf_off = 0;
|
||||||
|
|
||||||
|
if (!reported) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"{\"slot_id\":\"%s\",\"width\":%u,\"height\":%u,"
|
||||||
|
"\"fps_num\":%u,\"fps_den\":%u,"
|
||||||
|
"\"source_type\":\"%s\",\"pix_fmt\":\"uyvy422\"}\n",
|
||||||
|
slot_id, width, height, fps_num, fps_den, source_type);
|
||||||
|
fflush(stderr);
|
||||||
|
reported = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rfd);
|
||||||
|
/* Reap ffmpeg child */
|
||||||
|
int wstatus;
|
||||||
|
kill(pid, SIGTERM);
|
||||||
|
waitpid(pid, &wstatus, 0);
|
||||||
|
|
||||||
|
if (!listen || g_stop) break;
|
||||||
|
|
||||||
|
/* Listener mode: wait 1s then reconnect */
|
||||||
|
fprintf(stderr, "[net_ingest] listener: waiting for next connection\n");
|
||||||
|
struct timespec ts = { .tv_sec = 1 };
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(frame_buf);
|
||||||
|
shm_writer_close(&sw);
|
||||||
|
deregister_slot(fc_url, slot_id);
|
||||||
|
fprintf(stderr, "[net_ingest] done frames=%llu\n", (unsigned long long)frame_seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
108
services/framecache/src/registry.c
Normal file
108
services/framecache/src/registry.c
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* registry.c — In-memory slot registry + JSON persistence.
|
||||||
|
*/
|
||||||
|
#include "registry.h"
|
||||||
|
#include "slot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
fc_registry_entry_t g_registry[FC_MAX_SLOTS];
|
||||||
|
int g_registry_count = 0;
|
||||||
|
|
||||||
|
static const char *REGISTRY_JSON = "/dev/shm/framecache/registry.json";
|
||||||
|
|
||||||
|
void registry_add(struct fc_slot *slot)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (!g_registry[i].active) {
|
||||||
|
g_registry[i].active = 1;
|
||||||
|
g_registry[i].slot = slot;
|
||||||
|
strncpy(g_registry[i].slot_id, fc_slot_id(slot),
|
||||||
|
FC_MAX_SLOT_ID - 1);
|
||||||
|
g_registry_count++;
|
||||||
|
registry_write_json();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "[framecache] registry full (%d slots)\n", FC_MAX_SLOTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registry_remove(const char *slot_id)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (g_registry[i].active &&
|
||||||
|
strncmp(g_registry[i].slot_id, slot_id, FC_MAX_SLOT_ID) == 0)
|
||||||
|
{
|
||||||
|
g_registry[i].active = 0;
|
||||||
|
g_registry[i].slot = NULL;
|
||||||
|
g_registry[i].slot_id[0] = '\0';
|
||||||
|
g_registry_count--;
|
||||||
|
registry_write_json();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fc_slot *registry_find(const char *slot_id)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (g_registry[i].active &&
|
||||||
|
strncmp(g_registry[i].slot_id, slot_id, FC_MAX_SLOT_ID) == 0)
|
||||||
|
{
|
||||||
|
return g_registry[i].slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registry_write_json(void)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(REGISTRY_JSON, "w");
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
fprintf(f, "{\n \"version\": 1,\n \"slots\": {\n");
|
||||||
|
|
||||||
|
int first = 1;
|
||||||
|
for (int i = 0; i < FC_MAX_SLOTS; i++) {
|
||||||
|
if (!g_registry[i].active) continue;
|
||||||
|
fc_header_t *hdr = fc_slot_header(g_registry[i].slot);
|
||||||
|
|
||||||
|
char ts[32];
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *t = gmtime(&now);
|
||||||
|
strftime(ts, sizeof ts, "%Y-%m-%dT%H:%M:%SZ", t);
|
||||||
|
|
||||||
|
if (!first) fprintf(f, ",\n");
|
||||||
|
first = 0;
|
||||||
|
|
||||||
|
fprintf(f,
|
||||||
|
" \"%s\": {\n"
|
||||||
|
" \"shm_path\": \"%s\",\n"
|
||||||
|
" \"sem_name\": \"%s\",\n"
|
||||||
|
" \"width\": %u,\n"
|
||||||
|
" \"height\": %u,\n"
|
||||||
|
" \"fps_num\": %u,\n"
|
||||||
|
" \"fps_den\": %u,\n"
|
||||||
|
" \"pixel_format\": \"UYVY422\",\n"
|
||||||
|
" \"source_type\": \"%s\",\n"
|
||||||
|
" \"frame_size\": %u,\n"
|
||||||
|
" \"ring_depth\": %u,\n"
|
||||||
|
" \"created_at\": \"%s\"\n"
|
||||||
|
" }",
|
||||||
|
g_registry[i].slot_id,
|
||||||
|
fc_slot_shm_path(g_registry[i].slot),
|
||||||
|
fc_slot_sem_name(g_registry[i].slot),
|
||||||
|
hdr->width, hdr->height,
|
||||||
|
hdr->fps_num, hdr->fps_den,
|
||||||
|
hdr->source_type,
|
||||||
|
hdr->frame_size,
|
||||||
|
hdr->ring_depth,
|
||||||
|
ts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\n }\n}\n");
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
21
services/framecache/src/registry.h
Normal file
21
services/framecache/src/registry.h
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include "slot.h"
|
||||||
|
|
||||||
|
/* Maximum number of concurrent slots */
|
||||||
|
#define FC_MAX_SLOTS 256
|
||||||
|
|
||||||
|
/* Registry entry (in-memory) */
|
||||||
|
typedef struct {
|
||||||
|
int active;
|
||||||
|
struct fc_slot *slot;
|
||||||
|
char slot_id[FC_MAX_SLOT_ID];
|
||||||
|
} fc_registry_entry_t;
|
||||||
|
|
||||||
|
/* Global registry — managed by framecache.c */
|
||||||
|
extern fc_registry_entry_t g_registry[FC_MAX_SLOTS];
|
||||||
|
extern int g_registry_count;
|
||||||
|
|
||||||
|
void registry_add(struct fc_slot *slot);
|
||||||
|
void registry_remove(const char *slot_id);
|
||||||
|
struct fc_slot *registry_find(const char *slot_id);
|
||||||
|
void registry_write_json(void); /* writes /dev/shm/framecache/registry.json */
|
||||||
232
services/framecache/src/slot.c
Normal file
232
services/framecache/src/slot.c
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* slot.c — Framecache slot lifecycle: create, destroy, open.
|
||||||
|
*/
|
||||||
|
#include "slot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define SHM_DIR "/dev/shm/framecache"
|
||||||
|
#define SEM_PREFIX "/framecache-"
|
||||||
|
#define SEM_SUFFIX "-write"
|
||||||
|
|
||||||
|
/* Internal handle used by both server (writer) and client (reader) */
|
||||||
|
struct fc_slot {
|
||||||
|
int shm_fd;
|
||||||
|
void *base;
|
||||||
|
size_t shm_size;
|
||||||
|
sem_t *sem;
|
||||||
|
char slot_id[FC_MAX_SLOT_ID];
|
||||||
|
char shm_path[128];
|
||||||
|
char sem_name[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── helpers ─────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
static void build_paths(const char *slot_id,
|
||||||
|
char *shm_path, size_t sp_len,
|
||||||
|
char *sem_name, size_t sn_len)
|
||||||
|
{
|
||||||
|
snprintf(shm_path, sp_len, "%s/%s", SHM_DIR, slot_id);
|
||||||
|
snprintf(sem_name, sn_len, "%s%s%s", SEM_PREFIX, slot_id, SEM_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── server-side: create / destroy ───────────────────────────────────── */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new slot. Allocates and initialises the shm region.
|
||||||
|
* Returns handle on success, NULL on error (errno set).
|
||||||
|
*/
|
||||||
|
struct fc_slot *fc_slot_create(const char *slot_id,
|
||||||
|
uint32_t width, uint32_t height,
|
||||||
|
uint32_t fps_num, uint32_t fps_den,
|
||||||
|
uint32_t pixel_format,
|
||||||
|
const char *source_type)
|
||||||
|
{
|
||||||
|
char shm_path[128], sem_name[128];
|
||||||
|
build_paths(slot_id, shm_path, sizeof shm_path, sem_name, sizeof sem_name);
|
||||||
|
|
||||||
|
uint32_t frame_size = width * height * 2; /* UYVY422 */
|
||||||
|
size_t total = fc_slot_shm_size(frame_size);
|
||||||
|
|
||||||
|
/* Ensure directory exists */
|
||||||
|
mkdir(SHM_DIR, 0755);
|
||||||
|
|
||||||
|
/* Create shm file */
|
||||||
|
int fd = open(shm_path, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("[framecache] open shm");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ftruncate(fd, (off_t)total) < 0) {
|
||||||
|
perror("[framecache] ftruncate");
|
||||||
|
close(fd);
|
||||||
|
unlink(shm_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *base = mmap(NULL, total, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (base == MAP_FAILED) {
|
||||||
|
perror("[framecache] mmap");
|
||||||
|
close(fd);
|
||||||
|
unlink(shm_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(base, 0, total);
|
||||||
|
|
||||||
|
/* Initialise header */
|
||||||
|
fc_header_t *hdr = (fc_header_t *)base;
|
||||||
|
hdr->magic = FC_MAGIC;
|
||||||
|
hdr->version = FC_VERSION;
|
||||||
|
hdr->width = width;
|
||||||
|
hdr->height = height;
|
||||||
|
hdr->fps_num = fps_num;
|
||||||
|
hdr->fps_den = fps_den;
|
||||||
|
hdr->pixel_format = pixel_format;
|
||||||
|
hdr->frame_size = frame_size;
|
||||||
|
hdr->ring_depth = FC_RING_DEPTH;
|
||||||
|
atomic_store(&hdr->write_cursor, 0);
|
||||||
|
atomic_store(&hdr->dropped_frames, 0);
|
||||||
|
strncpy(hdr->source_type, source_type ? source_type : "unknown",
|
||||||
|
sizeof hdr->source_type - 1);
|
||||||
|
strncpy(hdr->slot_id, slot_id, sizeof hdr->slot_id - 1);
|
||||||
|
|
||||||
|
/* Create semaphore */
|
||||||
|
sem_unlink(sem_name); /* remove stale */
|
||||||
|
sem_t *sem = sem_open(sem_name, O_CREAT | O_EXCL, 0666, 0);
|
||||||
|
if (sem == SEM_FAILED) {
|
||||||
|
perror("[framecache] sem_open");
|
||||||
|
munmap(base, total);
|
||||||
|
close(fd);
|
||||||
|
unlink(shm_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fc_slot *s = calloc(1, sizeof *s);
|
||||||
|
if (!s) {
|
||||||
|
sem_close(sem); sem_unlink(sem_name);
|
||||||
|
munmap(base, total);
|
||||||
|
close(fd);
|
||||||
|
unlink(shm_path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
s->shm_fd = fd;
|
||||||
|
s->base = base;
|
||||||
|
s->shm_size = total;
|
||||||
|
s->sem = sem;
|
||||||
|
strncpy(s->slot_id, slot_id, sizeof s->slot_id - 1);
|
||||||
|
strncpy(s->shm_path, shm_path, sizeof s->shm_path - 1);
|
||||||
|
strncpy(s->sem_name, sem_name, sizeof s->sem_name - 1);
|
||||||
|
|
||||||
|
fprintf(stderr, "[framecache] slot created: %s (%ux%u %.2ffps %zuMB)\n",
|
||||||
|
slot_id, width, height,
|
||||||
|
fps_den ? (double)fps_num / fps_den : 0.0,
|
||||||
|
total / 1024 / 1024);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a slot: unmap, close fd, delete files, free handle.
|
||||||
|
*/
|
||||||
|
void fc_slot_destroy(struct fc_slot *s)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
sem_close(s->sem);
|
||||||
|
sem_unlink(s->sem_name);
|
||||||
|
munmap(s->base, s->shm_size);
|
||||||
|
close(s->shm_fd);
|
||||||
|
unlink(s->shm_path);
|
||||||
|
fprintf(stderr, "[framecache] slot destroyed: %s\n", s->slot_id);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── writer: called by ingest bridges ───────────────────────────────── */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write one frame into the ring. Never blocks — advances write_cursor
|
||||||
|
* atomically and posts the semaphore. Slow consumers will be skipped.
|
||||||
|
*/
|
||||||
|
void fc_slot_write_frame(struct fc_slot *s,
|
||||||
|
const uint8_t *data, uint32_t size,
|
||||||
|
uint64_t pts_us)
|
||||||
|
{
|
||||||
|
fc_header_t *hdr = (fc_header_t *)s->base;
|
||||||
|
uint64_t cur = atomic_load_explicit(&hdr->write_cursor, memory_order_relaxed);
|
||||||
|
fc_frame_t *frame = fc_frame_at(s->base, hdr->frame_size, cur);
|
||||||
|
|
||||||
|
frame->pts_us = pts_us;
|
||||||
|
frame->wall_us = (uint64_t)({ struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; });
|
||||||
|
frame->size = size < hdr->frame_size ? size : hdr->frame_size;
|
||||||
|
memcpy(frame->data, data, frame->size);
|
||||||
|
|
||||||
|
atomic_store_explicit(&hdr->write_cursor, cur + 1, memory_order_release);
|
||||||
|
sem_post(s->sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessors used by HTTP API */
|
||||||
|
fc_header_t *fc_slot_header(struct fc_slot *s) { return (fc_header_t *)s->base; }
|
||||||
|
const char *fc_slot_id(struct fc_slot *s) { return s->slot_id; }
|
||||||
|
const char *fc_slot_shm_path(struct fc_slot *s) { return s->shm_path; }
|
||||||
|
const char *fc_slot_sem_name(struct fc_slot *s) { return s->sem_name; }
|
||||||
|
|
||||||
|
/* ── client-side open / read / close (also used by capture-manager) ── */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open an existing slot for reading.
|
||||||
|
* Returns NULL if slot not found or header magic mismatch.
|
||||||
|
*/
|
||||||
|
struct fc_slot *fc_slot_open(const char *slot_id)
|
||||||
|
{
|
||||||
|
char shm_path[128], sem_name[128];
|
||||||
|
build_paths(slot_id, shm_path, sizeof shm_path, sem_name, sizeof sem_name);
|
||||||
|
|
||||||
|
int fd = open(shm_path, O_RDONLY);
|
||||||
|
if (fd < 0) return NULL;
|
||||||
|
|
||||||
|
/* Read header first to get frame_size */
|
||||||
|
fc_header_t tmp_hdr;
|
||||||
|
if (pread(fd, &tmp_hdr, sizeof tmp_hdr, 0) != sizeof tmp_hdr) {
|
||||||
|
close(fd); return NULL;
|
||||||
|
}
|
||||||
|
if (tmp_hdr.magic != FC_MAGIC) {
|
||||||
|
close(fd); return NULL;
|
||||||
|
}
|
||||||
|
size_t total = fc_slot_shm_size(tmp_hdr.frame_size);
|
||||||
|
|
||||||
|
void *base = mmap(NULL, total, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (base == MAP_FAILED) { close(fd); return NULL; }
|
||||||
|
|
||||||
|
sem_t *sem = sem_open(sem_name, 0);
|
||||||
|
if (sem == SEM_FAILED) { munmap(base, total); close(fd); return NULL; }
|
||||||
|
|
||||||
|
struct fc_slot *s = calloc(1, sizeof *s);
|
||||||
|
if (!s) { sem_close(sem); munmap(base, total); close(fd); return NULL; }
|
||||||
|
s->shm_fd = fd;
|
||||||
|
s->base = base;
|
||||||
|
s->shm_size = total;
|
||||||
|
s->sem = sem;
|
||||||
|
strncpy(s->slot_id, slot_id, sizeof s->slot_id - 1);
|
||||||
|
strncpy(s->shm_path, shm_path, sizeof s->shm_path - 1);
|
||||||
|
strncpy(s->sem_name, sem_name, sizeof s->sem_name - 1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a client-side slot handle. Does not destroy the slot.
|
||||||
|
*/
|
||||||
|
void fc_slot_close(struct fc_slot *s)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
sem_close(s->sem);
|
||||||
|
munmap(s->base, s->shm_size);
|
||||||
|
close(s->shm_fd);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
76
services/framecache/src/slot.h
Normal file
76
services/framecache/src/slot.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* slot.h — Framecache shared memory slot definitions.
|
||||||
|
*
|
||||||
|
* Layout per slot (/dev/shm/framecache/<slot_id>):
|
||||||
|
* [fc_header_t — 4KB aligned]
|
||||||
|
* [fc_frame_t × ring_depth — each FC_FRAME_HDR_SIZE + frame_size bytes]
|
||||||
|
*
|
||||||
|
* Writer advances write_cursor atomically and posts the named semaphore.
|
||||||
|
* Each consumer tracks its own read_cursor independently — writer never blocks.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
#define FC_MAGIC 0x46524D43u /* "FRMC" */
|
||||||
|
#define FC_VERSION 1u
|
||||||
|
#define FC_RING_DEPTH 120u /* ~2s at 59.94fps */
|
||||||
|
#define FC_HEADER_SIZE 4096u /* 4KB header block */
|
||||||
|
#define FC_FRAME_HDR_SIZE 24u /* pts_us(8) + wall_us(8) + size(4) + pad(4) */
|
||||||
|
#define FC_MAX_SLOT_ID 64u
|
||||||
|
|
||||||
|
/* Pixel format codes */
|
||||||
|
#define FC_PIX_UYVY422 0u
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic; /* FC_MAGIC */
|
||||||
|
uint32_t version; /* FC_VERSION */
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t fps_num;
|
||||||
|
uint32_t fps_den;
|
||||||
|
uint32_t pixel_format; /* FC_PIX_UYVY422 */
|
||||||
|
uint32_t frame_size; /* width * height * 2 */
|
||||||
|
uint32_t ring_depth; /* FC_RING_DEPTH */
|
||||||
|
uint32_t _reserved;
|
||||||
|
_Atomic uint64_t write_cursor; /* monotonically increasing frame index */
|
||||||
|
_Atomic uint64_t dropped_frames;
|
||||||
|
char source_type[32]; /* "deltacast" | "blackmagic" | "srt" | "rtmp" */
|
||||||
|
char slot_id[FC_MAX_SLOT_ID];
|
||||||
|
uint8_t _pad[FC_HEADER_SIZE - 112];
|
||||||
|
} fc_header_t;
|
||||||
|
|
||||||
|
/* Per-frame metadata + data (variable length — use fc_frame_at() accessor) */
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pts_us;
|
||||||
|
uint64_t wall_us;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t _pad;
|
||||||
|
uint8_t data[]; /* frame_size bytes */
|
||||||
|
} fc_frame_t;
|
||||||
|
|
||||||
|
/* Compile-time size check */
|
||||||
|
_Static_assert(sizeof(fc_header_t) == FC_HEADER_SIZE,
|
||||||
|
"fc_header_t must be exactly FC_HEADER_SIZE bytes");
|
||||||
|
_Static_assert(sizeof(fc_frame_t) == FC_FRAME_HDR_SIZE,
|
||||||
|
"fc_frame_t header must be exactly FC_FRAME_HDR_SIZE bytes");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute total shm size for a slot given frame_size.
|
||||||
|
* = FC_HEADER_SIZE + ring_depth * (FC_FRAME_HDR_SIZE + frame_size)
|
||||||
|
*/
|
||||||
|
static inline size_t fc_slot_shm_size(uint32_t frame_size) {
|
||||||
|
return (size_t)FC_HEADER_SIZE
|
||||||
|
+ (size_t)FC_RING_DEPTH * ((size_t)FC_FRAME_HDR_SIZE + frame_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return pointer to frame at ring index idx within a mapped shm base.
|
||||||
|
*/
|
||||||
|
static inline fc_frame_t *fc_frame_at(void *base, uint32_t frame_size, uint64_t idx) {
|
||||||
|
uint8_t *frames = (uint8_t *)base + FC_HEADER_SIZE;
|
||||||
|
return (fc_frame_t *)(frames + (idx % FC_RING_DEPTH)
|
||||||
|
* ((size_t)FC_FRAME_HDR_SIZE + frame_size));
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { randomBytes, createHash } from 'node:crypto';
|
import { randomBytes, createHash, timingSafeEqual } from 'node:crypto';
|
||||||
|
|
||||||
const PREFIX = 'dfl_';
|
const PREFIX = 'dfl_';
|
||||||
|
|
||||||
|
|
@ -10,6 +10,14 @@ export function hashToken(token) {
|
||||||
return createHash('sha256').update(token).digest('hex');
|
return createHash('sha256').update(token).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function compareTokens(tokenA, tokenB) {
|
||||||
|
if (!tokenA || !tokenB) return false;
|
||||||
|
const a = Buffer.from(tokenA);
|
||||||
|
const b = Buffer.from(tokenB);
|
||||||
|
if (a.length !== b.length) return false;
|
||||||
|
return timingSafeEqual(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
export function parseBearer(authorizationHeader) {
|
export function parseBearer(authorizationHeader) {
|
||||||
if (!authorizationHeader || typeof authorizationHeader !== 'string') return null;
|
if (!authorizationHeader || typeof authorizationHeader !== 'string') return null;
|
||||||
const m = authorizationHeader.match(/^Bearer\s+(\S+)$/i);
|
const m = authorizationHeader.match(/^Bearer\s+(\S+)$/i);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- Migration 031 — Add last_seen_at to cluster_nodes
|
||||||
|
--
|
||||||
|
-- Playout failover (routes/playout.js restartChannel) queries cluster_nodes.last_seen_at
|
||||||
|
-- to find healthy nodes for channel re-placement. Column was missing from original
|
||||||
|
-- cluster schema; heartbeat endpoint updates it via /cluster/heartbeat.
|
||||||
|
|
||||||
|
ALTER TABLE cluster_nodes ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMPTZ;
|
||||||
|
|
||||||
|
-- Backfill existing nodes to NOW() so they're immediately eligible for failover
|
||||||
|
UPDATE cluster_nodes SET last_seen_at = NOW() WHERE last_seen_at IS NULL;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- Migration 032: Per-recorder GPU affinity (Issue #167)
|
||||||
|
-- Adds a nullable GPU UUID to the recorders table so each recorder can be
|
||||||
|
-- pinned to a specific GPU on its node. The value is passed through to the
|
||||||
|
-- node-agent sidecar-start payload and becomes NVIDIA_VISIBLE_DEVICES for the
|
||||||
|
-- capture container. NULL = legacy behavior (NVIDIA_VISIBLE_DEVICES=all, i.e.
|
||||||
|
-- every GPU visible). Accepts an nvidia-smi GPU UUID (e.g. "GPU-xxxx") or a
|
||||||
|
-- numeric index string.
|
||||||
|
|
||||||
|
ALTER TABLE recorders
|
||||||
|
ADD COLUMN IF NOT EXISTS gpu_uuid TEXT DEFAULT NULL;
|
||||||
52
services/mam-api/src/db/migrations/033-playout-scte.sql
Normal file
52
services/mam-api/src/db/migrations/033-playout-scte.sql
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
-- Migration 033 — SCTE-35 ad-break markers for playout.
|
||||||
|
--
|
||||||
|
-- Adds the missing SCTE-35 splice feature to the playout (MCR) subsystem. An
|
||||||
|
-- operator can either schedule an ad break on a channel's timeline (relative to
|
||||||
|
-- the active playlist position, or at a wall-clock time) or fire one immediately
|
||||||
|
-- ("splice now"). Each break is recorded here and, when fired, also written to
|
||||||
|
-- the append-only as-run log so it shows in the compliance record alongside the
|
||||||
|
-- clips that aired.
|
||||||
|
--
|
||||||
|
-- type:
|
||||||
|
-- splice_insert — a scheduled break (out → return), duration_s seconds long
|
||||||
|
-- immediate — fire-now splice (operator pressed "Trigger ad break now")
|
||||||
|
-- splice_out — open-ended avail out (provider break start)
|
||||||
|
-- splice_in — return-to-network (provider break end)
|
||||||
|
--
|
||||||
|
-- status: pending → fired (when the engine acts on it) → done (when the break
|
||||||
|
-- window has elapsed). cancelled is set if the operator removes a pending break.
|
||||||
|
--
|
||||||
|
-- The engine (services/playout) acts on a break by logging the cue, marking the
|
||||||
|
-- as-run row, and — where the output path supports it — injecting a real
|
||||||
|
-- SCTE-35 cue (see playout-manager.triggerScte for the injection point/TODO).
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS playout_scte_breaks (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
channel_id UUID NOT NULL REFERENCES playout_channels(id) ON DELETE CASCADE,
|
||||||
|
-- Position on the active playlist this break should fire after (0-based item
|
||||||
|
-- index). NULL for immediate/wall-clock breaks.
|
||||||
|
playlist_pos INTEGER,
|
||||||
|
-- Wall-clock fire time for scheduled breaks. NULL for immediate breaks.
|
||||||
|
scheduled_at TIMESTAMPTZ,
|
||||||
|
duration_s INTEGER NOT NULL DEFAULT 30,
|
||||||
|
-- SCTE-35 event id (the splice_event_id carried in the cue). Auto-assigned.
|
||||||
|
event_id INTEGER NOT NULL DEFAULT 1,
|
||||||
|
type TEXT NOT NULL DEFAULT 'splice_insert',
|
||||||
|
status TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
fired_at TIMESTAMPTZ,
|
||||||
|
created_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
CHECK (type IN ('splice_insert','immediate','splice_out','splice_in')),
|
||||||
|
CHECK (status IN ('pending','fired','done','cancelled'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_playout_scte_channel ON playout_scte_breaks (channel_id, created_at DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_playout_scte_status ON playout_scte_breaks (status);
|
||||||
|
|
||||||
|
-- As-run gains a 'scte' result so fired breaks land in the compliance log next to
|
||||||
|
-- the clips. The original migration constrained result to played/skipped/error;
|
||||||
|
-- widen it.
|
||||||
|
ALTER TABLE playout_as_run DROP CONSTRAINT IF EXISTS playout_as_run_result_check;
|
||||||
|
ALTER TABLE playout_as_run ADD CONSTRAINT playout_as_run_result_check
|
||||||
|
CHECK (result IN ('played','skipped','error','scte'));
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- 2026-06: add 'pending_migration' to asset_status enum for manual SMB-to-S3 promotion
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'pending_migration' AND enumtypid = 'asset_status'::regtype) THEN
|
||||||
|
ALTER TYPE asset_status ADD VALUE 'pending_migration';
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- Add 'starting' and 'stopping' to recorder_schedules status check constraint
|
||||||
|
|
||||||
|
ALTER TABLE recorder_schedules DROP CONSTRAINT recorder_schedules_status_check;
|
||||||
|
|
||||||
|
ALTER TABLE recorder_schedules
|
||||||
|
ADD CONSTRAINT recorder_schedules_status_check
|
||||||
|
CHECK (status IN ('pending','running','completed','failed','cancelled','starting','stopping'));
|
||||||
|
|
@ -1,4 +1,16 @@
|
||||||
import { Pool } from 'pg';
|
import { Pool, types } from 'pg';
|
||||||
|
|
||||||
|
// node-postgres returns BIGINT (int8, OID 20) as a *string* by default, because
|
||||||
|
// a 64-bit integer can exceed JS Number.MAX_SAFE_INTEGER. Our int8 columns
|
||||||
|
// (duration_ms, file_size, …) are always well within 2^53, so a string here is
|
||||||
|
// pure footgun: it breaks any consumer that does arithmetic or comparison on the
|
||||||
|
// value (e.g. `duration_ms + x` silently string-concatenates, sorts go
|
||||||
|
// lexicographic, `!ms`/`Math.round` edge cases). Parse int8 to a real Number so
|
||||||
|
// the API always emits numeric duration_ms/file_size in its JSON.
|
||||||
|
//
|
||||||
|
// 20 = int8/bigint OID. Values above Number.MAX_SAFE_INTEGER would lose
|
||||||
|
// precision, but no column in this schema ever reaches that range.
|
||||||
|
types.setTypeParser(20, (val) => (val === null ? null : parseInt(val, 10)));
|
||||||
|
|
||||||
// Prefer DATABASE_URL (set in docker-compose) over individual DB_* vars
|
// Prefer DATABASE_URL (set in docker-compose) over individual DB_* vars
|
||||||
const pool = process.env.DATABASE_URL
|
const pool = process.env.DATABASE_URL
|
||||||
|
|
|
||||||
|
|
@ -41,18 +41,12 @@ import { startCleanupLoop } from './tasks/cleanupTempSegments.js';
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
// ── Middleware ────────────────────────────────────────────────────────────────
|
|
||||||
// Tightened CORS — once cookies carry authority, `origin: true` would let
|
|
||||||
// any site forge requests with the cookie. Drive the allowlist from env.
|
|
||||||
const allowedOrigins = (process.env.ALLOWED_ORIGINS || '')
|
const allowedOrigins = (process.env.ALLOWED_ORIGINS || '')
|
||||||
.split(',').map(s => s.trim()).filter(Boolean);
|
.split(',').map(s => s.trim()).filter(Boolean);
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: (origin, cb) => {
|
origin: (origin, cb) => {
|
||||||
// No Origin header (same-origin or curl) — allow.
|
|
||||||
if (!origin) return cb(null, true);
|
if (!origin) return cb(null, true);
|
||||||
if (allowedOrigins.length === 0 || allowedOrigins.includes(origin)) return cb(null, true);
|
if (allowedOrigins.length === 0 || allowedOrigins.includes(origin)) return cb(null, true);
|
||||||
// Reject cleanly: omit the Allow-Origin header so the browser surfaces
|
|
||||||
// a real CORS error instead of a 500 from a thrown Error in the callback.
|
|
||||||
console.warn('[cors] rejected origin:', origin);
|
console.warn('[cors] rejected origin:', origin);
|
||||||
return cb(null, false);
|
return cb(null, false);
|
||||||
},
|
},
|
||||||
|
|
@ -60,14 +54,8 @@ app.use(cors({
|
||||||
}));
|
}));
|
||||||
app.use(express.json({ limit: '50mb' }));
|
app.use(express.json({ limit: '50mb' }));
|
||||||
|
|
||||||
// Trust the reverse proxy only when explicitly told to (production HTTPS).
|
|
||||||
if (process.env.TRUST_PROXY === 'true') app.set('trust proxy', 1);
|
if (process.env.TRUST_PROXY === 'true') app.set('trust proxy', 1);
|
||||||
|
|
||||||
// HSTS — once a browser has seen this header over HTTPS for dragonflight.live,
|
|
||||||
// it auto-upgrades every future http:// request to https:// before hitting the
|
|
||||||
// wire. Cookies are Secure-only (below) and the CORS allowlist rejects HTTP,
|
|
||||||
// so without HSTS a user who lands on http:// silently can't log in.
|
|
||||||
// Only emit on actual HTTPS responses; req.secure honors trust proxy + X-Forwarded-Proto.
|
|
||||||
if (process.env.AUTH_ENABLED === 'true') {
|
if (process.env.AUTH_ENABLED === 'true') {
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if (req.secure) res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
if (req.secure) res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||||
|
|
@ -75,17 +63,13 @@ if (process.env.AUTH_ENABLED === 'true') {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hard-fail when production-mode auth has no stable session secret. Without
|
|
||||||
// this, express-session falls back to an in-memory random secret which
|
|
||||||
// invalidates every session on restart and breaks multi-node deployments.
|
|
||||||
if (process.env.AUTH_ENABLED === 'true' && !process.env.SESSION_SECRET) {
|
if (process.env.AUTH_ENABLED === 'true' && !process.env.SESSION_SECRET) {
|
||||||
console.error('[fatal] SESSION_SECRET is required when AUTH_ENABLED=true');
|
console.error('[fatal] SESSION_SECRET is required when AUTH_ENABLED=true');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session — actually wired this time. See specs/2026-05-27-auth-system-design.md.
|
|
||||||
app.use(session({
|
app.use(session({
|
||||||
store: new PgStore({ pool, tableName: 'sessions', pruneSessionInterval: 60 * 15 /* seconds = 15 min */ }),
|
store: new PgStore({ pool, tableName: 'sessions', pruneSessionInterval: 60 * 15 }),
|
||||||
secret: process.env.SESSION_SECRET,
|
secret: process.env.SESSION_SECRET,
|
||||||
name: 'dragonflight.sid',
|
name: 'dragonflight.sid',
|
||||||
cookie: {
|
cookie: {
|
||||||
|
|
@ -95,36 +79,26 @@ app.use(session({
|
||||||
path: '/',
|
path: '/',
|
||||||
maxAge: 8 * 3600 * 1000,
|
maxAge: 8 * 3600 * 1000,
|
||||||
},
|
},
|
||||||
rolling: false, // sliding renewal handled in requireAuth so idle + absolute can be enforced separately
|
rolling: false,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: false,
|
saveUninitialized: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// ── Health ────────────────────────────────────────────────────────────────────
|
|
||||||
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
|
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
|
||||||
|
|
||||||
// ── Auth gate ─────────────────────────────────────────────────────────────────
|
|
||||||
// req.path is relative to the /api/v1 mount, so /auth/login NOT /api/v1/auth/login.
|
|
||||||
const UNAUTH_PATHS = new Set([
|
const UNAUTH_PATHS = new Set([
|
||||||
'/auth/login', '/auth/login/totp', '/auth/setup', '/auth/setup-required',
|
'/auth/login', '/auth/login/totp', '/auth/setup', '/auth/setup-required',
|
||||||
'/auth/google', '/auth/google/callback', '/auth/google/enabled',
|
'/auth/google', '/auth/google/callback', '/auth/google/enabled',
|
||||||
]);
|
]);
|
||||||
// node-agent now authenticates /cluster/heartbeat with a bound api_token
|
|
||||||
// (migration 019 + bound_hostname on the token). requireAuth handles the
|
|
||||||
// bearer lookup and sets req.tokenBoundHostname; the heartbeat handler in
|
|
||||||
// routes/cluster.js verifies body.hostname matches that binding.
|
|
||||||
app.use('/api/v1', requireUiHeader);
|
app.use('/api/v1', requireUiHeader);
|
||||||
app.use('/api/v1', (req, res, next) => {
|
app.use('/api/v1', (req, res, next) => {
|
||||||
if (UNAUTH_PATHS.has(req.path)) return next();
|
if (UNAUTH_PATHS.has(req.path)) return next();
|
||||||
return requireAuth(req, res, next);
|
return requireAuth(req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── API Routes ────────────────────────────────────────────────────────────────
|
|
||||||
app.use('/api/v1/auth', authRouter);
|
app.use('/api/v1/auth', authRouter);
|
||||||
// User and group administration is admin-only (RBAC v2). The auth gate above
|
|
||||||
// already established req.user; requireAdmin rejects non-admins with 403.
|
|
||||||
app.use('/api/v1/auth/users', requireAdmin, usersRouter);
|
app.use('/api/v1/auth/users', requireAdmin, usersRouter);
|
||||||
app.use('/api/v1/users', requireAdmin, usersRouter); // alias for the SPA Users page
|
app.use('/api/v1/users', requireAdmin, usersRouter);
|
||||||
app.use('/api/v1/auth/tokens', requireAuth, tokensRouter);
|
app.use('/api/v1/auth/tokens', requireAuth, tokensRouter);
|
||||||
app.use('/api/v1/assets', assetsRouter);
|
app.use('/api/v1/assets', assetsRouter);
|
||||||
app.use('/api/v1/projects', projectsRouter);
|
app.use('/api/v1/projects', projectsRouter);
|
||||||
|
|
@ -147,21 +121,14 @@ app.use('/api/v1/assets/:assetId/comments', commentsRouter);
|
||||||
app.use('/api/v1/imports', importsRouter);
|
app.use('/api/v1/imports', importsRouter);
|
||||||
app.use('/api/v1/storage', storageRouter);
|
app.use('/api/v1/storage', storageRouter);
|
||||||
|
|
||||||
// ── Error handler ─────────────────────────────────────────────────────────────
|
|
||||||
app.use(errorHandler);
|
app.use(errorHandler);
|
||||||
|
|
||||||
// ── Start ────────────────────────────────────────────────────────────────────
|
|
||||||
import { readdirSync, readFileSync } from 'node:fs';
|
import { readdirSync, readFileSync } from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { dirname, join } from 'node:path';
|
import { dirname, join } from 'node:path';
|
||||||
|
|
||||||
const __dirnameMig = dirname(fileURLToPath(import.meta.url));
|
const __dirnameMig = dirname(fileURLToPath(import.meta.url));
|
||||||
async function runMigrations() {
|
async function runMigrations() {
|
||||||
// Issue #107 — previously the loop swallowed errors and let the server boot
|
|
||||||
// on a half-migrated schema. Now: track applied migrations in a table, run
|
|
||||||
// every pending one inside a transaction, and exit non-zero on failure so
|
|
||||||
// the orchestrator restarts (and so an operator notices) instead of serving
|
|
||||||
// 500s for the next month.
|
|
||||||
const dir = join(__dirnameMig, 'db', 'migrations');
|
const dir = join(__dirnameMig, 'db', 'migrations');
|
||||||
let files = [];
|
let files = [];
|
||||||
try { files = readdirSync(dir).filter(f => f.endsWith('.sql')).sort(); } catch { return; }
|
try { files = readdirSync(dir).filter(f => f.endsWith('.sql')).sort(); } catch { return; }
|
||||||
|
|
@ -174,7 +141,6 @@ async function runMigrations() {
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Allow forcing a re-run via env when iterating locally.
|
|
||||||
const force = process.env.MIGRATIONS_FORCE === '1';
|
const force = process.env.MIGRATIONS_FORCE === '1';
|
||||||
const allowFailures = process.env.MIGRATIONS_ALLOW_FAILURES === '1';
|
const allowFailures = process.env.MIGRATIONS_ALLOW_FAILURES === '1';
|
||||||
|
|
||||||
|
|
@ -200,7 +166,6 @@ async function runMigrations() {
|
||||||
console.error('[migration] FAILED ' + f + ': ' + err.message);
|
console.error('[migration] FAILED ' + f + ': ' + err.message);
|
||||||
client.release();
|
client.release();
|
||||||
if (allowFailures) continue;
|
if (allowFailures) continue;
|
||||||
// Hard fail — better to crash now than serve traffic on a broken schema.
|
|
||||||
console.error('[migration] aborting startup. Set MIGRATIONS_ALLOW_FAILURES=1 to override.');
|
console.error('[migration] aborting startup. Set MIGRATIONS_ALLOW_FAILURES=1 to override.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -209,13 +174,9 @@ async function runMigrations() {
|
||||||
}
|
}
|
||||||
await runMigrations();
|
await runMigrations();
|
||||||
|
|
||||||
// Load S3 config from DB so any settings saved via the Settings page override env vars
|
|
||||||
await loadS3ConfigFromDb();
|
await loadS3ConfigFromDb();
|
||||||
|
|
||||||
// ── Cluster self-heartbeat ────────────────────────────────────────────────────
|
|
||||||
function getLocalIp() {
|
function getLocalIp() {
|
||||||
// Prefer an explicit override — useful when running inside Docker where
|
|
||||||
// os.networkInterfaces() returns container bridge IPs, not the host LAN IP.
|
|
||||||
if (process.env.NODE_IP) return process.env.NODE_IP;
|
if (process.env.NODE_IP) return process.env.NODE_IP;
|
||||||
|
|
||||||
const ifaces = os.networkInterfaces();
|
const ifaces = os.networkInterfaces();
|
||||||
|
|
@ -227,9 +188,6 @@ function getLocalIp() {
|
||||||
return '127.0.0.1';
|
return '127.0.0.1';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect NVIDIA GPUs available to this container via nvidia-smi.
|
|
||||||
// Returns an array like [{ index: 0, name: 'Tesla P4', memory_mb: 7680 }, ...]
|
|
||||||
// or an empty array if nvidia-smi is unavailable or no GPUs found.
|
|
||||||
function detectGpus() {
|
function detectGpus() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
exec(
|
exec(
|
||||||
|
|
@ -251,6 +209,10 @@ function detectGpus() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Primary mam-api node self-registers in cluster_nodes every 30s. Must write
|
||||||
|
// BOTH last_seen (legacy column) and last_seen_at (added by mig 031, used by
|
||||||
|
// playout failover) — otherwise the primary appears stale to the failover
|
||||||
|
// query and channels get re-placed off it incorrectly.
|
||||||
async function selfHeartbeat() {
|
async function selfHeartbeat() {
|
||||||
const load = os.loadavg()[0];
|
const load = os.loadavg()[0];
|
||||||
const total = os.totalmem();
|
const total = os.totalmem();
|
||||||
|
|
@ -262,14 +224,15 @@ async function selfHeartbeat() {
|
||||||
pool.query(
|
pool.query(
|
||||||
`INSERT INTO cluster_nodes
|
`INSERT INTO cluster_nodes
|
||||||
(hostname, ip_address, role, version, api_url,
|
(hostname, ip_address, role, version, api_url,
|
||||||
cpu_usage, mem_used_mb, mem_total_mb, capabilities, last_seen)
|
cpu_usage, mem_used_mb, mem_total_mb, capabilities, last_seen, last_seen_at)
|
||||||
VALUES ($1,$2,'primary',$3,$4,$5,$6,$7,$8,NOW())
|
VALUES ($1,$2,'primary',$3,$4,$5,$6,$7,$8,NOW(),NOW())
|
||||||
ON CONFLICT (hostname) DO UPDATE SET
|
ON CONFLICT (hostname) DO UPDATE SET
|
||||||
ip_address = EXCLUDED.ip_address,
|
ip_address = EXCLUDED.ip_address,
|
||||||
cpu_usage = EXCLUDED.cpu_usage,
|
cpu_usage = EXCLUDED.cpu_usage,
|
||||||
mem_used_mb = EXCLUDED.mem_used_mb,
|
mem_used_mb = EXCLUDED.mem_used_mb,
|
||||||
mem_total_mb = EXCLUDED.mem_total_mb,
|
mem_total_mb = EXCLUDED.mem_total_mb,
|
||||||
capabilities = EXCLUDED.capabilities,
|
capabilities = EXCLUDED.capabilities,
|
||||||
|
last_seen_at = NOW(),
|
||||||
last_seen = NOW()`,
|
last_seen = NOW()`,
|
||||||
[
|
[
|
||||||
process.env.NODE_HOSTNAME || os.hostname(),
|
process.env.NODE_HOSTNAME || os.hostname(),
|
||||||
|
|
@ -294,39 +257,26 @@ const server = app.listen(PORT, () => {
|
||||||
if (process.env.AUTH_ENABLED === 'true' && process.env.TRUST_PROXY !== 'true') {
|
if (process.env.AUTH_ENABLED === 'true' && process.env.TRUST_PROXY !== 'true') {
|
||||||
console.warn('[auth] WARNING: AUTH_ENABLED=true but TRUST_PROXY=false — req.ip will be the proxy IP, login rate-limit will throttle all clients together. Set TRUST_PROXY=true when behind nginx/HTTPS.');
|
console.warn('[auth] WARNING: AUTH_ENABLED=true but TRUST_PROXY=false — req.ip will be the proxy IP, login rate-limit will throttle all clients together. Set TRUST_PROXY=true when behind nginx/HTTPS.');
|
||||||
}
|
}
|
||||||
// Boot the recorder scheduler tick loop after the HTTP server is live so
|
|
||||||
// the loop's self-calls to /recorders/:id/start|stop reach a ready socket.
|
|
||||||
startSchedulerLoop();
|
startSchedulerLoop();
|
||||||
|
|
||||||
// Boot the temp-segment cleanup loop (runs hourly).
|
|
||||||
startCleanupLoop();
|
startCleanupLoop();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Issue #100 — graceful shutdown. Without this, `docker stop` (SIGTERM) killed
|
|
||||||
// the process mid-scheduler-tick, leaving Redis connections and Docker
|
|
||||||
// sockets dangling and producing partial DB writes. Now: stop the scheduler,
|
|
||||||
// finish in-flight HTTP requests, close PG/Redis pools, and exit cleanly
|
|
||||||
// (or hard-exit after 25 s if something is stuck).
|
|
||||||
let _shuttingDown = false;
|
let _shuttingDown = false;
|
||||||
async function gracefulShutdown(signal) {
|
async function gracefulShutdown(signal) {
|
||||||
if (_shuttingDown) return;
|
if (_shuttingDown) return;
|
||||||
_shuttingDown = true;
|
_shuttingDown = true;
|
||||||
console.log(`[shutdown] received ${signal} — closing gracefully…`);
|
console.log(`[shutdown] received ${signal} — closing gracefully…`);
|
||||||
|
|
||||||
// Stop accepting new requests + wind down the scheduler tick.
|
|
||||||
try { stopSchedulerLoop(); } catch (_) {}
|
try { stopSchedulerLoop(); } catch (_) {}
|
||||||
|
|
||||||
// Force-exit watchdog so a hung connection can't keep us alive forever.
|
|
||||||
const killSwitch = setTimeout(() => {
|
const killSwitch = setTimeout(() => {
|
||||||
console.error('[shutdown] forced exit after 25s timeout');
|
console.error('[shutdown] forced exit after 25s timeout');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}, 25_000);
|
}, 25_000);
|
||||||
killSwitch.unref();
|
killSwitch.unref();
|
||||||
|
|
||||||
// Stop the HTTP server (waits for in-flight requests to finish).
|
|
||||||
await new Promise(resolve => server.close(resolve));
|
await new Promise(resolve => server.close(resolve));
|
||||||
|
|
||||||
// Close DB pool + S3 client + any other resources. Best-effort.
|
|
||||||
try { await pool.end(); } catch (e) { console.warn('[shutdown] pool.end:', e.message); }
|
try { await pool.end(); } catch (e) { console.warn('[shutdown] pool.end:', e.message); }
|
||||||
|
|
||||||
console.log('[shutdown] clean exit');
|
console.log('[shutdown] clean exit');
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue