# SDI colour output for post-production — 4.2

Decision recorded 13 September 2026: treat SDI as a post-production output with
explicit receiver colour encoding, independently of the desktop preview. Jamie
approved raw DCI XYZ and mapped P3/Rec.709 RGB for projectors and reference monitors.

## Operator configuration

Stop playback, open **Config → Video I/O → Output**, choose the device and mode,
select a colour profile, image fitting and code range. Selections apply automatically. Configure
the receiver to match. The profile and range survive service restarts and switching
to protected display output. Selecting a profile does not calibrate the monitor.

| Profile / API value | Components and transfer | Receiver white |
| --- | --- | --- |
| DCI X′Y′Z′ / `xyz-dci` | Original SDR DCP encoded XYZ, gamma 2.6; transported on RGB lanes | Receiver interprets DCI XYZ |
| P3 RGB / `p3-dci` | P3 primaries, gamma 2.6 | DCI x=0.314, y=0.351 |
| P3 RGB / `p3-d65` | P3 primaries, gamma 2.6 | D65 |
| Rec.709 RGB / `rec709-g24` | Rec.709 primaries, gamma 2.4 | D65 |
| sRGB / `srgb` | Rec.709 primaries, sRGB piecewise transfer | D65 |
| Legacy / `legacy-display` | Existing 8-bit display conversion, compatibility default | Existing receiver setup |

“RGB” describes components, not a complete colour space. P3-D65 gamma 2.6 is
**not Display P3**. Rec.709 gamma 2.4 targets a reference monitor with ideal black;
it is not a BT.709 camera OETF or a full implementation of BT.1886 with adjustable
black level. The P3 definitions follow the [ICC colour encoding registry](https://registry.color.org/rgb-registry/dcip3).
The reference-monitor distinction is informed by [ITU-R BT.1886](https://www.itu.int/rec/R-REC-BT.1886-0-201103-I).

Mapped SDR DCP profiles support explicit 8-, 10- and 12-bit RGB 4:4:4.
Rec.709/sRGB video-range profiles also support 8-bit UYVY and 10-bit v210
YCbCr 4:2:2. XYZ supports 8-, 10- and 12-bit 4:4:4 with full range. Unsupported
combinations fail explicitly; the player does not choose a lower precision.
General media and event streams use the legacy profile; HDR DCPs reject SDR
precision profiles. Synthetic black follows the selected transport and cadence.

Single/dual/quad link and 3G Level A/B are explicit selections, or may retain the
device configuration. The application saves these choices and reapplies them
for playback, restoring the previous device configuration afterwards. An
explicit mode is checked when applying settings; automatic mode is checked once
content dimensions and rate are known.

Precision-mode desktop preview is off by default to prioritise SDI. Enable
**Show desktop preview** to derive an sRGB preview from the composed SDI frame.
Subtitle/HUD composition takes place before transport packing. Desktop controls
are drawn afterwards and are not projected. Disable HUD for a clean QC feed.

## Conversion contract

- At 12-bit output, XYZ preserves every decoded component code, including low bits. It
  is encoded X′Y′Z′, not linear XYZ. No matrix, transfer conversion, LUT, range
  remapping is applied. With **Native pixels**, no resampling is applied: the image
  is centred with zero-code padding where smaller; pixels outside a smaller output
  raster are cropped without resampling.
  **Fit to container** resizes proportionally with padding; **Fill container**
  resizes proportionally and crops the edges. These choices retain XYZ encoding
  but resample its component values, so resized output is not sample-exact RAW.
- Mapped profiles decode SDR DCI XYZ using the existing player normalization:
  `(code / 4095)^2.6 * 52.37 / 48`. A target XYZ-to-RGB matrix and explicit transfer
  curve follow. This is relative rendering with Y=48 cd/m² as reference; set the
  target monitor's physical luminance separately.
- There is **no chromatic adaptation**. A source white different from the target
  white does not automatically become neutral. There is no creative look LUT,
  HDR tone mapping or perceptual gamut compression. RGB outside [0,1] clips.
- Full range is 0–4095. Mapped RGB may instead use video range, black=256 and
  nominal white=3760; this implementation clips rather than retaining excursions.
  Raw XYZ and legacy profiles require full range.
- Precision profiles force full-resolution JPEG 2000 decoding and reject output
  cadence mismatch. Image fitting is independent of colour encoding. Precision
  profiles use 12-bit bilinear interpolation of encoded components for Fit/Fill;
  legacy display uses nearest-neighbour interpolation. Native pixels preserves
  spatial samples for any profile. For critical pixel evaluation, choose Native
  pixels and a container large enough for the DCP.
- The optional desktop preview comes from the primary composed SDI buffer.
  Preview conversion never reduces the precision of the SDI picture.

## Device handling and qualification

The player packs R12L, R10l, BGRA8, v210 or UYVY as explicitly selected, directly from the primary components. There is no ARGB8888 intermediate in the precision output path.
Blackmagic documents this layout in its [pixel format reference](https://sdk-doc.blackmagicdesign.com/decklink-sdk/DeckLinkAPI/enums/bmdpixelformat.html).

Before enabling precision output, the player verifies that device conversion is
disabled, applies the selected sampling for the session, and checks the exact mode/pixel-format
combination using `DoesSupportVideoMode`. Unsupported combinations fail without
8-bit or YCbCr fallback. The original 4:4:4 setting is restored on normal completion
and initialization failure. A killed process cannot guarantee restoration; check
Desktop Video Setup after an abnormal exit. Link count and 3G A/B can be selected
in the application or left at the device setting. See Blackmagic's [configuration reference](https://sdk-doc.blackmagicdesign.com/decklink-sdk/DeckLinkAPI/enums/bmddecklinkconfigurationid.html).

The player does not advertise P3 or XYZ as Rec.709 metadata or promise automatic
receiver colour negotiation. **Manually match the receiver's interpretation and
levels.** Submitted buffer codes, device API support and wire measurements are
separate evidence: the implementation has software validation, but is not yet
qualified as bit-exact at a physical SDI receiver.

Qualification on each supported card/link/receiver combination must include:

1. Capture 12-bit ramps and verify all low bits, range endpoints, RGB component
   ordering and XYZ code preservation with a suitable analyser.
2. Measure DCI-white and D65 grey/primary patches, gamma and clipping on the
   calibrated receiver. Check video/full-range agreement and any receiver LUT.
3. Exercise supported and unsupported raster/cadence/link combinations, verify
   explicit failures, and check restoration after stop and initialization failure.
4. Run a feature-length full-resolution DCP; measure decoder throughput, memory,
   dropped frames and thermal behavior. Full-resolution precision output trades
   the existing automatic resolution reduction for QC image fidelity.

## API and diagnostics

Existing authenticated `PUT /api/video-io/output` adds optional fields:

```json
{"device":34079493,"mode":4294967295,"colour_profile":"p3-d65","output_range":"video"}
```

Omitted colour fields retain the saved selection, including `mode: 0` when
switching to display. Unknown profiles, empty supplied fields and invalid range
combinations return 400; native refusal returns 409. Configuration is acknowledged
by the native player before persistence. The existing video output settings use
separate database entries; a database write failure is returned to the caller and
must be resolved before relying on restart persistence.

`GET /api/video-io/devices` includes `colour_profile` and `output_range` as
**configured** values. These are configured values; actual playback diagnostics are in the telemetry API. Native logs report `sdi.output.colour_profile`,
`code_range`, `pixel_format`, `bit_depth`, `sampling`, receiver selection, white
adaptation and spatial fit. These are submitted-format diagnostics, not receiver
measurements. New CLI options are `--sdi-colour-profile` and `--sdi-output-range`;
service defaults can use `DCP_PLAYER_SDI_COLOUR_PROFILE` and
`DCP_PLAYER_SDI_OUTPUT_RANGE`, with saved configuration taking precedence.

## Validation in this change

Automated tests cover all 4096 raw codes, golden R12L bytes and round trips,
profile/range rejection, black/white/grey reference values, a P3 primary, malformed
frames, native acknowledgement/rejection, persistence and display switching.
Physical DeckLink, projector and reference-monitor qualification remains required.

The development build passed the full Go suite, frontend type/accessibility
checks (zero errors/warnings), native compilation with the DeckLink SDK, and the
colour tests under AddressSanitizer and UndefinedBehaviorSanitizer. A 4.2.0 amd64
package was built; this change has not been deployed or hardware-qualified.

A five-frame synthetic 2048×1080 conversion/packing microbenchmark on the build
host measured about 15 ms/frame for raw XYZ and 81–83 ms/frame for mapped profiles
on one thread. It excludes JPEG 2000 decoding, preview, SDI fitting/scheduling and
I/O; these numbers are not playback frame-rate guarantees. Conversion runs in the
existing parallel decode workers. The measurement prompted removal of redundant
transfer/rounding work, retaining the same reference-test results.

## 4.2.1 API additions

`PUT /api/video-io/output` and `GET /api/video-io/devices` additionally carry
`bit_depth` (8/10/12), `sampling` (`444`/`422`), `link`
(`device`/`single`/`dual`/`quad`), `level` (`device`/`A`/`B`) and
`desktop_preview` (boolean). Omitted fields retain the saved selection.
CLI equivalents are `--sdi-bit-depth`, `--sdi-sampling`, `--sdi-link`,
`--sdi-level`, and `--sdi-preview`.

The 4.2.0 validation description above is a historical checkpoint. The 4.2.1
hardware/software results are maintained in [the investigation record](cobalt-4.2-investigation.md).

## 4.2.4: RAW containers and single-cable 3G receivers

Video I/O → Output now exposes **SDI cable connection** beside the output mode.
Choose **Single cable — 3G-SDI maximum** for a receiver limited to one 3G link.
`Use Desktop Video setting` inherits the device preference; cable count is not
physically detected, and an inherited dual-link setting needs two cables.
The separate unrestricted single-cable option remains available for 6G/12G
hardware. Match the 3G Level A/B setting to the receiver.

The new persisted/API/CLI `link` value is `single-3g`. It requests single link
from DeckLink and restricts mode selection to an HD/2K 3G envelope, including
at most 30 fps for RGB/XYZ 4:4:4 at 1080 lines. The exact pixel format must also
pass the SDK's single-link support check. This preset is a configured ceiling,
not measured cable bandwidth or receiver capability detection.

Historical 4.2.4 behaviour (superseded by Native centre cropping in 4.2.6):
RAW Auto chose the smallest progressive container that fits the native
raster at its exact cadence. A 1998×1080 Flat CPL therefore selects 2048×1080,
not 1920×1080. A 4096-wide CPL needs a 4096-wide container: it cannot play as
native RAW through the 3G preset or a fixed 2K mode. Precision reel layouts are
checked before a consecutive-CPL playlist starts. Mapped profiles can resize;
RAW never silently crops or downsamples. Selecting RAW in the UI selects and
locks 12-bit, 4:4:4 and full range.

Desktop preview is optional and consumes additional CPU. With it disabled,
no playback picture is shown on the desktop. With it enabled, the composed
SDI frame is converted to sRGB for desktop monitoring; RAW wire sample values
remain XYZ carried in the RGB component slots.

Local DeckLink 4K Pro sender test on 14 September 2026: two 1998×1080/24 CPL
items in RAW Auto with `single-3g` selected 2Kp24 DCI/R12L, completed 51 SDI
frames, reported zero late SDI frames, and reused the output across the boundary.
The short test also reported source-frame drops; this is not sustained real-time
performance qualification. No receiver lock, captured pixel values or audible
channel checks were available. Logs: `/tmp/raw-xyz-single3g-hardware.log` and
`/tmp/raw-xyz-single3g-telemetry.json` on the development workstation.

Regression tests cover Flat/Scope/4K native containers, cadence mismatch,
fixed-mode rejection, 3G limits, mapped resizing, legacy Auto behaviour and
preservation of all 4096 RAW code values. The frontend and Go SDI tests also pass.

[Telestream’s published format table](https://www.telestream.net/video/prism-tech-spec.htm)
lists 2K RGB 4:4:4 10/12-bit at 24/25/30 fps for 3G-SDI. This establishes a
format family, not support on an unknown receiving card.

Blackmagic documents cable configuration, RGB/YCbCr and 3G mapping in the
[Desktop Video manual](https://documents.blackmagicdesign.com/UserManuals/DesktopVideoManual.pdf).
The SDK exposes explicit [single/dual/quad link support flags](https://sdk-doc.blackmagicdesign.com/decklink-sdk/DeckLinkAPI/enums/bmdsupportedvideomodeflags.html).
Media Express can convert between RGB sources and YUV capture codecs, so a
codec selection alone does not prove signal compatibility. For DPX from an RGB
source, its documented “capture from an RGB source” option applies. Receiver
model and project format must still be checked before claiming RAW capture
compatibility.

## Automatic configuration saving

Video I/O applies and saves each output selection while stopped, after native
validation. Selecting a mode enables SDI; the Enable SDI output checkbox disables
it in favour of desktop output. There is no separate Apply or protected-display
button. Failed requests refresh the accepted settings and show the error.
Coupled colour settings are submitted together (for example RAW selects 12-bit,
4:4:4, full range). Controls remain locked during playback.

DDL2/DDL3 display enrollment produces a warning here. Enrollment does not by
itself forbid DDL1 material: the content's actual DDL requirement is enforced at
playback. DDL2/DDL3 content fails on SDI without automatically changing outputs.

## Descriptive Video I/O selectors

Cable connection, 3G mapping, colour encoding, precision, component sampling
and code range now open option dialogs with explanations and selected markers.
Card/profile restrictions remain visible as unavailable choices. Selecting an
option retains automatic validation/saving; Cancel changes nothing. Buttons wrap
their labels and grow vertically, and dialogs scroll their options within the
viewport. Layouts were reviewed in a browser at 1000 pixels wide.

## Image calibration limitation found during SDI investigation

The current native image-calibration handler draws to the SDL desktop renderer
only. It does not submit a frame to DeckLink, even when SDI output is selected.
The calibration page now states this explicitly. An acknowledged calibration
command is not evidence of an SDI calibration signal.

A separate fixed-1080p24 legacy-output test on 14 September completed two media
items, reused the output, and reported single-link 4:2:2 BGRA8 with 16 embedded
channels and zero late SDI frames. It does not establish receiver lock, picture
or audible channels. Test records: `/tmp/sdi-current-fixed-test.log` and
`/tmp/sdi-current-fixed-telemetry.json` on the development workstation.

## 4.2.5 — independent image fitting

Decision, 14 September 2026: separate XYZ encoding from native raster preservation.
For the 1998×1080 CPL and a 1080p24 receiver, select **DCI X′Y′Z′**, **Fit to
container**, **1080p24** and **Single cable — 3G-SDI maximum**. The fitted image is
1920×1038, centred in 1920×1080 with 21 black rows above and below. This avoids
requiring a 2K capture mode; the receiver must still accept the selected 4:4:4 transport precision
and interpret the components as XYZ. It does not make an RGB-only or 4:2:2-only
receiver XYZ-compatible. The source and output frame rates must match.

API `image_fit` accepts `native`, `fit`, `fill` or `auto`; CLI uses
`--sdi-image-fit`. Settings persist across restarts and switching output targets.
Existing saved configurations default to `auto`, preserving native pixels for
XYZ and fitting for other profiles. The UI displays the resolved choice and
saves an explicit value so colour changes retain the operator's fitting choice.

Validation: component interpolation, exact native copying/padding, fit/crop
geometry, HD mode selection and cadence rejection have automated coverage.
A local DeckLink test accepted this CPL in XYZ/1080p24 across two items with no
transport change or late completions. Source-frame drops were observed; this is
not real-time performance or receiver capture qualification.

## 4.2.6 — native centre cropping

Decision, 14 September 2026: Native pixels must play larger sources by taking
the centre of the image, never resizing it. 1998×1080 into 1920×1080 removes
39 columns per side. 4096×2160 into HD takes the central 1920×1080 region,
removing 1088 columns per side and 540 rows above and below. Smaller dimensions
receive black padding; cropping and padding may occur together. An odd excess
pixel is removed from the right or bottom. Retained XYZ sample codes are exact.
Auto prefers a container holding the entire source, otherwise the container
retaining the largest centre region within the configured link limit. Output
cadence and device/transport validation remain enforced. Fit still resizes the
whole image; Fill still resizes and then crops.

## 4.2.7 — colour encoding independent of precision

XYZ and mapped P3/Rec.709/sRGB profiles support explicit 8-, 10- and 12-bit
RGB component output. XYZ remains full range and 4:4:4; it is not converted
to Rec.709 to obtain lower precision. The 12-bit composed buffer is rounded
at transport packing: 10-bit uses min(1023, floor((code+2)/4)); 8-bit uses
min(255, floor((code+8)/16)). This deterministic quantisation uses no dither.
All 4096 codes and component ordering are covered by transport tests.
Only 12-bit native XYZ preserves retained source codes exactly.

YCbCr remains available for Rec.709/sRGB in video range at 8 or 10 bits.
P3 and XYZ are carried in RGB slots and require receiver interpretation.
Lower host precision does not guarantee a lower SDI wire rate. The selected
raster, cadence, sampling and link must still pass the sender's SDK check.
There is no silent precision fallback or claim that sender acceptance verifies
the receiver.

The service saves `receiver_max_bits` (0=unspecified, or 8/10/12) separately
from output precision. This is an operator-configured ceiling, not detection;
it rejects excessive precision before issuing a native output command.
Select the desired lower output precision before lowering the receiver limit.
It survives disabling SDI and service restart. CLI users select output bit depth
directly; the receiver ceiling is a web/service policy. Other receiver limits
remain the explicit signal-format, sampling and cable choices.

For DeckLink SDI 4K testing: 1080p24, XYZ, 10-bit, RGB 4:4:4, full range,
single-cable 3G, Level B; Media Express must select 1080p24 and capture from
an RGB source. This is a compatibility test target, not verified receiver
capture. The configured output summary shows precision, sampling, encoding,
range, mode and connection; the existing SDI HUD reports the active output.

## 4.2.8 beta handoff

See [SDI beta qualification](sdi-beta-qualification.md) for observed receiver
results, unresolved RGB signal lock and precision-performance limitations,
reference configurations and the evidence requested from beta testers.
