Metadata-Version: 2.4
Name: pydecklink
Version: 1.0.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 :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
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
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_*`.

**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`, and the `ConfigurationID`, `AttributeID`
and `StatusID` identifier sets the config, attribute and status 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).
