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 IMA DAI SDK, as done in the 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. For the iOS Advanced example, use the user's interaction with your UI to detect when they have run a seek. Preserve the seek start time to check if they've passed an unplayed ad break in their seek. When the user releases the video controls, compare their current time to the time of the most recent ad break. If the ad break falls after the seek start time (meaning the user has jumped past it) and it hasn't yet been played, seek them back to the start of the ad break. Also, set a snapback flag to check later in your ad break complete handler:

- (IBAction)videoControlsTouchStarted:(id)sender {
  self.seekStartTime = self.contentPlayer.currentTime;
}

- (IBAction)videoControlsTouchEnded:(id)sender {
  self.seekEndTime = CMTimeMake(self.progressBar.value, 1);
  IMACuepoint *lastCuepoint =
      [self.streamManager previousCuepointForStreamTime:CMTimeGetSeconds(self.seekEndTime)];
  if (!lastCuepoint.played && (lastCuepoint.startTime > CMTimeGetSeconds(self.seekStartTime))) {
    self.snapbackMode = YES
    // Add 1 to the seek time to get the keyframe at the start of the ad to be your landing place.
    [self.contentPlayer seekToTime:CMTimeMakeWithSeconds(lastCuepoint.startTime + 1, NSEC_PER_SEC)];
  }
}

Put the user back to their original seek

In your ad-break-ended handler, check to see if the previous break was played as the result of snapback. If so, return the user to the place they were trying to seek to initially (as long as it wasn't the middle of the ad break that just played):

- (void)streamManager:(IMAStreamManager *)streamManager didReceiveAdEvent:(IMAAdEvent *)event {
  switch (event.type) {
    case kIMAAdEvent_AD_BREAK_ENDED: {
      if (self.snapbackMode) {
        self.snapbackMode = NO;
        if (CMTimeCompare(self.seekEndTime, self.contentPlayer.currentTime)) {
          [self.contentPlayer seekToTime:self.seekEndTime];
        }
      }
      break;
    }
  }
}