Compare commits
376 commits
feat/premi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 19f0abeabe | |||
| f21bc490e8 | |||
| 7451d7c703 | |||
| d3ad2397fb | |||
| 24d10fda5d | |||
| 59551f28a5 | |||
| e0e0b83810 | |||
| 5968d4f681 | |||
| 3122dfd1b9 | |||
| 3b2f9fe6a0 | |||
| 869ae1aa83 | |||
| abfbd034ab | |||
| 739d08d4b5 | |||
| 27a868aa5c | |||
| 1db0e81efb | |||
| 40e987b4a2 | |||
| 426273129d | |||
| 87d988810f | |||
| f28799317d | |||
| d778aa4cdb | |||
| 00b04aa4a8 | |||
| e8f91cf4b4 | |||
| e51cf1aa9c | |||
| f7cf56ae0d | |||
| 12115a053a | |||
| 43656a5e88 | |||
| 68461af990 | |||
| 8bc460025d | |||
| 3578c7b4e9 | |||
| cddcc9a29e | |||
| 0e844c0fc3 | |||
| 551af09dc7 | |||
| 4d6a999665 | |||
| f971d57bb9 | |||
| 7ab70948a0 | |||
| 13bbd4216e | |||
| fcd8e8dd2e | |||
| 67ac007706 | |||
| b4f2fb12ff | |||
| aa7f836493 | |||
| c2409bd037 | |||
| 42064acefa | |||
| 2e2b091653 | |||
|
|
c502d4a16f | ||
|
|
9d098e9778 | ||
|
|
02631f7b96 | ||
|
|
9436434599 | ||
|
|
f837e57969 | ||
|
|
ca71e47035 | ||
|
|
34352e3299 | ||
|
|
d505a488ac | ||
|
|
793011b78b | ||
|
|
5538683d78 | ||
|
|
d62af34e98 | ||
|
|
209f9fda52 | ||
|
|
29187a90df | ||
|
|
512267159a | ||
|
|
72fc608d8a | ||
|
|
3fe7d6bba2 | ||
|
|
2615143c6d | ||
|
|
0c3a4b625f | ||
|
|
fff0828d79 | ||
|
|
ec026195eb | ||
| 9d6bbf8112 | |||
| b449ef0ce3 | |||
| 39ef551489 | |||
| 8f26f1bd9a | |||
| a7ef0397e1 | |||
| cf1fe136d0 | |||
| 0818f15498 | |||
| 4473427515 | |||
| 9b47250388 | |||
| 8ea750f5df | |||
| a28dc43ed5 | |||
| 35fd9c0253 | |||
| 0ee0cb91ef | |||
| 9210b41589 | |||
| f2542bc929 | |||
| 68c8f47c8f | |||
|
|
17bf086ef2 | ||
| 0f6c715a30 | |||
| fdec2e307d | |||
|
|
dac5213354 | ||
|
|
3f203f326e | ||
| 92b460f503 | |||
|
|
7e9f1277d4 | ||
|
|
9d8adbbbc1 | ||
|
|
3430ef823e | ||
| 500599a955 | |||
| 634f1842bd | |||
|
|
08a0fb1b60 | ||
| 453103aee6 | |||
|
|
dd438b597a | ||
|
|
8746d71af1 | ||
| 6f64b55824 | |||
| 303f12e0f9 | |||
| 342b56af35 | |||
| f54c49d2dc | |||
| 888ca65045 | |||
| b6f5b9b407 | |||
| 354731a363 | |||
|
|
6a161c7133 | ||
|
|
1fcb927d26 | ||
|
|
6bc6478270 | ||
|
|
446a563647 | ||
|
|
71d8944a01 | ||
|
|
686b90294b | ||
|
|
fcf4c8bbe7 | ||
|
|
94b6710e2d | ||
|
|
6412b5c252 | ||
|
|
56d7479a35 | ||
|
|
aeecb6e32a | ||
|
|
0abef056e7 | ||
|
|
540d333758 | ||
|
|
e4e69973e5 | ||
|
|
c3b087020d | ||
|
|
c2a6c1557b | ||
|
|
b04882a310 | ||
| 60e5093c6b | |||
| 382f432693 | |||
|
|
e533566ae2 | ||
| c3e4306d9f | |||
| eeb0d9f65f | |||
| 7f0ca5922f | |||
| 469521d524 | |||
| 07840441b9 | |||
| 5774f61ac7 | |||
| 1fb790a569 | |||
| 11cb93aa51 | |||
| dbc67636b2 | |||
| 460b590d46 | |||
| f3a640a7c5 | |||
| baa289f6c3 | |||
| e5f218655e | |||
| 8119b57b45 | |||
| 9765fd91f7 | |||
| a25e4b6071 | |||
| 046d99f57a | |||
| fcc737e05b | |||
| 8f93302f45 | |||
| 17ca9bfc75 | |||
| f8fa0fa010 | |||
| 907058de83 | |||
| bfe0316067 | |||
| 5d94838830 | |||
| 76fff5efc2 | |||
| 5432c2dfa1 | |||
| b3b2655272 | |||
| 16366267c4 | |||
| 066718c968 | |||
| 60d0b09c63 | |||
| 2608d7a465 | |||
| cd18988d6d | |||
| be57eb0a50 | |||
| 25356ca439 | |||
|
|
4bea3c94f8 | ||
|
|
f1a3d6a24a | ||
|
|
91e4691230 | ||
|
|
8b48f03f6b | ||
|
|
9085835074 | ||
|
|
f5959620c8 | ||
|
|
e3afe38697 | ||
|
|
e7eff0ee8c | ||
|
|
e8ceb991a3 | ||
|
|
ac7730195d | ||
|
|
c24c6156dc | ||
|
|
7e3e6b2a28 | ||
| 5571768706 | |||
| 350c23f9d1 | |||
|
|
8028c4c4dd | ||
| e6da1432e5 | |||
| e22cf625bf | |||
| 552506ec7a | |||
|
|
e5c9c770d0 | ||
| a0a6bc9f20 | |||
|
|
c8e98ffa0d | ||
| 9dc572b913 | |||
|
|
14ece1a160 | ||
|
|
03d0d098f5 | ||
|
|
8ede44ae87 | ||
|
|
2aec4636cb | ||
|
|
cfe21e315e | ||
|
|
7e240d86c8 | ||
|
|
96effaaa3c | ||
|
|
d209a192c3 | ||
|
|
56b661ef65 | ||
|
|
b7f5a84d2d | ||
|
|
0bbaf80d2a | ||
|
|
d75a0241eb | ||
|
|
bcfc19e530 | ||
|
|
f8b6f7d5ef | ||
|
|
c9f9698b58 | ||
|
|
49a9543942 | ||
|
|
cb7cc9a43e | ||
|
|
9de4fe9ab9 | ||
|
|
88c3aa5149 | ||
|
|
a094df03ea | ||
|
|
1a723fe4c2 | ||
|
|
0248a68f57 | ||
|
|
3bca290e09 | ||
|
|
3fc8116dd3 | ||
|
|
14931d6362 | ||
|
|
1d3c0385dd | ||
|
|
5011d45391 | ||
|
|
99fae69960 | ||
| 1e51a4ca5d | |||
|
|
c2fd48b0ce | ||
|
|
183e10f8e6 | ||
| ad9e1ef5f1 | |||
| ada8105948 | |||
| c84519b606 | |||
| 33239a780e | |||
| 7a6113fc90 | |||
| de311321f4 | |||
| c48c7e6d7d | |||
| 48d54a32cf | |||
| 4172b0d70a | |||
|
|
9726dbb2df | ||
|
|
002e5acb82 | ||
| a48e1d9dd7 | |||
|
|
d1f9557dd1 | ||
| 34bf1c7b7f | |||
|
|
e71c330bdd | ||
| 5de1e3dc3d | |||
| e5e0656a6a | |||
|
|
65684aa577 | ||
|
|
cfcbec0c85 | ||
|
|
a86c1c72f9 | ||
|
|
04ce096e67 | ||
| 64d739b40d | |||
|
|
1535bbaefa | ||
|
|
a44d8bd7c9 | ||
| d257a19d9d | |||
| f0f615688e | |||
| a6f045b3d7 | |||
| 558c18e417 | |||
| 5ff507b81b | |||
| 726343db96 | |||
| 55ff2e717f | |||
| e4d4c00f52 | |||
| 03aa7a0673 | |||
| 37247fdfea | |||
| a03dd36f11 | |||
| a03c85f08a | |||
| 564cf6b18f | |||
| 89645f160e | |||
| e9eeb84c5f | |||
| 4f98f2b773 | |||
| b3c61134fc | |||
| 5edb4df35a | |||
| 07f8ffa6d5 | |||
| 8e0e94de3d | |||
| 602370be26 | |||
| 3ebe5d6639 | |||
| 6ee284e3f6 | |||
| bacdb9f49c | |||
| 6eb98d866b | |||
| cb0efdfdae | |||
| a6c9529c50 | |||
| e289554e44 | |||
| bec64e668d | |||
| a0b7b42524 | |||
| 09e2987c14 | |||
| ee0c2a12de | |||
| 782ff5b7b6 | |||
| a20b0d3fe3 | |||
| f420584e1a | |||
| 0d85899627 | |||
| be9ae32a3b | |||
| 7fc502513e | |||
| 2e1ac72585 | |||
| fba671ad40 | |||
| 33c82cab1a | |||
| 75c23448b4 | |||
| 548c2ab8a4 | |||
| 15b4d45375 | |||
| 4c8c3b72bb | |||
| 7ea3a235da | |||
| 0481fb3ecf | |||
| 37c406bf4d | |||
| b345f5f6a4 | |||
|
|
87f14b7c71 | ||
| c501d88c63 | |||
| 78539ec8b0 | |||
| de895dd7f8 | |||
| 3dad82d992 | |||
| 4673efac6a | |||
| 721f847b28 | |||
| c36c732f47 | |||
| 60e306d1db | |||
| ce31a45124 | |||
| 7189df7957 | |||
| f21157f3c7 | |||
| a5ab57d144 | |||
| 0ebc7ef777 | |||
| d94ed00312 | |||
| af905cf936 | |||
| c312991bac | |||
| 77130ac769 | |||
| a016175fc8 | |||
| 543248b8c2 | |||
| a6d789279c | |||
| 91325a4267 | |||
| 2b85fb49df | |||
| eb6c723713 | |||
| 6322b61a04 | |||
| ff2865b5d8 | |||
| 53049d1c4d | |||
| bec4bfaf31 | |||
| 0537378d82 | |||
| 3ffffd5b32 | |||
| 97f08b32de | |||
| 9a6ae3b786 | |||
| 5699cff4d0 | |||
| 5882c68217 | |||
| c0d1251c1f | |||
| 9ad88e4df4 | |||
| 7a2710dc9a | |||
| 79369c378a |
935 changed files with 47373 additions and 229753 deletions
55
.env.example
55
.env.example
|
|
@ -22,5 +22,56 @@ SESSION_SECRET=changeme
|
||||||
# MAM API Configuration
|
# MAM API Configuration
|
||||||
MAM_API_URL=http://mam-api:3000
|
MAM_API_URL=http://mam-api:3000
|
||||||
|
|
||||||
# Auth (set to 'true' to require login; false for open/dev mode)
|
# Node Agent Authentication
|
||||||
AUTH_ENABLED=false
|
# 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
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# RBAC v2 note: with AUTH_ENABLED=true, per-project access is enforced. Service
|
||||||
|
# API tokens (capture sidecar, Premiere panel, integrations) must belong to a
|
||||||
|
# user with the access they need — an 'admin' user (full access), or a user with
|
||||||
|
# the right project grants. A non-admin service token with no grants will get
|
||||||
|
# 403 on asset registration (ingest) and streaming. In dev mode the synthetic
|
||||||
|
# user is admin, so this only matters once auth is on.
|
||||||
|
AUTH_ENABLED=true
|
||||||
|
|
||||||
|
# CORS allowlist — comma-separated origins that may carry credentials to the API.
|
||||||
|
# Same-origin requests via the nginx reverse proxy do not need to be listed here.
|
||||||
|
# Leave empty to allow any origin (DEV ONLY).
|
||||||
|
ALLOWED_ORIGINS=
|
||||||
|
|
||||||
|
# Reverse-proxy trust — set 'true' when the API sits behind nginx terminating HTTPS,
|
||||||
|
# so secure-cookie + X-Forwarded-Proto behave correctly. ALSO required for accurate
|
||||||
|
# per-IP login rate-limiting (otherwise req.ip is always the nginx IP).
|
||||||
|
TRUST_PROXY=false
|
||||||
|
|
||||||
|
# Google OAuth (OIDC) sign-in — OPTIONAL. Leave the client id/secret blank to
|
||||||
|
# disable; the "Sign in with Google" button and the /auth/google routes only
|
||||||
|
# activate when all three of CLIENT_ID, CLIENT_SECRET, and REDIRECT_URL are set.
|
||||||
|
# Create an OAuth 2.0 Client (type: Web application) in Google Cloud Console and
|
||||||
|
# add OAUTH_REDIRECT_URL to its authorized redirect URIs.
|
||||||
|
GOOGLE_CLIENT_ID=
|
||||||
|
GOOGLE_CLIENT_SECRET=
|
||||||
|
# Must exactly match a redirect URI on the OAuth client, e.g.
|
||||||
|
# https://dragonflight.live/api/v1/auth/google/callback
|
||||||
|
OAUTH_REDIRECT_URL=
|
||||||
|
# Restrict sign-in to one Google Workspace domain (recommended). First login from
|
||||||
|
# an allowed-domain account auto-provisions a NEW 'viewer' account (matched only
|
||||||
|
# by Google's stable subject id, never by email — so a Google login can never
|
||||||
|
# seize a pre-existing local account). An admin then grants project access.
|
||||||
|
# Leave blank to allow any verified Google account to self-provision (NOT advised).
|
||||||
|
GOOGLE_ALLOWED_DOMAIN=
|
||||||
|
# Note: if a Google-linked account also has TOTP enabled, sign-in still requires
|
||||||
|
# the authenticator code (Google is treated as the first factor). Accounts without
|
||||||
|
# TOTP complete sign-in in one Google step.
|
||||||
|
|
||||||
|
# Playout / Master Control (MCR)
|
||||||
|
# Image tag the mam-api spawns when a channel starts. Build with:
|
||||||
|
# docker compose --profile build-only build playout
|
||||||
|
PLAYOUT_IMAGE=wild-dragon-playout:latest
|
||||||
|
# Base AMCP port — each channel binds to BASE + channel_id (in CasparCG terms).
|
||||||
|
PLAYOUT_AMCP_BASE_PORT=5250
|
||||||
|
|
|
||||||
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
|
||||||
|
|
|
||||||
153
DESIGN.md
Normal file
153
DESIGN.md
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
# Design system
|
||||||
|
|
||||||
|
Documented from the live tokens in `services/web-ui/public/styles.css` and the patterns used across `screens-*.jsx`. Treat this as the source of truth for shared primitives; pages may override locally but should not invent new color or type scales.
|
||||||
|
|
||||||
|
## Color
|
||||||
|
|
||||||
|
Dark theme only. Tokens live in `:root` in `styles.css`. Tinted neutrals — no `#000`, no `#fff`.
|
||||||
|
|
||||||
|
### Surfaces
|
||||||
|
|
||||||
|
| Token | Hex | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `--bg-0` | `#0B0D11` | Page background, deepest surface |
|
||||||
|
| `--bg-1` | `#14171E` | Panels, sidebars, primary chrome |
|
||||||
|
| `--bg-2` | `#1B1F27` | Panel headers, hover state |
|
||||||
|
| `--bg-3` | `#232833` | Inputs, raised buttons, badges |
|
||||||
|
| `--bg-4` | `#2D3340` | Strongly raised elements (rare) |
|
||||||
|
|
||||||
|
### Borders + overlays
|
||||||
|
|
||||||
|
| Token | Use |
|
||||||
|
|---|---|
|
||||||
|
| `--border` (rgba white 6%) | Default 1px separator |
|
||||||
|
| `--border-strong` (rgba white 10%) | Emphasized separator |
|
||||||
|
| `--border-stronger` (rgba white 14%) | Hover/active border |
|
||||||
|
| `--hover` (rgba white 4%) | Subtle hover fill |
|
||||||
|
| `--hover-strong` (rgba white 7%) | Stronger hover fill |
|
||||||
|
|
||||||
|
### Text
|
||||||
|
|
||||||
|
| Token | Hex | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `--text-1` | `#F2F3F6` | Primary content |
|
||||||
|
| `--text-2` | `#A8AEBC` | Secondary content |
|
||||||
|
| `--text-3` | `#6B7280` | Labels, metadata |
|
||||||
|
| `--text-4` | `#4B5260` | Section labels, off-month, disabled |
|
||||||
|
|
||||||
|
### Accent
|
||||||
|
|
||||||
|
`--accent: #5B7CFA` (Frame.io-ish blue). Soft variants `--accent-soft` (14%) and `--accent-soft-2` (22%) for fills. `--accent-text: #B4C3FF` for high-contrast accent text.
|
||||||
|
|
||||||
|
**Restrained strategy.** Accent is the only saturated color in chrome. Status colors below appear only where they reflect actual state. Project colors appear only on project chips.
|
||||||
|
|
||||||
|
### Status
|
||||||
|
|
||||||
|
| Token | Hex | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `--success` | `#2DD4A8` | Done, healthy, ready |
|
||||||
|
| `--warning` | `#F5A623` | Processing, attention-needed |
|
||||||
|
| `--danger` | `#FF5B5B` | Failed, error |
|
||||||
|
| `--live` | `#FF3B30` | Currently recording (broadcast red, intentionally hotter than `--danger`) |
|
||||||
|
| `--purple` | `#B57CFA` | Editor / dev tags |
|
||||||
|
|
||||||
|
Each has a `*-soft` variant at ~14% for background fills.
|
||||||
|
|
||||||
|
### Project palette
|
||||||
|
|
||||||
|
Six fixed colors cycled by index in `data.jsx` (`PROJECT_COLORS`):
|
||||||
|
|
||||||
|
`#5B7CFA · #2DD4A8 · #FF5B5B · #F5A623 · #B57CFA · #6B7280`
|
||||||
|
|
||||||
|
Used only on the project chip / rail dot. Do not reuse for status meaning.
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
- **Sans:** Geist (with `cv11`, `ss01` features enabled). All UI text by default.
|
||||||
|
- **Mono:** Geist Mono. URLs, IDs, timestamps, durations, technical metadata.
|
||||||
|
|
||||||
|
Body scale runs small for density:
|
||||||
|
|
||||||
|
| Size | Use |
|
||||||
|
|---|---|
|
||||||
|
| 10.5px, 600, uppercase, 0.06–0.08em letterspacing | Column heads, section labels |
|
||||||
|
| 11–11.5px | Metadata, secondary rows |
|
||||||
|
| 12–12.5px | Body in lists/tables |
|
||||||
|
| 13px, 500–600 | Row labels, button text |
|
||||||
|
| 14px | Default body |
|
||||||
|
| 15px, 600 | Modal titles, panel heads |
|
||||||
|
| 22–28px, 600+ | Page H1 (`.page-header h1`) |
|
||||||
|
|
||||||
|
Never use `gradient text` (impeccable absolute ban). Emphasis via weight and size only.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
- Sidebar: 232px fixed (`--sidebar-w`).
|
||||||
|
- Topbar: 56px (`--topbar-h`).
|
||||||
|
- Row height: 44px default, 36px compact (`--row-h`, `data-density="compact"`).
|
||||||
|
- Gap unit: 16px default, 12px compact (`--gap`).
|
||||||
|
- Border radius scale: 4 / 6 / 8 / 12 / 16 px (`--r-xs` → `--r-xl`).
|
||||||
|
- Panels (`.panel`): `--bg-1` + 1px `--border` + `--r-lg`. Use for grouped lists, not for every section.
|
||||||
|
- Do NOT nest panels.
|
||||||
|
|
||||||
|
### Page header
|
||||||
|
|
||||||
|
Standard screens use `.page > .page-header > h1`. Three screens are documented exceptions because they need full-bleed layouts and their own top-chrome:
|
||||||
|
|
||||||
|
- **Home** uses `.launcher` (lobby pattern: hero logo + tile grid + status pip).
|
||||||
|
- **Library** uses `.library-layout` (dual-pane rail + main). The h1 sits inside `.library-toolbar` as `.toolbar-title`.
|
||||||
|
- **Editor** uses `.editor-shell` (NLE with timeline + monitors). The beta banner doubles as its top chrome.
|
||||||
|
|
||||||
|
All other screens should render `<div className="page"><div className="page-header"><h1>…</h1>…</div>…</div>` for consistent IA and screen-reader hierarchy.
|
||||||
|
|
||||||
|
## Shadow
|
||||||
|
|
||||||
|
Two tokens, used sparingly:
|
||||||
|
|
||||||
|
- `--shadow-card`: subtle inset highlight + soft outer. Default for raised inputs.
|
||||||
|
- `--shadow-pop`: modal / popover / context menu.
|
||||||
|
|
||||||
|
No drop shadows on flat surfaces. No glow effects (except: the EPG now-line uses a tight `box-shadow` for the broadcast-red glow, and live status dots have a pulse halo; these are state cues, not decoration).
|
||||||
|
|
||||||
|
## Motion
|
||||||
|
|
||||||
|
- Default transition: 80–120ms on background/border (`transition: background 80ms, border 80ms`).
|
||||||
|
- Heavier reveals: 200ms.
|
||||||
|
- Easing: prefer ease-out (no bounce, no elastic).
|
||||||
|
- Don't animate layout (width/height/top); animate transforms and opacity.
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
|
||||||
|
### Status badges (`.badge`)
|
||||||
|
|
||||||
|
Variants: `live` (red, animated dot), `success`, `danger`, `warning`, `accent`, `neutral`, `outline`. Tiny — 9–10px font, ~2px vertical padding. Reserved for state, not labels.
|
||||||
|
|
||||||
|
### Row tables (`.user-row`, `.token-row`, `.job-row`, `.schedule-row`, etc.)
|
||||||
|
|
||||||
|
CSS-grid with explicit columns. Header row uses `.head` (uppercase 10.5px). No card stacking — these are dense data lists.
|
||||||
|
|
||||||
|
### Schedule EPG (`.epg-*`)
|
||||||
|
|
||||||
|
Broadcast timeline pattern. Recorder rows × time-of-day axis. Single scrolling container with sticky left gutter (220px) and sticky top ruler (32px). Hour rhythm via `repeating-linear-gradient`. Now-line is a 1px hot-red vertical bar with `--live` glow, animated by re-rendering the line component every second (transform-only positioning, no layout thrash). Event blocks are absolute-positioned within each row, colored via `--epg-block-color` set per recorder's project color. Live events get a red gradient + pulse; failures get a glyph + full red border; past events fade to 0.55 opacity.
|
||||||
|
|
||||||
|
### Inputs
|
||||||
|
|
||||||
|
`.field-input` — `--bg-3` fill, 1px `--border`, `--r-sm`, 12.5px font. Focus: `--border-strong`.
|
||||||
|
|
||||||
|
### Status dot (`.signal-dot`, `.rec-dot`, etc.)
|
||||||
|
|
||||||
|
Small (~6–8px) circle, used inline with text. Recording dots pulse with a keyframe animation.
|
||||||
|
|
||||||
|
## Impeccable absolute bans (apply project-wide)
|
||||||
|
|
||||||
|
- No `border-left` / `border-right` greater than 1px as a colored accent (rewrite with full borders, leading icons, or background tint).
|
||||||
|
- No `background-clip: text` gradient text.
|
||||||
|
- No glassmorphism (blur + translucent) decoratively.
|
||||||
|
- No hero-metric template (big number, small label, gradient accent, supporting stats).
|
||||||
|
- No identical card grids.
|
||||||
|
- Modal as last resort — exhaust inline alternatives first.
|
||||||
|
- No em dashes in code or copy. Use commas, colons, parentheses, periods.
|
||||||
|
|
||||||
|
## To extend
|
||||||
|
|
||||||
|
When a new design need arises, prefer adding a variant to an existing primitive over inventing a new token. New tokens land in `styles.css`. New components land in the relevant `screens-*.jsx` only if reused; otherwise keep them local.
|
||||||
61
PRODUCT.md
Normal file
61
PRODUCT.md
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Product
|
||||||
|
|
||||||
|
## What it is
|
||||||
|
|
||||||
|
Dragonflight (codebase name; UI brand: "Dragonflight · Wild Dragon Broadcast") is an on-prem broadcast media asset manager and live ingest controller. Operators capture from SRT, RTMP, and SDI sources, schedule windowed recordings against named recorders, transcode/proxy in the background, browse and clip in a library, import from YouTube, and hand off to Premiere or an in-house editor.
|
||||||
|
|
||||||
|
The deployable surface is a React (Babel-in-browser) single-page app served by nginx, talking to a Node/Express API backed by Postgres + Redis + BullMQ, with capture and worker containers handling the actual media.
|
||||||
|
|
||||||
|
Stack lives at `services/web-ui` (UI), `services/mam-api` (API), `services/capture`, `services/worker`, `services/editor`.
|
||||||
|
|
||||||
|
## Register
|
||||||
|
|
||||||
|
**Product.** This is operator UI for a working broadcast tool, not a brand site or marketing surface. Design serves the operator's job, not the brand's identity.
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
Primary: broadcast operators and engineers running live productions. They schedule and supervise back-to-back recordings across multiple recorders in a single shift. They care about: what's recording right now, what's about to start, what failed, and which recorder is bound to what source.
|
||||||
|
|
||||||
|
Secondary: editors and producers who consume the resulting library, comment on assets, request proxy regeneration. They mostly live in the Library and Asset detail screens, not the scheduler.
|
||||||
|
|
||||||
|
Tertiary: admins managing recorders, users, cluster nodes, and storage. Live in the Admin screens.
|
||||||
|
|
||||||
|
## Product purpose
|
||||||
|
|
||||||
|
Replace a stack of one-trick tools (NewBlue scheduler, vMix capture, ad-hoc Premiere ingest, manual S3 syncs) with a single operator surface that supervises recorders, owns the asset catalog through proxy generation, and stays out of the editor's way once footage lands.
|
||||||
|
|
||||||
|
## Tone
|
||||||
|
|
||||||
|
Function-first. Dense. Operations-room. Mono fonts where data lives. Small type. Restrained chrome. The operator should be able to glance at any screen and read the state of the system in under a second; they should never wonder "is this still happening" or "did that finish."
|
||||||
|
|
||||||
|
Not: marketing-warm, conversational, gamified, congratulatory.
|
||||||
|
|
||||||
|
## Strategic principles
|
||||||
|
|
||||||
|
1. **Glance-readable status.** Every list, every cell, every badge must answer "what is the state of this thing right now" without a hover.
|
||||||
|
2. **Trust the operator.** No confirmation modals for reversible actions. No nag, no toasts for routine success. Errors stay visible until acknowledged.
|
||||||
|
3. **Time is the spine.** This product is about time-based events (recordings, schedules, jobs). UIs should privilege time as a primary axis, not bury it under categorical filters.
|
||||||
|
4. **Density over whitespace.** Operators run multi-monitor setups and want maximum signal per pixel. Generous whitespace is a brand-site reflex; reject it here.
|
||||||
|
5. **No half-states.** Pending UIs disable controls; live UIs show live data; failed UIs show the failure inline, not in a separate notification feed.
|
||||||
|
|
||||||
|
## Anti-references
|
||||||
|
|
||||||
|
Steer away from:
|
||||||
|
|
||||||
|
- **Linear-pastel SaaS aesthetics.** Purples, mint accents, friendly empty states with cartoon line illustrations.
|
||||||
|
- **Google-Calendar generic.** A neutral month grid with rounded event chips, no operational signal, optimized for "is Friday free" rather than "is recorder A in conflict at 14:00."
|
||||||
|
- **Gantt-chart project-management feel.** Implies long-horizon planning of tasks with dependencies; this product is hour-scale broadcast operations.
|
||||||
|
- **Cards-for-everything.** Identical card grids of icon+label+value. Particularly the SaaS hero-metric template.
|
||||||
|
- **Decorative blur / glassmorphism / gradient text.** Read as decorative AI slop in a broadcast-ops context.
|
||||||
|
- **NewBlue / Wirecast skinning.** Heavy bevels, gradient buttons, drop shadows. Read as outdated broadcast software.
|
||||||
|
|
||||||
|
## Decision context (Schedule v2)
|
||||||
|
|
||||||
|
The Schedule screen was rebuilt as an EPG (electronic program guide) timeline. Operator confirmed:
|
||||||
|
|
||||||
|
- Density: **heavy / back-to-back, many recorders all day** — month grid was the wrong primary.
|
||||||
|
- At-a-glance signals: now/next, recorder bookings (conflicts), project context, failure history.
|
||||||
|
- Aesthetic: **studio / cinematic — dark, type-led, accent moments.** DaVinci-Resolve-panel territory.
|
||||||
|
- Scope: full rethink — replace the primary view.
|
||||||
|
|
||||||
|
Implementation: recorder rows × time-of-day horizontal axis, sticky gutter + ruler, vertical hot-red now-line, event blocks colored by project, status pills in a top strip. Today / Week / List views.
|
||||||
250
README.md
250
README.md
|
|
@ -1,49 +1,128 @@
|
||||||
# Dragonflight
|
# Dragonflight
|
||||||
|
|
||||||
Self-hosted broadcast media-asset management. Replaces Grass Valley AMPP
|
Self-hosted broadcast media-asset management system that replaces legacy tools like Grass Valley AMPP and FramelightX. Handles live ingest, growing-file editing, scheduling, transcoding, and asset management in a single operator-focused interface.
|
||||||
FramelightX. SDI / SRT / RTMP ingest, growing-file editing via Premiere
|
|
||||||
Pro, S3-compatible storage, scheduling, and a queue-driven proxy pipeline.
|
|
||||||
|
|
||||||
> Repo renamed from `wild-dragon` → `dragonflight` (2026-05-23). The old
|
> Repo renamed from `wild-dragon` → `dragonflight` (2026-05-23). The old URL still redirects.
|
||||||
> URL still redirects.
|
|
||||||
|
|
||||||
## Features
|
## Home Dashboard
|
||||||
|
|
||||||
- **Ingest** — SRT (caller + listener), RTMP, and SDI capture via
|
<img src="docs/screenshots/01-home.png" alt="Home Dashboard" width="800" />
|
||||||
Blackmagic DeckLink cards (FFmpeg patched against SDK 16.x); per-recorder
|
|
||||||
codec settings (ProRes / H.264 / DNxHR / HEVC) and audio routing
|
|
||||||
- **Growing-file editing** — capture writes the hi-res master to a local
|
|
||||||
SMB landing zone; editors can mount the share in Premiere Pro and edit
|
|
||||||
the live file via the included CEP panel, then relink to the final S3
|
|
||||||
master after promotion
|
|
||||||
- **Recorder scheduler** — one-shot, daily, or weekly windows; a 15s tick
|
|
||||||
loop fires the existing /recorders/:id/start + /stop endpoints
|
|
||||||
- **Library** — projects, bins, asset detail with frame-anchored
|
|
||||||
persistent comments, right-click context menu (move-to-bin, rename,
|
|
||||||
delete), and a global cmd/ctrl-K search across assets / projects /
|
|
||||||
recorders / jobs / users
|
|
||||||
- **Jobs** — BullMQ-backed proxy + thumbnail queue with per-job retry,
|
|
||||||
bulk "retry all failed", and inline error messages
|
|
||||||
- **Settings** — S3 (with env-var fallback), global proxy encoder
|
|
||||||
(CPU/libx264 or GPU/NVENC/VAAPI), growing-files config, capture SDK
|
|
||||||
uploader (Blackmagic / AJA / Deltacast)
|
|
||||||
- **Cluster** — primary + worker topology with heartbeat health, remote
|
|
||||||
node-agent for off-host DeckLink capture
|
|
||||||
- **API** — `deploy/api-smoke.sh` exercises every endpoint (27 routes,
|
|
||||||
pass/fail summary)
|
|
||||||
|
|
||||||
## Services
|
The home screen provides quick access to all major features and displays system status at a glance:
|
||||||
|
- **Library** — Browse projects, bins, and assets with hover-scrub previews
|
||||||
|
- **Recorders** — View configured capture devices and their status
|
||||||
|
- **Editor** — Timeline editor with cross-clip preview and render queue
|
||||||
|
- **Jobs** — Proxy and thumbnail queue with retry controls
|
||||||
|
- **Settings** — Configure storage, encoder, growing files, and capture SDK
|
||||||
|
- **Dashboard** — Operations view showing recent activity, job queue, and cluster health
|
||||||
|
|
||||||
| Service | Port | Description |
|
---
|
||||||
|---------|------|-------------|
|
|
||||||
| **web-ui** | 47434 | Browser SPA + capture controls |
|
|
||||||
| **mam-api** | 47432 | REST API + recorder orchestration + scheduler tick |
|
|
||||||
| **capture** | 47433 / 9000 / 1935 | DeckLink/SRT/RTMP capture sidecar |
|
|
||||||
| **worker** | — | BullMQ proxy + thumbnail workers |
|
|
||||||
| **db** | 5432 | PostgreSQL 16 |
|
|
||||||
| **queue** | 6379 | Redis 7 |
|
|
||||||
|
|
||||||
## Quick start
|
## Core Features
|
||||||
|
|
||||||
|
### 1. Live Ingest & Capture
|
||||||
|
**Multi-protocol source capture with per-recorder codec settings**
|
||||||
|
|
||||||
|
Dragonflight ingests from multiple sources simultaneously:
|
||||||
|
- **SRT** (Secure Reliable Transport) — caller and listener modes
|
||||||
|
- **RTMP** — standard streaming protocol
|
||||||
|
- **SDI** — via Blackmagic DeckLink cards with FFmpeg SDK 16.x patches
|
||||||
|
|
||||||
|
Each recorder can be configured with independent codec settings:
|
||||||
|
- ProRes (hi-res masters)
|
||||||
|
- H.264 / H.265 (proxies)
|
||||||
|
- DNxHR (Avid compatibility)
|
||||||
|
|
||||||
|
Audio routing and per-source configuration ensure flexibility for multi-camera productions.
|
||||||
|
|
||||||
|
### 2. Growing-File Editing
|
||||||
|
**Live editing in Premiere Pro while capture is still writing**
|
||||||
|
|
||||||
|
Editors mount the SMB landing zone directly in Premiere Pro and edit the live master file as it's being written. The included CEP (Custom Extension Panel) provides:
|
||||||
|
- Real-time clip detection and frame-accurate trimming
|
||||||
|
- One-click relink to final S3 master after promotion
|
||||||
|
- No waiting for capture to finish before editorial begins
|
||||||
|
|
||||||
|
### 3. Recorder Scheduler
|
||||||
|
**Time-windowed recording automation**
|
||||||
|
|
||||||
|
Schedule recordings with:
|
||||||
|
- One-shot, daily, or weekly recurrence
|
||||||
|
- Automatic start/stop via 15-second tick loop
|
||||||
|
- Conflict detection across recorders
|
||||||
|
- Project and bin assignment at schedule time
|
||||||
|
|
||||||
|
### 4. Library & Asset Management
|
||||||
|
**Browse, search, and organize captured footage**
|
||||||
|
|
||||||
|
The Library screen provides:
|
||||||
|
- Project and bin hierarchy
|
||||||
|
- Asset detail view with frame-anchored persistent comments
|
||||||
|
- Right-click context menu (move-to-bin, rename, delete)
|
||||||
|
- Global cmd/ctrl-K search across assets, projects, recorders, jobs, and users
|
||||||
|
- Hover-scrub preview with HLS playback
|
||||||
|
|
||||||
|
### 5. Jobs Queue
|
||||||
|
**BullMQ-backed proxy and thumbnail generation**
|
||||||
|
|
||||||
|
Automated background processing:
|
||||||
|
- Per-job retry logic with exponential backoff
|
||||||
|
- Bulk "retry all failed" for batch recovery
|
||||||
|
- Inline error messages with actionable diagnostics
|
||||||
|
- Status tracking: ingesting → processing → ready
|
||||||
|
|
||||||
|
Proxy encoder options:
|
||||||
|
- CPU-based: libx264 (H.264)
|
||||||
|
- GPU-accelerated: NVENC (NVIDIA) or VAAPI (AMD/Intel)
|
||||||
|
|
||||||
|
### 6. Timeline Conform & Export
|
||||||
|
**FCP XML export with server-side FFmpeg rendering**
|
||||||
|
|
||||||
|
The Premiere Pro panel exports FCP XML with:
|
||||||
|
- Server-side conform via FFmpeg
|
||||||
|
- Multiple output formats: H.264, H.265, ProRes
|
||||||
|
- Resolution presets: Broadcast, Web, Archive
|
||||||
|
- Batch processing with job queue integration
|
||||||
|
|
||||||
|
### 7. Hi-Res Auto-Relink
|
||||||
|
**One-click batch relink of proxy clips to frame-accurate server-trimmed masters**
|
||||||
|
|
||||||
|
After editing on proxies:
|
||||||
|
- Select clips in Premiere
|
||||||
|
- Trigger relink from the CEP panel
|
||||||
|
- Server trims hi-res segments to exact in/out points
|
||||||
|
- Concurrent trim worker pool for speed
|
||||||
|
- 24-hour TTL with automatic cleanup
|
||||||
|
|
||||||
|
### 8. Settings & Configuration
|
||||||
|
**Centralized control for storage, encoding, and capture**
|
||||||
|
|
||||||
|
Configure:
|
||||||
|
- **S3 Storage** — endpoint, bucket, credentials (with env-var fallback)
|
||||||
|
- **Proxy Encoder** — CPU vs GPU, bitrate, resolution
|
||||||
|
- **Growing Files** — SMB path, retention, auto-promotion
|
||||||
|
- **Capture SDK** — Blackmagic, AJA, or Deltacast uploader selection
|
||||||
|
|
||||||
|
### 9. Cluster & Distributed Capture
|
||||||
|
**Primary + worker topology with remote DeckLink nodes**
|
||||||
|
|
||||||
|
- Primary node runs API, scheduler, and web UI
|
||||||
|
- Worker nodes handle proxy/thumbnail jobs
|
||||||
|
- Remote capture nodes run DeckLink cards off-host
|
||||||
|
- Heartbeat health monitoring
|
||||||
|
- Automatic failover and recovery
|
||||||
|
|
||||||
|
### 10. Admin & User Management
|
||||||
|
**Role-based access, token auth, and cluster monitoring**
|
||||||
|
|
||||||
|
- User creation and role assignment
|
||||||
|
- API token generation for integrations
|
||||||
|
- Container and cluster node status
|
||||||
|
- System health dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone (repo renamed; old URL still redirects)
|
# Clone (repo renamed; old URL still redirects)
|
||||||
|
|
@ -81,23 +160,98 @@ assets POST ──► proxy job ──► worker
|
||||||
└─ status: ingesting → processing → ready
|
└─ status: ingesting → processing → ready
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tech stack
|
## Tech Stack
|
||||||
|
|
||||||
- **Runtime:** Node.js 22, Docker Compose
|
- **Runtime:** Node.js 22, Docker Compose
|
||||||
- **Backend:** Express, PostgreSQL 16, Redis 7 + BullMQ
|
- **Backend:** Express, PostgreSQL 16, Redis 7 + BullMQ
|
||||||
- **Frontend:** Vanilla React via in-browser Babel (no bundler), hls.js
|
- **Frontend:** Vanilla React via in-browser Babel (no bundler), hls.js
|
||||||
- **Media:** FFmpeg 7.1 with SDK 16 DeckLink patches; ProRes, H.264, HEVC,
|
- **Media:** FFmpeg 7.1 with SDK 16 DeckLink patches
|
||||||
DNxHR, MOV/MP4/MXF containers
|
- **Codecs:** ProRes, H.264, H.265, DNxHR, MOV/MP4/MXF containers
|
||||||
- **Storage:** S3-compatible (RustFS) for masters + proxies + thumbnails
|
- **Storage:** S3-compatible (RustFS) for masters, proxies, thumbnails
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
| Service | Port | Purpose |
|
||||||
|
|---------|------|---------|
|
||||||
|
| **web-ui** | 47434 | Browser SPA + capture controls |
|
||||||
|
| **mam-api** | 47432 | REST API + recorder orchestration + scheduler |
|
||||||
|
| **capture** | 47433 / 9000 / 1935 | DeckLink/SRT/RTMP ingest sidecar |
|
||||||
|
| **worker** | — | BullMQ proxy + thumbnail workers |
|
||||||
|
| **db** | 5432 | PostgreSQL 16 |
|
||||||
|
| **queue** | 6379 | Redis 7 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow Example: Live-to-Edit
|
||||||
|
|
||||||
|
1. **Operator** schedules a recording on Recorder A for 14:00–15:30, assigns to "News/Segment-A" project
|
||||||
|
2. **Capture** starts at 14:00, writes ProRes master to SMB landing zone
|
||||||
|
3. **Editor** mounts SMB in Premiere, opens the live .mov file via the CEP panel
|
||||||
|
4. **Editor** trims and marks in/out points while capture is still writing
|
||||||
|
5. **Capture** finishes at 15:30, promotion worker uploads master to S3
|
||||||
|
6. **Editor** clicks "Relink to Master" in CEP panel
|
||||||
|
7. **Server** trims hi-res segment to exact in/out, stores for 24 hours
|
||||||
|
8. **Premiere** relinks proxy clips to trimmed master
|
||||||
|
9. **Editor** exports final timeline via FCP XML conform
|
||||||
|
|
||||||
|
Total time from end of capture to relinked master: ~2 minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
- `deploy/api-smoke.sh` — verify every API endpoint after a deploy
|
- `deploy/api-smoke.sh` — verify every API endpoint after deploy
|
||||||
- `deploy/onboard-node.sh` — provision a remote worker host (DeckLink
|
- `deploy/onboard-node.sh` — provision a remote worker host
|
||||||
cards on a separate machine)
|
- `deploy/test-cluster.sh` — primary↔worker connectivity smoke test
|
||||||
- `deploy/test-cluster.sh` — primary↔worker connectivity smoke
|
- `docs/GROWING_FILES_QUICKSTART.md` — Premiere CEP panel install + growing-file flow
|
||||||
- `docs/GROWING_FILES_QUICKSTART.md` — Premiere CEP panel install +
|
|
||||||
growing-file capture flow
|
## Authentication
|
||||||
|
|
||||||
|
Dragonflight uses local username/password authentication with two transports:
|
||||||
|
|
||||||
|
- **Browser:** session cookie (`dragonflight.sid`), 8 hour absolute + 1 hour idle timeout.
|
||||||
|
- **Premiere panel / scripts:** SHA-256-hashed bearer tokens issued from `Settings → API Tokens`.
|
||||||
|
|
||||||
|
### First-run setup
|
||||||
|
|
||||||
|
On a fresh install with `AUTH_ENABLED=true`, navigate to the web UI in a browser.
|
||||||
|
With no users in the database, the login screen renders a "First-run setup" form
|
||||||
|
instead — fill it in to create the first admin and you are logged in immediately.
|
||||||
|
|
||||||
|
Subsequent users are created from `Settings → Users` (any signed-in user can
|
||||||
|
create others — flat access).
|
||||||
|
|
||||||
|
### Dev mode
|
||||||
|
|
||||||
|
Setting `AUTH_ENABLED=false` disables all auth checks; a synthetic `dev` user
|
||||||
|
is attached to every request. **Never deploy this way.** The dev user row is
|
||||||
|
seeded with a hash that no real password can match, so flipping
|
||||||
|
`AUTH_ENABLED=true` later does not expose the dev account.
|
||||||
|
|
||||||
|
### Recovering a forgotten admin password
|
||||||
|
|
||||||
|
Any signed-in user can reset another user's password from `Settings → Users`.
|
||||||
|
If no one can sign in (all admins forgot their passwords), reset directly in
|
||||||
|
Postgres:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- generate a fresh bcrypt hash with:
|
||||||
|
-- node -e "import('bcrypt').then(b => b.default.hash(process.argv[1], 12).then(h => console.log(h)))" 'new-passphrase-here'
|
||||||
|
UPDATE users SET password_hash = '<bcrypt-hash>', password_updated_at = NOW()
|
||||||
|
WHERE username = 'admin';
|
||||||
|
```
|
||||||
|
|
||||||
|
### `AUTH_ENABLED` transition
|
||||||
|
|
||||||
|
When flipping `AUTH_ENABLED=false` → `true` on an existing install:
|
||||||
|
|
||||||
|
1. Ensure `SESSION_SECRET` is set to a stable value (rotating it logs everyone out).
|
||||||
|
2. Set `ALLOWED_ORIGINS` to the public origin(s) of the web UI.
|
||||||
|
3. Set `TRUST_PROXY=true` when behind nginx (required for rate-limit accuracy).
|
||||||
|
4. Restart `mam-api`.
|
||||||
|
5. Visit the UI — first-run setup will appear if no real users exist yet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
101
WORK_LOG_PLAYOUT.md
Normal file
101
WORK_LOG_PLAYOUT.md
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Playout / Master Control — Implementation Work Log
|
||||||
|
|
||||||
|
**Branch:** `feat/playout-mcr` (off `main`)
|
||||||
|
**Started:** 2026-05-30
|
||||||
|
**Status:** Code complete, awaiting runtime validation
|
||||||
|
|
||||||
|
Tracks the build of the playout (MCR) subsystem against the design at
|
||||||
|
`docs/superpowers/specs/2026-05-30-playout-mcr-design.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit sequence
|
||||||
|
|
||||||
|
| # | Commit | Scope |
|
||||||
|
|---|--------|-------|
|
||||||
|
| 1 | `docs(playout)` | Design spec, §7 questions answered |
|
||||||
|
| 2 | `feat(mam-api): migration 029` | Six tables, failover columns, audio_normalized flag |
|
||||||
|
| 3 | `feat(worker): playout-stage` | S3 → /media + EBU R128 loudnorm + index.js wiring |
|
||||||
|
| 4 | `feat(playout): sidecar` | CasparCG image + AMCP shim, HLS preview consumer, fps-aware frame math |
|
||||||
|
| 5 | `feat(mam-api): /playout control plane + auto-failover` | Routes + scheduler health tick + restartChannel helper |
|
||||||
|
| 6 | `feat(web-ui): MCR page` | screens-playout, styles, app/shell/index.html wiring |
|
||||||
|
| 7 | `build(playout): compose wiring + .env knobs` | /media volume, queue addition, build-only service |
|
||||||
|
| 8 | `docs(playout): work log` | This file |
|
||||||
|
|
||||||
|
## Resolved §7 decisions (2026-05-30)
|
||||||
|
|
||||||
|
- **Audio loudness:** pre-normalize at stage time. ffmpeg `loudnorm` two-pass
|
||||||
|
(I=-23 LUFS, TP=-1 dBTP, LRA=11), linear mode preserves dynamics. Output
|
||||||
|
AAC 192k @ 48 kHz, video stream copied. Per-item `audio_normalized` flag
|
||||||
|
so re-stages of the same asset skip the pass.
|
||||||
|
- **Frame rate:** `1080p5994` default (was `1080i5994`). Per-channel
|
||||||
|
override allowed via `video_format`. `fpsFor(videoFormat)` helper in
|
||||||
|
the sidecar drives SEEK / LENGTH / transition-frames math.
|
||||||
|
- **Preview latency:** HLS v1. CasparCG runs a second FFMPEG consumer
|
||||||
|
alongside the primary output, writing `/media/live/<channel_id>/index.m3u8`
|
||||||
|
(~600 kbps, 2s segments, 6-window list). Web UI plays via the existing
|
||||||
|
HLS plumbing.
|
||||||
|
- **Failover:** auto-restart on healthy node for NDI/SRT/RTMP. Alert-only
|
||||||
|
for DeckLink (device-index pinning makes blind re-placement risky).
|
||||||
|
Scheduler tick (PG advisory lock, same lock as recorder schedules) polls
|
||||||
|
sidecar `/status`; ~3 missed checks → `restartChannel(id)` picks the most
|
||||||
|
recently-seen-online other node, bumps `restart_count`, calls `/start`.
|
||||||
|
|
||||||
|
## Architecture notes
|
||||||
|
|
||||||
|
**Sidecar model.** One CasparCG container per channel. Spawned by mam-api
|
||||||
|
via local Docker socket (primary node) or remote node-agent
|
||||||
|
`/sidecar/start`. Tracked in `playout_sidecars` plus `playout_channels.container_id`.
|
||||||
|
Killed on `/stop` or by `restartChannel` during failover.
|
||||||
|
|
||||||
|
**Media flow.**
|
||||||
|
```
|
||||||
|
S3 master/proxy → playout-stage worker → /media/playout/<assetId>.<ext>
|
||||||
|
(loudnormed, AAC@-23 LUFS)
|
||||||
|
↓
|
||||||
|
CasparCG channel #1
|
||||||
|
↓
|
||||||
|
primary consumer HLS consumer
|
||||||
|
(DeckLink/NDI/ ↓
|
||||||
|
SRT/RTMP) /media/live/<ch_id>/*.m3u8
|
||||||
|
```
|
||||||
|
|
||||||
|
**Port contention.** `assertDeckLinkFree()` blocks starting a SDI channel
|
||||||
|
when a recorder or another channel on the same node+device_index is active.
|
||||||
|
|
||||||
|
**Failover scope.** NDI/SRT/RTMP have no hardware tie, so any healthy
|
||||||
|
cluster_node is eligible. DeckLink channels surface an alert in the UI
|
||||||
|
(`status='error'` + `error_message`) and require operator intervention.
|
||||||
|
|
||||||
|
## Testing checklist
|
||||||
|
|
||||||
|
- [ ] Apply migration 029 on dev DB
|
||||||
|
- [ ] Build playout image: `docker compose --profile build-only build playout`
|
||||||
|
- [ ] Build web-ui (`screens-playout` joins the esbuild list automatically)
|
||||||
|
- [ ] Create channel via POST /api/v1/playout/channels (SRT first, no HW)
|
||||||
|
- [ ] Stage 2-3 assets to a playlist, verify loudnorm metadata in stderr
|
||||||
|
- [ ] Start channel → sidecar container appears in `docker ps`
|
||||||
|
- [ ] AMCP smoke: `telnet <host> 5250`, `VERSION`, `INFO`
|
||||||
|
- [ ] Play playlist; verify HLS at /media/live/<id>/index.m3u8
|
||||||
|
- [ ] Skip / pause / resume / stop
|
||||||
|
- [ ] As-run log: GET /api/v1/playout/channels/:id/asrun
|
||||||
|
- [ ] Kill sidecar container → scheduler should restart on another node
|
||||||
|
within ~3 ticks (~45s), restart_count increments
|
||||||
|
- [ ] DeckLink channel kill: status flips to 'error', NO restart attempt
|
||||||
|
- [ ] Try starting a decklink channel on a device_index already held by a
|
||||||
|
recorder → 409
|
||||||
|
- [ ] MCR UI smoke: nav entry visible, page renders, drag-drop adds items,
|
||||||
|
transport buttons hit the API
|
||||||
|
|
||||||
|
## Known gaps (deferred)
|
||||||
|
|
||||||
|
- No WebRTC preview (HLS-only v1 — 4-6s lag, fine for confidence monitor).
|
||||||
|
- No graphics/CG overlay layer in Phase A (templates land in Phase B).
|
||||||
|
- No Phase B scheduler / 24/7 wall-clock channel (schema is in place,
|
||||||
|
scheduler tick is not).
|
||||||
|
- No multi-channel grid view (one channel at a time per page).
|
||||||
|
- No timecode / remaining-duration overlay (would need CasparCG INFO poll).
|
||||||
|
- No audio level meters on the UI.
|
||||||
|
- `restartChannel` updates DB state and triggers `/start`; if the new node
|
||||||
|
also fails repeatedly, there's no exponential backoff yet — bounded only
|
||||||
|
by the manual stop button.
|
||||||
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,7 +16,9 @@
|
||||||
# Optional env vars (needed only if starting the worker or capture profiles):
|
# Optional env vars (needed only if starting the worker or capture profiles):
|
||||||
# REDIS_URL, DATABASE_URL, S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY
|
# REDIS_URL, DATABASE_URL, S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY
|
||||||
# BMD_DEVICE_0 DeckLink device path (default: /dev/blackmagic/dv0)
|
# BMD_DEVICE_0 DeckLink device path (default: /dev/blackmagic/dv0)
|
||||||
|
# (DeckLink IO / Quad cards expose /dev/blackmagic/io* instead — set BMD_DEVICE_PREFIX=io)
|
||||||
# BMD_DEVICE_1 DeckLink device path (default: /dev/blackmagic/dv1)
|
# BMD_DEVICE_1 DeckLink device path (default: /dev/blackmagic/dv1)
|
||||||
|
# BMD_DEVICE_PREFIX Naming prefix for synthesized BMD_COUNT-based devices (default: dv). Use 'io' for IO/Quad.
|
||||||
# LIVE_DIR Host path for HLS live segments (default: /mnt/NVME/MAM/wild-dragon-live)
|
# LIVE_DIR Host path for HLS live segments (default: /mnt/NVME/MAM/wild-dragon-live)
|
||||||
#
|
#
|
||||||
# Profiles:
|
# Profiles:
|
||||||
|
|
@ -45,16 +47,37 @@ services:
|
||||||
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}
|
||||||
GPU_COUNT: ${GPU_COUNT:--1}
|
GPU_COUNT: ${GPU_COUNT:--1}
|
||||||
BMD_COUNT: ${BMD_COUNT:--1}
|
BMD_COUNT: ${BMD_COUNT:--1}
|
||||||
BMD_MODEL: ${BMD_MODEL:-}
|
BMD_MODEL: ${BMD_MODEL:-}
|
||||||
|
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}
|
||||||
|
# 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
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
@ -96,6 +119,32 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon-worker
|
- wild-dragon-worker
|
||||||
|
|
||||||
|
# worker-l4: HEAVY tier (proxy/conform/trim) on the L4 (NVENC). Talks to
|
||||||
|
# zampp1's Redis/Postgres/S3 over the LAN (.200). No promotion scanner here.
|
||||||
|
worker-l4:
|
||||||
|
profiles: [gpu]
|
||||||
|
build:
|
||||||
|
context: ./services/worker
|
||||||
|
dockerfile: Dockerfile.gpu
|
||||||
|
image: wild-dragon-worker-gpu:latest
|
||||||
|
runtime: nvidia
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
REDIS_URL: ${REDIS_URL}
|
||||||
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
S3_ENDPOINT: ${S3_ENDPOINT}
|
||||||
|
S3_BUCKET: ${S3_BUCKET}
|
||||||
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||||
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
|
S3_REGION: ${S3_REGION:-us-east-1}
|
||||||
|
WORKER_QUEUES: proxy,conform,trim
|
||||||
|
PROXY_CONCURRENCY: "3"
|
||||||
|
NVIDIA_VISIBLE_DEVICES: GPU-13acf439-8bf4-a5e0-7804-c1071bca547a
|
||||||
|
WORKER_LABEL: "zampp2 / L4"
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
|
networks:
|
||||||
|
- wild-dragon-worker
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
wild-dragon-worker:
|
wild-dragon-worker:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ services:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- /mnt/NVME/MAM/wild-dragon-live:/live
|
- /mnt/NVME/MAM/wild-dragon-live:/live
|
||||||
- /mnt/NVME/MAM/wild-dragon-growing:/growing
|
- /mnt/NVME/MAM/wild-dragon-growing:/growing
|
||||||
|
- /mnt/NVME/MAM/wild-dragon-media:/media
|
||||||
- /mnt/NVME/MAM/sdk:/sdk
|
- /mnt/NVME/MAM/sdk:/sdk
|
||||||
- /dev/shm:/dev/shm
|
- /dev/shm:/dev/shm
|
||||||
- /run/dbus:/run/dbus
|
- /run/dbus:/run/dbus
|
||||||
|
|
@ -55,9 +56,19 @@ services:
|
||||||
S3_REGION: ${S3_REGION:-us-east-1}
|
S3_REGION: ${S3_REGION:-us-east-1}
|
||||||
SESSION_SECRET: ${SESSION_SECRET}
|
SESSION_SECRET: ${SESSION_SECRET}
|
||||||
AUTH_ENABLED: ${AUTH_ENABLED:-false}
|
AUTH_ENABLED: ${AUTH_ENABLED:-false}
|
||||||
|
TRUST_PROXY: ${TRUST_PROXY:-false}
|
||||||
|
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-}
|
||||||
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}
|
||||||
|
PLAYOUT_IMAGE: ${PLAYOUT_IMAGE:-wild-dragon-playout:latest}
|
||||||
|
PLAYOUT_AMCP_BASE_PORT: ${PLAYOUT_AMCP_BASE_PORT:-5250}
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
reservations:
|
reservations:
|
||||||
|
|
@ -92,8 +103,20 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon
|
- wild-dragon
|
||||||
|
|
||||||
worker:
|
# ── GPU worker pool (capability-routed) ──────────────────────────────
|
||||||
build: ./services/worker
|
# worker-p4: HEAVY tier (proxy/conform/trim) on the Tesla P4 (NVENC).
|
||||||
|
# Also runs the promotion scanner (RUN_PROMOTION) — exactly one worker must.
|
||||||
|
worker-p4:
|
||||||
|
build:
|
||||||
|
context: ./services/worker
|
||||||
|
dockerfile: Dockerfile.gpu
|
||||||
|
image: wild-dragon-worker-gpu:latest
|
||||||
|
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
|
||||||
|
|
@ -106,8 +129,61 @@ 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}
|
||||||
GROWING_PATH: /growing
|
GROWING_PATH: /growing
|
||||||
|
# Includes `import` (YouTube importer): the import queue had no consumer
|
||||||
|
# after the capability-routing split, so import jobs sat unprocessed and
|
||||||
|
# assets stayed `ingesting` forever. import is concurrency-1 + network-
|
||||||
|
# bound, so one consumer (this heavy/primary worker) is sufficient.
|
||||||
|
WORKER_QUEUES: proxy,conform,trim,import,playout-stage
|
||||||
|
RUN_PROMOTION: "true"
|
||||||
|
PROXY_CONCURRENCY: "2"
|
||||||
|
PLAYOUT_MEDIA_DIR: /media
|
||||||
|
NVIDIA_VISIBLE_DEVICES: GPU-79afca3e-2ab2-a6ea-1c44-706c1f0a26d6
|
||||||
|
WORKER_LABEL: "zampp1 / Tesla P4"
|
||||||
|
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
|
||||||
|
networks:
|
||||||
|
- wild-dragon
|
||||||
|
|
||||||
|
# worker-p400a/b: LIGHT tier (thumbnail/filmstrip) on the two Quadro P400s.
|
||||||
|
worker-p400a:
|
||||||
|
image: wild-dragon-worker-gpu:latest
|
||||||
|
runtime: nvidia
|
||||||
|
depends_on: [queue, db, worker-p4]
|
||||||
|
environment:
|
||||||
|
REDIS_URL: ${REDIS_URL}
|
||||||
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
S3_ENDPOINT: ${S3_ENDPOINT}
|
||||||
|
S3_BUCKET: ${S3_BUCKET}
|
||||||
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||||
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
|
S3_REGION: ${S3_REGION:-us-east-1}
|
||||||
|
WORKER_QUEUES: thumbnail,filmstrip
|
||||||
|
NVIDIA_VISIBLE_DEVICES: GPU-331c53ea-2ed9-0007-e364-c1451775948f
|
||||||
|
WORKER_LABEL: "zampp1 / P400 #1"
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
|
networks:
|
||||||
|
- wild-dragon
|
||||||
|
|
||||||
|
worker-p400b:
|
||||||
|
image: wild-dragon-worker-gpu:latest
|
||||||
|
runtime: nvidia
|
||||||
|
depends_on: [queue, db, worker-p4]
|
||||||
|
environment:
|
||||||
|
REDIS_URL: ${REDIS_URL}
|
||||||
|
DATABASE_URL: ${DATABASE_URL}
|
||||||
|
S3_ENDPOINT: ${S3_ENDPOINT}
|
||||||
|
S3_BUCKET: ${S3_BUCKET}
|
||||||
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||||
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
|
S3_REGION: ${S3_REGION:-us-east-1}
|
||||||
|
WORKER_QUEUES: thumbnail,filmstrip
|
||||||
|
NVIDIA_VISIBLE_DEVICES: GPU-b514a592-9077-44bd-d9e8-9efa0591ef88
|
||||||
|
WORKER_LABEL: "zampp1 / P400 #2"
|
||||||
|
NVIDIA_DRIVER_CAPABILITIES: video,compute,utility
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon
|
- wild-dragon
|
||||||
|
|
||||||
|
|
@ -117,20 +193,21 @@ services:
|
||||||
- "${PORT_WEB_UI:-7434}:80"
|
- "${PORT_WEB_UI:-7434}:80"
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/NVME/MAM/wild-dragon-live:/live
|
- /mnt/NVME/MAM/wild-dragon-live:/live
|
||||||
|
- /mnt/NVME/MAM/wild-dragon-media:/media:ro
|
||||||
- /dev/shm:/dev/shm
|
- /dev/shm:/dev/shm
|
||||||
- /run/dbus:/run/dbus
|
- /run/dbus:/run/dbus
|
||||||
- /run/systemd:/run/systemd
|
- /run/systemd:/run/systemd
|
||||||
networks:
|
networks:
|
||||||
- wild-dragon
|
- wild-dragon
|
||||||
|
|
||||||
editor:
|
# Build-only: the CasparCG sidecar image. mam-api spawns these on-demand per
|
||||||
build: ./services/editor
|
# channel (one container per playout channel), so this service is never up'd —
|
||||||
depends_on:
|
# it exists so `docker compose build playout` produces the image the API tags
|
||||||
- mam-api
|
# via PLAYOUT_IMAGE. Profile excludes it from default `up`.
|
||||||
ports:
|
playout:
|
||||||
- "${PORT_EDITOR:-7435}:80"
|
profiles: ["build-only"]
|
||||||
networks:
|
build: ./services/playout
|
||||||
- wild-dragon
|
image: wild-dragon-playout:latest
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|
|
||||||
186
docs/DESCRIPTION.md
Normal file
186
docs/DESCRIPTION.md
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
# Dragonflight · Feature Overview
|
||||||
|
|
||||||
|
Dragonflight is a self-hosted broadcast media-asset management system that replaces legacy tools like Grass Valley AMPP and FramelightX. It handles live ingest, growing-file editing, scheduling, transcoding, and asset management in a single operator-focused interface.
|
||||||
|
|
||||||
|
## Home Dashboard
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The home screen provides quick access to all major features and displays system status at a glance:
|
||||||
|
- **Library** — Browse projects, bins, and assets with hover-scrub previews
|
||||||
|
- **Recorders** — View configured capture devices and their status
|
||||||
|
- **Editor** — Timeline editor with cross-clip preview and render queue
|
||||||
|
- **Jobs** — Proxy and thumbnail queue with retry controls
|
||||||
|
- **Settings** — Configure storage, encoder, growing files, and capture SDK
|
||||||
|
- **Dashboard** — Operations view showing recent activity, job queue, and cluster health
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Features
|
||||||
|
|
||||||
|
### 1. Live Ingest & Capture
|
||||||
|
**Multi-protocol source capture with per-recorder codec settings**
|
||||||
|
|
||||||
|
Dragonflight ingests from multiple sources simultaneously:
|
||||||
|
- **SRT** (Secure Reliable Transport) — caller and listener modes
|
||||||
|
- **RTMP** — standard streaming protocol
|
||||||
|
- **SDI** — via Blackmagic DeckLink cards with FFmpeg SDK 16.x patches
|
||||||
|
|
||||||
|
Each recorder can be configured with independent codec settings:
|
||||||
|
- ProRes (hi-res masters)
|
||||||
|
- H.264 / H.265 (proxies)
|
||||||
|
- DNxHR (Avid compatibility)
|
||||||
|
|
||||||
|
Audio routing and per-source configuration ensure flexibility for multi-camera productions.
|
||||||
|
|
||||||
|
### 2. Growing-File Editing
|
||||||
|
**Live editing in Premiere Pro while capture is still writing**
|
||||||
|
|
||||||
|
Editors mount the SMB landing zone directly in Premiere Pro and edit the live master file as it's being written. The included CEP (Custom Extension Panel) provides:
|
||||||
|
- Real-time clip detection and frame-accurate trimming
|
||||||
|
- One-click relink to final S3 master after promotion
|
||||||
|
- No waiting for capture to finish before editorial begins
|
||||||
|
|
||||||
|
### 3. Recorder Scheduler
|
||||||
|
**Time-windowed recording automation**
|
||||||
|
|
||||||
|
Schedule recordings with:
|
||||||
|
- One-shot, daily, or weekly recurrence
|
||||||
|
- Automatic start/stop via 15-second tick loop
|
||||||
|
- Conflict detection across recorders
|
||||||
|
- Project and bin assignment at schedule time
|
||||||
|
|
||||||
|
### 4. Library & Asset Management
|
||||||
|
**Browse, search, and organize captured footage**
|
||||||
|
|
||||||
|
The Library screen provides:
|
||||||
|
- Project and bin hierarchy
|
||||||
|
- Asset detail view with frame-anchored persistent comments
|
||||||
|
- Right-click context menu (move-to-bin, rename, delete)
|
||||||
|
- Global cmd/ctrl-K search across assets, projects, recorders, jobs, and users
|
||||||
|
- Hover-scrub preview with HLS playback
|
||||||
|
|
||||||
|
### 5. Jobs Queue
|
||||||
|
**BullMQ-backed proxy and thumbnail generation**
|
||||||
|
|
||||||
|
Automated background processing:
|
||||||
|
- Per-job retry logic with exponential backoff
|
||||||
|
- Bulk "retry all failed" for batch recovery
|
||||||
|
- Inline error messages with actionable diagnostics
|
||||||
|
- Status tracking: ingesting → processing → ready
|
||||||
|
|
||||||
|
Proxy encoder options:
|
||||||
|
- CPU-based: libx264 (H.264)
|
||||||
|
- GPU-accelerated: NVENC (NVIDIA) or VAAPI (AMD/Intel)
|
||||||
|
|
||||||
|
### 6. Timeline Conform & Export
|
||||||
|
**FCP XML export with server-side FFmpeg rendering**
|
||||||
|
|
||||||
|
The Premiere Pro panel exports FCP XML with:
|
||||||
|
- Server-side conform via FFmpeg
|
||||||
|
- Multiple output formats: H.264, H.265, ProRes
|
||||||
|
- Resolution presets: Broadcast, Web, Archive
|
||||||
|
- Batch processing with job queue integration
|
||||||
|
|
||||||
|
### 7. Hi-Res Auto-Relink
|
||||||
|
**One-click batch relink of proxy clips to frame-accurate server-trimmed masters**
|
||||||
|
|
||||||
|
After editing on proxies:
|
||||||
|
- Select clips in Premiere
|
||||||
|
- Trigger relink from the CEP panel
|
||||||
|
- Server trims hi-res segments to exact in/out points
|
||||||
|
- Concurrent trim worker pool for speed
|
||||||
|
- 24-hour TTL with automatic cleanup
|
||||||
|
|
||||||
|
### 8. Settings & Configuration
|
||||||
|
**Centralized control for storage, encoding, and capture**
|
||||||
|
|
||||||
|
Configure:
|
||||||
|
- **S3 Storage** — endpoint, bucket, credentials (with env-var fallback)
|
||||||
|
- **Proxy Encoder** — CPU vs GPU, bitrate, resolution
|
||||||
|
- **Growing Files** — SMB path, retention, auto-promotion
|
||||||
|
- **Capture SDK** — Blackmagic, AJA, or Deltacast uploader selection
|
||||||
|
|
||||||
|
### 9. Cluster & Distributed Capture
|
||||||
|
**Primary + worker topology with remote DeckLink nodes**
|
||||||
|
|
||||||
|
- Primary node runs API, scheduler, and web UI
|
||||||
|
- Worker nodes handle proxy/thumbnail jobs
|
||||||
|
- Remote capture nodes run DeckLink cards off-host
|
||||||
|
- Heartbeat health monitoring
|
||||||
|
- Automatic failover and recovery
|
||||||
|
|
||||||
|
### 10. Admin & User Management
|
||||||
|
**Role-based access, token auth, and cluster monitoring**
|
||||||
|
|
||||||
|
- User creation and role assignment
|
||||||
|
- API token generation for integrations
|
||||||
|
- Container and cluster node status
|
||||||
|
- System health dashboard
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
SDI / SRT / RTMP ──► capture (FFmpeg)
|
||||||
|
├─ HLS preview tee ──► /live/<assetId>/index.m3u8
|
||||||
|
└─ master output
|
||||||
|
├─ growing_enabled=true:
|
||||||
|
│ /growing/<projectId>/<clip>.mov
|
||||||
|
│ (Premiere mounts SMB, edits live)
|
||||||
|
│ └─► promotion worker uploads to S3
|
||||||
|
│
|
||||||
|
└─ growing_enabled=false:
|
||||||
|
multipart stream → S3
|
||||||
|
|
||||||
|
assets POST ──► proxy job ──► worker
|
||||||
|
├─ libx264 (CPU) or NVENC/VAAPI (GPU)
|
||||||
|
├─ thumbnail job
|
||||||
|
└─ status: ingesting → processing → ready
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
- **Runtime:** Node.js 22, Docker Compose
|
||||||
|
- **Backend:** Express, PostgreSQL 16, Redis 7 + BullMQ
|
||||||
|
- **Frontend:** Vanilla React via in-browser Babel (no bundler), hls.js
|
||||||
|
- **Media:** FFmpeg 7.1 with SDK 16 DeckLink patches
|
||||||
|
- **Codecs:** ProRes, H.264, H.265, DNxHR, MOV/MP4/MXF containers
|
||||||
|
- **Storage:** S3-compatible (RustFS) for masters, proxies, thumbnails
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
| Service | Port | Purpose |
|
||||||
|
|---------|------|---------|
|
||||||
|
| **web-ui** | 47434 | Browser SPA + capture controls |
|
||||||
|
| **mam-api** | 47432 | REST API + recorder orchestration + scheduler |
|
||||||
|
| **capture** | 47433 / 9000 / 1935 | DeckLink/SRT/RTMP ingest sidecar |
|
||||||
|
| **worker** | — | BullMQ proxy + thumbnail workers |
|
||||||
|
| **db** | 5432 | PostgreSQL 16 |
|
||||||
|
| **queue** | 6379 | Redis 7 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow Example: Live-to-Edit
|
||||||
|
|
||||||
|
1. **Operator** schedules a recording on Recorder A for 14:00–15:30, assigns to "News/Segment-A" project
|
||||||
|
2. **Capture** starts at 14:00, writes ProRes master to SMB landing zone
|
||||||
|
3. **Editor** mounts SMB in Premiere, opens the live .mov file via the CEP panel
|
||||||
|
4. **Editor** trims and marks in/out points while capture is still writing
|
||||||
|
5. **Capture** finishes at 15:30, promotion worker uploads master to S3
|
||||||
|
6. **Editor** clicks "Relink to Master" in CEP panel
|
||||||
|
7. **Server** trims hi-res segment to exact in/out, stores for 24 hours
|
||||||
|
8. **Premiere** relinks proxy clips to trimmed master
|
||||||
|
9. **Editor** exports final timeline via FCP XML conform
|
||||||
|
|
||||||
|
Total time from end of capture to relinked master: ~2 minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
- `deploy/api-smoke.sh` — verify every API endpoint after deploy
|
||||||
|
- `deploy/onboard-node.sh` — provision a remote worker host
|
||||||
|
- `deploy/test-cluster.sh` — primary↔worker connectivity smoke test
|
||||||
|
- `docs/GROWING_FILES_QUICKSTART.md` — Premiere CEP panel install + growing-file flow
|
||||||
116
docs/WORK_LOG_2026_05.md
Normal file
116
docs/WORK_LOG_2026_05.md
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
# Work Log — May 2026
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
Session focused on auth system architecture, dashboard redesign, and audio track inspector. Multiple iterations on auth approach; settled on simplified local-account model with RBAC. Dashboard rebuilt as control-room status board. Audio tab completed with full metering and fader controls.
|
||||||
|
|
||||||
|
## Auth System Work
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- `002e5ac` — auth: top-to-bottom rework — local accounts, RBAC + client tag, audit log, env-bootstrap
|
||||||
|
- `d1f9557` — auth: park login flow — circle back
|
||||||
|
- `9726dbb` — Revert "auth: top-to-bottom rework..."
|
||||||
|
- `4172b0d` — rip out entire auth/login flow
|
||||||
|
|
||||||
|
### What Happened
|
||||||
|
Attempted comprehensive auth rewrite including:
|
||||||
|
- Local user account system with bcrypt hashing
|
||||||
|
- Role-based access control (admin/editor/viewer)
|
||||||
|
- Client tagging for audit trails
|
||||||
|
- Environment-based bootstrap (AUTH_ENABLED flag)
|
||||||
|
- Session management with PostgreSQL backing
|
||||||
|
|
||||||
|
**Decision**: Reverted entire auth work. Reason: complexity vs. current product stage. System was over-engineered for self-hosted use case where auth is optional.
|
||||||
|
|
||||||
|
**Current State**: Auth disabled by default (AUTH_ENABLED=false). When enabled, system returns synthetic /auth/me endpoint. No persistent user management yet.
|
||||||
|
|
||||||
|
### Related Fixes
|
||||||
|
- `cfcbec0` — fix(auth): make AUTH_ENABLED=true workable end-to-end
|
||||||
|
- `e71c330` — fix(auth): remove manual session.save() — was suppressing Set-Cookie header
|
||||||
|
- `65684aa` — fix(auth): ensure sessions table exists + log session.save errors
|
||||||
|
|
||||||
|
## Dashboard Redesign
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- `a48e1d9` — dashboard: rebuild as control-room status board (on air / up next / attention / work)
|
||||||
|
- `e5e0656` — dashboard: redesign stat cards, compress header, improve density
|
||||||
|
- `5de1e3d` — dashboard: add dense stat cards, cluster bars, job rows, sparkline fixes
|
||||||
|
- `48d54a3` — dashboard: add missing dash-* CSS classes; cluster: add stat-row/stat-card CSS
|
||||||
|
|
||||||
|
### What Changed
|
||||||
|
Transformed dashboard from generic metrics view to broadcast control-room interface:
|
||||||
|
- **On Air** section: live stream status, bitrate, duration
|
||||||
|
- **Up Next** section: queued clips/segments
|
||||||
|
- **Attention** section: warnings, errors, resource alerts
|
||||||
|
- **Work** section: active jobs, encoding progress
|
||||||
|
|
||||||
|
Added visual components:
|
||||||
|
- Dense stat cards with icon + value + trend
|
||||||
|
- Cluster health bars (CPU, memory, disk per node)
|
||||||
|
- Job progress rows with ETA
|
||||||
|
- Sparkline charts for trend visualization
|
||||||
|
|
||||||
|
CSS infrastructure added for consistent spacing/sizing across dashboard components.
|
||||||
|
|
||||||
|
## Audio Tab Implementation
|
||||||
|
|
||||||
|
### Commit
|
||||||
|
- `c48c7e6` — feat(audio-tab): full audio track inspector with meters, mute/solo, faders
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Per-track audio meters (VU-style, real-time)
|
||||||
|
- Mute/solo buttons per track
|
||||||
|
- Fader controls (0-100 dB range)
|
||||||
|
- Master output meter
|
||||||
|
- Track naming/labeling
|
||||||
|
- Visual feedback for clipping/peaks
|
||||||
|
|
||||||
|
## Other Work
|
||||||
|
|
||||||
|
### Storage & Admin
|
||||||
|
- `64d739b` — feat(admin): unified Storage settings page with mount/bucket health diagnostics
|
||||||
|
- `a44d8bd` — feat(admin): live video-presence indicators on cluster DeckLink ports
|
||||||
|
|
||||||
|
### Player & Streaming
|
||||||
|
- `a86c1c7` — fix(player): stitch S3 ranges around RustFS empty-body bug (#143)
|
||||||
|
- `d257a19` — fix(player): buffer indicator + 416 instead of 500 on out-of-range S3
|
||||||
|
- `37247fd` — fix(video): direct S3 signed URL for streaming + proxy bitrate 1.5Mbps
|
||||||
|
- `e4d4c00` — feat(proxy): VBR 500k-1M encoding for proxy generation
|
||||||
|
|
||||||
|
### Cluster & Hardware
|
||||||
|
- `55ff2e7` — feat(cluster): full hardware breakdown per node
|
||||||
|
- `5ff507b` — fix(node-agent): use nsenter to run nvidia-smi in host mount namespace
|
||||||
|
- `558c18e` — fix(node-agent): detect GPUs via docker run --gpus all ubuntu:22.04
|
||||||
|
- `a6f045b` — fix(node-agent): probe GPU via Docker API async at startup, cache result
|
||||||
|
|
||||||
|
### Release & Cleanup
|
||||||
|
- `04ce096` — chore: 1.2 ship-prep sweep — close 38 issues
|
||||||
|
- `f0f6156` — release: add v1.1.0 ZXP artifact (Growing tab + visual system alignment)
|
||||||
|
|
||||||
|
## Blockers / Open Questions
|
||||||
|
|
||||||
|
### Auth System
|
||||||
|
- **Decision needed**: Should auth be mandatory for production? Current design assumes optional.
|
||||||
|
- **API endpoints missing**: `/users`, `/auth/me`, `/groups` routes not yet implemented in mam-api
|
||||||
|
- **Frontend expects**: Users list, groups management, role-based UI filtering
|
||||||
|
|
||||||
|
### Dashboard
|
||||||
|
- Real data integration needed (currently mock data)
|
||||||
|
- Cluster stats endpoint integration
|
||||||
|
- Job queue polling/WebSocket updates
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
- Backend audio processing pipeline not yet connected
|
||||||
|
- Metering data source undefined
|
||||||
|
- Fader changes need routing to encoder
|
||||||
|
|
||||||
|
## Next Steps (Recommended)
|
||||||
|
|
||||||
|
1. **Clarify auth requirements**: Is user management needed for v1.2? If yes, implement `/users` and `/groups` endpoints.
|
||||||
|
2. **Connect dashboard to live data**: Wire cluster stats, job queue, stream status to real endpoints.
|
||||||
|
3. **Audio backend integration**: Define audio processing pipeline and metering data flow.
|
||||||
|
4. **Testing**: Add integration tests for auth flow, dashboard data binding, audio control.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Session ended**: 2026-05-27 06:31 CDT
|
||||||
|
**Status**: Work logged, auth decision documented, next steps identified
|
||||||
197
docs/design/2026-05-29-all-intra-hevc-ingest.md
Normal file
197
docs/design/2026-05-29-all-intra-hevc-ingest.md
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
# All-Intra HEVC (NVENC) Growing-File Ingest
|
||||||
|
|
||||||
|
Date: 2026-05-29 | Status: design, pending validation gate (see §8)
|
||||||
|
Authors: Zac + Claude
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
Replace the CPU-bound ProRes capture encode with **All-Intra HEVC on NVENC**
|
||||||
|
as the growing-file master codec, so we can:
|
||||||
|
|
||||||
|
- **Offload ingest encode from CPU to GPU** (the current scaling wall), and
|
||||||
|
- **Keep edit-while-record** (all-intra => growing file stays editable), and
|
||||||
|
- **Scale to up to 8 simultaneous signals per machine**, across Blackmagic
|
||||||
|
today and Deltacast + AJA later.
|
||||||
|
|
||||||
|
This doc captures the target design AND the current working system it builds on,
|
||||||
|
so it is self-contained for whoever implements it.
|
||||||
|
|
||||||
|
## 2. Why this codec
|
||||||
|
|
||||||
|
Growing-file editing (Premiere/Avid mounting a still-recording file over SMB)
|
||||||
|
requires two things: **intra-frame** (every frame a keyframe, so a partial file
|
||||||
|
is decodable to the last whole frame) and a **container whose index is not
|
||||||
|
deferred to EOF**. ProRes/DNxHR satisfy this but are CPU-only (NVIDIA has no
|
||||||
|
ProRes encoder). Long-GOP H.264/HEVC/AV1 do NOT work for edit-while-record.
|
||||||
|
|
||||||
|
**All-Intra HEVC (`-g 1 -bf 0`) via `hevc_nvenc`** is the one path that is both
|
||||||
|
GPU-accelerated AND all-intra: it breaks the "ProRes must be CPU" constraint
|
||||||
|
without losing edit-while-record. Trade-off: All-Intra bitrate approaches
|
||||||
|
ProRes, so the win is **CPU offload, not storage**. AV1 is rejected (no NLE
|
||||||
|
edit support; av1_nvenc absent from our ffmpeg builds).
|
||||||
|
|
||||||
|
## 3. Current working system (what we build on)
|
||||||
|
|
||||||
|
### Topology
|
||||||
|
- **zampp1** (172.18.91.200): primary. Runs db (postgres), queue (redis),
|
||||||
|
mam-api (:47432), web-ui (:47434), and the GPU worker pool. GPUs: Tesla P4 +
|
||||||
|
2x Quadro P400. Repo at /opt/wild-dragon (its own clone).
|
||||||
|
- **zampp2** (172.18.91.216): worker/capture node. 12-vCPU QEMU VM, NVIDIA L4,
|
||||||
|
4x Blackmagic DeckLink (exposed as /dev/blackmagic/io0..io3). Runs node-agent
|
||||||
|
(:7436). Repo at /opt/wild-dragon (separate clone).
|
||||||
|
- The repo is checked out independently on BOTH nodes; node-specific files
|
||||||
|
(node-agent, capture, worker overlay) are edited on the node that runs them.
|
||||||
|
|
||||||
|
### Capture (current)
|
||||||
|
mam-api `POST /recorders/:id/start` pre-creates a `live` asset and dispatches
|
||||||
|
`POST /sidecar/start` to the recorder's node-agent, which spawns a
|
||||||
|
`wild-dragon-capture:latest` container (host network, privileged,
|
||||||
|
/dev/blackmagic bound). The capture ffmpeg:
|
||||||
|
- input: `-f decklink -i "DeckLink Duo (N)"`
|
||||||
|
- filter: `yadif` (CPU deinterlace)
|
||||||
|
- output 0 (master): `prores_ks` (CPU) -> S3 (pipe) or growing SMB file
|
||||||
|
- output 1 (preview): `libx264` veryfast HLS -> /live/{assetId} (CPU)
|
||||||
|
DeckLink does capture (cheap); BOTH encodes are CPU. ~5 vCPU per 1080i signal
|
||||||
|
=> ~2 signals saturate the 12-vCPU VM. GPUs are idle during capture.
|
||||||
|
|
||||||
|
### Stop / finalize (working)
|
||||||
|
node-agent stops the sidecar with a **180s grace** (was 10s -> SIGKILL bug).
|
||||||
|
Capture's SIGTERM handler finalises the session and calls
|
||||||
|
`POST /assets/:id/finalize` (the live asset id passed as ASSET_ID), which flips
|
||||||
|
the asset out of `live`, records duration + S3 keys, and kicks the
|
||||||
|
proxy -> thumbnail -> filmstrip chain. (Earlier 409 bug: it used to POST a new
|
||||||
|
asset and collide with the live row.)
|
||||||
|
|
||||||
|
### Live monitor (working)
|
||||||
|
SDI HLS preview is a 2nd output of the capture ffmpeg (one DeckLink read ->
|
||||||
|
split -> ProRes + H.264 HLS), written to /live/{assetId} on the capture node.
|
||||||
|
node-agent serves GET /live/* over HTTP; mam-api proxies
|
||||||
|
GET /api/v1/recorders/:id/live/* to the recorder's node-agent; the web-ui
|
||||||
|
HlsPreview loads the proxied URL. Browser auth is the session cookie
|
||||||
|
(same-origin).
|
||||||
|
|
||||||
|
### GPU worker pool (working, post-capture)
|
||||||
|
BullMQ on shared Redis; queues are type-named (proxy/thumbnail/filmstrip/
|
||||||
|
conform/trim). Workers are capability-routed by `WORKER_QUEUES`, one GPU-pinned
|
||||||
|
container per card (`NVIDIA_VISIBLE_DEVICES` by UUID):
|
||||||
|
- HEAVY (proxy/conform/trim): Tesla P4 (zampp1) + L4 (zampp2), `h264_nvenc`.
|
||||||
|
- LIGHT (thumbnail/filmstrip): 2x Quadro P400 (zampp1).
|
||||||
|
DB setting `gpu_transcode_enabled=true` + `gpu_codec=h264_nvenc` enable NVENC.
|
||||||
|
Each worker stamps `WORKER_LABEL` onto job data -> Jobs UI "Node" column.
|
||||||
|
`RUN_PROMOTION=true` on exactly one worker runs the growing-files->S3 scan.
|
||||||
|
The worker GPU image is built from services/worker/Dockerfile.gpu (CUDA base +
|
||||||
|
Ubuntu ffmpeg with h264/hevc_nvenc; NO av1_nvenc).
|
||||||
|
|
||||||
|
### Deploy gotchas (learned)
|
||||||
|
- Service source is BAKED into images; edits need rebuild + recreate (or the
|
||||||
|
GPU image rebuild reuses cached layers so only final COPY changes -> fast).
|
||||||
|
- The capture image can only build on zampp2 (DeckLink SDK present there).
|
||||||
|
- Per-node `.env`: zampp2's REDIS_URL/DATABASE_URL/S3_* now point at zampp1
|
||||||
|
(.200); secrets live only in .env, never in committed compose.
|
||||||
|
- Clear all containers on both nodes before a full redeploy (user preference).
|
||||||
|
|
||||||
|
## 4. Target design
|
||||||
|
|
||||||
|
### 4.1 Capture ffmpeg gains NVENC
|
||||||
|
The capture image's custom FFmpeg 7.1 is currently built WITHOUT nvenc (only
|
||||||
|
prores_ks/dnxhd/libx264). Rebuild `services/capture/Dockerfile` ffmpeg with:
|
||||||
|
`--enable-cuda-nvcc --enable-libnpp --enable-nvenc --enable-cuvid` plus
|
||||||
|
nv-codec-headers (ffnvcodec) installed before configure. Keep `--enable-decklink`
|
||||||
|
and the existing codecs (ProRes stays available as a selectable fallback).
|
||||||
|
Verify `ffmpeg -encoders | grep nvenc` shows hevc_nvenc/h264_nvenc afterwards.
|
||||||
|
|
||||||
|
### 4.2 Capture sidecar gets a GPU
|
||||||
|
node-agent `handleSidecarStart` currently spawns the capture container with no
|
||||||
|
GPU. Add NVIDIA runtime + device pinning to the sidecar create spec:
|
||||||
|
`HostConfig.Runtime='nvidia'` (or DeviceRequests with the node's GPU) and env
|
||||||
|
`NVIDIA_VISIBLE_DEVICES=<uuid>` + `NVIDIA_DRIVER_CAPABILITIES=video,compute,utility`.
|
||||||
|
The capture node's GPU is shared with its worker-l4 (see capacity, §5).
|
||||||
|
|
||||||
|
### 4.3 Encode parameters (master)
|
||||||
|
All-Intra HEVC on NVENC:
|
||||||
|
`-c:v hevc_nvenc -preset p4 -rc vbr -g 1 -bf 0 -profile:v main10 -pix_fmt p010le` (10-bit 4:2:2 is not NVENC-native; NVENC HEVC is 4:2:0 8/10-bit.
|
||||||
|
If 4:2:2 mezzanine is required, that is a HARD blocker for NVENC and we stay on
|
||||||
|
ProRes for those feeds — see §8). Bitrate target tuned per format (1080i59.94
|
||||||
|
~100-160 Mbps to rival ProRes HQ). `-g 1 -bf 0` => every frame IDR (all-intra).
|
||||||
|
|
||||||
|
### 4.4 Container (growing-file)
|
||||||
|
Write the master to a growing file on the SMB share (GROWING_PATH), same path
|
||||||
|
the promotion worker already uploads on EOF. Container candidates, in order of
|
||||||
|
preference for Premiere growing-file mounts:
|
||||||
|
1. **MXF OP1a** (`-f mxf`) — broadcast standard, designed for growing/edit-while-
|
||||||
|
ingest; best Avid/Premiere support. HEVC-in-MXF support in Premiere is the
|
||||||
|
key unknown to validate (§8).
|
||||||
|
2. **Fragmented MOV/MP4** (`-movflags +frag_keyframe+empty_moov+default_base_moof`)
|
||||||
|
— no moov-at-EOF, readable while growing; fallback if MXF+HEVC is unsupported.
|
||||||
|
The HLS preview path is unchanged except it can also move to h264_nvenc now that
|
||||||
|
capture has NVENC (frees the last libx264 CPU cost).
|
||||||
|
|
||||||
|
## 5. Capacity & scaling (8 signals/machine)
|
||||||
|
|
||||||
|
After the move, per-signal CPU is just: DeckLink capture + yadif + mux + frame
|
||||||
|
upload to the GPU. The heavy HEVC encode is on NVENC. The constraint shifts from
|
||||||
|
CPU to **NVENC throughput + GPU memory + PCIe/host bandwidth**:
|
||||||
|
- The **L4 is a datacenter card => unlimited NVENC sessions** (no consumer
|
||||||
|
3-session cap). 8x 1080i HEVC-I encode sessions are well within an L4.
|
||||||
|
- GPU memory: ~8 concurrent 1080 NVENC sessions + frame buffers fit in 24 GB.
|
||||||
|
- The capture node's L4 is shared between capture (per-signal HEVC-I) and the
|
||||||
|
worker-l4 proxy jobs. Under 8-signal load, give capture priority; consider
|
||||||
|
moving worker-l4 (post-record proxies) to zampp1's P4 only, or gate worker-l4
|
||||||
|
intake while signals are live.
|
||||||
|
- yadif on CPU is still ~0.5-1 vCPU/signal; consider `yadif_cuda`/`bwdif_cuda`
|
||||||
|
(GPU deinterlace) once frames are uploaded to the GPU, keeping CPU near-idle.
|
||||||
|
|
||||||
|
**Node sizing:** a 12-vCPU VM was the ProRes wall; with GPU encode the same VM
|
||||||
|
should carry many more signals, but for 8x SDI + GPU + card passthrough prefer a
|
||||||
|
larger VM or bare metal with proper PCIe passthrough. Or spread signals across
|
||||||
|
multiple capture nodes (the node-agent model already supports N nodes; mam-api
|
||||||
|
routes each recorder to its node).
|
||||||
|
|
||||||
|
## 6. Multi-vendor capture (Blackmagic / Deltacast / AJA)
|
||||||
|
|
||||||
|
Today capture is hard-wired to `-f decklink`. Before three vendors accrue
|
||||||
|
special-cases, introduce a **source-backend abstraction** in capture-manager:
|
||||||
|
each backend returns ffmpeg input args + device discovery.
|
||||||
|
- **Blackmagic**: `-f decklink -i "<name>"` (current). Devices via
|
||||||
|
`ffmpeg -sources decklink`.
|
||||||
|
- **Deltacast**: VideoMaster SDK. No native ffmpeg demuxer upstream — needs an
|
||||||
|
SDK-backed capture (their SDK -> pipe to ffmpeg, or a small grabber). Plan a
|
||||||
|
`deltacast` backend that shells their tool into ffmpeg stdin (rawvideo).
|
||||||
|
- **AJA**: libajantv2. Also no upstream ffmpeg input; AJA ships `ntv2` capture
|
||||||
|
tools. Plan an `aja` backend feeding rawvideo into ffmpeg.
|
||||||
|
All backends converge on the SAME encode/output stage (HEVC-I NVENC + HLS), so
|
||||||
|
only the input differs. node-agent already binds the right /dev nodes per
|
||||||
|
sourceType (decklink/deltacast); extend for AJA.
|
||||||
|
|
||||||
|
## 7. Risks
|
||||||
|
|
||||||
|
- **4:2:2 / 10-bit chroma:** NVENC HEVC is 4:2:0 (8/10-bit). ProRes HQ is 4:2:2
|
||||||
|
10-bit. If a workflow REQUIRES 4:2:2 mezzanine, NVENC HEVC cannot match it and
|
||||||
|
those feeds stay on ProRes (CPU). Decide per-workflow.
|
||||||
|
- **Premiere growing HEVC support:** edit-while-record for HEVC-in-MXF (or frag
|
||||||
|
MOV) is unproven in our stack — this is the make-or-break validation (§8).
|
||||||
|
- **GPU contention** between live capture and post-record proxies on the same
|
||||||
|
L4; mitigate by prioritising capture / relocating proxy load.
|
||||||
|
- **Storage:** All-Intra HEVC bitrate ~ ProRes; expect similar disk usage.
|
||||||
|
- **Editor performance:** HEVC-I decode in Premiere is heavier than ProRes on
|
||||||
|
the edit workstation (decode cost moves to the editor). Validate scrubbing.
|
||||||
|
- **NVENC quality at all-intra** vs ProRes for archival; tune bitrate/preset.
|
||||||
|
|
||||||
|
## 8. Validation gate (do FIRST, before building the pipeline)
|
||||||
|
|
||||||
|
Prove the editor story on ONE channel before wiring 8:
|
||||||
|
1. Rebuild capture ffmpeg with NVENC; give the sidecar the L4.
|
||||||
|
2. Capture one DeckLink feed to All-Intra HEVC, writing a GROWING file to the
|
||||||
|
SMB share in (a) MXF OP1a, then (b) fragmented MOV.
|
||||||
|
3. While still recording, mount it in Premiere over SMB and confirm:
|
||||||
|
edit-while-record works, scrubbing is acceptable, audio in sync, file remains
|
||||||
|
valid after stop. Pick the container that works; if neither does, HEVC-I is
|
||||||
|
capture-only (no growing edit) and we keep ProRes for growing workflows.
|
||||||
|
|
||||||
|
## 9. Rollout
|
||||||
|
1. Validation gate (§8) on one channel.
|
||||||
|
2. Make capture codec/container a recorder setting; default growing feeds to
|
||||||
|
HEVC-I NVENC, keep ProRes selectable.
|
||||||
|
3. Move HLS preview to h264_nvenc.
|
||||||
|
4. Source-backend abstraction (§6) — land before Deltacast/AJA hardware.
|
||||||
|
5. GPU deinterlace + capacity test to 8 signals; finalise node sizing.
|
||||||
BIN
docs/screenshots/01-home.png
Normal file
BIN
docs/screenshots/01-home.png
Normal file
Binary file not shown.
|
|
@ -0,0 +1,73 @@
|
||||||
|
# NLE Editor: React SPA Polish — Phases 1–3 Implementation Plan
|
||||||
|
|
||||||
|
> **Date:** 2026-05-24
|
||||||
|
> **Status:** Phase 1 IN PROGRESS
|
||||||
|
> **Progress:** Tasks 1.1–1.6 code-complete, pending test/deploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Core Editor (IN PROGRESS)
|
||||||
|
|
||||||
|
### Task 1.1: Sequence API helpers in data.jsx ✅
|
||||||
|
- Added `getSequences`, `createSequence`, `getSequence`, `updateSequence`, `deleteSequence`, `syncSequenceClips`, `exportSequenceEDL` to `data.jsx`
|
||||||
|
- All exported on `window.ZAMPP_API`
|
||||||
|
|
||||||
|
### Task 1.2: Timecode.js wired into SPA ✅
|
||||||
|
- Added `<script src="js/timecode.js">` and `<script src="js/timeline.js">` to `index.html` before `screens-editor.jsx`
|
||||||
|
- `window.TC` available globally for 59.94 DF timecode math
|
||||||
|
|
||||||
|
### Task 1.3: TimelinePanel React component ✅
|
||||||
|
- `tlRef` container div in editor layout
|
||||||
|
- `useEffect` mounts `window.Timeline.init()` on first render
|
||||||
|
- `useEffect` pushes scale changes via `window.Timeline.setScale()`
|
||||||
|
- `onClipsChanged` / `onPlayheadMoved` callbacks connect timeline engine to React state
|
||||||
|
|
||||||
|
### Task 1.4: screens-editor.jsx rewrite ✅ (455 lines, was 162)
|
||||||
|
Full rewrite with:
|
||||||
|
- **App state**: `projectId`, `sequences`, `currentSeq`, `assets`, `sourceAsset`, `srcIn`/`srcOut`, `playheadFrames`, `history` (undo stack), `scale`, `tool`, `saveStatus`, `isDirty`
|
||||||
|
- **Source monitor**: `<video>` + `apiFetch('/assets/:id/stream')` + Mark In/Out + Insert button
|
||||||
|
- **Program monitor**: Virtual clip-by-clip playback — loads V1 clips sorted by `timeline_in_frames`, advances on `timeUpdate`/`ended` events
|
||||||
|
- **Media panel**: Asset list from `ZAMPP_DATA.ASSETS`, filter by bin, `AssetThumb` thumbnails, double-click loads source
|
||||||
|
- **Sequence management**: Picker `<select>`, "New sequence" modal, `openSequence()` loads via API
|
||||||
|
- **Auto-save**: `markDirty()` → debounce 2s → `syncSequenceClips()` → status updates
|
||||||
|
- **Undo/redo**: 50-step history stack, Ctrl+Z / Ctrl+Shift+Z
|
||||||
|
- **EDL export**: Button triggers `window.ZAMPP_API.exportSequenceEDL()`
|
||||||
|
- **Tool toolbar**: V/C/H buttons synced with `Timeline.setTool()`
|
||||||
|
- **Zoom slider**: Range input driving `window.Timeline.setScale()`
|
||||||
|
- **Keyboard handler**: I/O, V/C/H, Ctrl+Z/Shift+Z, Ctrl+S
|
||||||
|
|
||||||
|
### Task 1.5: "In Development" overlay removed ✅
|
||||||
|
- Deleted the `position: absolute; inset: 0; backdropFilter: blur(6px)` overlay div (was lines 98-117)
|
||||||
|
- Removed `FauxFrame` component reference
|
||||||
|
- All buttons are now functional
|
||||||
|
|
||||||
|
### Task 1.6: Editor nav badge removed ✅
|
||||||
|
- `shell.jsx`: `{ id: "editor", label: "Editor", icon: "editor" }` — no more `badge: { kind: "dev", text: "DEV" }`
|
||||||
|
|
||||||
|
### NEXT: Task 1.7 — Test & Deploy
|
||||||
|
1. `docker compose up -d --build web-ui`
|
||||||
|
2. Navigate to Editor route in browser
|
||||||
|
3. Verify: source monitor loads video, timeline renders with 4 track rows, Insert places clip, auto-save fires, EDL export downloads
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2 — UX Polish & Growing File (PENDING)
|
||||||
|
|
||||||
|
- [ ] 2.1 Multi-track refinements (ripple delete, snap, locking, overlap prevention)
|
||||||
|
- [ ] 2.2 Zoom slider + adaptive ruler
|
||||||
|
- [ ] 2.3 JKL transport + frame stepping
|
||||||
|
- [ ] 2.4 Waveform display on audio tracks
|
||||||
|
- [ ] 2.5 Inspector panel wiring
|
||||||
|
- [ ] 2.6 Style migration to Tailwind primitives
|
||||||
|
- [ ] 2.7 HLS live preview during capture
|
||||||
|
|
||||||
|
## Phase 3 — Export, Conform & Features (PENDING)
|
||||||
|
|
||||||
|
- [ ] 3.1 FCP XML export + conform queue
|
||||||
|
- [ ] 3.2 Hi-Res Auto-Relink
|
||||||
|
- [ ] 3.3 Timecoded Comments
|
||||||
|
- [ ] 3.4 Player Rebuild (P1)
|
||||||
|
- [ ] 3.5 Subclips (P2)
|
||||||
|
- [ ] 3.6 Multi-select & Bulk Ops (P3)
|
||||||
|
- [ ] 3.7 Smart Bins (P6)
|
||||||
|
- [ ] 3.8 Metadata Templates (P7)
|
||||||
2888
docs/superpowers/plans/2026-05-27-auth-system.md
Normal file
2888
docs/superpowers/plans/2026-05-27-auth-system.md
Normal file
File diff suppressed because it is too large
Load diff
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
|
||||||
272
docs/superpowers/specs/2026-05-23-youtube-importer-design.md
Normal file
272
docs/superpowers/specs/2026-05-23-youtube-importer-design.md
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
# YouTube Importer — Design Spec
|
||||||
|
|
||||||
|
> Status: **design approved 2026-05-23**, awaiting user review of the spec before the implementation plan is written.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The Ingest group in Dragonflight today covers file Upload, Recorders (SRT/RTMP/SDI), Capture (DeckLink), Monitors, and Schedule. There is no path to bring in media that already lives on the public web. The frequent ask is: "I want to grab a YouTube link and have it become an asset in my project, with the same proxy/thumbnail pipeline as anything else." This spec adds a YouTube importer that mirrors the existing upload flow: paste a URL, pick a project, click Import, and the asset shows up in the Library once the worker is done.
|
||||||
|
|
||||||
|
The importer rides on the existing job pipeline. After the download lands in S3, the asset re-enters the same proxy → thumbnail → ready path as a regular upload, so there is no parallel "imported asset" lifecycle to maintain.
|
||||||
|
|
||||||
|
## Goals & non-goals
|
||||||
|
|
||||||
|
**Goals**
|
||||||
|
- Paste a public YouTube URL, end up with a `ready` asset in the chosen project.
|
||||||
|
- Reuse the existing `assets` table, S3 layout, BullMQ pipeline, and Jobs screen — no parallel state machine.
|
||||||
|
- Progress visible from both the import screen (queue rows) and the Jobs screen.
|
||||||
|
- Clear, actionable errors for the obvious failure modes (private, age-gated, removed, geo-blocked, network).
|
||||||
|
|
||||||
|
**Non-goals**
|
||||||
|
- Playlists, channels, or batch-paste of multiple URLs. Single URL per submission. (Easy to add later.)
|
||||||
|
- Cookies / login. Private, members-only, and age-gated videos are out of scope v1.
|
||||||
|
- Quality picker. Always grabs best MP4 (with M4A audio merge fallback).
|
||||||
|
- Non-YouTube sources (Vimeo, Twitch VODs, Dropbox links, etc.). The route is `/imports/youtube` precisely to leave room for siblings later.
|
||||||
|
- Auto-update of yt-dlp inside the running container. Updates land via image rebuild.
|
||||||
|
- Copyright enforcement. We surface a one-line "only import videos you have rights to use" note and stop there.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The importer threads through four existing layers:
|
||||||
|
|
||||||
|
```
|
||||||
|
[web-ui] YouTube screen ──POST /imports/youtube──▶ [mam-api]
|
||||||
|
│
|
||||||
|
assets row (status='ingesting')
|
||||||
|
jobs row (type='youtube_import')
|
||||||
|
│
|
||||||
|
BullMQ "import" queue
|
||||||
|
▼
|
||||||
|
[worker]
|
||||||
|
yt-dlp download → S3 originals/
|
||||||
|
ffprobe metadata → assets row
|
||||||
|
status='processing'
|
||||||
|
│
|
||||||
|
BullMQ "proxy" queue ◀── existing path
|
||||||
|
▼
|
||||||
|
proxy → thumbnail → ready
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the worker hands off to the `proxy` queue, the asset is indistinguishable from one that came through Upload — same proxy worker, same thumbnail worker, same Library list.
|
||||||
|
|
||||||
|
## 1. UX
|
||||||
|
|
||||||
|
### Nav
|
||||||
|
|
||||||
|
A 6th child is added to the **Ingest** group in `shell.jsx`, between Upload and Recorders:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{ id: "youtube", label: "YouTube", icon: "download" },
|
||||||
|
```
|
||||||
|
|
||||||
|
The `download` glyph already exists in `icons.jsx`. The matching `ingestChildren` array in `shell.jsx` and the crumbs map in `app.jsx` both gain `"youtube"`.
|
||||||
|
|
||||||
|
### Screen
|
||||||
|
|
||||||
|
A new `YouTubeImport` component lives in `screens-ingest.jsx` and is exported on `window` alongside `Upload`, `Recorders`, etc. It is registered as a route in `app.jsx`.
|
||||||
|
|
||||||
|
Layout — visually a sibling of the Upload screen:
|
||||||
|
|
||||||
|
- **Header**: title "YouTube", subtitle "Paste a link — we download and import the best available MP4."
|
||||||
|
- **Project selector**: same `select` element as Upload's, pre-selected to the first project.
|
||||||
|
- **URL input**: a single-line `field-input` with placeholder "Paste a YouTube URL (youtube.com/watch, youtu.be, or shorts)…" and an inline **Import** button. Enter submits. The button is disabled until a URL pattern matches.
|
||||||
|
- **Subtitle line under the input**: "Only import videos you have rights to use. Private, age-gated, and members-only videos are not supported."
|
||||||
|
- **Queue panel**: identical structure to Upload's queue — one row per submitted URL, showing:
|
||||||
|
- Source icon (use `link` glyph) and the URL (truncated middle, full URL in `title` tooltip).
|
||||||
|
- Title once known (filled in by a poll on the asset row).
|
||||||
|
- Progress bar tied to job `progress` (0–100). The worker drives this between 5 and 60 % for download and 60 to 100 % for upload + DB writes.
|
||||||
|
- Status pill: queued → downloading → processing → done / failed.
|
||||||
|
- Error text if the job fails (red, one line).
|
||||||
|
- A "Clear done" button at the top of the queue.
|
||||||
|
|
||||||
|
The queue persists for the session in component state only — no separate UI table. Jobs screen remains the canonical history.
|
||||||
|
|
||||||
|
### URL validation (client-side, before POST)
|
||||||
|
|
||||||
|
Accept (case-insensitive) any of these patterns:
|
||||||
|
- `https?://(www\.|m\.)?youtube\.com/watch\?[^ ]*v=[A-Za-z0-9_-]{11}`
|
||||||
|
- `https?://youtu\.be/[A-Za-z0-9_-]{11}`
|
||||||
|
- `https?://(www\.)?youtube\.com/shorts/[A-Za-z0-9_-]{11}`
|
||||||
|
|
||||||
|
Anything else is rejected inline ("That doesn't look like a YouTube URL") without an API call. The server re-validates as a defense-in-depth check.
|
||||||
|
|
||||||
|
### Out-of-scope v1 (called out, not built)
|
||||||
|
|
||||||
|
- Pasting a playlist URL. Server returns 400 "Playlists aren't supported yet."
|
||||||
|
- Multi-line paste. Single URL only.
|
||||||
|
- Quality picker. yt-dlp format string is hard-coded.
|
||||||
|
- Cookies upload. Private videos fail with a clear message.
|
||||||
|
|
||||||
|
## 2. API
|
||||||
|
|
||||||
|
### Route
|
||||||
|
|
||||||
|
New file `services/mam-api/src/routes/imports.js`, mounted at `/api/v1/imports` in `services/mam-api/src/index.js`.
|
||||||
|
|
||||||
|
**`POST /api/v1/imports/youtube`**
|
||||||
|
|
||||||
|
Request body:
|
||||||
|
```json
|
||||||
|
{ "url": "https://youtu.be/dQw4w9WgXcQ", "projectId": "uuid", "binId": "uuid?" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
1. Validate `url` against the same three regexes as the client. 400 on miss.
|
||||||
|
2. Reject playlist URLs (URL contains `list=`) with 400 "Playlists aren't supported yet."
|
||||||
|
3. Generate `assetId = uuidv4()`.
|
||||||
|
4. Insert into `assets` with:
|
||||||
|
- `status='ingesting'`
|
||||||
|
- `media_type='video'`
|
||||||
|
- `filename = url` (placeholder; worker overwrites with the sanitized title once yt-dlp prints metadata — keeps the row queryable in the meantime)
|
||||||
|
- `display_name = url` (same; worker overwrites)
|
||||||
|
- `original_s3_key = NULL` (worker fills in)
|
||||||
|
- `source_url = url` (new column — see Schema)
|
||||||
|
- `project_id`, `bin_id`, timestamps.
|
||||||
|
5. Insert into `jobs` with `type='youtube_import'`, `asset_id`, `payload={ url }`, `status='queued'`, `progress=0`.
|
||||||
|
6. Enqueue BullMQ job on the `import` queue:
|
||||||
|
```js
|
||||||
|
await importQueue.add('youtube', { assetId, url });
|
||||||
|
```
|
||||||
|
7. Respond `200 { assetId, jobId }`.
|
||||||
|
|
||||||
|
Errors:
|
||||||
|
- Missing fields → 400.
|
||||||
|
- Bad URL → 400 with `error: 'Invalid YouTube URL'`.
|
||||||
|
- Playlist URL → 400 with `error: 'Playlists aren't supported yet'`.
|
||||||
|
- Project not found → 404.
|
||||||
|
- DB / queue failure → 500 (next(err)).
|
||||||
|
|
||||||
|
### Jobs screen integration
|
||||||
|
|
||||||
|
`services/web-ui/public/screens-jobs.jsx` already normalizes job types via a `kindMap`. Add one entry:
|
||||||
|
```js
|
||||||
|
const kindMap = { proxy: 'Proxy', thumbnail: 'Thumbnail', conform: 'Conform', transcode: 'Transcode', youtube_import: 'YouTube' };
|
||||||
|
```
|
||||||
|
Retry, delete, and the SSE event stream all work for the new type with no further changes because they key off `job.id`, not `job.type`.
|
||||||
|
|
||||||
|
## 3. Worker
|
||||||
|
|
||||||
|
### Container changes
|
||||||
|
|
||||||
|
`services/worker/Dockerfile` gains two packages:
|
||||||
|
```dockerfile
|
||||||
|
RUN apk add --no-cache ffmpeg yt-dlp python3
|
||||||
|
```
|
||||||
|
`yt-dlp` is in the Alpine `community` repo and pulls `python3` as a runtime dep — we list it explicitly for clarity. Image grows by ~25 MB.
|
||||||
|
|
||||||
|
### New worker
|
||||||
|
|
||||||
|
`services/worker/src/workers/youtube-import.js`, registered in `services/worker/src/index.js`:
|
||||||
|
```js
|
||||||
|
const workers = [
|
||||||
|
createWorker('proxy', proxyWorker),
|
||||||
|
createWorker('thumbnail', thumbnailWorker),
|
||||||
|
createWorker('conform', conformWorker),
|
||||||
|
createWorker('import', youtubeImportWorker),
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Job handler
|
||||||
|
|
||||||
|
For a job with `{ assetId, url }`:
|
||||||
|
|
||||||
|
1. `job.updateProgress(2)` — accepted.
|
||||||
|
2. Build a temp directory `tmpdir()/yt-${jobId}`.
|
||||||
|
3. Run yt-dlp:
|
||||||
|
```sh
|
||||||
|
yt-dlp \
|
||||||
|
--no-playlist \
|
||||||
|
--no-warnings \
|
||||||
|
--restrict-filenames \
|
||||||
|
-f "bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b" \
|
||||||
|
--merge-output-format mp4 \
|
||||||
|
--print-json \
|
||||||
|
--newline \
|
||||||
|
-o "<tmpdir>/<assetId>.%(ext)s" \
|
||||||
|
"<url>"
|
||||||
|
```
|
||||||
|
- `--print-json` writes one JSON line at the end with title, duration, width, height, uploader, etc.
|
||||||
|
- `--newline` makes progress lines newline-terminated so we can parse them.
|
||||||
|
- `--restrict-filenames` prevents shell-special characters in temp paths.
|
||||||
|
4. Stream stdout line-by-line. Lines matching `r'\[download\]\s+(\d+(\.\d+)?)%'` map to `job.updateProgress(5 + Math.floor(pct * 0.55))` so download takes us from 5 to 60 %.
|
||||||
|
5. On yt-dlp non-zero exit: parse stderr for the first line containing `ERROR:` and use it as the job's error message. Mark the asset `status='error'`, mark the job failed, throw so BullMQ records it. Surface a friendly substitution for the common cases:
|
||||||
|
- "Private video" → "Private video — not supported."
|
||||||
|
- "Sign in to confirm your age" → "Age-restricted video — not supported."
|
||||||
|
- "Video unavailable" → "Video unavailable or removed."
|
||||||
|
- "This video is not available in your country" → "Video is geo-blocked from this region."
|
||||||
|
- HTTP 429 → "YouTube rate-limited the importer — try again later."
|
||||||
|
- Anything else → use yt-dlp's stderr line verbatim, truncated to 300 chars.
|
||||||
|
6. Parse the last stdout line as JSON to read metadata. The resulting file is `<tmpdir>/<assetId>.mp4`.
|
||||||
|
7. `getMediaInfo` (existing helper in `services/worker/src/ffmpeg/executor.js`) on that path. Use ffprobe's values for codec/fps/duration when yt-dlp's are missing or wrong.
|
||||||
|
8. Sanitize the title for the S3 filename: keep `[A-Za-z0-9 ._-]`, collapse runs of whitespace, trim, cap at 120 chars, append `.mp4`. If the sanitized title is empty, fall back to `youtube-<videoId>.mp4`.
|
||||||
|
9. Upload to `originals/{assetId}/{sanitized-title}.mp4` via the existing `uploadToS3` helper. Progress 60 → 90 %.
|
||||||
|
10. UPDATE the assets row with:
|
||||||
|
- `filename = <sanitized title>.mp4`
|
||||||
|
- `display_name = <yt-dlp title untouched>`
|
||||||
|
- `original_s3_key = originals/<assetId>/<sanitized-title>.mp4`
|
||||||
|
- `codec`, `resolution`, `fps`, `duration_ms`, `file_size` from ffprobe.
|
||||||
|
- `status = 'processing'`
|
||||||
|
- `updated_at = NOW()`
|
||||||
|
11. Enqueue a `proxy` job on the existing `proxy` queue with the same payload shape `upload.js` uses:
|
||||||
|
```js
|
||||||
|
await proxyQueue.add('generate', {
|
||||||
|
assetId,
|
||||||
|
inputKey: asset.original_s3_key,
|
||||||
|
outputKey: `proxies/${assetId}.mp4`,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
12. `job.updateProgress(100)`. Return — BullMQ marks the import job done. The proxy job picks up the rest exactly like a regular upload.
|
||||||
|
13. Always `rm -rf` the temp directory in a `finally`.
|
||||||
|
|
||||||
|
### Concurrency & retries
|
||||||
|
|
||||||
|
- Default BullMQ concurrency for this queue: **1** per worker process. Two simultaneous yt-dlp invocations risk YouTube rate-limiting more than they help throughput. Configurable later via env if needed.
|
||||||
|
- No automatic BullMQ retry — yt-dlp failures are almost always permanent (private, geo, removed) and a silent retry storm would chew through quota. The Jobs screen's manual Retry button is the right knob for "this should be transient" cases.
|
||||||
|
|
||||||
|
## 4. Schema migration
|
||||||
|
|
||||||
|
New file `services/mam-api/src/db/migrations/011-youtube-import.sql`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 1. Add the new job type to the enum.
|
||||||
|
-- Postgres requires ALTER TYPE ... ADD VALUE for enum changes.
|
||||||
|
ALTER TYPE job_type ADD VALUE IF NOT EXISTS 'youtube_import';
|
||||||
|
|
||||||
|
-- 2. Remember where an asset came from. NULL for everything that
|
||||||
|
-- pre-dates the importer; populated for any imported asset.
|
||||||
|
ALTER TABLE assets ADD COLUMN IF NOT EXISTS source_url TEXT;
|
||||||
|
```
|
||||||
|
|
||||||
|
`source_url` is exposed on the asset drawer as a "Source" line ("imported from youtu.be/…") in a follow-up PR — out of scope for this spec, but worth noting that the column exists for it.
|
||||||
|
|
||||||
|
## 5. Files touched
|
||||||
|
|
||||||
|
**New**
|
||||||
|
- `services/mam-api/src/routes/imports.js`
|
||||||
|
- `services/mam-api/src/db/migrations/011-youtube-import.sql`
|
||||||
|
- `services/worker/src/workers/youtube-import.js`
|
||||||
|
|
||||||
|
**Edited**
|
||||||
|
- `services/mam-api/src/index.js` — mount the new route.
|
||||||
|
- `services/web-ui/public/screens-ingest.jsx` — add `YouTubeImport`, export on `window`.
|
||||||
|
- `services/web-ui/public/shell.jsx` — add the nav child, extend `ingestChildren`.
|
||||||
|
- `services/web-ui/public/app.jsx` — register the route and the crumb.
|
||||||
|
- `services/web-ui/public/screens-jobs.jsx` — extend `kindMap` with `youtube_import: 'YouTube'`.
|
||||||
|
- `services/worker/src/index.js` — register the `import` queue worker.
|
||||||
|
- `services/worker/Dockerfile` — add `yt-dlp` and `python3` to the apk install line.
|
||||||
|
|
||||||
|
## 6. Risks & trade-offs
|
||||||
|
|
||||||
|
- **Worker egress**. The worker container needs outbound HTTPS to YouTube. Fine in the current homelab; will fail in a locked-down cluster. Documented in the implementation plan.
|
||||||
|
- **yt-dlp drift**. YouTube changes break old yt-dlp versions every few weeks. The Alpine package lags upstream by days. Fix is to rebuild the worker image. We do not auto-update inside the running container — too risky for an offline / locked-down deploy. If imports start failing en masse, the runbook is `docker compose build worker && docker compose up -d worker`.
|
||||||
|
- **Single-URL UX feels light**. That is deliberate for v1. Adding multi-URL paste and playlist expansion are both small follow-ups once the single-URL path is stable.
|
||||||
|
- **No copyright enforcement**. We rely on the one-line notice in the UI. If misuse becomes a real concern, the next step would be an admin allowlist of domains or a per-user import quota — not in this spec.
|
||||||
|
- **`filename = url` placeholder**. Briefly, the asset row in the Library shows the URL as the name. The worker overwrites it within seconds for a successful import. Acceptable; the Library already handles "ingesting" assets with placeholder names from the upload path.
|
||||||
|
|
||||||
|
## 7. Acceptance
|
||||||
|
|
||||||
|
The feature is done when:
|
||||||
|
- A user can navigate to **Ingest → YouTube**, paste a public YouTube URL, pick a project, click Import, and within a minute or two see the asset appear in the Library with proxy and thumbnail.
|
||||||
|
- A failed import (private video, removed video, bogus URL) shows a clear error message on both the YouTube screen's queue row and the Jobs screen.
|
||||||
|
- The Jobs screen lists "YouTube" jobs alongside Proxy / Thumbnail / Conform, with Retry working.
|
||||||
|
- `source_url` is populated on the imported asset row.
|
||||||
|
- Image rebuild + `docker compose up -d worker` is the documented recovery path if YouTube changes break yt-dlp.
|
||||||
269
docs/superpowers/specs/2026-05-27-auth-system-design.md
Normal file
269
docs/superpowers/specs/2026-05-27-auth-system-design.md
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
# Dragonflight User Authentication — Design
|
||||||
|
|
||||||
|
**Status:** Approved, ready for implementation planning
|
||||||
|
**Date:** 2026-05-27
|
||||||
|
**Brainstormed with:** Zac
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Dragonflight has the skeleton of an auth system spread across the codebase:
|
||||||
|
|
||||||
|
- `users` table (`id`, `username`, `password_hash`, `display_name`, `role`)
|
||||||
|
- `sessions` table (`sid`, `sess`, `expire`) for `connect-pg-simple`
|
||||||
|
- `groups`, `user_groups`, `api_tokens` tables
|
||||||
|
- `SESSION_SECRET` env var
|
||||||
|
- `AUTH_ENABLED` env flag with boot-log toggle
|
||||||
|
- PR #26 frontend handler that bounces to `/login.html` on 401
|
||||||
|
- Issue #94 "session security fixes" deployed 2026-05-26 (commit `3ebe5d6`)
|
||||||
|
|
||||||
|
But the actual `express-session` middleware was never mounted in `services/mam-api/src/index.js`. There is no `/api/v1/auth/*` router. There is no `requireAuth` middleware. As a result, when `AUTH_ENABLED=true` was tried:
|
||||||
|
|
||||||
|
1. User submits login, server returns 200 OK from a stub endpoint.
|
||||||
|
2. No `Set-Cookie` is ever sent (no session middleware mounted).
|
||||||
|
3. The next request to a protected route returns 401.
|
||||||
|
4. Frontend bounces to `/login.html`.
|
||||||
|
5. **Infinite redirect loop.**
|
||||||
|
|
||||||
|
The prior attempts failed because auth was being built reactively in pieces, with no single source of truth for what "logged in" means.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- One coherent, readable auth code path.
|
||||||
|
- Web UI logins survive page reloads and container restarts.
|
||||||
|
- Premiere panel can authenticate via long-lived bearer tokens.
|
||||||
|
- First-run setup works on a fresh install with no env var or CLI gymnastics.
|
||||||
|
- The whole auth flow can be exercised by automated tests, including a regression test for the redirect-loop failure mode.
|
||||||
|
|
||||||
|
## Non-goals (v1)
|
||||||
|
|
||||||
|
- MFA / TOTP.
|
||||||
|
- OAuth / OIDC delegation (Forgejo, Google, etc.).
|
||||||
|
- Per-project or per-recorder permissions. Flat access: logged in = full access.
|
||||||
|
- Email-based "forgot password" (no SMTP assumed; admin-reset only).
|
||||||
|
- Audit log of who-did-what (the `last_login_at` column is the minimum).
|
||||||
|
- Service-to-service auth for `node-agent` — keeps existing `019-node-token-binding` mechanism.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
| Decision | Choice | Reasoning |
|
||||||
|
|---|---|---|
|
||||||
|
| Client surface | Web UI + Premiere panel | Two transports (cookies + bearer), one identity backend |
|
||||||
|
| Permission model | Flat (logged in = full access) | Small homogeneous operator population. `groups` / `user_groups` schemas stay inert. |
|
||||||
|
| Identity provider | Local username/password | On-prem broadcast operators won't tolerate OIDC roundtrips. Matches existing schema. |
|
||||||
|
| First-user bootstrap | First-run setup page | Hardest to mis-configure. No env vars to leak. No CLI to remember. |
|
||||||
|
| Session lifetime | 8h absolute + 1h sliding idle | Operator security posture, tighter than typical SaaS. |
|
||||||
|
| Auth library | Hand-rolled (`express-session` + `connect-pg-simple`) | Explicit, debuggable. Rejected JWT and Passport for this codebase. |
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Single source of truth
|
||||||
|
|
||||||
|
"Logged in" means exactly one of two things:
|
||||||
|
|
||||||
|
1. The request carries a valid `dragonflight.sid` cookie whose row in `sessions` hasn't expired and isn't past its 1h-idle or 8h-absolute window, OR
|
||||||
|
2. The request carries `Authorization: Bearer <token>` whose SHA-256 matches an `api_tokens` row that hasn't been revoked or expired.
|
||||||
|
|
||||||
|
Nothing else counts. No `localStorage` flags, no JWT, no client-side "I think I'm logged in" hints.
|
||||||
|
|
||||||
|
### One middleware, one check
|
||||||
|
|
||||||
|
`services/mam-api/src/middleware/auth.js` exposes a single `requireAuth` function:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export async function requireAuth(req, res, next) {
|
||||||
|
// Dev mode preserved. The 'dev' user is a real row in `users` seeded at
|
||||||
|
// boot when AUTH_ENABLED !== 'true', so FK-bearing routes (api_tokens,
|
||||||
|
// future comments, audit fields) keep working without conditional logic.
|
||||||
|
if (process.env.AUTH_ENABLED !== 'true') {
|
||||||
|
req.user = DEV_USER; // { id: <UUID of seeded 'dev' user>, username: 'dev' }
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Session check
|
||||||
|
if (req.session?.user_id) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - req.session.first_seen_at > 8 * 3600 * 1000) return destroyAnd401(req, res);
|
||||||
|
if (now - req.session.last_seen_at > 1 * 3600 * 1000) return destroyAnd401(req, res);
|
||||||
|
req.session.last_seen_at = now;
|
||||||
|
req.user = await loadUser(req.session.user_id);
|
||||||
|
if (!req.user) return destroyAnd401(req, res);
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Bearer check
|
||||||
|
const bearer = parseBearer(req.headers.authorization);
|
||||||
|
if (bearer) {
|
||||||
|
const hash = sha256hex(bearer);
|
||||||
|
const row = await pool.query(
|
||||||
|
`SELECT t.id, t.user_id, t.expires_at, u.username
|
||||||
|
FROM api_tokens t JOIN users u ON u.id = t.user_id
|
||||||
|
WHERE t.token_hash = $1`, [hash]);
|
||||||
|
if (row.rows.length && (!row.rows[0].expires_at || row.rows[0].expires_at > new Date())) {
|
||||||
|
pool.query(`UPDATE api_tokens SET last_used_at = NOW() WHERE id = $1`, [row.rows[0].id]).catch(() => {});
|
||||||
|
req.user = { id: row.rows[0].user_id, username: row.rows[0].username };
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Otherwise
|
||||||
|
return res.status(401).json({ error: 'unauthorized' });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Mounted at the `/api/v1` level in `services/mam-api/src/index.js`, **before** the individual route mounts, with an allowlist for the three pre-login auth paths:
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.use('/api/v1', (req, res, next) => {
|
||||||
|
const unauth = ['/auth/login', '/auth/setup', '/auth/setup-required'];
|
||||||
|
if (unauth.some(p => req.path === p)) return next();
|
||||||
|
return requireAuth(req, res, next);
|
||||||
|
});
|
||||||
|
// then: app.use('/api/v1/assets', assetsRouter), etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
`/health` lives at the root, outside the `/api/v1` mount, so it's naturally unaffected. `/api/v1/cluster/*` keeps its existing `019-node-token-binding` service-auth path: requireAuth runs first, fails with 401 for an unauthenticated request, **but** the cluster routes themselves do their own token check on request bodies, so node-agent traffic must include a valid user session OR an api_token (which is the change — node-agent will need to be issued an api_token at install time). Alternative: carve `/api/v1/cluster/*` out of the requireAuth gate too, and keep node-agent on its existing binding token alone. Implementer should pick — flagged in the implementation order.
|
||||||
|
|
||||||
|
### Session middleware (actually wired this time)
|
||||||
|
|
||||||
|
In `services/mam-api/src/index.js`, **before any route**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import session from 'express-session';
|
||||||
|
import connectPgSimple from 'connect-pg-simple';
|
||||||
|
const PgStore = connectPgSimple(session);
|
||||||
|
|
||||||
|
if (process.env.TRUST_PROXY === 'true') app.set('trust proxy', 1);
|
||||||
|
|
||||||
|
app.use(session({
|
||||||
|
store: new PgStore({ pool, tableName: 'sessions', pruneSessionInterval: 60 * 15 }),
|
||||||
|
secret: process.env.SESSION_SECRET,
|
||||||
|
name: 'dragonflight.sid',
|
||||||
|
cookie: {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: process.env.TRUST_PROXY === 'true',
|
||||||
|
path: '/',
|
||||||
|
maxAge: 8 * 3600 * 1000,
|
||||||
|
},
|
||||||
|
rolling: false, // sliding renewal handled in requireAuth so we can enforce idle + absolute separately
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth router
|
||||||
|
|
||||||
|
`services/mam-api/src/routes/auth.js`:
|
||||||
|
|
||||||
|
| Method | Path | Auth | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `GET` | `/api/v1/auth/setup-required` | none | `{ required: bool }`. Cheap, no auth. |
|
||||||
|
| `POST` | `/api/v1/auth/setup` | none | Only succeeds if `users` is empty. Creates first user, logs them in. |
|
||||||
|
| `POST` | `/api/v1/auth/login` | none | `{ username, password }` -> 200 + cookie or 401 |
|
||||||
|
| `POST` | `/api/v1/auth/logout` | required | Destroys session row, clears cookie |
|
||||||
|
| `GET` | `/api/v1/auth/me` | required | `{ id, username, display_name }` |
|
||||||
|
| `POST` | `/api/v1/auth/password` | required | Change own password (requires current) |
|
||||||
|
| `GET/POST/DELETE` | `/api/v1/auth/users[/:id]` | required | User CRUD |
|
||||||
|
| `GET/POST/DELETE` | `/api/v1/auth/tokens[/:id]` | required | Current user's API tokens |
|
||||||
|
|
||||||
|
### Data model
|
||||||
|
|
||||||
|
Existing schema is almost right. One small migration:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- services/mam-api/src/db/migrations/023-auth-session-timestamps.sql
|
||||||
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS password_updated_at TIMESTAMPTZ DEFAULT NOW();
|
||||||
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_login_at TIMESTAMPTZ;
|
||||||
|
-- idle / absolute timestamps live inside session.sess JSONB; no schema change needed
|
||||||
|
```
|
||||||
|
|
||||||
|
`groups` and `user_groups` stay as-is, unused for v1. `api_tokens` is already correctly shaped.
|
||||||
|
|
||||||
|
## Flows
|
||||||
|
|
||||||
|
### Browser login (the one that broke last time)
|
||||||
|
|
||||||
|
1. SPA boots, `<AuthGate>` calls `GET /api/v1/auth/me`.
|
||||||
|
2. `requireAuth` returns 401.
|
||||||
|
3. AuthGate calls `GET /api/v1/auth/setup-required`. If `true`, render Setup screen. Otherwise, render Login screen.
|
||||||
|
4. User submits `POST /api/v1/auth/login`. Server `bcrypt.compare`s, sets `req.session.user_id`, `first_seen_at`, `last_seen_at`. **Critical:** `await new Promise(r => req.session.save(r))` before responding, so the cookie is persisted to Postgres before the next request can arrive.
|
||||||
|
5. AuthGate re-calls `/api/v1/auth/me`, gets 200, renders the app.
|
||||||
|
|
||||||
|
**Why this doesn't loop:** the explicit `req.session.save()` callback before response guarantees the cookie row exists before the SPA can fire its next request. `requireAuth` returns a clean 401 (not a redirect) so the SPA decides what to render. The static `/login.html` is deleted; there is no HTML bounce.
|
||||||
|
|
||||||
|
### Premiere panel bearer
|
||||||
|
|
||||||
|
1. Web UI -> Settings -> API Tokens -> "New token" named "Premiere panel".
|
||||||
|
2. `POST /api/v1/auth/tokens` returns `{ token: 'dfl_<32 hex>', prefix: 'dfl_a3f2', id }` **exactly once**.
|
||||||
|
3. Premiere panel sends `Authorization: Bearer dfl_<...>` on every request. `requireAuth` SHA-256s it, looks up `api_tokens.token_hash`, updates `last_used_at`.
|
||||||
|
|
||||||
|
### Idle + absolute timeout (inside `requireAuth`)
|
||||||
|
|
||||||
|
```
|
||||||
|
if session present:
|
||||||
|
if now - session.first_seen_at > 8h -> destroy session, 401
|
||||||
|
if now - session.last_seen_at > 1h -> destroy session, 401
|
||||||
|
session.last_seen_at = now
|
||||||
|
req.user = lookup(session.user_id)
|
||||||
|
next()
|
||||||
|
```
|
||||||
|
|
||||||
|
Bearer tokens have their own optional `expires_at` (`NULL` = never expires); checked the same way.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
- **`services/web-ui/src/auth-gate.jsx`** — new component that wraps the SPA. On mount: `GET /me`. On 401: check `setup-required`, render either Setup or Login. On 200: render the app shell.
|
||||||
|
- **Login screen** — layout B from brainstorm: 22px wordmark over "WILD DRAGON BROADCAST" tagline above a `--bg-1` card containing username, password, "Sign in" button. Matches DESIGN.md tokens.
|
||||||
|
- **Setup screen** — same chrome; fields = username, password, confirm password; button = "Create admin".
|
||||||
|
- **Settings -> Account section** — change password.
|
||||||
|
- **Settings -> API Tokens section** — list / create / revoke. New token shown exactly once with a copy affordance.
|
||||||
|
- **Fetch wrapper** — the central `ZAMPP_API.fetch` (already exists) gains a 401 handler that re-mounts AuthGate's Login state with the current path saved as `last_path`, restored after re-auth.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- The static `/login.html` page (PR #26's bounce target) is deleted. SPA handles login internally; no full-page reload.
|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
| Case | Behavior |
|
||||||
|
|---|---|
|
||||||
|
| Wrong username or password | `401 { error: 'invalid credentials' }`. Same message either way, no user enumeration. |
|
||||||
|
| Login rate limiting | Per-IP exponential backoff (1s, 2s, 4s, 8s, max 30s). In-memory `Map`. Single-instance limitation documented. |
|
||||||
|
| Idle / absolute expiry | 401 -> AuthGate Login. Last path saved, restored on re-auth. |
|
||||||
|
| Setup after first user exists | `409 { error: 'setup already complete' }`. Permanently disabled. |
|
||||||
|
| Token revoke | `DELETE /api/v1/auth/tokens/:id` — only owner can revoke. Subsequent bearer requests 401. |
|
||||||
|
| Delete-self when only user | `409 { error: 'cannot delete last user' }`. |
|
||||||
|
| Forgot password | No self-serve. Any logged-in user can reset another via `POST /api/v1/auth/users/:id/password`. Documented as the recovery path. |
|
||||||
|
| Password rules | Min 12 chars, no max, no character class requirements (NIST SP 800-63B). `bcrypt` cost 12. |
|
||||||
|
| CSRF | `SameSite=Lax` + same origin + required `X-Requested-With: dragonflight-ui` header on mutating requests (belt-and-suspenders). |
|
||||||
|
| Session table growth | `connect-pg-simple` `pruneSessionInterval: 60 * 15` (every 15 min). |
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- **Unit — `services/mam-api/test/middleware/auth.test.js`**: requireAuth with (a) no creds, (b) valid session, (c) idle-expired session, (d) absolute-expired session, (e) valid bearer, (f) invalid bearer, (g) bearer matching a deleted user.
|
||||||
|
- **Integration — `services/mam-api/test/auth.integration.test.js`**: spin up Express + test Postgres. Walks: setup -> login -> /me -> mutating call -> logout -> /me 401. Second pass: idle timeout simulated by mutating `last_seen_at` in DB. Third pass: bearer issue -> use -> revoke -> 401.
|
||||||
|
- **Regression test for the redirect-loop bug:** explicit test that after `POST /auth/login` returns 200, a subsequent `GET /auth/me` with the returned cookie returns 200 in the same test client. This is the test that would have caught the original failure.
|
||||||
|
- **Manual smoke (documented in PR):** fresh install -> setup -> create admin -> land on dashboard -> reload (stays logged in) -> wait 1h idle -> reload -> bounce to login.
|
||||||
|
|
||||||
|
## Implementation order
|
||||||
|
|
||||||
|
Suggested sequencing for the implementation plan (writing-plans will refine):
|
||||||
|
|
||||||
|
1. Migration `023-auth-session-timestamps.sql`. Add idempotent seed of the dev user (`INSERT ... ON CONFLICT DO NOTHING` with a fixed UUID) so dev mode FK-bearing routes work out of the box.
|
||||||
|
2. `express-session` + `connect-pg-simple` wiring in `index.js`.
|
||||||
|
3. `requireAuth` middleware (with `DEV_USER` constant resolved from the seeded row).
|
||||||
|
4. Auth router (setup, login, logout, me, password).
|
||||||
|
5. Apply `requireAuth` to API router with allowlist. Decide cluster carve-out (see Architecture).
|
||||||
|
6. Auth tests (unit + integration + regression).
|
||||||
|
7. Frontend `<AuthGate>` + Login screen + Setup screen.
|
||||||
|
8. Frontend Settings -> Account + API Tokens.
|
||||||
|
9. Delete `/login.html`.
|
||||||
|
10. User CRUD + token CRUD routes.
|
||||||
|
11. Rate limiting + CSRF header.
|
||||||
|
12. Documentation: README updates, `AUTH_ENABLED` transition notes.
|
||||||
|
|
||||||
|
## Out-of-band notes for the implementer
|
||||||
|
|
||||||
|
- The current `cors({ origin: true, credentials: true })` in `index.js` is too permissive once cookies start carrying authority. Tighten to a specific origin list (driven by an `ALLOWED_ORIGINS` env var) at the same time as wiring the session middleware — otherwise we're undoing the `SameSite=Lax` protection from the other side.
|
||||||
|
- node-agent -> mam-api traffic on `/api/v1/cluster/*` must keep working. Add a route-level carve-out comment that this path uses the existing `019-node-token-binding` token, not the user-auth path.
|
||||||
|
- The boot log currently says `Authentication: ENABLED` / `DISABLED (set AUTH_ENABLED=true for production)`. Once this lands, the recommended default flips: `AUTH_ENABLED=true` becomes the documented default in `.env.example` and the README, and `AUTH_ENABLED=false` is documented as a dev-only escape hatch.
|
||||||
84
docs/superpowers/specs/2026-05-29-hls-vod-playback-design.md
Normal file
84
docs/superpowers/specs/2026-05-29-hls-vod-playback-design.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# HLS VOD Playback for Browser
|
||||||
|
|
||||||
|
Date: 2026-05-29 | Status: design → implementation
|
||||||
|
Authors: Zac + Claude
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Replace the browser playback path for **recorded (VOD) assets** with HLS, retiring
|
||||||
|
the MP4 range-stitching workaround. The MP4 proxy is **kept** (supplements, not
|
||||||
|
replaces) because the Premiere UXP panel and conform pipeline consume it.
|
||||||
|
|
||||||
|
## Background — current state
|
||||||
|
|
||||||
|
- `GET /assets/:id/stream` returns `{ url: /api/v1/assets/:id/video, type: 'mp4' }`
|
||||||
|
for ready assets.
|
||||||
|
- `GET /assets/:id/video` streams `proxies/<id>.mp4` through Node with the
|
||||||
|
**RustFS range-stitching hack** (`stitchedS3Stream`): RustFS mis-serves ranged
|
||||||
|
GETs whose start offset is past ~5.8 MB, so the endpoint streams from byte 0 and
|
||||||
|
drops bytes. Works, but wastes bandwidth/CPU per seek and is fragile.
|
||||||
|
- **Live** assets already use HLS (`type: 'hls'`, `/live/<id>/index.m3u8`), and
|
||||||
|
`hls.js` is already loaded and wired in `screens-asset.jsx` for `type === 'hls'`.
|
||||||
|
- The proxy worker (`services/worker/src/workers/proxy.js`) produces a single
|
||||||
|
H.264/AAC/yuv420p MP4 — already HLS-compatible.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- **Supplement, not replace.** Keep `proxies/<id>.mp4`; add an HLS rendition.
|
||||||
|
- **Generate in the proxy worker** via fast remux (`-c copy`) — no re-encode.
|
||||||
|
- **Serve segments through mam-api** as whole-file GETs (no Range) — sidesteps the
|
||||||
|
RustFS range bug entirely and reuses session auth.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### 1. Generation (worker/proxy.js)
|
||||||
|
After uploading `proxies/<id>.mp4`, remux it to HLS into a temp dir:
|
||||||
|
```
|
||||||
|
ffmpeg -i <proxy.mp4> -c copy -f hls \
|
||||||
|
-hls_time 6 -hls_playlist_type vod -hls_flags independent_segments \
|
||||||
|
-hls_segment_filename <tmp>/seg_%03d.ts <tmp>/index.m3u8
|
||||||
|
```
|
||||||
|
Upload every file in the temp dir to `hls/<assetId>/` (playlist + `.ts`). Set
|
||||||
|
`assets.hls_s3_key = 'hls/<assetId>/index.m3u8'`. Remux is seconds; failure is
|
||||||
|
non-fatal (MP4 path still works as fallback).
|
||||||
|
|
||||||
|
### 2. Storage / schema
|
||||||
|
Migration adds `assets.hls_s3_key TEXT` (nullable). Presence = HLS available.
|
||||||
|
Segment objects live under `hls/<assetId>/seg_NNN.ts`; playlist references
|
||||||
|
**relative** segment names so the serving endpoint is path-agnostic.
|
||||||
|
|
||||||
|
### 3. Serving (mam-api)
|
||||||
|
New `GET /assets/:id/hls/:file` (file = `index.m3u8` or `seg_NNN.ts`):
|
||||||
|
- Validate `:file` against `^(index\.m3u8|seg_\d+\.ts)$` (no traversal).
|
||||||
|
- Whole-object GET of `hls/<id>/<file>` from S3 — **no Range handling**.
|
||||||
|
- Content-Type: `application/vnd.apple.mpegurl` (m3u8) / `video/mp2t` (ts).
|
||||||
|
- `Cache-Control: private, max-age=3600` for segments; `no-cache` for the playlist.
|
||||||
|
- Covered by the existing `requireAuth` gate; `hls.js` carries the same-origin
|
||||||
|
session cookie (same mechanism the live HLS path already relies on).
|
||||||
|
|
||||||
|
### 4. Stream selection (mam-api `/stream`)
|
||||||
|
For non-live assets: if `hls_s3_key` is set →
|
||||||
|
`{ url: '/api/v1/assets/:id/hls/index.m3u8', type: 'hls' }`. Else fall back to the
|
||||||
|
existing MP4 `/video` response. Live unchanged.
|
||||||
|
|
||||||
|
### 5. Backfill (existing assets)
|
||||||
|
Add an `hls` BullMQ job + `POST /assets/:id/reprocess?type=hls`: downloads the
|
||||||
|
existing `proxy_s3_key`, remuxes to HLS, uploads, sets `hls_s3_key`. No re-encode.
|
||||||
|
|
||||||
|
### 6. Frontend
|
||||||
|
No change required — `screens-asset.jsx` already plays `type: 'hls'` via `hls.js`.
|
||||||
|
Verify `hls.js` xhr carries credentials (same-origin cookie) for the proxied
|
||||||
|
segments; add `xhrSetup` withCredentials only if needed.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
- Multi-bitrate/ABR ladders (single rendition for now).
|
||||||
|
- Replacing the MP4 proxy or the `/video` endpoint (kept as fallback + for panel).
|
||||||
|
- Live-asset playback changes (already HLS).
|
||||||
|
|
||||||
|
## Test plan
|
||||||
|
1. Upload/capture an asset → proxy job produces MP4 **and** `hls/<id>/index.m3u8`.
|
||||||
|
2. `/stream` returns `type: 'hls'`; `/assets/:id/hls/index.m3u8` → 200 m3u8;
|
||||||
|
`/assets/:id/hls/seg_000.ts` → 200 `video/mp2t`, whole-file (no 206/Range).
|
||||||
|
3. Browser: asset plays + seeks via hls.js (no range-stitching path hit).
|
||||||
|
4. `reprocess?type=hls` backfills an older asset; it then plays via HLS.
|
||||||
|
5. MP4 proxy + `/hires` download still work (panel workflow intact).
|
||||||
235
docs/superpowers/specs/2026-05-30-playout-mcr-design.md
Normal file
235
docs/superpowers/specs/2026-05-30-playout-mcr-design.md
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
# Wild Dragon MAM — Playout / Master Control (MCR)
|
||||||
|
|
||||||
|
**Date:** 2026-05-30 (revised 2026-05-30 — §7 closed)
|
||||||
|
**Status:** APPROVED — implementation in progress (code drafted but uncommitted; see WORK_LOG_PLAYOUT.md)
|
||||||
|
**Author:** Zac + Claude
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Decisions
|
||||||
|
|
||||||
|
| Question | Decision |
|
||||||
|
|----------|----------|
|
||||||
|
| Playout engine | **CasparCG Server** (orchestrated via AMCP), not ffmpeg-native |
|
||||||
|
| Channel count | **Multi-channel from start** — N independent channels, placed across cluster nodes by capability (mirrors recorders) |
|
||||||
|
| Scheduling model | **Phased** — Phase A: on-demand playlist player; Phase B: 24/7 continuous channel |
|
||||||
|
| Output targets | SDI (DeckLink), NDI, SRT, RTMP — all via CasparCG consumers |
|
||||||
|
| Media source | Assets live in **S3**; must be staged to a CasparCG-local media volume before play (see §4) |
|
||||||
|
| CasparCG packaging | **Build our own image** (like `capture/build-with-decklink.sh`) — GL context via GPU passthrough or Xvfb; NDI + DeckLink SDKs fetched at build time (not redistributable) |
|
||||||
|
| Master codec playability | Zac confirms current masters **play fine in CasparCG** — no transcode-for-playout step; staging is a plain S3→/media copy. Validate on hardware but do not gate on it |
|
||||||
|
| Management UI | **Single Dragonflight `playout.html` GUI** drives everything via AMCP; operator never touches CasparCG directly |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Playout adds **master-control-room** capability to Dragonflight: take library assets, arrange them on a timeline / playlist, and play them out continuously to a broadcast output — SDI via DeckLink, or stream via SRT / RTMP / NDI. Drag-and-drop scheduling, a program/preview monitor, and as-run logging.
|
||||||
|
|
||||||
|
This is the **mirror image** of the existing capture path. Capture is `input → ffmpeg encode → S3`. Playout is `S3 asset → engine → output`. We reuse three things wholesale:
|
||||||
|
|
||||||
|
1. **Cluster node + capability model** — nodes already advertise DeckLink/Deltacast/GPU in `cluster_nodes.capabilities`; channels are placed on nodes that have a free output port, exactly as recorders claim input ports.
|
||||||
|
2. **Sidecar orchestration** — mam-api spawns containers via the local Docker socket or the remote `node-agent /sidecar/start`. A CasparCG channel is just a different sidecar image.
|
||||||
|
3. **Scheduler tick + PG advisory lock** — `src/scheduler.js` already runs a single-leader tick over a schedule table. Phase B's wall-clock channel reuses this pattern.
|
||||||
|
|
||||||
|
### Why CasparCG over ffmpeg-native
|
||||||
|
|
||||||
|
The capture stack proves we can drive ffmpeg + DeckLink. But playout's hard part is **gapless, frame-accurate, clean transitions between clips** — every clip boundary in an ffmpeg-per-clip model is a black flash unless we engineer a concat-feeder. CasparCG solves this natively: a channel is a persistent output with a playlist, hard/mix/wipe transitions, layered graphics/logo (CG), and DeckLink/NDI/SRT/RTMP consumers built in. We orchestrate it over **AMCP** (its TCP control protocol) instead of reinventing a feeder. Trade: a new dependency + container image, and media must be on a CasparCG-visible disk (§4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Data Model
|
||||||
|
|
||||||
|
New migration `029-playout.sql`. Five tables.
|
||||||
|
|
||||||
|
### 1.1 `playout_channels`
|
||||||
|
A logical output. One channel → one engine instance → one output target.
|
||||||
|
|
||||||
|
```
|
||||||
|
id uuid pk
|
||||||
|
name text -- "Channel 1", "Pop-up SDI"
|
||||||
|
node_id uuid -> cluster_nodes(id) -- where the engine runs (null = primary)
|
||||||
|
output_type text -- 'decklink' | 'ndi' | 'srt' | 'rtmp'
|
||||||
|
output_config jsonb -- { device_index } | { ndi_name } | { url, latency } | { url, key }
|
||||||
|
video_format text -- '1080i5994' | '1080p5994' | '720p5994' ...
|
||||||
|
status text -- 'stopped' | 'starting' | 'running' | 'error'
|
||||||
|
container_id text -- running CasparCG sidecar
|
||||||
|
project_id uuid -> projects(id) -- RBAC scoping (nullable = admin-only)
|
||||||
|
created_at / updated_at
|
||||||
|
```
|
||||||
|
|
||||||
|
`output_type` + `output_config` map straight to a CasparCG consumer:
|
||||||
|
- `decklink` → `ADD <ch> DECKLINK <device> ...`
|
||||||
|
- `ndi` → `ADD <ch> NDI ...`
|
||||||
|
- `srt`/`rtmp` → `ADD <ch> FFMPEG <url> -f mpegts ...` (CasparCG 2.3+ ffmpeg consumer)
|
||||||
|
|
||||||
|
### 1.2 `playout_playlists`
|
||||||
|
An ordered list of items bound to a channel. Phase A's primary object.
|
||||||
|
|
||||||
|
```
|
||||||
|
id, channel_id -> playout_channels(id)
|
||||||
|
name, loop boolean, created_at / updated_at
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 `playout_items`
|
||||||
|
One entry on a playlist OR one entry on the 24/7 timeline.
|
||||||
|
|
||||||
|
```
|
||||||
|
id
|
||||||
|
playlist_id uuid -> playout_playlists(id) -- Phase A
|
||||||
|
asset_id uuid -> assets(id)
|
||||||
|
sort_order int -- position in playlist (Phase A)
|
||||||
|
scheduled_at timestamptz -- wall-clock start (Phase B, null in A)
|
||||||
|
in_point numeric -- seconds, trim head (reuse subclip in/out from editor)
|
||||||
|
out_point numeric -- seconds, trim tail
|
||||||
|
transition text -- 'cut' | 'mix' | 'wipe'
|
||||||
|
transition_ms int
|
||||||
|
graphics jsonb -- optional CG/template overlay (Phase B+)
|
||||||
|
media_status text -- 'pending' | 'staging' | 'ready' | 'error' (see §4)
|
||||||
|
media_path text -- resolved path inside the CasparCG media volume
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 `playout_schedule` (Phase B)
|
||||||
|
Day-ahead, wall-clock-bound timeline rows. Same shape as `playout_items` but `scheduled_at` is authoritative and the scheduler tick (§5) drives transitions. Phase A can ignore this table.
|
||||||
|
|
||||||
|
### 1.5 `playout_as_run`
|
||||||
|
Append-only log: what actually played, when, for how long. Compliance / billing.
|
||||||
|
|
||||||
|
```
|
||||||
|
id, channel_id, asset_id, item_id
|
||||||
|
started_at, ended_at, duration_s, result -- 'played' | 'skipped' | 'error'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Services & Components
|
||||||
|
|
||||||
|
### 2.1 New sidecar: `services/playout/` (CasparCG wrapper)
|
||||||
|
A thin container: **CasparCG Server** + a small Node control shim exposing HTTP, the same way `capture` wraps ffmpeg.
|
||||||
|
|
||||||
|
- Base image: official/community `casparcg/server` (Linux build with DeckLink + NDI + FFmpeg producers/consumers).
|
||||||
|
- Node shim (`src/index.js`): opens an AMCP TCP socket to local CasparCG, exposes:
|
||||||
|
- `POST /channel/start` → `ADD <ch> <consumer>` for the channel's output target
|
||||||
|
- `POST /play` → `PLAY <ch>-<layer> <media> [transition]`
|
||||||
|
- `POST /loadbg` + `/play` → preview/cue then take (preview monitor)
|
||||||
|
- `POST /stop`, `GET /status` → `INFO <ch>` (current clip, position, fps)
|
||||||
|
- playlist load → translate `playout_items` rows into a sequence of AMCP `LOADBG`/`PLAY` calls, advancing on `OnTransition` / end-of-clip events.
|
||||||
|
- Mirrors capture's status-polling contract so the UI monitor reuses existing plumbing.
|
||||||
|
|
||||||
|
### 2.2 mam-api: `src/routes/playout.js`
|
||||||
|
CRUD + control, RBAC-scoped via the existing `assertProjectAccess` helper (channels carry `project_id`).
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /playout/channels list (project-filtered)
|
||||||
|
POST /playout/channels create (edit on project)
|
||||||
|
POST /playout/channels/:id/start|stop spawn/kill CasparCG sidecar
|
||||||
|
GET /playout/channels/:id/status proxy engine INFO
|
||||||
|
POST /playout/channels/:id/play|pause|skip transport control
|
||||||
|
GET/POST/PUT/DELETE /playout/playlists... playlist + item CRUD, reorder
|
||||||
|
POST /playout/items/:id/stage kick S3→media-volume staging (§4)
|
||||||
|
GET /playout/channels/:id/asrun as-run log
|
||||||
|
```
|
||||||
|
|
||||||
|
Channel start/stop reuses `resolveNodeTarget()` + the Docker-socket / `node-agent /sidecar/start` split already in `recorders.js`. **Refactor opportunity:** lift that sidecar-spawn logic out of `recorders.js` into `src/orchestration/sidecar.js` so both recorders and playout share it (keep this small — only what both need).
|
||||||
|
|
||||||
|
### 2.3 web-ui: `playout.html` + `public/playout.jsx`
|
||||||
|
New MCR page. Layout:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ PREVIEW ───────────┬─ PROGRAM (on air) ──────┐
|
||||||
|
│ [cued clip] │ [live output] ● ON AIR │
|
||||||
|
│ TC / duration │ TC / remaining │
|
||||||
|
│ [CUE] [TAKE] │ [PLAY][PAUSE][SKIP][STOP]│
|
||||||
|
├─ MEDIA BIN ─────────┴──────────────────────────┤
|
||||||
|
│ (draggable asset list, reuse asset browser) │
|
||||||
|
├─ PLAYLIST / TIMELINE ──────────────────────────┤
|
||||||
|
│ ▸ clip A ──▸ clip B ──▸ clip C (drag-drop) │ Phase A: ordered list
|
||||||
|
│ └ 24h time grid w/ now-bar │ Phase B: time-of-day grid
|
||||||
|
└────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
- Drag-drop: reuse whatever the NLE editor timeline uses (check `editor.jsx`); assets drag from the bin into the playlist/grid.
|
||||||
|
- API via existing `ZAMPP_API.fetch` wrapper.
|
||||||
|
- Program monitor: HLS preview of the output — CasparCG can emit a second low-bitrate FFmpeg consumer to HLS, reusing the `/live/<id>` HLS plumbing capture already uses.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Channel placement & ports
|
||||||
|
|
||||||
|
A DeckLink port is exclusive — same constraint capture already handles. A node's DeckLink port can be an **input (recorder)** or an **output (playout channel)**, never both at once. So:
|
||||||
|
|
||||||
|
- Extend the capability/port-claim check: when starting a channel on `output_type=decklink`, verify the target node has that device index free (no active recorder, no active channel).
|
||||||
|
- NDI / SRT / RTMP outputs have no hardware contention → can stack many per node (GPU/CPU-bound only).
|
||||||
|
- Surface a unified "device map" (extend the existing cluster DeckLink-status endpoint) showing each port's role: idle / recording / playing-out.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Media staging (the S3 ⇄ CasparCG gap)
|
||||||
|
|
||||||
|
**The crux.** Assets live in S3 (`original_s3_key` / `proxy_s3_key`). CasparCG plays from a **local media folder**. Options:
|
||||||
|
|
||||||
|
- **A — Pre-stage to a shared media volume (recommended).** Before a clip can go on air, download/symlink it from S3 to a CasparCG-visible volume (`/media`), set `playout_items.media_status='ready'` + `media_path`. A new BullMQ `playout-stage` job (reuses the worker pattern) does the pull. UI shows per-item readiness; TAKE is blocked until `ready`. Mirrors the growing-file SMB share already mounted for capture.
|
||||||
|
- **B — Stream from S3 via presigned URL.** CasparCG FFmpeg producer plays an HTTPS presigned URL directly. Zero staging, but seeking/trim and gapless transitions over network are fragile for broadcast. Acceptable as a fallback for SRT/RTMP, risky for SDI.
|
||||||
|
|
||||||
|
**Decision:** Phase A uses **A** (stage proxies for preview, masters for air) with **B** available as a per-channel "low-latency / no-stage" toggle. Zac confirms the current masters play fine in CasparCG, so staging is a **plain S3→/media copy — no transcode-for-playout step**. (Validate on hardware during implementation, but the model does not assume a transcode stage.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Scheduling
|
||||||
|
|
||||||
|
### Phase A — playlist player
|
||||||
|
No wall clock. Operator builds a `playout_playlists` row, drags items in, hits PLAY. The playout sidecar walks `playout_items` by `sort_order`, cueing the next clip during the current one (`LOADBG`) and taking it at end-of-clip with the configured transition. `loop` repeats. As-run logged per item.
|
||||||
|
|
||||||
|
### Phase B — 24/7 continuous channel
|
||||||
|
Wall-clock timeline in `playout_schedule`. Reuse `src/scheduler.js`:
|
||||||
|
- Add a second tick (or extend the existing one) under the **same PG advisory lock pattern** — exactly-one-leader, so a multi-replica deploy doesn't double-fire.
|
||||||
|
- Tick responsibilities: stage upcoming items (look-ahead window), verify the on-air item matches the schedule, **fill gaps** (loop a filler/slate asset when the timeline has a hole — a channel must never go black), roll the day forward.
|
||||||
|
- As-run becomes the compliance record.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Phasing / Milestones
|
||||||
|
|
||||||
|
**Phase A — Playlist playout MVP**
|
||||||
|
1. Migration `029-playout.sql` (channels, playlists, items, as-run).
|
||||||
|
2. `services/playout/` sidecar: CasparCG image + AMCP control shim, single output target (start with **SRT or NDI** — no hardware needed for dev; DeckLink behind hardware check).
|
||||||
|
3. mam-api `routes/playout.js` — channel + playlist CRUD, start/stop, transport, RBAC.
|
||||||
|
4. `playout-stage` BullMQ job (S3 → /media).
|
||||||
|
5. web-ui `playout.html` — bin + drag-drop ordered playlist + program/preview monitors + transport.
|
||||||
|
6. DeckLink output on real hardware; port-contention check vs recorders.
|
||||||
|
|
||||||
|
**Phase B — 24/7 continuous channel**
|
||||||
|
7. `playout_schedule` + time-of-day grid UI.
|
||||||
|
8. Scheduler tick (advisory-locked) — look-ahead staging, gap-fill/slate, day-roll.
|
||||||
|
9. As-run reporting view.
|
||||||
|
10. Graphics/CG overlay (logo bug, lower-thirds) via CasparCG templates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Open Questions (for review)
|
||||||
|
|
||||||
|
**Resolved (2026-05-30):**
|
||||||
|
- ~~CasparCG packaging~~ → **build our own image.** Fetch DeckLink + NDI SDKs at build time (not redistributable — same as capture's DeckLink build). GL context for the mixer comes from GPU passthrough on a real node, or **Xvfb** (virtual framebuffer) where there's no display — community images run `--privileged` + X11 socket. Pin the NDI SDK version to what the server expects (`.so` version mismatch is the common docker failure).
|
||||||
|
- ~~Master codec playability~~ → Zac confirms masters **play fine**; no transcode-for-playout. Staging = plain S3→/media copy.
|
||||||
|
- ~~Management GUI~~ → **single Dragonflight `playout.html`** drives everything via AMCP; operator never touches CasparCG.
|
||||||
|
- ~~Audio loudness~~ → **pre-normalize at stage time** (Zac, 2026-05-30). `playout-stage` job runs ffmpeg `loudnorm` (EBU R128, target −23 LUFS, true-peak −1 dBTP) once, on the S3→/media copy. Output is the cached version CasparCG plays. Staging is no longer a pure copy — staging cost ≈ realtime CPU per clip on first stage; results are reusable across channels. Override (`media_status='ready'` + raw copy) available for clips already mastered to spec.
|
||||||
|
- ~~Frame rate~~ → **`1080p5994`** default for new channels (Zac, 2026-05-30). Progressive 1080 @ 59.94 fps. Per-channel override allowed (`video_format` column). Streaming-friendly (SRT/RTMP/NDI) and current SDI gear accepts it; matches capture's 59.94 cadence.
|
||||||
|
- ~~Preview latency~~ → **HLS v1** (Zac, 2026-05-30). Reuse capture's `/live/<id>` HLS plumbing. CasparCG emits a second low-bitrate FFmpeg consumer to HLS. ~4–6s lag, fine for confidence monitor. Operator desk gets a real downstream monitor off the SDI/NDI output anyway. Revisit WebRTC if MCR operators complain.
|
||||||
|
- ~~Failover~~ → **auto-restart on healthy node** (Zac, 2026-05-30). Scheduler tick (§5) monitors `playout_sidecars` health (AMCP ping + container alive); on N missed checks marks the channel `error`, re-places it on another capability-matching node with a free output port, resumes the playlist from the next item after the as-run-logged on-air item. Gap = black/slate for ~5–30 s during respawn (operator sees a flag in the UI). **DeckLink channels are not auto-failed-over in v1** — device-index pinning makes the destination port non-trivial; v1 alerts and lets the operator move the channel. NDI/SRT/RTMP channels (no hardware contention) failover automatically. Tracked via `restart_count` + `last_restart_at` on `playout_channels`.
|
||||||
|
|
||||||
|
**Still open:**
|
||||||
|
- (none — all §7 questions resolved 2026-05-30)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Reused building blocks (already in the repo)
|
||||||
|
|
||||||
|
| Need | Existing piece |
|
||||||
|
|------|----------------|
|
||||||
|
| Spawn engine container local/remote | `recorders.js` Docker-socket + `node-agent /sidecar/start` |
|
||||||
|
| Node capability / port model | `cluster_nodes.capabilities`, cluster DeckLink-status endpoint |
|
||||||
|
| Single-leader scheduled transitions | `src/scheduler.js` + PG advisory lock |
|
||||||
|
| Background media jobs | BullMQ worker (`services/worker`) |
|
||||||
|
| RBAC scoping | `src/auth/authz.js` `assertProjectAccess` (channel/project_id) |
|
||||||
|
| HLS preview plumbing | capture's `/live/<id>` HLS output |
|
||||||
|
| Subclip in/out points | NLE editor in/out marking |
|
||||||
|
| API wrapper / SPA shell | `ZAMPP_API.fetch`, esbuild JSX pages |
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
# Storage Settings Warning + Growing-Files SMB Auth + Per-Recorder Growing Mode
|
||||||
|
**Date:** 2026-05-31
|
||||||
|
**Branch:** `feat/playout-mcr` (Forgejo: WildDragonLLC/dragonflight)
|
||||||
|
**Status:** Approved, ready for implementation plan
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Three related refinements to the Settings page and growing-files capture pipeline:
|
||||||
|
|
||||||
|
1. **Storage warning header** — a prominent set-once warning at the top of the Storage settings section.
|
||||||
|
2. **Growing-files SMB credentials + system CIFS mount** — store an SMB username/password and have the capture stack mount the growing share itself (Approach A).
|
||||||
|
3. **Per-recorder growing mode** — remove the global "capture writes to local SMB share first" toggle; make growing-files mode a per-recorder setting.
|
||||||
|
|
||||||
|
All changes live on the `growing-files` / storage-settings path. No playout changes (handled separately).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Background (current state)
|
||||||
|
|
||||||
|
- **Settings storage:** single key/value `settings(key TEXT PRIMARY KEY, value TEXT, updated_at)` table (migration 006). Secrets like `s3_secret_key` are stored but `GET /settings/s3` returns only `s3_secret_key_exists` (never the value).
|
||||||
|
- **Growing settings UI:** `GrowingSettingsCard` in `services/web-ui/public/screens-admin.jsx` (rendered by `StorageSection` alongside `MountHealthStrip` and `S3SettingsCard`). Current keys: `growing_enabled`, `growing_path`, `growing_smb_url`, `growing_promote_after_seconds`.
|
||||||
|
- **Settings API:** `services/mam-api/src/routes/settings.js` — `GET/PUT /settings/growing` over `GROWING_KEYS`.
|
||||||
|
- **Global enable today:** the `growing_enabled` setting (checkbox labelled "Capture writes to the local SMB share first; Premier can edit while it's still growing"). `recorders.js` reads this global key at recorder-start and passes `GROWING_ENABLED=true/false` to the capture container.
|
||||||
|
- **Capture write path:** `services/capture/src/capture-manager.js` reads `process.env.GROWING_ENABLED` and `GROWING_PATH` (default `/growing`). When on, it writes the master to `/growing/{projectId}/{clipName}.{ext}` instead of streaming to S3; the promotion worker uploads to S3 after stop.
|
||||||
|
- **Current mount model:** `/growing` is a pre-mounted host bind-mount; the app never authenticates to SMB.
|
||||||
|
- **Per-recorder column already exists:** migration 014 added `recorders.growing_enabled BOOLEAN DEFAULT NULL` ("NULL = use global"), but recorder-start logic ignores it and the new-recorder modal does not expose it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 1 — Storage warning header
|
||||||
|
|
||||||
|
Add a danger-styled banner at the **top of `StorageSection()`**, above `MountHealthStrip`.
|
||||||
|
|
||||||
|
- Visual: full-width banner, danger token styling (`--danger` border + subtle danger background), alert icon, bold uppercase text.
|
||||||
|
- Exact copy:
|
||||||
|
> **⚠ WARNING — THESE SETTINGS ARE MEANT TO BE SET ONCE AT INITIAL DEPLOYMENT. CHANGING STORAGE PATHS MID-CYCLE CAN CAUSE DATABASE CORRUPTION AND FILE LOSS. PLEASE USE WITH CAUTION.**
|
||||||
|
- Pure presentational; no backend, no dismiss state (always visible).
|
||||||
|
|
||||||
|
**Files:** `services/web-ui/public/screens-admin.jsx` (and a small style rule in the appropriate CSS file if needed).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 2 — Growing-files SMB credentials + system CIFS mount (Approach A)
|
||||||
|
|
||||||
|
The growing share is **shared infrastructure**, so the SMB connection config is global.
|
||||||
|
|
||||||
|
### New settings keys
|
||||||
|
| Key | Purpose | Notes |
|
||||||
|
|-----|---------|-------|
|
||||||
|
| `growing_smb_mount` | CIFS source for the system mount, e.g. `//10.0.0.25/mam-growing` | Distinct from `growing_smb_url` |
|
||||||
|
| `growing_smb_username` | SMB user | Returned in GET (not secret) |
|
||||||
|
| `growing_smb_password` | SMB password | **Write-only** — never returned |
|
||||||
|
| `growing_smb_vers` *(optional)* | CIFS protocol version, default `3.0` | Avoids mount negotiation failures |
|
||||||
|
|
||||||
|
`growing_smb_url` (the `smb://…` string) is retained unchanged as the **editor-facing** display value (Premiere connect string).
|
||||||
|
|
||||||
|
### Settings API (`settings.js`)
|
||||||
|
- Extend `GROWING_KEYS` with the new keys (except the password is handled specially).
|
||||||
|
- `GET /settings/growing`: return `growing_smb_mount`, `growing_smb_username`, `growing_smb_vers`, and `growing_smb_password_exists: boolean` — **never** the password value. (Mirror the existing `s3_secret_key_exists` pattern.)
|
||||||
|
- `PUT /settings/growing`: upsert each provided key. For `growing_smb_password`, only write it when a non-empty value is provided (an empty/omitted field leaves the stored password unchanged). Provide a way to clear it (explicit empty sentinel or a "clear" affordance) — see Resolved decisions below.
|
||||||
|
|
||||||
|
### Settings UI (`GrowingSettingsCard`)
|
||||||
|
Add three fields to the card:
|
||||||
|
- **SMB mount (CIFS):** text input bound to `growing_smb_mount`, placeholder `//10.0.0.25/mam-growing`.
|
||||||
|
- **SMB username:** text input bound to `growing_smb_username`.
|
||||||
|
- **SMB password:** masked password input. Shows a "saved" indicator when `growing_smb_password_exists` is true; typing a new value replaces it; leaving it blank keeps the existing one. `autoComplete="new-password"`.
|
||||||
|
|
||||||
|
### Capture image (`services/capture/Dockerfile`)
|
||||||
|
Add `cifs-utils` to the installed packages so `mount -t cifs` is available inside the capture container.
|
||||||
|
|
||||||
|
### Capture-manager (`capture-manager.js`)
|
||||||
|
On capture start, when growing mode is active **and** `GROWING_SMB_MOUNT` is set:
|
||||||
|
1. Write a root-only credentials file (e.g. `/run/smb-creds`, mode `0600`) containing:
|
||||||
|
```
|
||||||
|
username=<GROWING_SMB_USERNAME>
|
||||||
|
password=<GROWING_SMB_PASSWORD>
|
||||||
|
```
|
||||||
|
(Credentials go in the file, **not** the mount command line, to avoid `ps`/log exposure.)
|
||||||
|
2. `mkdir -p $GROWING_PATH` then `mount -t cifs $GROWING_SMB_MOUNT $GROWING_PATH -o credentials=/run/smb-creds,uid=0,gid=0,file_mode=0664,dir_mode=0775,vers=$GROWING_SMB_VERS`.
|
||||||
|
3. If the mount succeeds → proceed writing the master to `$GROWING_PATH/...` (existing behaviour).
|
||||||
|
4. If the mount **fails** → log the error and fall back to S3 streaming (`growingPath = null`), so a recording is never lost.
|
||||||
|
5. On capture stop/cleanup, unmount `$GROWING_PATH` (best-effort; ignore "not mounted").
|
||||||
|
|
||||||
|
Mount isolation: each recorder runs in its **own** capture container, so each container mounts CIFS at its own private `/growing` — no cross-recorder mount conflicts, no ref-counting needed.
|
||||||
|
|
||||||
|
### Recorder start (`recorders.js`)
|
||||||
|
- Pass new env to the spawned capture container: `GROWING_SMB_MOUNT`, `GROWING_SMB_USERNAME`, `GROWING_SMB_PASSWORD`, `GROWING_SMB_VERS` (read from the `settings` table at start).
|
||||||
|
- The dynamically-spawned capture container must get `/growing` as an **empty mountpoint** (not a host bind-mount) so the in-container CIFS mount lands cleanly. Confirm/adjust the container spec accordingly. The container is already privileged (required for `mount`).
|
||||||
|
|
||||||
|
### Security notes
|
||||||
|
- The password is stored plaintext in the `settings` table, identical to the existing `s3_secret_key` handling — acceptable within this app's current secret model.
|
||||||
|
- The password reaches the capture container as an env var (visible via `docker inspect`), same as S3 keys already are. The credentials **file** (not the command line) is used for the actual mount.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part 3 — Per-recorder growing mode (remove the global toggle)
|
||||||
|
|
||||||
|
### Remove global enable
|
||||||
|
- Delete the global "Capture writes to the local SMB share first…" checkbox (`growing_enabled` key) from `GrowingSettingsCard`. The card no longer carries a global on/off — it is **infrastructure-only**: container mount path, SMB URL (editor), SMB mount + credentials, promote threshold.
|
||||||
|
- The `growing_enabled` settings *key* is retired from the UI. (It may remain in the table harmlessly; recorder-start no longer reads it.)
|
||||||
|
|
||||||
|
### Per-recorder semantics
|
||||||
|
- Reuse the existing `recorders.growing_enabled BOOLEAN` column. New semantics (no global to defer to): `TRUE` = this recorder uses growing-files mode; `NULL`/`FALSE` = off.
|
||||||
|
- `recorders.js` recorder-start: read the **recorder's own** `growing_enabled` (defaulting `NULL`→off) and set `GROWING_ENABLED` for the capture container from that, instead of the global setting.
|
||||||
|
- Add `growing_enabled` to `RECORDER_FIELDS` so create/update accept it.
|
||||||
|
|
||||||
|
### UI
|
||||||
|
- **New-recorder modal** (`modal-new-recorder.jsx`): add a "Growing-files mode" toggle that sets `growing_enabled` on the created recorder (default off).
|
||||||
|
- **Recorder edit** (wherever recorders are edited): same toggle.
|
||||||
|
- Helper text on the toggle notes that growing-files requires the SMB share to be configured in Settings → Storage.
|
||||||
|
|
||||||
|
### Fallback
|
||||||
|
If a recorder has `growing_enabled = true` but `growing_smb_mount` is not configured globally, capture logs a warning and falls back to S3 streaming (same fallback path as a failed mount). Recording is never blocked.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `services/web-ui/public/screens-admin.jsx` | Storage warning banner; SMB mount/username/password fields in `GrowingSettingsCard`; remove global growing-enable checkbox |
|
||||||
|
| `services/web-ui/public/modal-new-recorder.jsx` | Per-recorder "Growing-files mode" toggle |
|
||||||
|
| `services/mam-api/src/routes/settings.js` | New growing SMB keys; write-only password (`growing_smb_password_exists`) |
|
||||||
|
| `services/mam-api/src/routes/recorders.js` | Read per-recorder `growing_enabled`; pass SMB env to capture; `RECORDER_FIELDS` += `growing_enabled`; empty `/growing` mountpoint |
|
||||||
|
| `services/capture/Dockerfile` | Add `cifs-utils` |
|
||||||
|
| `services/capture/src/capture-manager.js` | CIFS mount-on-start (creds file), unmount-on-stop, fallback to S3 on failure |
|
||||||
|
| CSS (storage warning / fields) | Minor styles if needed |
|
||||||
|
|
||||||
|
No DB migration required (the `recorders.growing_enabled` column already exists; new settings are key/value rows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved decisions
|
||||||
|
|
||||||
|
- **Clearing the SMB password:** `PUT /settings/growing` treats a field value of the literal sentinel `""` with an explicit `growing_smb_password_clear: true` flag as "remove the stored password"; a blank field with no clear flag leaves it unchanged. (Keeps the common "don't retype the password on every save" UX while still allowing removal.)
|
||||||
|
- **CIFS version:** default `growing_smb_vers = 3.0`; overridable via settings to support older NAS targets.
|
||||||
|
- **Recorders already recording when the toggle changes:** the per-recorder `growing_enabled` is read at **start** only; changing it mid-recording has no effect on the active session (consistent with how all recorder encode settings already behave).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Out of scope (deferred)
|
||||||
|
|
||||||
|
- Encrypting secrets at rest (the app's existing model stores `s3_secret_key` in plaintext; SMB password follows the same model).
|
||||||
|
- A global "growing-files master kill switch" (removed by design — control is now per-recorder).
|
||||||
|
- Exposing `growing_retention_days` in the UI (seeded in DB, still unsurfaced; unrelated to this work).
|
||||||
|
- Playout HLS preview fix (handled by a separate parallel effort).
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -1,4 +1,27 @@
|
||||||
# ── Stage 1: Build FFmpeg with DeckLink 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 2: Build FFmpeg with DeckLink + NVENC (HEVC/H264) support ─────────
|
||||||
|
# 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
|
||||||
|
# nv-codec-headers (header-only, no driver / no full CUDA toolkit needed)
|
||||||
|
# so ffmpeg's configure can light up hevc_nvenc / h264_nvenc / cuvid.
|
||||||
|
# At runtime, /dev/nvidia* + the host driver libs (via the NVIDIA Container
|
||||||
|
# Toolkit) supply the actual encoder.
|
||||||
FROM debian:bookworm AS ffmpeg-builder
|
FROM debian:bookworm AS ffmpeg-builder
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
|
@ -13,6 +36,11 @@ COPY sdk/ /decklink-sdk/
|
||||||
COPY patch_decklink.py /patch_decklink.py
|
COPY patch_decklink.py /patch_decklink.py
|
||||||
COPY decklink-sdk16.patch /decklink-sdk16.patch
|
COPY decklink-sdk16.patch /decklink-sdk16.patch
|
||||||
|
|
||||||
|
# nv-codec-headers — just the ffnvcodec public headers + a pkg-config file.
|
||||||
|
# Pin to a tag known to work with FFmpeg 7.1 (n12.x series).
|
||||||
|
RUN git clone --depth=1 --branch n12.1.14.0 https://github.com/FFmpeg/nv-codec-headers.git /nv-codec-headers \
|
||||||
|
&& make -C /nv-codec-headers PREFIX=/usr/local install
|
||||||
|
|
||||||
# Pull FFmpeg 7.1 source
|
# Pull FFmpeg 7.1 source
|
||||||
RUN git clone --depth=1 --branch release/7.1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg
|
RUN git clone --depth=1 --branch release/7.1 https://git.ffmpeg.org/ffmpeg.git /ffmpeg
|
||||||
|
|
||||||
|
|
@ -20,8 +48,15 @@ RUN git clone --depth=1 --branch release/7.1 https://git.ffmpeg.org/ffmpeg.git /
|
||||||
RUN python3 /patch_decklink.py
|
RUN python3 /patch_decklink.py
|
||||||
|
|
||||||
WORKDIR /ffmpeg
|
WORKDIR /ffmpeg
|
||||||
|
# NVENC adds: --enable-nvenc (encoder), --enable-cuvid (decoder), --enable-ffnvcodec.
|
||||||
|
# We deliberately do NOT enable --enable-cuda-nvcc / --enable-libnpp here — those
|
||||||
|
# require the full ~3GB CUDA toolkit and are only needed for GPU filters like
|
||||||
|
# yadif_cuda / scale_cuda. If §5's GPU deinterlace stretch goal goes ahead,
|
||||||
|
# rebuild this image off nvidia/cuda:12.x-devel and flip those flags on.
|
||||||
RUN ./configure \
|
RUN ./configure \
|
||||||
--prefix=/usr/local \
|
--prefix=/usr/local \
|
||||||
|
--extra-cflags="-I/decklink-sdk -I/usr/local/include" \
|
||||||
|
--extra-ldflags="-L/usr/local/lib" \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-nonfree \
|
--enable-nonfree \
|
||||||
--enable-libx264 \
|
--enable-libx264 \
|
||||||
|
|
@ -32,20 +67,60 @@ RUN ./configure \
|
||||||
--enable-libsrt \
|
--enable-libsrt \
|
||||||
--enable-libzmq \
|
--enable-libzmq \
|
||||||
--enable-decklink \
|
--enable-decklink \
|
||||||
--extra-cflags="-I/decklink-sdk" \
|
--enable-ffnvcodec \
|
||||||
|
--enable-nvenc \
|
||||||
|
--enable-cuvid \
|
||||||
--disable-doc \
|
--disable-doc \
|
||||||
--disable-debug \
|
--disable-debug \
|
||||||
--disable-ffplay \
|
--disable-ffplay \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& make install
|
&& make install
|
||||||
|
|
||||||
|
# Sanity-check: hevc_nvenc and h264_nvenc must be present in the encoder list,
|
||||||
|
# otherwise the resulting image is useless for the All-Intra HEVC pipeline.
|
||||||
|
RUN /usr/local/bin/ffmpeg -hide_banner -encoders 2>&1 | grep -E 'nvenc' \
|
||||||
|
|| (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
|
||||||
|
|
||||||
# Runtime deps for compiled ffmpeg libs
|
# Runtime deps for compiled ffmpeg libs.
|
||||||
|
# cifs-utils provides mount.cifs so growing-files capture can mount the SMB
|
||||||
|
# landing-zone share inside the (privileged) container at start (Approach A).
|
||||||
|
# util-linux supplies mount/umount/mountpoint.
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
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 \
|
||||||
|
libexpat1 liburiparser1 libuuid1 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy compiled ffmpeg/ffprobe
|
# Copy compiled ffmpeg/ffprobe
|
||||||
|
|
@ -56,7 +131,39 @@ 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
|
||||||
|
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.
|
||||||
|
# /live — HLS preview output (bound from host LIVE_DIR by node-agent)
|
||||||
|
# /growing — growing-file master output (bound from host /mnt/NVME/MAM/growing)
|
||||||
|
RUN mkdir -p /live /growing
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|
|
||||||
27
services/capture/deltacast-bridge/CMakeLists.txt
Normal file
27
services/capture/deltacast-bridge/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
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
|
||||||
|
)
|
||||||
395
services/capture/deltacast-bridge/main.c
Normal file
395
services/capture/deltacast-bridge/main.c
Normal file
|
|
@ -0,0 +1,395 @@
|
||||||
|
/* 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;
|
||||||
|
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; /* caller will fail on signal lock */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 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 ────────────────────────────────────────────
|
||||||
|
*
|
||||||
|
* CRITICAL: ffmpeg opens ALL of its inputs before it starts processing any of
|
||||||
|
* them, and input 1 is this audio FIFO. Opening the read end of a FIFO blocks
|
||||||
|
* until a writer connects, so if this thread fails to open the FIFO writer
|
||||||
|
* ffmpeg hangs forever on input 1 -> no video frames are ever read from
|
||||||
|
* pipe:0 -> 0 fps and an empty HLS preview. Therefore the FIFO writer is
|
||||||
|
* opened UNCONDITIONALLY and FIRST, independent of any VideoMaster audio open,
|
||||||
|
* and the thread then feeds the FIFO a CONTINUOUS, wall-clock-paced s16le
|
||||||
|
* stereo stream (real samples when available, otherwise silence) so ffmpeg's
|
||||||
|
* A/V demux stays alive and video keeps flowing. */
|
||||||
|
typedef struct {
|
||||||
|
HANDLE board;
|
||||||
|
unsigned port;
|
||||||
|
ULONG video_std;
|
||||||
|
ULONG clock_div;
|
||||||
|
int fps_num;
|
||||||
|
int fps_den;
|
||||||
|
const char *fifo_path;
|
||||||
|
} AudioArgs;
|
||||||
|
|
||||||
|
/* Write exactly `len` bytes; returns 0 on success, -1 if writing should stop
|
||||||
|
* (EPIPE when ffmpeg is gone, or any other error). */
|
||||||
|
static int write_all(int fd, const unsigned char *p, size_t len) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *audio_thread(void *arg) {
|
||||||
|
AudioArgs *a = (AudioArgs *)arg;
|
||||||
|
|
||||||
|
/* 1. Open the FIFO writer FIRST, unconditionally. This is what unblocks
|
||||||
|
* ffmpeg's input 1; we must reach it even if the VHD audio open fails. */
|
||||||
|
int fd = open(a->fifo_path, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "[audio] open FIFO failed: %s\n", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Pacing + silence buffer sized to one video frame of 48kHz stereo
|
||||||
|
* s16le. samples_per_frame = 48000 * fps_den / fps_num (rounded). */
|
||||||
|
const int AUDIO_RATE = 48000;
|
||||||
|
const int CHANNELS = 2;
|
||||||
|
const size_t FRAME_BYTES = (size_t)CHANNELS * 2; /* s16le stereo */
|
||||||
|
int fps_num = a->fps_num > 0 ? a->fps_num : 25;
|
||||||
|
int fps_den = a->fps_den > 0 ? a->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)a->video_std,
|
||||||
|
(VHD_CLOCKDIVISOR)a->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); /* zeroed -> doubles as silence */
|
||||||
|
if (!buf) { close(fd); return NULL; }
|
||||||
|
|
||||||
|
/* 3. Try to open the VideoMaster audio stream (best effort, NON-FATAL). */
|
||||||
|
HANDLE stream = NULL;
|
||||||
|
int have_vhd_audio = 0;
|
||||||
|
VHD_AUDIOINFO ai;
|
||||||
|
memset(&ai, 0, sizeof(ai));
|
||||||
|
|
||||||
|
ULONG r = VHD_OpenStreamHandle(a->board, rx_streamtype(a->port),
|
||||||
|
VHD_SDI_STPROC_DISJOINED_ANC,
|
||||||
|
NULL, &stream, NULL);
|
||||||
|
if (r == VHDERR_NOERROR) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
have_vhd_audio = 1;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[audio] VHD_StartStream failed - feeding silence\n");
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
stream = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[audio] VHD_OpenStreamHandle failed (%lu) - feeding silence\n", r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. Continuous, wall-clock-paced feed loop: real audio when available,
|
||||||
|
* otherwise silence, so ffmpeg's input 1 never starves. */
|
||||||
|
struct timespec next;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &next);
|
||||||
|
long frame_ns = (long)(1000000000.0 * (double)fps_den / (double)fps_num);
|
||||||
|
HANDLE slot = NULL;
|
||||||
|
|
||||||
|
while (!atomic_load(&g_stop)) {
|
||||||
|
size_t out_bytes = 0;
|
||||||
|
|
||||||
|
if (have_vhd_audio) {
|
||||||
|
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);
|
||||||
|
} else if (r != VHDERR_TIMEOUT) {
|
||||||
|
fprintf(stderr, "[audio] lock error %lu - degrading to silence\n", r);
|
||||||
|
VHD_StopStream(stream);
|
||||||
|
VHD_CloseStreamHandle(stream);
|
||||||
|
stream = NULL;
|
||||||
|
have_vhd_audio = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_bytes == 0) {
|
||||||
|
memset(buf, 0, tick_bytes); /* one frame of silence */
|
||||||
|
out_bytes = tick_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_all(fd, buf, out_bytes) < 0) {
|
||||||
|
atomic_store(&g_stop, 1); /* ffmpeg closed the FIFO (EPIPE) */
|
||||||
|
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; /* fell behind (real-audio burst) - resync */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
if (stream) {
|
||||||
|
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);
|
||||||
|
/* Don't let a dying ffmpeg kill us with SIGPIPE - writes return EPIPE
|
||||||
|
* and the FIFO/stdout write loops handle that by stopping cleanly. */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 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_CORE_BP_PASSIVE_LOOPBACK_<n> only exists for ports 0-3 in SDK 6.34.1,
|
||||||
|
* and the board reports passive-loopback capability 0, so skipping ports 4-7
|
||||||
|
* is harmless. */
|
||||||
|
if (port_id < 4) {
|
||||||
|
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_BUFFERQUEUE_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,
|
||||||
|
vi.fps_num, vi.fps_den, 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) {
|
||||||
|
fprintf(stderr, "[video] VHD_LockSlotHandle error %lu — stopping\n", r);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cleanup ─────────────────────────────────────────────────── */
|
||||||
|
VHD_StopStream(video_stream);
|
||||||
|
VHD_CloseStreamHandle(video_stream);
|
||||||
|
if (audio_tid) pthread_join(audio_tid, NULL);
|
||||||
|
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
|
|
@ -9,6 +9,7 @@ dotenv.config();
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3001;
|
const PORT = process.env.PORT || 3001;
|
||||||
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 || '';
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
@ -62,6 +63,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 {
|
||||||
|
|
@ -71,6 +79,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,
|
||||||
|
|
@ -128,17 +138,42 @@ async function gracefulShutdown(signal) {
|
||||||
try {
|
try {
|
||||||
await fetch(`${MAM_API_URL}/api/v1/assets/${liveAssetId}/mark-empty`, {
|
await fetch(`${MAM_API_URL}/api/v1/assets/${liveAssetId}/mark-empty`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', ...(MAM_API_TOKEN ? { 'Authorization': `Bearer ${MAM_API_TOKEN}` } : {}) },
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
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 as a .ts,
|
||||||
|
// NOT in S3 yet. The promotion worker (which watches the same share)
|
||||||
|
// uploads it to S3 and enqueues the proxy from the real, finalized key.
|
||||||
|
// We must NOT call /finalize here: that sets original_s3_key to a key
|
||||||
|
// that doesn't exist yet and enqueues a proxy that instantly fails with
|
||||||
|
// "unable to open the file on disk." Leave the asset 'live' for the
|
||||||
|
// promotion worker to flip to 'ready'.
|
||||||
|
console.log(`[shutdown] growing capture finalized on share (${completed.growingPath}); leaving promotion worker to upload + proxy`);
|
||||||
|
} else if (liveAssetId) {
|
||||||
|
// Finalise the pre-created live asset by id (avoids POST / 409 collision).
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${MAM_API_URL}/api/v1/assets/${liveAssetId}/finalize`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', ...(MAM_API_TOKEN ? { 'Authorization': `Bearer ${MAM_API_TOKEN}` } : {}) },
|
||||||
|
body: JSON.stringify({ hiresKey: completed.hiresKey, proxyKey: completed.proxyKey, duration: completed.duration }),
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
console.warn(`[shutdown] mam-api finalize returned ${res.status}: ${await res.text()}`);
|
||||||
|
} else {
|
||||||
|
console.log('[shutdown] live asset finalised with mam-api');
|
||||||
|
}
|
||||||
|
} catch (mamErr) {
|
||||||
|
console.error('[shutdown] failed to finalise asset:', mamErr.message);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${MAM_API_URL}/api/v1/assets`, {
|
const res = await fetch(`${MAM_API_URL}/api/v1/assets`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json', ...(MAM_API_TOKEN ? { 'Authorization': `Bearer ${MAM_API_TOKEN}` } : {}) },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
projectId: completed.projectId,
|
projectId: completed.projectId,
|
||||||
binId: completed.binId,
|
binId: completed.binId,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { execSync, spawn } from 'child_process';
|
import { execSync, spawn } from 'child_process';
|
||||||
|
import { existsSync, readdirSync } from 'node:fs';
|
||||||
import captureManager from '../capture-manager.js';
|
import captureManager from '../capture-manager.js';
|
||||||
|
|
||||||
import dgram from 'dgram';
|
import dgram from 'dgram';
|
||||||
|
|
@ -95,8 +96,8 @@ router.get('/devices', (req, res) => {
|
||||||
output = error.stderr ? error.stderr.toString() : error.toString();
|
output = error.stderr ? error.stderr.toString() : error.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse ffmpeg output for DeckLink device names
|
// Parse ffmpeg output for DeckLink device names.
|
||||||
// Format: [decklink @ ...] "DeckLink Quad 2" (input #0)
|
// DeckLink source lines: " 81:76669a80:00000000 [DeckLink Duo (1)] (none)"
|
||||||
const lines = output.split('\n');
|
const lines = output.split('\n');
|
||||||
let deviceIndex = 0;
|
let deviceIndex = 0;
|
||||||
|
|
||||||
|
|
@ -118,6 +119,57 @@ router.get('/devices', (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /devices/deltacast
|
||||||
|
* List available Deltacast ports.
|
||||||
|
* Reads /dev/deltacast<N> nodes; falls back to env DELTACAST_PORT_COUNT
|
||||||
|
* so nodes without hardware still report their configured port count
|
||||||
|
* (test-card mode).
|
||||||
|
*/
|
||||||
|
router.get('/devices/deltacast', (req, res) => {
|
||||||
|
try {
|
||||||
|
const devices = [];
|
||||||
|
|
||||||
|
// First: enumerate actual /dev/deltacast* device nodes.
|
||||||
|
try {
|
||||||
|
const devEntries = readdirSync('/dev').filter(n => /^deltacast\d+$/.test(n));
|
||||||
|
devEntries.sort();
|
||||||
|
for (const entry of devEntries) {
|
||||||
|
const m = entry.match(/^deltacast(\d+)$/);
|
||||||
|
if (m) {
|
||||||
|
devices.push({
|
||||||
|
index: parseInt(m[1], 10),
|
||||||
|
name: `Deltacast Port ${m[1]}`,
|
||||||
|
device: `/dev/${entry}`,
|
||||||
|
present: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) { /* /dev always exists; ignore */ }
|
||||||
|
|
||||||
|
// Second: if DELTACAST_PORT_COUNT env is set and larger than what we found,
|
||||||
|
// fill in the remaining slots as test-card entries (no physical device).
|
||||||
|
const envCount = parseInt(process.env.DELTACAST_PORT_COUNT || '0', 10);
|
||||||
|
const found = new Set(devices.map(d => d.index));
|
||||||
|
for (let i = 0; i < envCount; i++) {
|
||||||
|
if (!found.has(i)) {
|
||||||
|
devices.push({
|
||||||
|
index: i,
|
||||||
|
name: `Deltacast Port ${i} (test card)`,
|
||||||
|
device: `/dev/deltacast${i}`,
|
||||||
|
present: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.sort((a, b) => a.index - b.index);
|
||||||
|
res.json({ devices });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error listing Deltacast devices:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to list Deltacast devices' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /status
|
* GET /status
|
||||||
* Get current capture status
|
* Get current capture status
|
||||||
|
|
@ -150,6 +202,28 @@ router.post('/probe', async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (source_type === 'deltacast') {
|
||||||
|
// Enumerate /dev/deltacast* nodes; report present/absent per index.
|
||||||
|
try {
|
||||||
|
const envCount = parseInt(process.env.DELTACAST_PORT_COUNT || '0', 10);
|
||||||
|
const devEntries = readdirSync('/dev').filter(n => /^deltacast\d+$/.test(n)).sort();
|
||||||
|
const found = devEntries.map(n => {
|
||||||
|
const m = n.match(/^deltacast(\d+)$/);
|
||||||
|
return { index: parseInt(m[1], 10), device: `/dev/${n}`, present: true };
|
||||||
|
});
|
||||||
|
const foundIdx = new Set(found.map(d => d.index));
|
||||||
|
for (let i = 0; i < envCount; i++) {
|
||||||
|
if (!foundIdx.has(i)) {
|
||||||
|
found.push({ index: i, device: `/dev/deltacast${i}`, present: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found.sort((a, b) => a.index - b.index);
|
||||||
|
return res.json({ ok: true, source_type, devices: found });
|
||||||
|
} catch (err) {
|
||||||
|
return res.json({ ok: false, source_type, error: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (listen) {
|
if (listen) {
|
||||||
return res.json({ ok: false, source_type, error: 'Listener-mode sources cannot be probed standalone. Start the recorder and watch the signal indicator.' });
|
return res.json({ ok: false, source_type, error: 'Listener-mode sources cannot be probed standalone. Start the recorder and watch the signal indicator.' });
|
||||||
}
|
}
|
||||||
|
|
@ -251,9 +325,13 @@ 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`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
65
services/editor/.gitignore
vendored
65
services/editor/.gitignore
vendored
|
|
@ -1,65 +0,0 @@
|
||||||
# Dependencies
|
|
||||||
node_modules/
|
|
||||||
.pnpm-store/
|
|
||||||
|
|
||||||
# Build outputs
|
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
.next/
|
|
||||||
out/
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Environment variables
|
|
||||||
.env
|
|
||||||
.env.local
|
|
||||||
.env.*.local
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs/
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
coverage/
|
|
||||||
.nyc_output/
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
.cache/
|
|
||||||
.temp/
|
|
||||||
.docs/
|
|
||||||
docs/
|
|
||||||
# Project-specific
|
|
||||||
/public/projects/
|
|
||||||
*.openreel
|
|
||||||
apps/cloud/
|
|
||||||
apps/ios
|
|
||||||
apps/android
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Local files
|
|
||||||
FEATURES_TWITTER.md
|
|
||||||
.claude-tasks.md
|
|
||||||
CLAUDE.md
|
|
||||||
*-PLAN.md
|
|
||||||
*-PLAN-*.md
|
|
||||||
.playwright-mcp/
|
|
||||||
.wrangler/
|
|
||||||
|
|
||||||
|
|
||||||
mobile-mockup/
|
|
||||||
2
services/editor/.serena/.gitignore
vendored
2
services/editor/.serena/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
/cache
|
|
||||||
/project.local.yml
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
# the name by which the project can be referenced within Serena
|
|
||||||
project_name: "openreel-video"
|
|
||||||
|
|
||||||
|
|
||||||
# list of languages for which language servers are started; choose from:
|
|
||||||
# al bash clojure cpp csharp
|
|
||||||
# csharp_omnisharp dart elixir elm erlang
|
|
||||||
# fortran fsharp go groovy haskell
|
|
||||||
# java julia kotlin lua markdown
|
|
||||||
# matlab nix pascal perl php
|
|
||||||
# php_phpactor powershell python python_jedi r
|
|
||||||
# rego ruby ruby_solargraph rust scala
|
|
||||||
# swift terraform toml typescript typescript_vts
|
|
||||||
# vue yaml zig
|
|
||||||
# (This list may be outdated. For the current list, see values of Language enum here:
|
|
||||||
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
|
||||||
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
|
||||||
# Note:
|
|
||||||
# - For C, use cpp
|
|
||||||
# - For JavaScript, use typescript
|
|
||||||
# - For Free Pascal/Lazarus, use pascal
|
|
||||||
# Special requirements:
|
|
||||||
# Some languages require additional setup/installations.
|
|
||||||
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
|
||||||
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
|
||||||
# The first language is the default language and the respective language server will be used as a fallback.
|
|
||||||
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
|
||||||
languages:
|
|
||||||
- typescript
|
|
||||||
|
|
||||||
# the encoding used by text files in the project
|
|
||||||
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
|
||||||
encoding: "utf-8"
|
|
||||||
|
|
||||||
# line ending convention to use when writing source files.
|
|
||||||
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
|
||||||
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
|
||||||
line_ending:
|
|
||||||
|
|
||||||
# The language backend to use for this project.
|
|
||||||
# If not set, the global setting from serena_config.yml is used.
|
|
||||||
# Valid values: LSP, JetBrains
|
|
||||||
# Note: the backend is fixed at startup. If a project with a different backend
|
|
||||||
# is activated post-init, an error will be returned.
|
|
||||||
language_backend:
|
|
||||||
|
|
||||||
# whether to use project's .gitignore files to ignore files
|
|
||||||
ignore_all_files_in_gitignore: true
|
|
||||||
|
|
||||||
# list of additional paths to ignore in this project.
|
|
||||||
# Same syntax as gitignore, so you can use * and **.
|
|
||||||
# Note: global ignored_paths from serena_config.yml are also applied additively.
|
|
||||||
ignored_paths: []
|
|
||||||
|
|
||||||
# whether the project is in read-only mode
|
|
||||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
|
||||||
# Added on 2025-04-18
|
|
||||||
read_only: false
|
|
||||||
|
|
||||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
|
||||||
# Below is the complete list of tools for convenience.
|
|
||||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
|
||||||
# execute `uv run scripts/print_tool_overview.py`.
|
|
||||||
#
|
|
||||||
# * `activate_project`: Activates a project by name.
|
|
||||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
|
||||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
|
||||||
# * `delete_lines`: Deletes a range of lines within a file.
|
|
||||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
|
||||||
# * `execute_shell_command`: Executes a shell command.
|
|
||||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
|
||||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
|
||||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
|
||||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
|
||||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
|
||||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
|
||||||
# Should only be used in settings where the system prompt cannot be set,
|
|
||||||
# e.g. in clients you have no control over, like Claude Desktop.
|
|
||||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
|
||||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
|
||||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
|
||||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
|
||||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
|
||||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
|
||||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
|
||||||
# * `read_file`: Reads a file within the project directory.
|
|
||||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
|
||||||
# * `remove_project`: Removes a project from the Serena configuration.
|
|
||||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
|
||||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
|
||||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
|
||||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
|
||||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
|
||||||
# * `switch_modes`: Activates modes by providing a list of their names
|
|
||||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
|
||||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
|
||||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
|
||||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
|
||||||
excluded_tools: []
|
|
||||||
|
|
||||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
|
|
||||||
included_optional_tools: []
|
|
||||||
|
|
||||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
|
||||||
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
|
||||||
fixed_tools: []
|
|
||||||
|
|
||||||
# list of mode names to that are always to be included in the set of active modes
|
|
||||||
# The full set of modes to be activated is base_modes + default_modes.
|
|
||||||
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
|
|
||||||
# Otherwise, this setting overrides the global configuration.
|
|
||||||
# Set this to [] to disable base modes for this project.
|
|
||||||
# Set this to a list of mode names to always include the respective modes for this project.
|
|
||||||
base_modes:
|
|
||||||
|
|
||||||
# list of mode names that are to be activated by default.
|
|
||||||
# The full set of modes to be activated is base_modes + default_modes.
|
|
||||||
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
|
|
||||||
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
|
||||||
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
|
||||||
default_modes:
|
|
||||||
|
|
||||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
|
||||||
# (contrary to the memories, which are loaded on demand).
|
|
||||||
initial_prompt: ""
|
|
||||||
|
|
||||||
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
|
||||||
# such as docstrings or parameter information.
|
|
||||||
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
|
||||||
# If null or missing, use the setting from the global configuration.
|
|
||||||
symbol_info_budget:
|
|
||||||
|
|
||||||
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
|
||||||
# Extends the list from the global configuration, merging the two lists.
|
|
||||||
read_only_memory_patterns: []
|
|
||||||
|
|
@ -1,387 +0,0 @@
|
||||||
# Contributing to OpenReel
|
|
||||||
|
|
||||||
Thank you for your interest in contributing to OpenReel! This document provides guidelines and instructions for contributing.
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
- [Code of Conduct](#code-of-conduct)
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
- [Development Setup](#development-setup)
|
|
||||||
- [Project Structure](#project-structure)
|
|
||||||
- [Coding Standards](#coding-standards)
|
|
||||||
- [Making Changes](#making-changes)
|
|
||||||
- [Testing](#testing)
|
|
||||||
- [Submitting Changes](#submitting-changes)
|
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
Be respectful, constructive, and professional. We're building something great together!
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
- Node.js 18 or higher
|
|
||||||
- pnpm (recommended) or npm
|
|
||||||
- Git
|
|
||||||
- Modern browser with WebCodecs support (Chrome 94+, Edge 94+)
|
|
||||||
|
|
||||||
### Development Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Fork and clone the repository
|
|
||||||
git clone https://github.com/Augani/openreel-video.git
|
|
||||||
cd openreel-video
|
|
||||||
|
|
||||||
# 2. Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# 3. Start development server
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# 4. Open browser to http://localhost:5173
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
openreel/
|
|
||||||
├── apps/
|
|
||||||
│ └── web/ # Main web application
|
|
||||||
│ ├── public/ # Static assets
|
|
||||||
│ └── src/
|
|
||||||
│ ├── components/ # React components
|
|
||||||
│ ├── stores/ # State management (Zustand)
|
|
||||||
│ ├── bridges/ # Core engine bridges
|
|
||||||
│ └── services/ # Business logic
|
|
||||||
├── packages/
|
|
||||||
│ └── core/ # Shared core logic
|
|
||||||
│ ├── src/
|
|
||||||
│ │ ├── actions/ # Action system
|
|
||||||
│ │ ├── video/ # Video processing
|
|
||||||
│ │ ├── audio/ # Audio processing
|
|
||||||
│ │ ├── graphics/ # Graphics & SVG
|
|
||||||
│ │ ├── text/ # Text & titles
|
|
||||||
│ │ └── export/ # Export engine
|
|
||||||
│ └── types/ # TypeScript types
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coding Standards
|
|
||||||
|
|
||||||
### TypeScript
|
|
||||||
|
|
||||||
- **Strict mode**: Always use TypeScript strict mode
|
|
||||||
- **Types**: Prefer interfaces over types for object shapes
|
|
||||||
- **No `any`**: Avoid `any` - use `unknown` or proper types
|
|
||||||
- **Naming**:
|
|
||||||
- Components: `PascalCase` (e.g., `Timeline`, `Preview`)
|
|
||||||
- Functions: `camelCase` (e.g., `handleClick`, `processVideo`)
|
|
||||||
- Constants: `UPPER_SNAKE_CASE` (e.g., `MAX_DURATION`)
|
|
||||||
- Files: `kebab-case.tsx` or `PascalCase.tsx` for components
|
|
||||||
|
|
||||||
### Code Style
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ✅ Good
|
|
||||||
interface VideoClip {
|
|
||||||
id: string;
|
|
||||||
duration: number;
|
|
||||||
startTime: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processClip(clip: VideoClip): ProcessedClip {
|
|
||||||
if (!clip.id) {
|
|
||||||
throw new Error('Clip ID is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...clip,
|
|
||||||
processed: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ❌ Avoid
|
|
||||||
function processClip(clip: any) {
|
|
||||||
console.log('Processing...'); // Remove debug logs
|
|
||||||
const result = clip; // Unclear what's happening
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### React Components
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ✅ Good
|
|
||||||
interface TimelineProps {
|
|
||||||
tracks: Track[];
|
|
||||||
onClipSelect: (clipId: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Timeline: React.FC<TimelineProps> = ({ tracks, onClipSelect }) => {
|
|
||||||
const handleClick = useCallback((id: string) => {
|
|
||||||
onClipSelect(id);
|
|
||||||
}, [onClipSelect]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="timeline">
|
|
||||||
{tracks.map(track => (
|
|
||||||
<Track key={track.id} track={track} onClick={handleClick} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Comments
|
|
||||||
|
|
||||||
- **Do**: Comment complex algorithms and business logic
|
|
||||||
- **Don't**: Comment obvious code
|
|
||||||
- **Do**: Add JSDoc for public APIs
|
|
||||||
- **Don't**: Leave TODO comments without issues
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ✅ Good - Explains WHY
|
|
||||||
// Use binary search for O(log n) performance on large timelines
|
|
||||||
const clipIndex = binarySearch(clips, targetTime);
|
|
||||||
|
|
||||||
// ❌ Bad - States the obvious
|
|
||||||
// Loop through clips
|
|
||||||
for (const clip of clips) { }
|
|
||||||
|
|
||||||
// ✅ Good - Public API documentation
|
|
||||||
/**
|
|
||||||
* Applies a filter to a video clip
|
|
||||||
* @param clipId - The clip identifier
|
|
||||||
* @param filter - Filter configuration
|
|
||||||
* @returns Updated clip with filter applied
|
|
||||||
*/
|
|
||||||
export function applyFilter(clipId: string, filter: Filter): Clip {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Making Changes
|
|
||||||
|
|
||||||
### 1. Create a Branch
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Feature branch
|
|
||||||
git checkout -b feat/add-transition-effects
|
|
||||||
|
|
||||||
# Bug fix branch
|
|
||||||
git checkout -b fix/timeline-scroll-bug
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
git checkout -b docs/update-contributing-guide
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Make Your Changes
|
|
||||||
|
|
||||||
- Write clean, self-documenting code
|
|
||||||
- Follow the existing code style
|
|
||||||
- Keep commits focused and atomic
|
|
||||||
- Write meaningful commit messages
|
|
||||||
|
|
||||||
### 3. Commit Messages
|
|
||||||
|
|
||||||
Follow conventional commits:
|
|
||||||
|
|
||||||
```
|
|
||||||
feat: add crossfade transition effect
|
|
||||||
fix: resolve timeline scrubbing lag
|
|
||||||
docs: update API documentation
|
|
||||||
refactor: simplify video processing pipeline
|
|
||||||
test: add tests for audio mixer
|
|
||||||
perf: optimize waveform rendering
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Keep Your Branch Updated
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git fetch origin
|
|
||||||
git rebase origin/main
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
### Running Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run all tests (watch mode)
|
|
||||||
pnpm test
|
|
||||||
|
|
||||||
# Run tests once (CI mode)
|
|
||||||
pnpm test:run
|
|
||||||
|
|
||||||
# Type checking
|
|
||||||
pnpm typecheck
|
|
||||||
|
|
||||||
# Linting
|
|
||||||
pnpm lint
|
|
||||||
```
|
|
||||||
|
|
||||||
### Writing Tests
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import { processClip } from './clip-processor';
|
|
||||||
|
|
||||||
describe('processClip', () => {
|
|
||||||
it('should process a valid clip', () => {
|
|
||||||
const clip = { id: '123', duration: 10, startTime: 0 };
|
|
||||||
const result = processClip(clip);
|
|
||||||
|
|
||||||
expect(result.processed).toBe(true);
|
|
||||||
expect(result.id).toBe('123');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error for invalid clip', () => {
|
|
||||||
const clip = { id: '', duration: 10, startTime: 0 };
|
|
||||||
|
|
||||||
expect(() => processClip(clip)).toThrow('Clip ID is required');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Submitting Changes
|
|
||||||
|
|
||||||
### 1. Push Your Branch
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git push origin feat/your-feature-name
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Create a Pull Request
|
|
||||||
|
|
||||||
1. Go to GitHub and create a pull request
|
|
||||||
2. Fill out the PR template:
|
|
||||||
- **Description**: What does this PR do?
|
|
||||||
- **Motivation**: Why is this change needed?
|
|
||||||
- **Testing**: How was this tested?
|
|
||||||
- **Screenshots**: For UI changes
|
|
||||||
- **Breaking Changes**: Any breaking changes?
|
|
||||||
|
|
||||||
### 3. PR Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Description
|
|
||||||
Brief description of changes
|
|
||||||
|
|
||||||
## Type of Change
|
|
||||||
- [ ] Bug fix
|
|
||||||
- [ ] New feature
|
|
||||||
- [ ] Breaking change
|
|
||||||
- [ ] Documentation update
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
- [ ] Tested locally
|
|
||||||
- [ ] Added/updated tests
|
|
||||||
- [ ] All tests passing
|
|
||||||
|
|
||||||
## Screenshots (if applicable)
|
|
||||||
[Add screenshots for UI changes]
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
- [ ] Code follows project style guidelines
|
|
||||||
- [ ] Self-review completed
|
|
||||||
- [ ] Comments added for complex code
|
|
||||||
- [ ] Documentation updated
|
|
||||||
- [ ] No console.log or debug code left
|
|
||||||
- [ ] Tests pass
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Code Review Process
|
|
||||||
|
|
||||||
- Respond to feedback promptly
|
|
||||||
- Make requested changes
|
|
||||||
- Push updates to the same branch
|
|
||||||
- Re-request review when ready
|
|
||||||
|
|
||||||
## Areas to Contribute
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
|
||||||
- Check [Issues](https://github.com/Augani/openreel-video/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
|
||||||
- Reproduce the bug
|
|
||||||
- Write a failing test
|
|
||||||
- Fix the bug
|
|
||||||
- Verify the test passes
|
|
||||||
|
|
||||||
### ✨ New Features
|
|
||||||
- Discuss in [Discussions](https://github.com/Augani/openreel-video/discussions) first
|
|
||||||
- Get approval before large changes
|
|
||||||
- Break into smaller PRs if possible
|
|
||||||
- Update documentation
|
|
||||||
|
|
||||||
### 📖 Documentation
|
|
||||||
- Fix typos and errors
|
|
||||||
- Add examples
|
|
||||||
- Improve clarity
|
|
||||||
- Add tutorials
|
|
||||||
|
|
||||||
### 🎨 Effects & Presets
|
|
||||||
- Create new video effects
|
|
||||||
- Add transition effects
|
|
||||||
- Build color grading presets
|
|
||||||
- Contribute templates
|
|
||||||
|
|
||||||
### 🧪 Testing
|
|
||||||
- Add missing tests
|
|
||||||
- Improve test coverage
|
|
||||||
- Add integration tests
|
|
||||||
- Performance testing
|
|
||||||
|
|
||||||
### 🌍 Translation
|
|
||||||
- Add new language support
|
|
||||||
- Improve existing translations
|
|
||||||
- Fix translation errors
|
|
||||||
|
|
||||||
## Development Tips
|
|
||||||
|
|
||||||
### Hot Reload
|
|
||||||
Changes to React components hot reload automatically. For core engine changes, you may need to refresh.
|
|
||||||
|
|
||||||
### Debugging
|
|
||||||
```typescript
|
|
||||||
// Use browser DevTools
|
|
||||||
// Set breakpoints in TypeScript source
|
|
||||||
// Check Network tab for media loading
|
|
||||||
// Use Performance profiler for optimization
|
|
||||||
```
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
- Profile before optimizing
|
|
||||||
- Use Web Workers for heavy processing
|
|
||||||
- Leverage WebCodecs API for video
|
|
||||||
- Cache expensive computations
|
|
||||||
- Use useMemo/useCallback appropriately
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
**Issue**: Video won't play
|
|
||||||
- Check browser support for WebCodecs
|
|
||||||
- Verify codec support
|
|
||||||
- Check browser console for errors
|
|
||||||
|
|
||||||
**Issue**: Build fails
|
|
||||||
- Clear node_modules and reinstall
|
|
||||||
- Check Node.js version (18+)
|
|
||||||
- Verify pnpm version
|
|
||||||
|
|
||||||
**Issue**: Tests fail
|
|
||||||
- Try running `pnpm test:run` for a single run
|
|
||||||
- Check for console errors
|
|
||||||
- Verify test environment setup
|
|
||||||
- Run `pnpm typecheck` to check for type errors
|
|
||||||
|
|
||||||
## Questions?
|
|
||||||
|
|
||||||
- **Discord**: [Join our Discord](https://discord.gg/openreeel)
|
|
||||||
- **Discussions**: [GitHub Discussions](https://github.com/Augani/openreel-video/discussions)
|
|
||||||
- **Email**: contribute@openreeel.video
|
|
||||||
|
|
||||||
## Recognition
|
|
||||||
|
|
||||||
Contributors are recognized in:
|
|
||||||
- README.md contributors section
|
|
||||||
- GitHub contributors page
|
|
||||||
- Release notes for significant contributions
|
|
||||||
|
|
||||||
Thank you for contributing to OpenReel! 🎬
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1.6
|
|
||||||
FROM node:20-alpine AS builder
|
|
||||||
RUN apk add --no-cache python3 make g++ git bash
|
|
||||||
RUN corepack enable && corepack prepare pnpm@9.7.0 --activate
|
|
||||||
WORKDIR /build
|
|
||||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.base.json mediabunny.d.ts ./
|
|
||||||
COPY apps ./apps
|
|
||||||
COPY packages ./packages
|
|
||||||
RUN pnpm install --frozen-lockfile=false
|
|
||||||
RUN pnpm build:wasm || echo "no wasm build step, continuing"
|
|
||||||
RUN pnpm --filter @openreel/web build
|
|
||||||
RUN ls -la apps/web/dist
|
|
||||||
|
|
||||||
FROM nginx:alpine AS runtime
|
|
||||||
RUN rm -rf /usr/share/nginx/html/* /etc/nginx/conf.d/default.conf
|
|
||||||
COPY --from=builder /build/apps/web/dist /usr/share/nginx/html
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
EXPOSE 80
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# Z-AMPP <-> openreel-video integration
|
|
||||||
|
|
||||||
Vendored from https://github.com/Augani/openreel-video (MIT). The upstream .git directory was removed so this lives as plain source we can patch freely.
|
|
||||||
|
|
||||||
## Files added (Z-AMPP-only, not upstream)
|
|
||||||
- Dockerfile, nginx.conf, VENDOR.txt, INTEGRATION.md
|
|
||||||
- apps/web/src/mam-bridge.ts: boot hook + pickFromMAM() modal
|
|
||||||
- packages/core/src/export/mam-export-target.ts: helpers for upload-to-MAM
|
|
||||||
|
|
||||||
## Upstream files patched
|
|
||||||
- apps/web/package.json: build script changed `tsc --noEmit && vite build` -> `vite build`. Original preserved as build:strict. (upstream tsc fails on pre-existing WebGPU + import.meta errors.)
|
|
||||||
- apps/web/src/bridges/media-bridge.ts: appended importFromURL(url, name, contentType?) as the last method of the MediaBridge class.
|
|
||||||
- apps/web/src/main.tsx: appended `import "./mam-bridge";` so the bridge boot hook runs.
|
|
||||||
|
|
||||||
## Query params honored
|
|
||||||
- ?asset=<uuid> auto-imports that asset on load.
|
|
||||||
- ?project=<uuid> stored in localStorage.mamProjectId for save-to-MAM.
|
|
||||||
|
|
||||||
## Ports
|
|
||||||
Container exposes 80; compose maps ${PORT_EDITOR:-47435}:80.
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024-2026 Augustus Otu and Contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
@ -1,308 +0,0 @@
|
||||||
# OpenReel Video
|
|
||||||
|
|
||||||
> **The open source CapCut alternative. Professional video editing in your browser. No uploads. No installs. 100% open source.**
|
|
||||||
|
|
||||||
OpenReel Video is a fully-featured browser-based video editor that runs entirely client-side. Built with React, TypeScript, WebCodecs, and WebGPU for professional-grade video editing without the need for expensive software or cloud processing.
|
|
||||||
|
|
||||||
**[Try it Live](https://openreel.video)** | **[Documentation](CONTRIBUTING.md)** | **[Discussions](https://github.com/Augani/openreel-video/discussions)** | **[Twitter](https://x.com/python_xi)**
|
|
||||||
|
|
||||||
   
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why OpenReel?
|
|
||||||
|
|
||||||
- **100% Client-Side** - Your videos never leave your device. No uploads, no cloud processing, complete privacy.
|
|
||||||
- **No Installation** - Works in Chrome/Edge. Just open and start editing.
|
|
||||||
- **Professional Features** - Multi-track timeline, keyframe animations, color grading, audio effects, and more.
|
|
||||||
- **GPU Accelerated** - WebGPU and WebCodecs for smooth 4K editing and fast exports.
|
|
||||||
- **Free Forever** - MIT licensed, no subscriptions, no watermarks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Video Editing
|
|
||||||
- **Multi-track timeline** - Unlimited video, audio, image, text, and graphics tracks
|
|
||||||
- **Real-time preview** - Smooth playback with GPU acceleration
|
|
||||||
- **Precision editing** - Frame-accurate scrubbing, cut, trim, split, ripple delete
|
|
||||||
- **Transitions** - Crossfade, dip to black/white, wipe, slide effects
|
|
||||||
- **Video effects** - Brightness, contrast, saturation, blur, sharpen, glow, vignette, chroma key
|
|
||||||
- **Blend modes** - Multiply, screen, overlay, add, subtract, and more
|
|
||||||
- **Speed control** - 0.25x to 4x with audio pitch preservation
|
|
||||||
- **Crop & transform** - Position, scale, rotation with 3D perspective
|
|
||||||
|
|
||||||
### Graphics & Text
|
|
||||||
- **Professional text editor** - Rich styling, shadows, outlines, gradients
|
|
||||||
- **20+ text animations** - Typewriter, fade, slide, bounce, pop, elastic, glitch
|
|
||||||
- **Karaoke-style subtitles** - Word-by-word highlighting synced to audio
|
|
||||||
- **Shape tools** - Rectangle, circle, arrow, polygon, star with fill/stroke
|
|
||||||
- **SVG support** - Import SVGs with color tinting and animations
|
|
||||||
- **Stickers & emoji** - Built-in library
|
|
||||||
- **Background generator** - Solid colors, gradients, mesh gradients, patterns
|
|
||||||
- **Keyframe animations** - Animate any property over time with 20+ easing curves
|
|
||||||
|
|
||||||
### Audio
|
|
||||||
- **Multi-track mixing** - Unlimited audio tracks with real-time mixing
|
|
||||||
- **Waveform visualization** - Visual audio editing
|
|
||||||
- **Audio effects** - EQ, compressor, reverb, delay, chorus, flanger, distortion
|
|
||||||
- **Volume & panning** - Per-clip controls with fade in/out
|
|
||||||
- **Beat detection** - Auto-generate markers synced to music
|
|
||||||
- **Audio ducking** - Auto-reduce music when dialog plays
|
|
||||||
- **Noise reduction** - 3-pass noise removal (tonal, broadband, rumble)
|
|
||||||
|
|
||||||
### Color Grading
|
|
||||||
- **Color wheels** - Lift, gamma, gain controls
|
|
||||||
- **HSL adjustments** - Hue, saturation, lightness fine-tuning
|
|
||||||
- **Curves editor** - RGB and individual channel curves
|
|
||||||
- **LUT support** - Import and apply 3D LUTs
|
|
||||||
- **Built-in presets** - One-click color grading
|
|
||||||
|
|
||||||
### Export
|
|
||||||
- **MP4 (H.264/H.265)** - Universal compatibility
|
|
||||||
- **WebM (VP8/VP9/AV1)** - Web-optimized format
|
|
||||||
- **ProRes** - Professional intermediate format (Proxy, LT, Standard, HQ, 4444)
|
|
||||||
- **Quality presets** - 4K @ 60fps, 1080p, 720p, 480p
|
|
||||||
- **Custom settings** - Bitrate, frame rate, codec options, color depth
|
|
||||||
- **Hardware encoding** - WebCodecs for fast exports
|
|
||||||
- **AI upscaling** - Enhance resolution with WebGPU shaders
|
|
||||||
- **Audio export** - MP3, WAV, AAC, FLAC, OGG
|
|
||||||
- **Image sequences** - JPG, PNG, WebP frame export
|
|
||||||
- **Progress tracking** - Real-time progress with cancel support
|
|
||||||
|
|
||||||
### Professional Tools
|
|
||||||
- **Unlimited undo/redo** - Full history with recovery
|
|
||||||
- **Auto-save** - Never lose work (IndexedDB storage)
|
|
||||||
- **Keyboard shortcuts** - Professional workflow
|
|
||||||
- **Snap to grid** - Magnetic alignment
|
|
||||||
- **Track management** - Show/hide, lock/unlock, reorder
|
|
||||||
- **Subtitle support** - SRT import with customizable styling
|
|
||||||
- **Screen recording** - Record screen, camera, or both
|
|
||||||
- **Project sharing** - Export/import project files
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
- **WebGPU rendering** - GPU-accelerated compositing
|
|
||||||
- **WebCodecs API** - Hardware video decoding/encoding
|
|
||||||
- **Frame caching** - LRU cache for smooth playback
|
|
||||||
- **Web Workers** - Background processing
|
|
||||||
- **4K support** - Edit and export in 4K resolution
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### Try Online
|
|
||||||
Visit **[openreel.video](https://openreel.video)** to start editing immediately.
|
|
||||||
|
|
||||||
### Run Locally
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone the repository
|
|
||||||
git clone https://github.com/Augani/openreel-video.git
|
|
||||||
cd openreel-video
|
|
||||||
|
|
||||||
# Install dependencies (requires Node.js 18+)
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# Open http://localhost:5173
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build for Production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm build
|
|
||||||
pnpm preview
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Browser Requirements
|
|
||||||
|
|
||||||
| Browser | Version | Status |
|
|
||||||
|---------|---------|--------|
|
|
||||||
| Chrome | 94+ | Full support |
|
|
||||||
| Edge | 94+ | Full support |
|
|
||||||
| Firefox | 130+ | Full support |
|
|
||||||
| Safari | 16.4+ | Full support |
|
|
||||||
|
|
||||||
All major browsers now support WebCodecs for hardware-accelerated video encoding/decoding.
|
|
||||||
|
|
||||||
**Recommended:**
|
|
||||||
- 8GB+ RAM
|
|
||||||
- Dedicated GPU for 4K editing
|
|
||||||
- Modern multi-core CPU
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Monorepo Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
openreel/
|
|
||||||
├── apps/web/ # React frontend (~66k lines)
|
|
||||||
│ └── src/
|
|
||||||
│ ├── components/ # UI components
|
|
||||||
│ │ └── editor/ # Editor panels (Timeline, Preview, Inspector)
|
|
||||||
│ ├── stores/ # Zustand state management
|
|
||||||
│ ├── services/ # Auto-save, shortcuts, screen recording
|
|
||||||
│ └── bridges/ # Engine coordination
|
|
||||||
│
|
|
||||||
└── packages/core/ # Core engines (~59k lines)
|
|
||||||
└── src/
|
|
||||||
├── video/ # Video processing, WebGPU rendering
|
|
||||||
├── audio/ # Web Audio API, effects, beat detection
|
|
||||||
├── graphics/ # Canvas/THREE.js, shapes, SVG
|
|
||||||
├── text/ # Text rendering, animations
|
|
||||||
├── export/ # MP4/WebM encoding
|
|
||||||
└── storage/ # IndexedDB, serialization
|
|
||||||
```
|
|
||||||
|
|
||||||
### Key Technologies
|
|
||||||
|
|
||||||
- **React 18** + **TypeScript** - Type-safe UI
|
|
||||||
- **Zustand** - Lightweight state management
|
|
||||||
- **MediaBunny** - Video/audio processing
|
|
||||||
- **WebCodecs** - Hardware encoding/decoding
|
|
||||||
- **WebGPU** - GPU-accelerated rendering
|
|
||||||
- **Web Audio API** - Professional audio processing
|
|
||||||
- **THREE.js** - 3D transforms and effects
|
|
||||||
- **IndexedDB** - Local project storage
|
|
||||||
|
|
||||||
### Design Principles
|
|
||||||
|
|
||||||
- **Action-based editing** - Every edit is an undoable action
|
|
||||||
- **Immutable state** - Predictable updates with Zustand
|
|
||||||
- **Engine separation** - Video, audio, graphics engines are independent
|
|
||||||
- **Progressive enhancement** - Graceful fallbacks (WebGPU → Canvas2D)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## AI-Managed Development
|
|
||||||
|
|
||||||
OpenReel is an experiment in AI-assisted open source development. Claude AI helps manage:
|
|
||||||
|
|
||||||
- **Issue triage** - Reviews and responds to issues
|
|
||||||
- **Code implementation** - Writes features and fixes bugs
|
|
||||||
- **Code review** - Maintains quality standards
|
|
||||||
- **Documentation** - Keeps docs up to date
|
|
||||||
|
|
||||||
Human oversight from Augustus ensures strategic direction and final approval on major changes. All code is public, tested, and follows best practices.
|
|
||||||
|
|
||||||
**What this means for contributors:**
|
|
||||||
- Issues get reviewed quickly (usually within 24 hours)
|
|
||||||
- Bug fixes ship fast
|
|
||||||
- Clear, detailed responses to questions
|
|
||||||
- High code quality standards
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
||||||
|
|
||||||
**Ways to contribute:**
|
|
||||||
- Report bugs with reproduction steps
|
|
||||||
- Suggest features in Discussions
|
|
||||||
- Submit PRs for bugs or features
|
|
||||||
- Improve documentation
|
|
||||||
- Write tests
|
|
||||||
- Share effect presets
|
|
||||||
|
|
||||||
**Development workflow:**
|
|
||||||
```bash
|
|
||||||
# Fork and clone
|
|
||||||
git clone https://github.com/Augani/openreel-video.git
|
|
||||||
|
|
||||||
# Create feature branch
|
|
||||||
git checkout -b feat/your-feature
|
|
||||||
|
|
||||||
# Make changes, then test
|
|
||||||
pnpm typecheck
|
|
||||||
pnpm test
|
|
||||||
pnpm lint
|
|
||||||
|
|
||||||
# Commit with conventional commits
|
|
||||||
git commit -m "feat: add your feature"
|
|
||||||
|
|
||||||
# Push and open PR
|
|
||||||
git push origin feat/your-feature
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
### Completed
|
|
||||||
- Multi-track timeline with drag-and-drop
|
|
||||||
- Real-time video preview with GPU acceleration
|
|
||||||
- Full editing suite (cut, trim, split, transitions)
|
|
||||||
- Text editor with 20+ animations
|
|
||||||
- Graphics (shapes, SVG, stickers, backgrounds)
|
|
||||||
- Audio mixing with effects and beat detection
|
|
||||||
- Color grading with LUT support
|
|
||||||
- Keyframe animation system
|
|
||||||
- Export to MP4/WebM (4K supported)
|
|
||||||
- Screen recording
|
|
||||||
- AI upscaling
|
|
||||||
- Undo/redo with auto-save
|
|
||||||
|
|
||||||
### In Progress
|
|
||||||
- Nested sequences (timeline in timeline)
|
|
||||||
- Motion tracking
|
|
||||||
- More export formats (ProRes, GIF)
|
|
||||||
- Plugin system
|
|
||||||
|
|
||||||
### Planned
|
|
||||||
- Adjustment layers
|
|
||||||
- Advanced masking
|
|
||||||
- Audio spectral editing
|
|
||||||
- Collaborative editing
|
|
||||||
- Mobile optimization
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT License - Use freely for personal and commercial projects.
|
|
||||||
|
|
||||||
See [LICENSE](LICENSE) for details.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Acknowledgments
|
|
||||||
|
|
||||||
**Built with:**
|
|
||||||
- [MediaBunny](https://mediabunny.dev) - Media processing
|
|
||||||
- [React](https://react.dev) - UI framework
|
|
||||||
- [Zustand](https://zustand-demo.pmnd.rs/) - State management
|
|
||||||
- [THREE.js](https://threejs.org) - 3D rendering
|
|
||||||
- [TailwindCSS](https://tailwindcss.com) - Styling
|
|
||||||
|
|
||||||
**Inspired by:**
|
|
||||||
- DaVinci Resolve - Professional tools done right
|
|
||||||
- CapCut - Accessible editing for everyone
|
|
||||||
- Figma - Browser-based professional software
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
- **GitHub Issues** - Bug reports and feature requests
|
|
||||||
- **GitHub Discussions** - Questions and community chat
|
|
||||||
- **Twitter/X** - [@python_xi](https://x.com/python_xi)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## $OPENREEL Token
|
|
||||||
|
|
||||||
CA: `B7wDnfrdtvdG7SCkRjSMJ6LkVwGWvdWrQ75iV8G9pump`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Built with care by [@python_xi](https://x.com/python_xi) and AI working together.**
|
|
||||||
|
|
||||||
*Making professional video editing accessible to everyone. Forever free. Forever open source.*
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Vendored from Augani/openreel-video @ 2026-05-18T01:29:08Z
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import js from "@eslint/js";
|
|
||||||
import tseslint from "@typescript-eslint/eslint-plugin";
|
|
||||||
import tsparser from "@typescript-eslint/parser";
|
|
||||||
import reactHooks from "eslint-plugin-react-hooks";
|
|
||||||
import globals from "globals";
|
|
||||||
|
|
||||||
export default [
|
|
||||||
js.configs.recommended,
|
|
||||||
{
|
|
||||||
files: ["**/*.{ts,tsx}"],
|
|
||||||
languageOptions: {
|
|
||||||
parser: tsparser,
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
ecmaFeatures: {
|
|
||||||
jsx: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.es2021,
|
|
||||||
...globals.node,
|
|
||||||
NodeJS: "readonly",
|
|
||||||
CanvasTextAlign: "readonly",
|
|
||||||
CanvasTextBaseline: "readonly",
|
|
||||||
CanvasLineCap: "readonly",
|
|
||||||
CanvasLineJoin: "readonly",
|
|
||||||
CanvasFillRule: "readonly",
|
|
||||||
GlobalCompositeOperation: "readonly",
|
|
||||||
ImageBitmap: "readonly",
|
|
||||||
OffscreenCanvas: "readonly",
|
|
||||||
OffscreenCanvasRenderingContext2D: "readonly",
|
|
||||||
React: "readonly",
|
|
||||||
JSX: "readonly",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
"@typescript-eslint": tseslint,
|
|
||||||
"react-hooks": reactHooks,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
...tseslint.configs.recommended.rules,
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"warn",
|
|
||||||
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-explicit-any": "warn",
|
|
||||||
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
||||||
"prefer-const": "warn",
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"no-empty": "warn",
|
|
||||||
"no-case-declarations": "warn",
|
|
||||||
"react-hooks/rules-of-hooks": "warn",
|
|
||||||
"react-hooks/exhaustive-deps": "warn",
|
|
||||||
},
|
|
||||||
linterOptions: {
|
|
||||||
reportUnusedDisableDirectives: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
"dist/**",
|
|
||||||
"node_modules/**",
|
|
||||||
"*.config.js",
|
|
||||||
"*.config.ts",
|
|
||||||
"vite.config.ts",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<link rel="manifest" href="/manifest.json" />
|
|
||||||
<link rel="apple-touch-icon" href="/favicon.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="theme-color" content="#22c55e" />
|
|
||||||
<meta name="description" content="Professional browser-based graphic design editor - Create stunning visuals offline" />
|
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=DM+Sans:wght@400;500;700&family=Poppins:wght@300;400;500;600;700;800;900&family=Montserrat:wght@300;400;500;600;700;800;900&family=Playfair+Display:wght@400;500;600;700;800;900&family=Roboto:wght@300;400;500;700;900&family=Open+Sans:wght@300;400;600;700;800&family=Lato:wght@300;400;700;900&family=Oswald:wght@300;400;500;600;700&family=Bebas+Neue&family=Pacifico&family=Lobster&family=Dancing+Script:wght@400;700&family=Great+Vibes&display=swap" rel="stylesheet" />
|
|
||||||
<title>OpenReel Image - Professional Graphic Design Editor</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
<script>
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@openreel/image",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "tsc --noEmit && vite build",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"deploy": "wrangler pages deploy dist --project-name=openreel-image",
|
|
||||||
"deploy:preview": "wrangler pages deploy dist --project-name=openreel-image --branch=preview",
|
|
||||||
"test": "vitest",
|
|
||||||
"test:run": "vitest run",
|
|
||||||
"lint": "eslint src",
|
|
||||||
"typecheck": "tsc --noEmit",
|
|
||||||
"clean": "rm -rf dist node_modules/.vite"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@imgly/background-removal": "^1.7.0",
|
|
||||||
"@openreel/image-core": "workspace:*",
|
|
||||||
"@openreel/ui": "workspace:*",
|
|
||||||
"@radix-ui/react-context-menu": "^2.2.16",
|
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
||||||
"@radix-ui/react-popover": "^1.1.15",
|
|
||||||
"@radix-ui/react-select": "^2.2.6",
|
|
||||||
"@radix-ui/react-slider": "^1.3.6",
|
|
||||||
"@radix-ui/react-tabs": "^1.1.13",
|
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"framer-motion": "^12.23.24",
|
|
||||||
"lucide-react": "^0.555.0",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-dom": "^18.3.1",
|
|
||||||
"tailwind-merge": "^3.4.0",
|
|
||||||
"uuid": "^13.0.0",
|
|
||||||
"zod": "^4.4.3",
|
|
||||||
"zustand": "^4.5.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "^9.39.2",
|
|
||||||
"@testing-library/jest-dom": "^6.4.6",
|
|
||||||
"@testing-library/react": "^16.0.0",
|
|
||||||
"@types/react": "^18.3.3",
|
|
||||||
"@types/react-dom": "^18.3.0",
|
|
||||||
"@types/uuid": "^11.0.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
||||||
"@typescript-eslint/parser": "^8.53.0",
|
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
|
||||||
"autoprefixer": "^10.4.19",
|
|
||||||
"eslint": "^9.39.2",
|
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
|
||||||
"globals": "^17.0.0",
|
|
||||||
"jsdom": "^24.1.0",
|
|
||||||
"postcss": "^8.4.38",
|
|
||||||
"tailwindcss": "^3.4.4",
|
|
||||||
"tailwindcss-animate": "^1.0.7",
|
|
||||||
"typescript": "^5.4.5",
|
|
||||||
"vite": "^5.3.1",
|
|
||||||
"vitest": "^1.6.0",
|
|
||||||
"wrangler": "^3.114.17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export default {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue