logo
Build a UGC Live Streaming App with Amazon IVS: Adding Real-Time Stream Playback (Lesson 5.3)

Build a UGC Live Streaming App with Amazon IVS: Adding Real-Time Stream Playback (Lesson 5.3)

Welcome to Lesson 5.3 in this series where we're looking at building a web based user-generated content live streaming application with Amazon IVS. This entire series is available in video format on the AWS Developers YouTube channel and all of the code related to the sample application used in this series can be viewed on GitHub. Refer to the links at the end of the post for more information.

TS
Todd Sharp
Amazon Employee
Published Jan 2, 2024

In this lesson, we'll learn how the StreamCat application provides real-time stream playback for viewers.

In lesson 4.3, we learned how StreamCat provides for broadcasting a real-time stream with multiple hosts. Adding playback for a real-time stream is nearly identical to the process for broadcasting, except that the individual videos for each broadcaster are not shown. Instead, a composited view is presented to provide a familiar look and feel to traditional low-latency live streams.
The markup for real-time playback includes a <canvas> element for the composite stream, and an <audio> tag to provide the voice track for all broadcasting participants.
1
2
<canvas id="broadcast" class="rounded shadow w-100"></canvas>
<audio id="audio-player" autoplay></audio>
If you recall from earlier lessons, each participant in a real-time stream must have a stage participant token to join the stream. Broadcasters are granted a token that grants them both the PUBLISH and SUBSCRIBE capabilities, but viewers only require the SUBSCRIBE capability. The playback endpoint generates this token via the RealTimeService that we learned about in earlier lessons.
1
2
3
4
5
6
const stageToken = await RealTimeService.createStageToken(
userId,
username,
stage?.arn!,
['SUBSCRIBE']
);
The process for creating an instance of the IVSBroadcastClient, and subscribing to broadcast client events and rendering participants is identical as it was when creating the broadcast page, so we won't repeat the steps here.
The main difference is the addition of the <audio> tag. This audio is initialized on page load.
1
2
3
4
5
initAudio() {
this.audioStream = new MediaStream();
const audioOutput = document.getElementById('audio-player');
audioOutput.srcObject = this.audioStream;
}
When a participant stream is added, the audio track for that stream is added to the overall audioStream.
1
2
3
4
5
6
7
async renderAudioToClient(participant, stream) {
const broadcastClient = Alpine.raw(this.broadcastClient);
if (!stream?.mediaStreamTrack) return;
const participantId = participant.id;
this.audioStream.addTrack(stream.mediaStreamTrack);
return Promise.resolve();
}
This audio track is removed when a participant stream is removed.
1
2
const participantAudioStream = streams.find((s) => s.mediaStreamTrack.kind === 'audio');
this.audioStream.removeTrack(participantAudioStream.mediaStreamTrack);

By default, a real-time channel has no "go live" status. When users join the real-time stream, it's automatically "live". The StreamCat application gives broadcasters the ability to decide when viewers can view a real-time stream by setting a boolean isLive on the Stage object. When this happens, a custom chat event is broadcast via the ChatRoomService called StreamCat:RealTimeUpdate.
1
2
3
4
5
await ChatRoomService.sendChatEvent(
chatArn,
'StreamCat:RealTimeUpdate',
{ isLive: isLive.toString() }
);
The chat component listens for this event, and broadcasts a custom event so that the parent page can act upon it.
1
2
3
4
if (data.EventName === 'StreamCat:RealTimeUpdate') {
const isLive = data.Attributes.isLive === 'true';
dispatchEvent(new CustomEvent('realTimeUpdate', { detail: { isLive } }));
}
The real-time stream playback page listens for this event, and joins or leaves the stage as appropriate.
1
2
3
4
5
6
7
8
addEventListener('realTimeUpdate', async (evt) => {
this.isLive = evt.detail.isLive;
if (!evt.detail.isLive) {
this.stage.leave();
} else {
await this.stage.join();
}
});

In this lesson, we learned how StreamCat provides real-time stream playback. This concludes lesson 5. In lesson 6, we'll look at how StreamCat deals with VOD discovery and playback.


Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.