Generic Stream Concurrency

This document describes how to limit stream concurrency to end-viewers with a heartbeat mechanism and without DRM.

Introduction

Generic Stream Concurrency lets you define the number of video streams that a specific user can watch concurrently. Limiting stream concurrency helps you prevent content being stolen or illegally watched through the theft or inappropriate sharing of credentials.

This feature is part of Playback Restrictions. It is an alternative to stream concurrency with DRM.

When to use GSC

Brightcove offers two solutions for managing concurrency:

The table below offers a comparison of the two to help you decide which is more appropriate for your situation.

Stream Concurrency Solutions
Generic Stream Concurrency Stream Concurrency with DRM
Advantages:
  • Does not require DRM
  • Active stream sessions can be listed via the API
Advantages
  • There is no way to disable it from the client-side
  • Renewal mechanism is transparent on custom implementations
  • More secure
Disadvantages
  • Heartbeat runs from client-side
  • Custom implementations require integrating the heartbeat mechanism
Disadvantages
  • It requires many DRM licenses
  • no way to list all active sessions of a specific user

How it works

Heartbeat

The heartbeat is a mechanism that requests on a frequency base the active sessions for a specific user, to enforce that it is a valid session through the entire playback. The heartbeat can be enabled for the Brightcove web player and the native SDK players.

The frequency of the heartbeat defines how often the player makes mid-stream checks to see if the conditions for playback are still met. By default the frequency is set to 1 minute, but this can be changed.

Blocking streams

When the maximum number of concurrent streams is reached and the viewer, or someone with their account credentials, tries to open an additional stream, any new stream from that user identified as a different streaming location will be blocked.

Correlator identifier

A correlator identifier is used to define viewer streaming locations. The characteristics of this identifier are:

  • It should be specific enough to correlate all requests from the same Viewer. If the correlator is too generic it will bucket multiple viewers with the same viewer ID together and group them all into the same slot.
  • It should be consistent across all video views for the same Viewer.

When the "correlator" is different, it will try to fill a "slot" for the viewer id, which means if that value changes during the same viewing, it will be treated as if it were a different viewer and prevent playback.

The correlator is set in the JWT using the sid claim

Implementation

Using Brightcove web or SDK players

  1. If you wish to change the heartbeat frequency from the default (1 minute) contact Support.
  2. Create a JWT for playback restrictions.

    The following claims are required:

    • climit - The concurrency limit claim indicates how many watchers or streams can play at the same time
    • uid - The viewer identifier is used to correlate multiple sessions to enforce Stream Concurrency
    • sid - Correlator identifier defines the streaming locations for a viewer.

      Examples:
      • Chrome MAC (Cadmium) HTML 5 - 1112223334
      • Apple iPad 7th Gen 10.2 (Wi-Fi) - 2223334444
      • Apple Apple TV TBD Apple TV - 3334445555
      • Android DefaultWidevineL3Phone Android Phone - 1112224567
      • Firefox MAC (Cadmium) HTML 5 - 1112226754
      • Google Chromecast streaming stick - 1112346677
  3. Register the public key for the JWT with Brightcove. See Using Authentication APIs for details.
  4. Enable Generic Stream Concurrency on client players: see Implementation in players below
Sample JSON Web Token (JWT) claims
{
  // account id: JWT is only valid for this account
  "accid":"4590388311111",
  // limit of concurrent users
  "climit": 3,
  // user id
  "uid": "108.26.184.3_1634052241",
  // correlator identifier
  "sid": "Firefox MAC (Cadmium) HTML 5 - 1112346677"
  }

Notes

  • When the maximum number of sessions is exceeded for a viewer, a session will be stopped. It may take as long as the heartbeat frequency for the session to be stopped.
  • If the client player cannot connect to the server, it will re-try three times. If it still cannot connect, playback will stop.

Implementation in players

Requirements

  • Generic Stream Concurrency requires the Brightcove web player 6.63.2 or later.
  • Generic Stream Concurrency requires the Brightcove iOS SDK player 6.10.1, or later.
  • Generic Stream Concurrency requires the Brightcove Android SDK player 6.17.2 or later.

Brightcove web player

Generic stream concurrency in the Brightcove Player can be enabled using the video_cloud.stream_concurrency player configuration.

At this time, there is no dedicated UI for this feature in Studio, so the JSON editor must be used. The configuration will look something like this:

"stream_concurrency" : true
...
  "video_cloud": {
  "stream_concurrency": true,
  "policy_key": "BCpk..."
  },
  "player": {
    "template": {
    "name": "single-video-template",
    "version": "6.63.1"
    }
  },
  ...

If this key/value pair is not present in JSON or the value is false, GSC feature would not be enabled for the player.

Setting the JWT at Runtime

Similar to the EPA stream concurrency limiting feature, generic stream concurrency depends on a JSON Web Token.

Once the player is configured for generic stream concurrency, as above, the remaining step is to provide a JWT to the player at runtime. This is the same process as when using EPA:

player.catalog.setBcovAuthToken('your token');
Example

After adding a JWT token, the final step is to request data from the Playback API and load it into the player. This example demonstrates fetching a single video:

// Set the authorization token.
  player.catalog.setBcovAuthToken('your token');

    // Initiate a catalog request. API selection will occur each time this
    // is called.
    player.catalog.get({id: '1', type: 'video'}).
    then(function(data) {

    // When the request is complete, you must load the returned metadata
    // and sources into the player.
    player.catalog.load(data);
    }).
    catch(function(error) {
    throw new Error(error);
  });

iOS

To enable the Generic Stream Concurrency feature for iOS SDK, you need to enable the option streamConcurrencyEnabled in your playbackController. Optionally, you can send the value for sid. If the sid is empty, this value will not be sent as header.

Objective-C

self.playbackController.streamConcurrencyEnabled = YES;
  // Optional. Set custom sid
  self.playbackController.options ■ (?{ kBCOVAuthHeartbeatPropertyKeySessionld: G'sessionld" };

Swift

self.playbackController.streamConcurrencyEnabled ■ true
  // Optional. Set custom sid
  self.playbackController.options = [ kBCOVAuthHeartbeatPropertyKeySessionld: "sessionld" ]

For more details, see the Brightcove Native Player for iOS Reference.

Android

In your player Activity’s onCreate method, add this line:

brightcoveVideoView.setStreamConcurrencyEnabled(true);

In the onCreate method add an event listener for the DID_SET_VIDEO event, with this code to set heartbeat headers. Note that the same JWT used to retrieve the video is used here, and should include a uid claim (and optionally, a sid claim):

Map<String, String> requestHeaders = new
  HashMap<>();
  requestHeaders.put(ConcurrencyClient.HEARTBEAT_VIDEO_HEADER_KEY, video.getId());
  requestHeaders.put(ConcurrencyClient.HEARTBEAT_ACCOUNTID_HEADER_KEY, accountId);
  requestHeaders.put(BrightcoveTokenAuthorizer.BRIGHTCOVE_AUTHORIZATION_HEADER_KEY, jwtToken);
  brightcoveVideoView.setStreamConcurrencyRequestHeaders(requestHeaders);

Implementation via API

This feature can be implemented via the Concurrency Service API without using the Brightcove Web Player or SDK players. See the API Reference for additional details.

The base URL for the Concurrency Service API is:

https://edge-gsc.api.brightcove.com

The authorization method is through the JWT sent in an Authorization header:

Authorization: Bearer {token}

The basic logic your player/app needs to perform is shown in the diagram below:

Logic for Concurrency
Logic for Concurrency

The API endpoints

Session
This endpoint is used to create new streaming sessions with a heartbeat for concurrency management:
/api/v1/accounts/{{account_id}}/sessions

Method: POST

Request body:

{
  "video": "the_video_id"
}
Active sessions
This endpoint allows you to list streaming sessions to keep track of them - mainly useful if you are implementing logic to decide which session to stop if the concurrency limit is reached:
/api/v1/accounts/{{account_id}}/sessions

Method: GET

Stop sessions
This endpoint allows you to to stop a streaming session - you would use this if you are implementing logic to decide which session to stop when a new playback request exceeds the concurrency limit. This endpoint is mostly for backend use to remove sessions when completed:
/api/v1/accounts/{{account_id}}/sessions/{{session_id}}

Method: DELETE