[object Object] Icon

Encoding
Learn how to create, start, manage and modify Encodings

[object Object] Icon

Player
Learn how to create, start, manage and modify Players

[object Object] Icon

Analytics
Learn how to create, start, manage and modify Analyticss

Docs Home
User shortcuts for search
Focus by pressing f
Hide results by pressing Esc
Navigate via   keys

Wed Sep 12 2018

Measuring concurrent users of live streams

OverviewLink Icon

When you provide a live stream to your users, it’s interesting to find out the maximum number of people who watched your stream simultaneously.

In this article, we’ll show you how to measure peaks of concurrent users using the Bitmovin Analytics API.

Before we start, make sure you’ve set up the Bitmovin JavaScript client according to the “Getting Started” guide.

Counting unique usersLink Icon

It just takes one query to find out how many people watched your live stream in a period:

1queryBuilder
2 .count('USER_ID')
3 .between(fromDate, toDate)
4 .filter('VIDEO_ID', 'EQ', 'my-livestream-id')
5 .query()

However, this number doesn’t tell us how many people watched the video at the same time: It could be that some viewers left our livestream before others even started watching it. These viewers never watched the stream simultaneously. Therefore the maximum number of concurrent viewers will usually be lower than this number.

Unique users per minuteLink Icon

To measure the number of concurrent viewers, we need to count users in narrow time intervals. The smallest interval supported by the Bitmovin API is a minute. This query lists the number of viewers per minute:

1queryBuilder
2 .count('USER_ID')
3 .between(fromDate, toDate)
4 .interval('MINUTE')
5 .filter('VIDEO_ID', 'EQ', 'my-livestream-id')
6 .query()
7 .then(console.log)

The result will look something like this:

1{
2 rowCount: 5,
3 rows: [
4 [1512547200000, 4271],
5 [1512547260000, 4364],
6 [1512547320000, 6231],
7 [1512547380000, 6912],
8 [1512547440000, 5895]
9 ],
10 columnLabels: [
11 { key: "USER_ID", label: "User id" }
12 ]
13}

In this example, our peak user number is 6912. Here’s how we can determine the maximum number:

1async function getPeakUsers() {
2 const { rows } = await queryBuilder
3 .count('USER_ID')
4 .between(fromDate, toDate)
5 .interval('MINUTE')
6 .filter('VIDEO_ID', 'EQ', 'my-livestream-id')
7 .query();
8
9 const concurrentUsers = rows.map(r => r[1]);
10 return Math.max(...concurrentUsers);
11}
12
13getPeakUsers().then(console.log);

Breaking the limitsLink Icon

The query above works well for periods of up to 150 minutes. The Bitmovin API is limited to 150 result rows per query, so you’ll get incomplete results for longer periods. To fetch all data, we need to make multiple subsequent queries and merge the results:

1const apiLimit = 150;
2
3async function getPeakUsers() {
4 const query = queryBuilder
5 .count('USER_ID')
6 .between(fromDate, toDate)
7 .interval('MINUTE')
8 .filter('VIDEO_ID', 'EQ', 'my-livestream-id')
9 .orderBy('MINUTE', 'ASC');
10
11 const fetchRows = async (offset = 0) => {
12 const { rows } = await query
13 .limit(apiLimit)
14 .offset(offset)
15 .query();
16
17 if (rows.length === apiLimit) {
18 return [...rows, ...await fetchRows(offset + apiLimit)];
19 } else {
20 return rows;
21 }
22 };
23
24 const allRows = await fetchRows();
25 const concurrentUsers = allRows.map(r => r[1]);
26 return Math.max(...concurrentUsers);
27}
28
29getPeakUsers().then(console.log);

Note that we’ve added orderBy('MINUTE') to the query. Without this clause, the query would return arbitrary records, rendering our offset clause useless.

1const apiLimit = 150;
2
3async function getPeakUsers() {
4 const query = queryBuilder
5 .count('USER_ID')
6 .between(fromDate, toDate)
7 .interval('MINUTE')
8 .filter('VIDEO_ID', 'EQ', 'my-livestream-id')
9 .orderBy('MINUTE', 'ASC');
10
11 const fetchRows = async (offset = 0) => {
12 const { rows } = await query
13 .limit(apiLimit)
14 .offset(offset)
15 .query();
16
17 if (rows.length === apiLimit) {
18 return [...rows, ...await fetchRows(offset + apiLimit)];
19 } else {
20 return rows;
21 }
22 };
23
24 const allRows = await fetchRows();
25 return allRows.reduce((max, row) => row[1] > max[1] ? row : max);
26}
27
28getPeakUsers().then(console.log);

This will give us the row with most concurrent users, like [1512547380000, 6912]. The first array element is the Unix timestamp of the beginning of the minute, the second is the number of users watching the video during this minute.

You can apply the same techniques to find peak error rates, peak impressions or peak buffering times. For an extensive list of available attributes, check out the Bitmovin Analytics API docs.

Give us feedback