---
sidebar_position: 7
sidebar_label: Live streams
---

# Live streams

One person presents live to everyone watching: the picture, a chat beside it, reactions, and the count of who watches. In `WordPlusRealtimeCalls`, on the same media servers as the browser: an app watches a stream a browser presents, and goes live itself.

## Who is who

Your server declares streams by pattern, as it declares rooms and calls ([live streams in depth](../apps/streams.md#who-is-who)):

**Node**

```ts
realtime.streams( 'show:{id}', ( user ) => ( user === ANN ? 'host' : 'viewer' ), { reactions: true } );
```

**PHP**

```php
$realtime->streams( 'show:{id}', fn ( $user ) => ANN === $user ? 'host' : 'viewer', [ 'reactions' => true ] );
```

**Python**

```python
realtime.streams("show:{id}", lambda user: "host" if user == ANN else "viewer", reactions=True)
```

The app asks the calls route of your server, as for calls: nothing more to add.

## Watching

```swift
import WordPlusRealtimeCalls

let show = try await rt.calls.stream("show:mugs")

let player = LiveStreamVideoView()
player.show(show)                       // the main picture of the stream as it changes, its sound plays by itself

show.onChange {                         // the state, the count, the chat, the pictures
    stateLabel.text = show.state == .live ? "Live now" : "Not live"
    countLabel.text = "\(show.viewers) watching"
}
show.onMessage { message in add(message.name, message.text) }
try await show.send("Hello!")
show.react(.heart)
show.leave()
```

- **`state`** is `idle`, `live` or `ended`. `media` is where the app is in the room of the stream: `off`, `connecting`, `live`, `reconnecting`.
- **The main picture** is the one the host chose: a shared screen, the picture from OBS, else the camera of the host. `show.tiles` has each picture, and `player.show(show, tile: identity)` draws one of them.
- **The chat:** `messages` (the history, oldest first), `pinned`, `canWrite`, `slow`. Each message has the app user id of who wrote it, nil for a visitor.
- **`onEnded`** says why the app is out of the stream: `left`, `banned`, `kicked`.

## Going live

The app of the host shows its camera, then goes live:

```swift
try await show.preview()                // the camera and microphone, in the same LiveStreamVideoView
try await show.setCamera(false)         // or setMicrophone(_:)
try await show.goLive()
try await show.end()
```

- **The camera and microphone** need `NSCameraUsageDescription` and `NSMicrophoneUsageDescription` in `Info.plist`, as for calls.
- **`goLive()`** with nothing on turns the camera and microphone on first.
- **What a role may not do** throws its code: `not_host`, `read_only`, `slow`, `timeout`.
- **From OBS:** `let (url, key) = try await show.ingest()`. `ingest(renew: true)` makes a new key and cuts off the old one.

## Keeping the chat

The host, and anyone the rule made `mod`: `deleteMessage(id)`, `pin(id)`, `unpin()`, `timeout(user, seconds:)` (from a minute to a day, they go on watching), `setSlow(seconds)`, `clearChat()`, `kick(user)` (out now, they may come back) and `ban(user)`.

The show card of the Example Shop watches the show of Ann from the app: [the example app](example.md).
