WAW Media Talk to us

API documentation

Base URL https://waw-media.bulsan.site/v1. Everything is JSON over HTTPS except the frame stream, which is gRPC over HTTP/2. The OpenAPI 3.1 document lives at /v1/openapi.json and is regenerated on every release.

Authentication

Send a bearer token. Tokens are scoped per project and can be restricted to a single channel prefix.

curl https://waw-media.bulsan.site/v1/channels \
  -H "Authorization: Bearer wm_live_1f9c…"
Tokens starting with wm_test_ hit the sandbox: real encoders, synthetic egress, no invoice.

Channels

A channel is the long-lived object. It owns its stream key, its ladder and its manifest URLs; restarting a broadcast does not change any of them.

FieldTypeNotes
idstringStable, prefixed ch_
regionenumeu-waw, eu-fra, eu-ams
ladderstringPreset name or inline ladder object
stateenumidle · live · draining
redundancyboolSecond ingest in a paired region

Ingest endpoints

Each channel exposes SRT (caller or listener), RTMPS and WHIP. Contribution keys rotate on request without interrupting an active session — the old key stays valid for one reconnect window.

# SRT, caller mode
ffmpeg -re -i source.mkv -c copy -f mpegts \
  "srt://ingest.eu-waw.waw-media.bulsan.site:9000?streamid=ch_8f21c0d4:KEY"

Transcode ladders

Presets cover the common shapes; anything else is an inline ladder. Per-title analysis is on by default and typically shaves 18–26% off egress for talking-head content.

PresetRenditionsCodecs
web-720p360p / 540p / 720pH.264
web-1080p360p → 1080p, 5 stepsH.264 + AV1
event-2160p540p → 2160p, 7 stepsH.264 + AV1

Jobs & retries

Every transcode is a job with an idempotency key. Replaying a request with the same key returns the original job instead of starting a second one, so a retry storm costs nothing.

Jobs older than 30 days are pruned from the API but their output stays in storage until you delete the channel.

Manifests

LL-HLS by default with a 2-second part target; DASH is available on the same origin. Manifests can be signed with a short-lived token bound to a viewer IP prefix.

https://waw-media.bulsan.site/p/ch_8f21c0d4/index.m3u8
https://waw-media.bulsan.site/p/ch_8f21c0d4/manifest.mpd

Media stream (gRPC)

For consumers that want frames rather than segments, the stream service pushes decoded metadata and keyframe-aligned payloads over a single long-lived HTTP/2 connection. The service name on the wire is grpc-media-stream.

// media/stream/v1/stream.proto
service MediaStream {
  // Server-streaming; one message per frame group.
  rpc Subscribe(SubscribeRequest) returns (stream Frame);
  // Client-streaming acknowledgements, batched.
  rpc Ack(stream AckRequest) returns (AckSummary);
}

message SubscribeRequest {
  string channel_id = 1;
  uint64 from_sequence = 2;  // 0 = live edge
  bool   include_payload = 3;
}

message Frame {
  uint64 sequence = 1;
  int64  pts_us = 2;
  bool   keyframe = 3;
  bytes  payload = 4;
}

Connections are multiplexed: one HTTP/2 stream per channel subscription, all sharing a single TCP connection per client. Deadlines are honoured — set one, or the server closes idle subscriptions after 15 minutes.

Flow control is per subscription. If a consumer stops reading, the server buffers up to one GOP, then drops forward to the next keyframe and reports the gap in AckSummary.skipped. Sequence numbers are never reused.

Errors

CodeMeaningRetry
401Token missing, expired or out of scopeNo
409Channel busy — a session is already liveAfter drain
422Ladder rejected (bad rendition order)No
429Control-plane rate limit, 120 req/minBackoff
503Region draining, retry against the pairYes