Snapback

As a video publisher, you may want to prevent your viewers from seeking past your mid-roll ads. When a user seeks past an ad break, you can take them back to the start of that ad break, and then return them to their seek location after that ad break has completed. This feature is called "snapback."

As an example, see the diagram below. Your viewer is watching a video, and decides to seek from the 5-minute mark to the 15-minute mark. There is, however, an ad break at the 10-minute mark that you want them to watch before they can watch the content after it:

In order to show this ad break, take the following steps:

  1. Check if the user ran a seek that jumped past an unwatched ad break, and if so, take them back to the ad break.
  2. After the ad break completes, return them to their original seek.

In diagram form, that looks like this:

Here's how to implement this workflow in the tvOS IMA SDK, as done in AdvancedExample.

Prevent a seek from leaving an ad break unwatched

Check if the user has run a seek that went past an unwatched ad break, and if so, take them back to the ad break. The tvOS advanced example takes advantage of the AVPlayerViewController, which has a delegate method to tell you that the user has run a seek. If the seek start time comes before the previous ad break (meaning the user has jumped past it) and that break hasn't yet been played, seek them back to the start of the ad break. Also, record the start time of the initially requested seek to check later in your ad-break-did-end handler:

- (void)playerViewController:(AVPlayerViewController *)playerViewController
  willResumePlaybackAfterUserNavigatedFromTime:(CMTime)oldTime
                      toTime:(CMTime)targetTime {
  if (self.streamManager) {
    IMACuepoint *prevCuepoint = [self.streamManager
        previousCuepointForStreamTime:CMTimeGetSeconds(targetTime)];
    if (prevCuepoint && !prevCuepoint.isPlayed && oldTime < prevCuepoint.startTime) {
      self.userSeekTime = CMTimeGetSeconds(targetTime);
      [self.playerViewController.player seekToTime:CMTimeMakeWithSeconds(
                 prevCuepoint.startTime, NSEC_PER_SEC)
                 toleranceBefore:kCMTimeZero
                  toleranceAfter:kCMTimeZero];
    }
  }
}

Put the user back to their original seek

In your event delegate, modify the AD_BREAK_ENDED case to check if the previous ad break was played as the result of snapback.


- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdEvent:(IMAAdEvent *)event {
  NSLog(@"StreamManager event (%@).", event.typeString);
  switch (event.type) {
    // Your other events go here as normal.
    case kIMAAdEvent_AD_BREAK_ENDED: {
      if (self.userSeekTime > 0) {
        self.playerViewController.player
            seekToTime:CMTimeMakeWithSeconds(self.userSeekTime, NSEC_PER_SEC)
        toleranceBefore:kCMTimeZero
        toleranceAfter:kCMTimeZero];
        self.userSeekTime = 0;

      // existing handling for AD_BREAK_ENDED goes here.
      break;
    }
    // And so on for other events.
    default:
      break;
  }
}