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:
- Create a Live ad configuration
- Setup your Live stream
- 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 theEventType.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, theEvent.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:
- Add the
@Default
annotation to your listener. - 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.