---
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 `cloud.wordplus:realtime-calls`, 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

```kotlin
import cloud.wordplus.realtime.calls.LiveStreamVideoView
import cloud.wordplus.realtime.calls.calls
import cloud.wordplus.realtime.calls.stream

// In a coroutine on the main thread, such as one of lifecycleScope.
val show = rt.calls(context).stream("show:mugs")

val player = LiveStreamVideoView(context)
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
    state.text = if (show.state == LiveStreamState.LIVE) "Live now" else "Not live"
    count.text = "${show.viewers} watching"
}
show.onMessage { message -> add(message.name, message.text) }
show.send("Hello!")
show.react(LiveStreamReaction.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. Call `player.release()` once the view leaves the screen for good.
- **The chat:** `messages` (the history, oldest first), `pinned`, `canWrite`, `slow`. Each message has the app user id of who wrote it, null for a visitor.
- **`onEnded`** says why the app is out of the stream: `left`, `banned`, `kicked`.

In Compose, put the player in an `AndroidView`, and read the state of the stream from a counter that `onChange` raises, as the example app does.

## Going live

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

```kotlin
show.preview()                          // the camera and microphone, in the same LiveStreamVideoView
show.setCamera(false)                   // or setMicrophone(on)
show.goLive()
show.end()
```

- **The camera and microphone** need `CAMERA` and `RECORD_AUDIO`, asked for at runtime before `preview()`, as for calls.
- **`goLive()`** with nothing on turns the camera and microphone on first.
- **What a role may not do** throws a `CallException` with its code: `not_host`, `read_only`, `slow`, `timeout`.
- **From OBS:** `val (url, key) = 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).
