SSAI Plugin changes in 6.9.0

The Brightcove SDK v6.9.0 was released on October 22, 2019, and it includes several changes (as seen in the release notes). But in this post I want to talk specifically about the changes to the SSAI plugin, particularly the added support for Live SSAI.

Live SSAI overview

What is Live SSAI? In short, Live SSAI is a Live stream that has dynamically stitched ads in it. You can find more information about the Live SSAI API in Live API: Server-Side Ad Insertion (SSAI) — and also check Video Cloud SSAI Overview to learn more about SSAI in general.

When you play a Live SSAI stream in any player, you will see both the content and the ads as part of the same stream, and there will not be any visual indication between the two. This is where the Brightcove SDK and SSAI plugin comes in handy. The SSAI plugin will detect when an ad break is playing and the controller will change to display useful information, such as the duration of the ad break, the duration of the individual ad and the ad number countdown.

Getting started

Before I start talking about Android and the SSAI plugin code, you will need to do the following things:

  1. Create a Live ad configuration
  2. Setup your Live stream
  3. Find and copy your Live adConfigId

Create a Live ad configuration

You must create an ad configuration in the same way you would with a VOD video. To do so, please follow the following guide: Creating a Live ad configuration. You can also take a look at Implementing Server-Side ads in the Live Module.

Setup your Live stream

To create your Live stream with SSAI support, please follow: Creating a live event that supports server-side ads.

Find and copy your Live adConfigId

When you create a Live ad configuration in step 1, you get an Id and you might be tempted to use it as your adConfigId the same way it is done for VOD SSAI. However, this is not the Id we need for Live SSAI.

To find the Live adConfigId, you need to follow the steps in Publishing the live event. In the last step, you will be able to see the Player URL. This Player URL contains the adConfigId we’re looking for:

You will be able to differentiate it because this adConfigId starts with “live.”

Requesting your Live SSAI video with the SSAI plugin

Now we are ready to request the video and play it back.

The first thing we need to do is to create an HttpRequestConfig object with the Live adConfigId.

String adConfigIdKey = "ad_config_id";
String adConfigIdValue = "live.eb5YO2S2Oqdzlhc3BCHAoXKYJJl4JZlWXeiH49VFaYO2qdTkNe_GdEBSJjir";

HttpRequestConfig httpRequestConfig = new HttpRequestConfig.Builder()
  .addQueryParameter(adConfigIdKey, adConfigIdValue)
   .build();

Secondly, we create the SSAIComponent object, which handles the Live SSAI stream in the same way as a VOD SSAI stream.

SSAIComponent plugin = new SSAIComponent(this, brightcoveVideoView);

Then we create the Catalog object.

Catalog catalog = new Catalog(eventEmitter, accountId, policyKey);

Finally, we make the Video request using the HttpRequestConfig previously created and we process the received Video with the SSAI plugin. The SSAI plugin will automatically add the video to the Brightcove Video View.

catalog.findVideoByID(videoId, httpRequestConfig, new VideoListener() {
            @Override
            public void onVideo(Video video) {
                   plugin.processVideo(video);
            }
        });

You will notice that this process is exactly the same as with a VOD SSAI. The only difference is the adConfigId used.

Breaking changes

In the SSAI plugin v6.9.0, we had to introduce a potential breaking change due to an issue with the stored playhead position when interrupting and resuming the Live video. This will impact you only if you rely on the Event.PLAYHEAD_POSITION property from either the EventType.PROGRESS or EventType.AD_PROGRESS event.

The Event.PLAYHEAD_POSITION in earlier versions contained the relative playhead position to the ad or the main content, but it has the absolute playhead position in 6.9.0. 

Let me explain it through an example. In the following figure, we have a VOD SSAI video with a total duration of 43 seconds. The main content time is 15 seconds, and it has three ads, a pre-roll of 6 seconds, a mid-roll of 14 seconds and a post-roll of 8 seconds. When we start playing the mid-roll, the absolute playhead position will be 11 seconds, but the relative playhead position to the mid-roll will be zero. The Event.PLAYHEAD_POSITION value will be zero in earlier versions but it is 11 seconds in version 6.9.0.

NOTE: All the actual playhead position values are given in milliseconds in the Brightcove SDK.

On the other hand, we also introduced a new event property called Event.PROGRESS_BAR_PLAYHEAD_POSITION which contains the relative playhead position. This name is more descriptive of its purpose, which is ultimately to display the playhead position of the playing block (ad or content) to the user through the progress bar. Having said this, if you depend on the Event.PLAYHEAD_POSITION value, you only need to start using Event.PROGRESS_BAR_PLAYHEAD_POSITION instead.

The Event.ORIGINAL_PLAYHEAD_POSITION was unchanged for compatibility purposes, and it still contains the absolute playhead position.

The following table summarize the changes:

Event property Previous to 6.9.0 6.9.0 and higher
Event.PLAYHEAD_POSITION Relative playhead position Absolute playhead position
Event.ORIGINAL_PLAYHEAD_POSITION Absolute playhead position Absolute playhead position
Event.PROGRESS_BAR_PLAYHEAD_POSITION N/A Relative playhead position

Considerations when listening to the PROGRESS event
 

There are a few things you must be aware when listening to the EventType.PROGRESS event in the SSAI plugin. The time when you add your listener matters.

I want to start by giving you some context on what the Brightcove SDK and SSAI plugin do.

1. First, the EventType.PROGRESS is originally emitted by the ExoPlayerVideoDisplayComponent class and the Event.PLAYHEAD_POSITION property has the playhead position retrieved from ExoPlayer (I assume you are using ExoPlayer), which is the absolute playhead position in the SSAI context.

2. Then the SSAI plugin catches the EventType.PROGRESS event and compares the playhead position against the SSAI timeline, to determine whether it is playing content or ad. In both cases we calculate the relative playhead position and add a new Event.PROGRESS_BAR_PLAYHEAD_POSITION property with this value.

  • If playing content, we let the event be propagated to the rest of the listeners.
  • If playing an ad, we stop propagating the EventType.PROGRESS event, and emit the EventType.AD_PROGRESS with the same properties.

3. Finally, all listeners added after SSAIEventType.AD_DATA_READY is emitted or those with the @Default annotation will receive the EventType.PROGRESS event.

Because of how the EventType.PROGRESS is processed, depending on when you add your listener, you might not get the expected values. For example, if you add the EventType.PROGRESS listener before the SSAIEventType.AD_DATA_READY is emitted, your listener will be called before it gets processed by the SSAI plugin, therefore you will not be able to get the Event.PROGRESS_BAR_PLAYHEAD_POSITION value.

NOTE: This also happens in older versions, but instead of not getting Event.PROGRESS_BAR_PLAYHEAD_POSITION value, the Event.PLAYHEAD_POSITION will have the absolute playhead position instead of the expected relative playhead position (again, this is for older versions).

NOTE: This problem does not happen when listening to the  EventType.AD_PROGRESS event.

In case you run into this situation, there are a couple of things you can try:

  1. Add the @Default annotation to your listener.
  2. Add your listener after SSAIEventType.AD_DATA_READY is emitted.

Add the @Default annotation to your listener

Internally, we annotate certain listeners with the @Default annotation. This makes all default-annotated listeners wait for all non-default listeners to be called first, before default-annotated listeners are called.

By adding this annotation to your EventType.PROGRESS listener, you make it wait until the non-default SSAI Plugin EventType.PROGRESS listener processes the event. The only caveat is that this behavior might change if the SSAI Plugin EventType.PROGRESS listener is annotated with the @Default annotation in a future release (I am not currently foreseeing this to happen).

The example looks like this:

eventEmitter.on(EventType.PROGRESS, new EventListener() {
   @Default
   @Override
   public void processEvent(Event event) {
       int absolutePlayheadPosition = event.getIntegerProperty(Event.PLAYHEAD_POSITION);
       int relativePlayheadPosition = event.getIntegerProperty(Event.PROGRESS_BAR_PLAYHEAD_POSITION);
   }
});

Add your listener after SSAIEventType.AD_DATA_READY is emitted

Alternatively, you can add your listener after the SSAIEventType.AD_DATA_READY event is emitted, which guarantees your listener will be called after the SSAI plugin had the chance to process the event.

The example looks like this:

eventEmitter.once(SSAIEventType.AD_DATA_READY, adDataReady -> {
   eventEmitter.on(EventType.PROGRESS, progressEvent -> {
       int absolutePlayheadPosition = progressEvent.getIntegerProperty(Event.PLAYHEAD_POSITION);
       int relativePlayheadPosition = progressEvent.getIntegerProperty(Event.PROGRESS_BAR_PLAYHEAD_POSITION);
   });
});

There is a caveat. Since the SSAIEventType.AD_DATA_READY event is emitted every time the SSAI video (Live and VOD) is opened, you need to make sure you are not adding your EventType.PROGRESS listener multiple times.

One way to avoid this is by saving the token when adding the EventType.PROGRESS event listener to the Event Emitter and use it to remove such listener after every SSAIEventType.AD_DATA_READY event. For example:

// Declare member variable
private int mCurrentProgressToken = -1;


// Code called every time a SSAI video is processed with the SSAI plugin
eventEmitter.once(SSAIEventType.AD_DATA_READY, adDataReady -> {
   if (mCurrentProgressToken != -1) {
       eventEmitter.off(EventType.PROGRESS, mCurrentProgressToken);
   }
  
   mCurrentProgressToken = eventEmitter.on(EventType.PROGRESS, progressEvent -> {
       int absolutePlayheadPosition = progressEvent.getIntegerProperty(Event.PLAYHEAD_POSITION);
       int relativePlayheadPosition = progressEvent.getIntegerProperty(Event.PROGRESS_BAR_PLAYHEAD_POSITION);
   });
});

Summary

The SSAI Plugin 6.9.0 does the heavy work to support Live SSAI. There aren't any significant changes in the plugin API used to start playing Live SSAI streams compared to VOD SSAI. The hard part is setting up your Live SSAI stream and identifying the right ad config Id you need to pass to the HttpRequestConfig when making the Catalog request.

There are potential breaking changes you must be aware of, but once you identify if you are affected, it’s very straightforward to fix your code. And in case you depend on the EventType.PROGRESS event, you must also put special attention on when you are adding your progress listener and verify you are getting the expected values.