← back to console

DAI Control · API Reference

SCTE-35 ad-break orchestration via AWS MediaLive · YouTube SRT ingest testing

Channel: ml-jstest-lhr-chan (id 6636275) · Region: eu-west-2 · Endpoints: PID 500 SCTE-35

Overview

The console fires SCTE-35 ad-break markers into a live SRT stream produced by AWS MediaLive. The same MediaLive channel multiplexes the markers into 3 SRT outputs (1× listener for TAG MCM9000, 2× callers for YouTube SRT a+b). The frontend is a stateless local page; the local Python server proxies HTTP requests to aws medialive batch-update-schedule using the operator's AWS credentials.

   browser (index.html)
       │ fetch POST /api/dai/{in,out,force,timesignal,short}
       ▼
   local Python server (127.0.0.1:8080)
       │ subprocess: aws medialive batch-update-schedule
       ▼
   AWS MediaLive channel ml-jstest-lhr-chan (eu-west-2)
       │ injects SCTE-35 message on PID 500 in MPEG-TS
       ▼
   ┌────────────────┬──────────────────┬──────────────────┐
   │ SRT listener   │ SRT caller       │ SRT caller       │
   │ → TAG MCM9000  │ → YT a (copy=0)  │ → YT b (copy=1)  │
   └────────────────┴──────────────────┴──────────────────┘

Authentication check

The server has no own credentials. Every action runs aws sts get-caller-identity via the operator's local AWS environment (Leapp session, named profile, or env vars). Account is hard-coded to 396095212012 (EMX).

GET /api/auth
$ curl http://127.0.0.1:8080/api/auth
{
  "authenticated": true,
  "account": "396095212012",
  "expected": "396095212012",
  "region": "eu-west-2",
  "channel_id": "6636275"
}

Polled every 30 s by the frontend. Red pill if token expired.

Controls

ENTRA DAI — start ad break (cue-out)

Signals to all downstream receivers (TAG, YouTube) that the stream is entering an advertising window of 210 s. A 210-second duration is YouTube's standard slot length. The marker is emitted on PID 500 within ~4 s.

Request
POST /api/dai/in
Content-Type: application/json

{}
Response
{
  "success": true,
  "message": "OK",
  "splice_id": 1839472018,
  "type": "in"
}
Backend → AWS MediaLive call
aws medialive batch-update-schedule \
  --region eu-west-2 --channel-id 6636275 \
  --creates '{
    "ScheduleActions": [{
      "ActionName": "dai-in-1747844218-1839472018",
      "ScheduleActionStartSettings": {
        "ImmediateModeScheduleActionStartSettings": {}
      },
      "ScheduleActionSettings": {
        "Scte35SpliceInsertSettings": {
          "SpliceEventId": 1839472018,
          "Duration": 18900000
        }
      }
    }]
  }'

SCTE-35 semantics: splice_insert with out_of_network_indicator=1 and break_duration.duration=18,900,000 ticks (210 s in 90 kHz clock). SpliceEventId is a 32-bit uint generated server-side, used later to match the cue-in.

OUT (match) — end ad break, paired cue-in

Sends return_to_network referencing the same SpliceEventId as the most recent IN, signalling that the ad break terminates earlier than the declared duration. In our YouTube SRT testing this is currently ignored: YT honours the declared duration regardless.

Request
POST /api/dai/out
Content-Type: application/json

{ "splice_id": 1839472018 }
Response
{
  "success": true,
  "message": "OK",
  "splice_id": 1839472018,
  "type": "out_match"
}
Backend → AWS MediaLive call
aws medialive batch-update-schedule \
  --region eu-west-2 --channel-id 6636275 \
  --creates '{
    "ScheduleActions": [{
      "ActionName": "dai-out-1747844240-1839472018",
      "ScheduleActionStartSettings": {
        "ImmediateModeScheduleActionStartSettings": {}
      },
      "ScheduleActionSettings": {
        "Scte35ReturnToNetworkSettings": {
          "SpliceEventId": 1839472018
        }
      }
    }]
  }'

SCTE-35 semantics: splice_insert with out_of_network_indicator=0 sharing the same SpliceEventId. The pair (cue-out, cue-in) is the canonical way to bracket an ad break.

FORCE OUT — unpaired cue-in (new SpliceEventId)

Sends return_to_network with a brand new random SpliceEventId, decoupled from any preceding cue-out. Tests whether the receiver honours any return_to_network or only those that match an open ad break. Per the SCTE-35 spec this is technically out-of-spec; most receivers treat it as a no-op.

Request
POST /api/dai/force
Content-Type: application/json

{}
Response
{
  "success": true,
  "message": "OK",
  "splice_id": 871553902,
  "type": "out_force"
}
Backend → AWS MediaLive call
aws medialive batch-update-schedule \
  --region eu-west-2 --channel-id 6636275 \
  --creates '{
    "ScheduleActions": [{
      "ActionName": "dai-out-1747844260-871553902",
      "ScheduleActionStartSettings": {
        "ImmediateModeScheduleActionStartSettings": {}
      },
      "ScheduleActionSettings": {
        "Scte35ReturnToNetworkSettings": {
          "SpliceEventId": 871553902
        }
      }
    }]
  }'

TIME-SIGNAL OUT — Provider Ad End (segmentation_type_id 0x31)

Uses a completely different SCTE-35 command (time_signal with a segmentation_descriptor) rather than splice_insert. segmentation_type_id = 0x31 = Provider Advertisement End. Some platforms route ad-decision logic through segmentation descriptors instead of splice_insert pairs.

Request
POST /api/dai/timesignal
Content-Type: application/json

{}
Response
{
  "success": true,
  "message": "OK",
  "splice_id": 412658,
  "type": "out_ts"
}
Backend → AWS MediaLive call
aws medialive batch-update-schedule \
  --region eu-west-2 --channel-id 6636275 \
  --creates '{
    "ScheduleActions": [{
      "ActionName": "dai-ts-1747844275-412658",
      "ScheduleActionStartSettings": {
        "ImmediateModeScheduleActionStartSettings": {}
      },
      "ScheduleActionSettings": {
        "Scte35TimeSignalSettings": {
          "Scte35Descriptors": [{
            "Scte35DescriptorSettings": {
              "SegmentationDescriptorScte35DescriptorSettings": {
                "SegmentationCancelIndicator": "SEGMENTATION_EVENT_NOT_CANCELED",
                "SegmentationEventId": 412658,
                "SegmentationTypeId": 49,
                "SegmentationDuration": 0
              }
            }
          }]
        }
      }
    }]
  }'

Reference: SCTE-35 §10.3.3.1 Segmentation Type IDs: 0x30 Provider Advertisement Start · 0x31 Provider Advertisement End · 0x34 Distributor Advertisement Start · 0x35 Distributor Advertisement End.

SHORT BREAK — overlay cue-out with 1-second duration

Fires a new splice_insert cue-out with a tiny duration (1 s). Hypothesis: a receiver that has committed an ad slot to the previous 210 s cue-out might re-schedule the slot when a new cue-out arrives, effectively truncating the active break. Behaviour is platform-specific.

Request
POST /api/dai/short
Content-Type: application/json

{}
Response
{
  "success": true,
  "message": "OK",
  "splice_id": 2014567103,
  "type": "short"
}
Backend → AWS MediaLive call
aws medialive batch-update-schedule \
  --region eu-west-2 --channel-id 6636275 \
  --creates '{
    "ScheduleActions": [{
      "ActionName": "dai-in-1747844290-2014567103",
      "ScheduleActionStartSettings": {
        "ImmediateModeScheduleActionStartSettings": {}
      },
      "ScheduleActionSettings": {
        "Scte35SpliceInsertSettings": {
          "SpliceEventId": 2014567103,
          "Duration": 90000
        }
      }
    }]
  }'

Duration: 90000 = 1 second (1 × 90 kHz). Same payload schema as ENTRA DAI, only the duration differs.

MARK visible — client-side latency measurement

Does not call any API. Marks the wall-clock instant the operator visually observes a state change in the YouTube player (ad appeared / content returned). Used to compute the round-trip propagation delay from MediaLive emission to viewer-perceived event.

State-machine logic:

  • If IN fired and ad not yet marked visible → records tInVisible
  • If OUT fired and content not yet marked visible → records tOutVisible
  • Otherwise: no-op

Reset — clears cycle state

Client-side only. Zeroes all cycle timestamps and the cached SPLICE_ID. Does not send any cue-in to MediaLive; if a break is in flight upstream, it continues to its declared duration.

Observed receiver behaviour

Strategy TAG MCM9000 (broadcast monitor) YouTube Live SRT (a + b)
ENTRA DAI (cue-out 210 s)Detects "SCTE-35 Out" on PID 500Triggers ad break, 210 s countdown starts
OUT (match)Detects "SCTE-35 In" on PID 500Ignored — continues full 210 s
FORCE OUTDetects unpaired cue-inTo test
TIME-SIGNAL OUTDetects time_signal segmentationTo test
SHORT BREAKDetects new cue-out 1 sTo test (potential override behaviour)

Last update: 2026-05-21 · YouTube observation captured during live test with stream key ending …3v6m.

Notes for the YouTube team