Metadata-Version: 2.4
Name: pydecklink
Version: 1.1.0
Summary: Python bindings for Blackmagic DeckLink SDK
Keywords: decklink,blackmagic,sdi,video-capture,broadcast
Author: Fuse Technical Group
License-Expression: BSD-3-Clause
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Multimedia :: Video :: Capture
Classifier: Typing :: Typed
Project-URL: Homepage, https://github.com/Fuse-Technical-Group/pydecklink
Project-URL: Repository, https://github.com/Fuse-Technical-Group/pydecklink
Project-URL: Issues, https://github.com/Fuse-Technical-Group/pydecklink/issues
Requires-Python: >=3.12
Requires-Dist: numpy>=1.26
Requires-Dist: pypixelpack<1,>=0.2
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: psutil>=5.9; extra == "test"
Provides-Extra: cuda-examples
Requires-Dist: cuda-python>=12; extra == "cuda-examples"
Description-Content-Type: text/markdown

# pydecklink

Python bindings for the [Blackmagic DeckLink SDK](https://www.blackmagicdesign.com/developer/product/capture-and-playback),
exposing the capture and scheduled playback APIs via CPU buffers (numpy),
and the network surface of the DeckLink IP cards that carry SMPTE ST 2110.

## Requirements

- **Linux**, **macOS**, or **Windows** with
  [Blackmagic Desktop Video](https://www.blackmagicdesign.com/support/family/capture-and-playback)
  16.0 or later installed — the binding is built against the 16.0 SDK
  headers, whose interfaces an older runtime does not serve
- Blackmagic DeckLink hardware
- Python 3.12+

## Install

```bash
uv pip install pydecklink
```

Prebuilt wheels ship for Linux (manylinux x86_64), macOS, and Windows.
Building from source requires a C++ toolchain — see
[CONTRIBUTING.md](CONTRIBUTING.md).

## Usage

```python
import pydecklink

# Desktop Video runtime version
print(pydecklink.api_version().string)

# Enumerate DeckLink devices
for info in pydecklink.list_devices():
    print(f"{info.index}: {info.model_name}")

# Display modes a device can output
dev = pydecklink.Device(0)
for m in dev.list_output_modes():
    fps = pydecklink.get_mode_fps(m.mode)
    print(f"{m.name}: {m.width}x{m.height} @ {fps:.2f}")
```

## Examples

The [`examples/`](examples) directory in the repo contains runnable scripts:

| Script | What it does |
|---|---|
| `passthrough.py` | Zero-copy SDI capture → playout loop. |
| `cuda_passthrough.py` | Canonical SDI → CUDA kernel → SDI recipe (drop in your own kernel callable). |
| `cuda_loopback_latency.py` | Fingerprint loopback benchmark for end-to-end latency. |
| `cuda_register_pinned.py` | Register CUDA pinned memory for the H2D capture path. |
| `detect_signals.py` | Walk all inputs, report which carry an active signal. |
| `dump_topology.py` | Print each device's identity and profile attributes. |

CUDA examples need the `cuda-examples` extra
(`uv pip install "pydecklink[cuda-examples]"`).

## API

The package ships type stubs (`pydecklink/_bindings.pyi`) and a `py.typed`
marker, so editors and `mypy` see the full typed surface. Key entry points:

**Device discovery**

- `list_devices() -> list[DeviceInfo]`, `device_count() -> int`
- `api_version() -> APIVersion` — Desktop Video runtime version
- `connector_label(device) -> str | None` — physical SDI port label

**Display-mode helpers**

- `get_mode_width(mode)`, `get_mode_height(mode)`, `get_mode_fps(mode)`
- `get_mode_frame_duration(mode)`, `get_frame_bytes(mode, pixel_format)`,
  `get_row_bytes(pixel_format, width)`

**`Device`** — open a card with `Device(index)`, then:

- *Capture*: `enable_video_input(...)`, `start_streams()`, and
  `pop_capture_frame()` (copying) or `pop_capture_frame_ref()` (zero-copy).
- *Scheduled playback*: `enable_video_output(...)`, `create_frame_pool(...)`,
  `acquire_output_frame()`, `schedule_output_frame(...)`,
  `start_scheduled_playback(...)`.
- *Zero-copy passthrough*: `schedule_capture_frame(...)` forwards a captured
  frame straight to output with no memcpy.

**DeckLink IP** — the card runs its own IP stack, so the host has no network
device for its media port and the address is set through the SDK or not at
all. Each Ethernet connector has its own address:
`ConfigurationID.ConfigParamEthernet*` covers a connector's address, subnet,
gateway and the multicast group each stream sends to, and
`StatusID.ParamEthernet*` reports what that connector resolved and
negotiated. Reach them with the `*_with_param` accessors, passing the
connector's zero-based index; `AttributeID.NumberOfEthernetConnectors`
counts them. `ConfigurationID.ConfigEthernetPTP*` covers the device-wide
PTP domain, priorities and `FollowerOnly`. Addresses are dotted-quad
**strings**, since `set_config_int` on one answers `E_INVALIDARG`:

```python
cfg, status = pydecklink.ConfigurationID, pydecklink.StatusID
dev.set_config_string_with_param(
    cfg.ConfigParamEthernetStaticLocalIPAddress, 1, "192.0.2.40"
)
dev.get_status_int_with_param(status.ParamEthernetLink, 1)
```

`StatisticID` reads PTP lock, temperature, per-connector packet counts and
the optical module's readings through `get_statistic_*`.

Each sub-device's streams are `IPFlow`s, one per essence and direction,
from `dev.ip_extensions`. An output flow's status reads the SDP the card
offers; an input flow takes the SDP of the peer it receives, and `enable()`
starts it. The card answers success to an SDP it rejects and keeps the
previous one, so read the setting back:

```python
D, T, S = pydecklink.IPFlowDirection, pydecklink.IPFlowType, pydecklink.IPFlowSettingID
video_in = next(
    f for f in dev.ip_extensions.get_ip_flows()
    if f.direction == D.Input and f.type == T.Video
)
video_in.set_setting_string(S.PeerSDP, peer_sdp)
assert video_in.get_setting_string(S.PeerSDP) == peer_sdp
video_in.enable()
```

**Frames** — `CaptureFrame`, `CaptureFrameRef` (zero-copy), and `MutableFrame`
expose pixel data as a numpy array via `.data`, alongside `.width`,
`.height`, `.row_bytes`.

**Enums** — `DisplayMode`, `PixelFormat`, `VideoConnection`, `VideoInputFlag`,
`VideoOutputFlag`, `FieldDominance`, `IPFlowDirection`, `IPFlowType`, and
the `ConfigurationID`, `AttributeID`, `StatusID` and `IPFlow*ID` identifier
sets the config, attribute, status and flow accessors take.

**Custom memory** — `VideoBufferAllocator` / `VideoBufferAllocatorProvider`
back capture and playback with caller-owned buffers (e.g. CUDA pinned memory)
for direct GPU DMA. **Connector profiles** — `ProfileManager`, `Profile`,
and `ProfileID` switch a card's duplex/sub-device layout.

## License

BSD-3-Clause — see [LICENSE](LICENSE).
