Skip to content

SDI capture from a DekTec card

Sapsan takes the signal from an SDI port of a DekTec card. The card is driven not by the server itself but by a separate process, dektec_ingest — one per port; sapsan starts it, supervises it and reads ready frames from it.

The process boundary here is a requirement, not an implementation detail: a failure of the vendor SDK, linked into the media server, takes down not one stream but the whole server. The boundary costs one process per port, and in exchange sapsan's build carries neither the SDK nor a C++ shim.

Prerequisites

  • A DekTec card and its driver. Verified on the DTA-2178 (HD and 12G up to 3840×2160p50) and the DTA-2174B (HD).
  • The dektec-bridge package — it installs dektec_ingest and dektec_probe into PATH. Without it the input fails with the producer_unavailable cause and names the binary it looked for, instead of going silent until a timeout.
  • A licence that supports SDI.

The server creates the socket directory (/run/sapsan/sdi) itself; the systemd unit provides /run/sapsan through RuntimeDirectory. The input has no deployment parameters of its own.

Configuration

streams:
  - name: tv1
    inputs:
      - dektec:
          serial: 2174223653
          port: 1
          pixel_format: v210
          audio:
            - channels: [1, 2]
              lang: rus
            - channels: [5, 6]
              lang: eng
Parameter Description
serial the card serial number. The card is addressed by it and not by its index in the system: an index changes when cards are swapped and when the driver load order changes
port card port number, one-based
pixel_format uyvy422 (8 bit, default) or v210 (10 bit)
audio the audio track map, see below. An empty list means the producer default: one PCM stereo track from the first pair
ad_marks a track of raw SCTE-104 marks from VANC; enabled by default
peer_timeout_ms how long to wait for frames before declaring the source dead; the same value is the socket silence window. 30,000 by default; zero means the default, not "wait forever"

The unit of ownership is the pair "serial and port": the driver's exclusivity applies to a port. Two streams on one such pair are rejected by configuration, which names the stream already holding the port; different ports of the same card work independently and need no coordination.

Raw video is not playable: HLS, DASH and the archive stay empty until the stream has encoded tracks. To get them, enable the transcoder.

Audio tracks

A track's position in the audio list is its number in the stream (a1, a2, …). Each track is assembled from the listed SDI audio channels, and the order of channels in the list is the track's channel layout.

Field Description
channels SDI audio channel numbers, one-based, up to 16 (eight embedded pairs is the SDI limit)
sample_type pcm (default) or smpte337
lang track language per ISO 639-2

The language is declared, not derived: SDI carries no language descriptors, and the only party that knows the language is whoever laid the programmes out across the pairs. The kind of audio is declared as well and is not detected from the data — a pair carrying AC-3 today and silence tomorrow must not change the stream's track list.

Radio programmes in neighbouring pairs become tracks of the same stream, and can be listened to with the track selector on output. They do not become separate streams: a track has no name, no retention depth and no statistics of its own.

Uncompressed audio arrives as planar 32-bit (s32p) and is decoded with the declared channel count. A track wider than stereo is accepted and decoded honestly, but the encoder folds it down to stereo — that is the existing transcoder boundary. Multichannel audio survives intact only inside the SMPTE 337 wrapper, where nobody touches the bitstream.

AC-3 and E-AC-3 over SMPTE 337

A track declared as sample_type: smpte337 arrives as ready ac3 or eac3 frames: the producer strips the wrapper, and sapsan neither decodes nor repackages the audio. Only one variant is supported: SMPTE 337 encoded in two channels. Such a track must declare exactly two channels, or the configuration is rejected.

audio:
  - channels: [1, 2]
    lang: rus
  - channels: [3, 4]
    sample_type: smpte337
    lang: rus

Gaps in a compressed track — silence between packets — are legitimate: the input neither synthesises silence nor counts a gap as an error.

VANC ancillary data

The input parses the VANC bag of every frame and routes its contents:

  • Teletext and OP-47 subtitles — into the common captions pipeline, by the same path as OP-47 from ST 2110.
  • VBI teletext lines — the same destination, but from an SD source: in SD teletext rides in a blanking line, not in an ANC packet. It is assembled by the same function as OP-47.
  • SCTE-104 — into a separate application track, as raw packets stamped with the carrying frame. The input does not parse them and does not convert them into SCTE-35.
  • CDP (CEA-608/708) — parsed and counted, but not published as a track: the carrier for captions in the pipeline is SEI of the encoded output, and insertion there is not done yet. The counter exists so that "there are no captions in the signal" and "there is nowhere to deliver the captions" do not look the same from the outside.
  • Other DID/SDID pairs — counted, without flooding the log.

The mark track exists from the first frame, empty. Were it created on the first mark, packaging would reinitialise right at the start of the ad break; MPEG-TS works the same way, where the mark PID is declared in the table long before the first message.

Failures and recovery

Source liveness, failover to a backup and retry backoff are shared with every other protocol — the input runs no supervision of its own. It makes exactly two observations of its own: socket silence and a persistent "no signal" on the frames.

Cause What happened What to do
no_signal no detectable signal on the port check the cable and the source
device_busy the driver refused the port first check the port direction, then your own stuck process, and only then look for a foreign holder
producer_unavailable dektec_ingest is missing or will not start install the dektec-bridge package
device_error the producer exited with a non-zero code that has no dedicated meaning: unknown serial, a port without the required capability, a device setup failure the stage is named by its startup event in the log
producer_stuck the process exits neither on stdin close nor on SIGKILL a sign of a jammed card; deal with it at the host level
producer_killed the producer was killed by someone outside sapsan — the OOM killer, systemd, a person find out who terminated the process rather than fixing the stream
protocol_error datagrams arrive but none of them parses the versions of dektec-bridge and sapsan have diverged; they are rolled out together

There are no terminal failures here: after any of them the input tries to come up again, with a growing interval between attempts. A server that started before the card driver comes up on its own as soon as the driver appears; a busy port is released, a re-seated card is found.

Two cases are not counted as errors. A change of video standard is a routine producer restart: the pipeline reinitialises for the new self-description of the frames, and the error counter does not grow. A single bad frame — "late" or "duplicate" — does not break the stream.

Signal loss on the fly is a case of its own. The producer does not exit: it keeps delivering frames on schedule, repeating the last frame and silence, and to the liveness check a source with a cut cable looks perfectly healthy. So the input, seeing a persistent "no signal" level on the frames, stops feeding frames into the pipeline — otherwise a frozen frame would go on air until somebody intervened. From there the common machinery works: the source is declared dead, the backup takes over, a retry brings the capture back up.

Counters

The main question this input's statistics answer is where exactly the degradation is, and the three diagnoses differ by arithmetic:

Observation Diagnosis
datagram numbers break transport: the producer's queue or the socket
numbers are intact, video pts breaks the line
the process exited with a code the producer

The API v4 snapshot carries three quantities for the input: sdi_line_state (the current line state), sdi_producer_restarts_total (producer restarts) and sdi_boundary_loss_total (datagrams lost between the processes). Next to them is failure_cause: the current failure cause from the table above, which clears as soon as the failure is over — unlike the cumulative errors_*.

In the Prometheus metrics the same quantities live under the stream_input_sdi_ prefix, and with them the material for an incident review: truncated and unparsed datagrams, corrupt frame references, unknown VANC pairs, CDP counters and the producer's own per-cause breakdown. The diagnostic pair the two counters exist for: sdi_producer_datagram_dropped_total against sdi_boundary_loss_total — if they agree, the loss is in the producer's queue; if ours is larger, it is on the socket itself.

The extended detail is unlocked by the "extended counters" option, see Statistics.

What the input does not do

  • It does not send a signal into a card. The input only receives; the reverse direction — SDI output — is not implemented in sapsan yet.
  • It does not decode AC-3 or Dolby E into PCM.
  • It does not convert SCTE-104 into SCTE-35 and emits no marks into TS/HLS/DASH.
  • It does not publish a CEA-608/708 caption track.
  • It does not work with non-DekTec cards.
  • It does not watch card health: temperature, fan speed and the port layout are taken by a separate dektec_probe process and shown by the management system — in installations with mcaster, by its "Capture cards" section.

What next