> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Serve a video room as an HLS or DASH livestream with DVR

> Broadcast a scheduled video room to a large passive audience over HLS or MPEG-DASH from your CDN, with optional DVR time-shift, while interactive speakers stay on WebRTC.

WebRTC gives every participant sub-second interactivity, but one media-plane seat per viewer is the wrong tool for a 5,000-person all-hands. Orbit's broadcast egress renders the room once and writes standard streaming segments (an HLS `.m3u8` playlist or a DASH `.mpd` manifest) to object storage that any CDN fronts, so audience size stops being something your media plane scales for. This guide shows how to start, detach, and play back HLS and DASH egresses on a scheduled room, including the DVR window that lets late joiners rewind an in-progress stream.

This guide covers pull-or-push HLS/DASH distribution and scaling passive viewership. If you instead push a single composite feed to a third-party platform (YouTube, LinkedIn Live, a custom RTMP ingest), see the RTMP simulcast guide. The two compose: many town halls simulcast to a public platform and simultaneously serve a private CDN audience from this page's endpoints.

## HLS or DASH?

| | HLS | MPEG-DASH |
| - | - | - |
| Player reach | Native on every Apple platform (Safari, iOS, tvOS); hls.js everywhere else | Native on Android (ExoPlayer/Media3); dash.js or Shaka Player on the web |
| Latency | Governed by `segment_duration` (1–10 s, default 6); set `low_latency` for LL-HLS part-based playback | Same segmented model; latency tracks your segment length |
| DRM and SSAI | Fine where your CDN terminates FairPlay or Widevine | The format most Widevine/PlayReady and SSAI pipelines standardize on |

Pick HLS when Apple-native playback matters, or when you want the broadest player reach with the least client code: a plain HLS URL plays in `<video>` on iOS and Safari with no player library. Pick DASH when Android-native players (ExoPlayer/Media3, Android TV fleets) dominate your audience, or when your DRM/SSAI stack consumes MPEG-DASH manifests.

### When capping WebRTC participants is the right choice

A WebRTC join earns its keep for people who talk, react, or get spotlighted. If a viewer will never unmute, that seat is wasted media-plane capacity. The pattern that scales:

* Cap interactive WebRTC joins at speakers, moderators, and Q\&A panelists.
* Serve everyone else the HLS or DASH manifest from your CDN edge.

One composite egress feeds any number of passive viewers over plain HTTPS; the CDN absorbs the fan-out, not your tenant's media plane. The `egress/participants` endpoint (below) adds isolated per-speaker feeds on top when you need them.

## Prerequisites

* **A scheduled room id.** The egress endpoints hang off `/api/v1/video/rooms-scheduled/{id}`, so the room must be a scheduled room (the same object webinars and recurring events use), not an ad-hoc room.
* **A CDN target.** Two options:
  * Orbit-managed playback (default): omit `recording_storage` and read playback URLs from `GET /livestream` (below).
  * Your own origin: pass `recording_storage` with your S3 or GCS bucket, the same object the [bring-your-own recording storage](/guides/video-recording-byo-storage) guide uses, and front it with Fastly, CloudFront, or Cloudflare Stream. Segments and playlists upload to your bucket; your CDN serves them.
* **A playback policy.** Manifests and segments are ordinary HTTP objects. Enforce signed URLs, token auth, or geo rules at your CDN; the egress endpoints only write the media, they do not police who reads it.
* **Consent, where applicable.** On tenants with two-party recording consent, a broadcast egress persists replayable media, so the start call requires `recording_consent_receipt_id`, the same gate as recording and RTMP egress.
* **Your limits.** Each egress (and each ABR `variants` tier, which launches one composite egress per tier) counts against your tenant's concurrent egress ceiling. Read it from the scheduled-rooms limits endpoint (`listVideoRoomsScheduledLimits` in the [video API reference](/api-reference/endpoints/video)) before sizing a ladder.

## Start an HLS egress

Worked example: an all-hands for 5,000 passive viewers behind the corporate CDN, with an hour of DVR so people joining from the west coast can catch up.

```bash theme={null}
curl -X POST "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/hls" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "layout": "speaker",
    "segment_duration": 4,
    "dvr": true,
    "dvr_window": 3600
  }'
```

* `layout: "speaker"` foregrounds the active presenter in the composite, the usual choice for a presentation-style room.
* `segment_duration: 4` shortens the default 6 s segments, trading a little playlist churn for a shorter time-to-first-frame at the live edge.
* `dvr: true` with `dvr_window: 3600` keeps one hour of rewindable media behind the live edge. The window accepts 30 s to 4 h and defaults to 30 minutes.

The response returns the egress handle, an `EG_`-prefixed id. Store it: `DELETE /egress/hls/{egressId}` needs it later.

Two optional blocks worth knowing:

* **Low latency.** `"low_latency": true` switches the egress to LL-HLS (part-based playback) for sub-second glass-to-glass latency on compatible players; `part_duration` (0.2–1.0 s, default 0.5) sets the part target. Leave both off for ordinary segment-based HLS.
* **Adaptive bitrate.** `variants` takes a ladder of tiers, each with a unique `name`, `width`, `height`, and `video_bitrate_kbps`. Orbit launches one composite egress per tier and writes a master playlist, so a viewer on a weak connection steps down instead of re-buffering. Remember each tier counts against your egress limits.

If you see a 422, check the consent gate and the `variants` bounds (resolution and bitrate ranges match the recording output ranges, and tier names must be unique). A 503 means broadcast media is not yet configured for your tenant.

## Start a DASH egress

The DASH start call is the same shape with `/egress/dash`, and it accepts the same `layout`, `segment_duration`, `recording_storage`, consent receipt, and `variants` fields. DVR time-shift and LL-HLS are HLS-only; DASH players do their own timeshift buffering against the manifest, and DRM is configured at your packager/CDN, so the DASH body stays lean:

```bash theme={null}
curl -X POST "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/dash" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "layout": "speaker",
    "variants": [
      { "name": "1080p", "width": 1920, "height": 1080, "video_bitrate_kbps": 6000 },
      { "name": "720p",  "width": 1280, "height": 720,  "video_bitrate_kbps": 3000 },
      { "name": "480p",  "width": 854,  "height": 480,  "video_bitrate_kbps": 1200 }
    ]
  }'
```

Each tier becomes one `<Representation>` inside the manifest's `<AdaptationSet>`, so the ladder maps 1:1 to what ExoPlayer or Shaka sees. Omit `variants` for a single-encode manifest.

## Combine WebRTC speakers with a streamed audience

The full pattern for a large interactive session:

1. Speakers, moderators, and panelists join over WebRTC with ordinary participant tokens (see [video room access tokens](/guides/video-room-access-tokens)).
2. Start an HLS or DASH egress as above; the passive audience watches the manifest, never touching the RTC media plane.
3. If you also need isolated per-speaker media for post-production, a per-speaker compliance archive, or training clips, start a participant egress:

```bash theme={null}
curl -X POST "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/participants" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "mp4",
    "participant_identities": ["speaker-alice", "speaker-bob"]
  }'
```

`format` is `mp4` (default), `webm`, or `ogg` for audio-only speakers; omit `participant_identities` to capture every live participant. This endpoint records each speaker as their own isolated feed in addition to the composite; it is not the audience path. The audience path is always the HLS/DASH manifest.

## Detach an egress before the room ends

When you want to stop a broadcast early, hand the media off to a VOD pipeline, or tear down a ladder, delete by handle:

```bash theme={null}
# stop one HLS egress
curl -X DELETE "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/hls/$EGRESS_ID" \
  -H "Authorization: Bearer $ORBIT_API_KEY"

# stop one DASH egress
curl -X DELETE "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/dash/$EGRESS_ID" \
  -H "Authorization: Bearer $ORBIT_API_KEY"

# stop all per-participant egresses at once
curl -X DELETE "https://orbit.devotel.io/api/v1/video/rooms-scheduled/$ROOM_ID/egress/participants" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

Detach as soon as your downstream pipeline has the media it needs: a running egress keeps encoding and counts against your limits until it stops.

## Playback URLs and embedding

* `GET /api/v1/video/rooms-scheduled/{id}/livestream` returns the room's live-edge playback surface: the advertised HLS/DASH manifest URLs for the running egresses. Hand the manifest URL to any player, or put it behind your CDN's signed-URL policy first.
* `GET /api/v1/video/rooms-scheduled/{id}/livestream/dvr` returns the DVR playback surface pinned at the live edge. Add `?rewind_seconds=300` to get a `catch_up_url` five minutes behind the edge, or pass an absolute `start` (and optional `end`) as ISO 8601 timestamps. `rewind_seconds` and `start` are mutually exclusive, and seeks are clamped to your `dvr_window`.

Embedding choices by audience:

* **Passive viewers in your app or web page:** any HLS/DASH-capable player pointed at the manifest URL (native `<video>` on iOS and Safari, hls.js/ExoPlayer/Shaka elsewhere). Embed patterns are covered in the [video embed demo](/guides/developer-video-embed-demo).
* **Guests who need to be seen and heard:** that is a WebRTC join, not a stream; use the [WHEP guest player](/guides/whep-guest-player) for low-latency guest playback with an uplink.

Broadcast viewership feeds the same analytics pipeline as the rest of your video events; see [video engagement analytics](/guides/video-engagement-analytics) for reading it.

## Limits, costs, and DVR notes

* The scheduled-rooms limits endpoint (`listVideoRoomsScheduledLimits` in the [video API reference](/api-reference/endpoints/video)) reports your tenant's concurrent egress ceiling. Count one egress per running HLS/DASH start, plus one per `variants` tier, plus one per participant in a participant egress.
* `dvr_window` accepts 30 s to 4 h and defaults to 30 minutes. A deeper window keeps more segments in the playlist and costs proportionally more storage; size it to how long viewers actually arrive late, not to the full event.
* DVR and LL-HLS are HLS-side features. A DASH egress gives you the manifest and lets the player and packager handle timeshift and DRM.

## Related

* [Run video webinars](/guides/video-webinars)
* [Bring-your-own recording storage](/guides/video-recording-byo-storage)
* [WHEP guest player](/guides/whep-guest-player)
* [Video engagement analytics](/guides/video-engagement-analytics)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.