Ad bookmarks
This guide shows how to implement bookmarking when using the ad insertion for video-on-demand (VOD) streams. This assumes a working ad insertion implementation, such as the one presented in Setup ad insertion.
Store a bookmark position
When playing a VOD with ads, number and position could change over time. It is necessary to take ads into account when storing a position for a later playback. The lib is providing a create a bookmark position API that translate the current position with ads to a position without ads and this value can be saved into the app.
- Android
- iOS & tvOS
- Web
public class PlaybackActivity extends Activity {
...
private StreamingSession mSession;
protected void onPause() {
// Generate the bookmark position with SmartLib
long bookmarkPosition = mSession.getPositionForBookmark();
// Store the position in your custom app storage
mAppStorage.storePosition(mContentId, bookmarkPosition);
}
...
}
#import "SmartLib.h"
@interface PlaybackViewController ()
@property (nonatomic,strong) StreamingSession *session;
@end
@implementation PlaybackViewController
...
- (void)onPause {
// Generate the bookmark position with SmartLib
long bookmarkPosition = [session getPositionForBookmark];
// Store the position in your custom app storage
[self.appStorage storePosition:self.contentId position:bookmarkPosition];
}
...
@end
class PlaybackView {
...
session;
onPause() {
// Generate the bookmark position with SmartLib
const bookmarkPosition = this.session.getPositionForBookmark();
// Store the position in your custom app storage
this.appStorage.storePosition(this.contentId, bookmarkPosition);
}
...
}
Restore a bookmarked position
Load the bookmark when re-requesting a stream.
To ensure that the restore a bookmark position API will return
the correct result, call it once onAdData
has been triggered (see Listen to ad data).
- Android
- iOS & tvOS
- Web
public class PlaybackActivity extends Activity {
// Your player object
private Object mPlayer;
// SmartLib StreamingSession
private StreamingSession mSession;
// It is recommended to use a different thread to start a session
private ExecutorService mExecutor = Executors.newSingleThreadExecutor();
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
// Create the player
mPlayer = PlayerFactory.newInstance();
// Registering to player error events
mPlayer.setOnErrorEvent(this);
// Create a StreamingSession object
mSession = SmartLib.getInstance().createStreamingSession();
// Attach the player to the session
mSession.attachPlayer(mPlayer);
// Listen to ad data, triggered during SmartLib getURL
mSession.setAdDataListener(adList -> {
// Get the position from your custom app storage
long bookmarkPosition = mAppStorage.getPosition(mContentId);
// Generate the playback position with SmartLib
long playbackPosition = mSession.getPositionForPlayback(bookmarkPosition, false);
mPlayer.seek(playbackPosition);
mPlayer.play();
});
// Activate the advertising workflow
mSession.activateAdvertising();
// Start the session in a different thread, getURL can make requests to the Broadpeak CDN, nanoCDN...
mExecutor.submit(() -> {
StreamingSessionResult result = mSession.getURL(...);
if (!result.isError()) {
// Load URL without playing
mPlayer.loadURL(result.getURL());
} else {
mSession.stopStreamingSession();
showNoSessionCreatedMessage();
}
});
}
@Override
public void onPlayerError(PlaybackException error) {
// If the playback has been canceled because of a player error
// (i.e. non-recoverable error), stop the streaming session
if (error.playbackCanceled) {
mSession.stopStreamingSession();
showPlayerTriggeredAnErrorMessage();
}
}
...
}
#import "SmartLib.h"
@interface PlaybackViewController ()
// Your player object
@property (nonatomic,strong) NSObject *player;
// SmartLib StreamingSession
@property (nonatomic,strong) StreamingSession *session;
@end
@implementation PlaybackViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Start the session in a different thread, getURL can make requests to the Broadpeak CDN, nanoCDN...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// Create the player
self.player = [PlayerFactory newInstance];
// Registering to player error events
[self.player setOnErrorEvent:self];
// Create a StreamingSession object
self.session = [SmartLib createStreamingSession];
// Attach the player to SmartLib
[self.session attachPlayer:self.player];
// Listen to ad data, triggered during SmartLib getURL
[self.session setAdDataListener:self];
// Activate the advertising workflow
[self.session activateAdvertising];
// Start the session
StreamingSessionResult *result = [self.session getURL:...];
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (![result isError]) {
// Load URL without playing
[self.player loadURL:[result getURL]];
} else {
[self.session stopStreamingSession];
[self showNoSessionCreatedMessage];
}
});
});
}
- (void)onAdData:(nonnull NSArray<AdBreakData *> *)adList {
// Get the position from your custom app storage
long bookmarkPosition = [self.appStorage getPosition:self.contentId];
// Generate the playback position with SmartLib
long playbackPosition = [self.session getPositionForPlayback:bookmarkPosition beforeAdBreak:NO];
[self.player seek:playbackPosition]
[self.player play]
}
- (void)onPlayerError:(PlayerError *)error {
// If the playback has been canceled because of a player error
// (i.e. non-recoverable error), stop the streaming session
if (error.playbackCanceled) {
[self.session stopStreamingSession];
[self showPlayerTriggeredAnErrorMessage];
}
}
@end
class PlaybackView {
player;
session;
...
async onCreate() {
...
// Create the player
this.player = PlayerFactory.newInstance();
// Registering to player error events
this.player.setOnErrorEvent(this);
// Create a StreamingSession object
this.session = SmartLib.getInstance().createStreamingSession();
// Attach the player to the session
this.session.attachPlayer(mPlayer);
// Listen to ad data, triggered during SmartLib getURL
this.session.setAdDataListener(adList -> {
// Get the position from your custom app storage
const bookmarkPosition = this.appStorage.getPosition(this.contentId);
// Generate the playback position with SmartLib
const playbackPosition = this.session.getPositionForPlayback(bookmarkPosition, false);
this.player.seek(playbackPosition);
this.player.play();
});
// Activate the advertising workflow
this.session.activateAdvertising();
// Start the session in a different thread, getURL can make requests to the Broadpeak CDN, nanoCDN...
const result = await this.session.getURL(...);
if (!result.isError()) {
// Load URL without playing
this.player.loadURL(result.getURL());
} else {
this.session.stopStreamingSession();
this.showNoSessionCreatedMessage();
}
}
onPlayerError(error) {
// If the playback has been canceled because of a player error
// (i.e. non-recoverable error), stop the streaming session
if (error.playbackCanceled) {
this.session.stopStreamingSession();
this.showPlayerTriggeredAnErrorMessage();
}
}
...
}