Skip to content

Commit

Permalink
Adding Experience Composer (#43)
Browse files Browse the repository at this point in the history
* adding EC

* cleaning up

* adding composed view to rtc

* getting rid of brand logo when rounded

* updating readme

* updating readme

* updating readme

* updating readme

* adding images

* updating readme

* updating readme with images

* updating readme

* updating readme

* updating images

* updating readme

* updating readme

* tackling pr comments

* tackling pr comments

Co-authored-by: Javier Molina <[email protected]>
Co-authored-by: Javier Molina <[email protected]>
  • Loading branch information
3 people authored Nov 9, 2022
1 parent c6f334b commit 9c1ca40
Show file tree
Hide file tree
Showing 18 changed files with 2,617 additions and 443 deletions.
45 changes: 38 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ to a single HLS stream that can be accessed from an HLS player. The expected lat
is 10-15 seconds and for low latency HLS is shorter. The host can select different options to start the broadcast (Full HD, Low latency and DVR).
The viewers can move back and forth from the HLS viewer view to the WebRTC view.

Instead of the regular Broadcast view, the Host can decide to compose a custom view with all the published streams in the session. This is done through the [Experience Composer API](https://www.tokbox.com/developer/guides/experience-composer/) which allows publishing a custom application view with your own layout as a stream into the session. The host has additional controls to apply a custom background to the composed view and round the corners of the video tiles. If a background option is selected, an Experience Composer stream will be published into the session, otherwise a regular broadcast will be composed.

**NOTE**: The price for a Experience Composer stream differs from a regular stream. Check [this article](https://video-api.support.vonage.com/hc/en-us/articles/6714156901780-Experience-Composer-Activation-and-Pricing) for further pricing information

You can configure and run this sample app within just a few minutes!

This guide has the following sections:
Expand All @@ -46,9 +50,9 @@ To be prepared to develop your Video API Broadcast app:
1. Review the [OpenTok.js](https://tokbox.com/developer/sdks/js/) requirements.
2. Your app will need an Video API **API Key** and **API Secret**, which you can get from
the [OpenTok Developer Dashboard](https://dashboard.tokbox.com/). Set the API Key and
API Secret in [config.json](./config.json).

To run the Video API Broadcast Sample App, run the following commands:
API Secret along with your **production_url** in [config.json](./config.json).
3. Enable Experience Composer in the [account Portal](https://tokbox.com/account)
To run the Video API Broadcast Sample App, run the following commands:

```bash
npm i
Expand Down Expand Up @@ -95,6 +99,8 @@ the [OpenTok.js Reference](https://tokbox.com/developer/sdks/js/reference/).
- [Guest](#guest)
- [Viewer](#viewer)
- [Host](#host)
- [HLS Viewer](#hls-viewer)
- [Experience Composer](#experience-composer)

_**NOTE:** The sample app contains logic used for logging. This is used to submit anonymous usage data for internal Vonage purposes only. We request that you do not modify or remove any logging code in your use of this sample application._

Expand All @@ -119,9 +125,13 @@ sample app yourself. This allows you to customize the app as desired.
control whether guests are broadcasting, though the host does have a moderator token that
can be used for that purpose.

- **[viewer.js](./public/js/viewer.js)**: Viewers can view the live WebRTC stream.
- **[viewer.js](./public/js/viewer.js)**: Viewers can view the live WebRTC stream and switch to the Guest and HLS views.

- **[hls-viewer.js](./public/js/hls-viewer.js)**: HLS Viewers can only view the broadcast and switch to the viewer view.

- **[viewer.js](./public/js/hls-viewer.js)**: HLS Viewers can only view the broadcast.
- **[hls-viewer.js](./public/js/hls-viewer.js)**: HLS Viewers can only view the broadcast.

- **[ec.js](./public/js/hls-viewer.js)**: This page defines the code that the Experience Composer instance needs to execute.

- **[CSS files](./public/css)**: Defines the client UI style.

Expand Down Expand Up @@ -151,7 +161,7 @@ route is configured in server.js:
app.get('/host', async (req, res) => {
const roomName = req.query.room;
try {
const credentials = await giveMeCredentials('host', roomName);
const credentials = await generateCredentials('host', roomName);
res.render('pages/host', {
credentials: JSON.stringify(credentials),
});
Expand Down Expand Up @@ -188,10 +198,13 @@ The credentials are then retrieved in [host.js](./public/js/host.js) and used to
});
};


```

When the web page is loaded, those credentials are retrieved from the HTML and are used to initialize the session.

The logic needed to start and stop the Experience Composer stream is defined in [opentok-api.js](./services/opentok-api.js) (`createRender` and `deleteRender` respectively). At the time of updating this sample application, the nodeJS SDK does not have support for Experience Composer, so the [REST API](https://www.dev.tokbox.com/developer/rest/#starting_experience_composer) will be used. The `createRender` function creates an Experience Composer instance that will navigate to **[ec.js](./public/js/hls-viewer.js)** and publish a new stream named `EC` into the session.

### Guest

The functions in [guest.js](./public/js/guest.js) retrieve the credentials from the HTML,
Expand All @@ -201,7 +214,11 @@ subscribe to the host stream and other guest streams, and publish audio and vide

The functions in [viewer.js](./public/js/hls-viewer.js) check whether the broadcast is active or not. The HLS viewer can also move to the Viewer view (WebRTC session). Your application is responsible to let the HLS viewers when the HLS stream has started, this could be via WSS or any other way. For simplicity, this sample app has a button that checks the server for the broadcast URL.

Note: The Vonage Video API does not support the `#EXT-X-ENDLIST` tag for HLS streams as stated in the [Knowlegdebase page](https://tokbox.com/developer/guides/broadcast/live-streaming/#live-streaming-known-issues). Thereore, your application logic needs to update the HLS player once the stream is over.
**NOTE**: The price for an Experience Composer stream differs from a regular stream. Check [this article](https://video-api.support.vonage.com/hc/en-us/articles/6714156901780-Experience-Composer-Activation-and-Pricing) for further pricing information.

### Experience Composer

The code in [ec.js](./public/js/ec.js) will connect the Experience Composer instance to the session, subscribe to all streams but itself and the screen share stream from the Host and customise the appearance of the page. The result of the interaction with this code, that is, what is visible on this page, will be published as a new stream into the session (see logic on `opentok-api.js`).

### Viewer

Expand Down Expand Up @@ -274,6 +291,20 @@ which invokes the [Video API Broadcast API](https://tokbox.com/developer/rest/#s
endpoint, which terminates the CDN stream. This is a recommended best practice, as the default
is that broadcasts remain active until a 120-minute timeout period has completed.
## Screenshots
### Host View
![Host-view](https://github.com/nexmo-se/broadcast-sample-app/blob/main/public/images/host.jpg?raw=true)
### WebRTC Viewer
![Host-view](https://github.com/nexmo-se/broadcast-sample-app/blob/main/public/images/webrtc.jpg?raw=true)
### HLS Viewer
![Host-view](https://github.com/nexmo-se/broadcast-sample-app/blob/main/public/images/hls.jpg?raw=true)
## Development and Contributing
Interested in contributing? We :heart: pull requests! See the [Contribution](CONTRIBUTING.md) guidelines.
Expand Down
9 changes: 4 additions & 5 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@
"website": "https://vonage.com",
"repository": "https://github.com/opentok/broadcast-sample-app",
"image": "https://assets.tokbox.com/img/vonage/Vonage_VideoAPI_black.svg",
"keywords": [
"vonage",
"broadcast",
"javascript"
],
"keywords": ["vonage", "broadcast", "javascript"],
"env": {
"apiKey": {
"description": "Vonage Video API Project API Key"
},
"apiSecret": {
"description": "Vonage Video API Project API Secret"
},
"production_url": {
"description": "Your production URL"
}
}
}
81 changes: 75 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ app.get('/guest', async (req, res) => {
}
});

app.get('/ec', async (req, res) => {
try {
const roomName = req.query.room;
const credentials = await generateCredentials('guest', roomName);
res.render('pages/ec', {
credentials: JSON.stringify(credentials),
});
} catch (e) {
res.status(500).send(error);
}
});

app.get('/broadcast/:room', (req, res) => {
const { room } = req.params;

Expand All @@ -95,18 +107,14 @@ app.get('/broadcast/:room', (req, res) => {
}
});

app.get('*', (req, res) => {
res.redirect('/viewer');
});

/*
* API Endpoints
*/
app.post('/broadcast/start', (req, res) => {
const { rtmp, lowLatency, fhd, dvr, sessionId } = req.body;
const { rtmp, lowLatency, fhd, dvr, sessionId, streamMode } = req.body;

opentok
.startBroadcast(rtmp, lowLatency, fhd, dvr, sessionId)
.startBroadcast(rtmp, lowLatency, fhd, dvr, sessionId, streamMode)
.then((data) => res.send(data))
.catch((error) => {
console.log(error);
Expand Down Expand Up @@ -139,6 +147,67 @@ app.post('/broadcast/end', (req, res) => {
.catch((error) => res.status(500).send(error));
});

app.post('/addStream', (req, res) => {
const { streamId, roomName } = req.body;
const broadcastId = opentok.activeBroadcast[sessions[roomName]]?.id;
console.log('broadcastId ' + broadcastId);
console.log('streamId ' + streamId);

opentok
.addStreamToBroadcast(broadcastId, streamId)
.then(() => res.status(200).send('okay'))
.catch((err) => {
console.log('error' + err.message);
//this is temporary due to an issue on opentok nodeJS 2.14.3
if (err.message.includes('204')) {
res.status(200).send('okay');
} else {
res.status(500).send('something went wrong');
}
});
});

app.post('/render', async (req, res) => {
try {
const { sessionId, roomName, bgChoice, round } = req.body;
console.log('request to start render in ' + sessionId + bgChoice);
if (sessionId && roomName) {
const data = await opentok.createRender(sessionId, roomName, bgChoice, round);
console.log(data);
const { id } = data;
sessions[roomName].renderId = id;
res.status(200).send({ id });
} else {
res.status(500);
}
} catch (e) {
console.log(e);

res.status(500).send({ message: e });
}
});

app.get('/render/stop/:id', async (req, res) => {
try {
const { id } = req.params;
console.log(id);

if (id) {
const data = await opentok.deleteRender(id);
console.log(data);
res.status(200).send(data);
} else {
res.status(500);
}
} catch (e) {
res.status(500).send({ message: e });
}
});

app.get('*', (req, res) => {
res.redirect('/viewer');
});

/*
* Listen
*/
Expand Down
Loading

0 comments on commit 9c1ca40

Please sign in to comment.