Article

Working with Interstitial Content (tvOS)

Present additional content such as legal text, content warnings, or advertisements alongside your main media content.

Overview

Media playback apps often present additional content such as legal text, content warnings, or advertisements alongside their main media content. The best solution to present this kind of content is to use HTTP Live Streaming support for serving stitched playlists separated with EXT-X-DISCONTINUITY tags (see Example Playlist Files for use with HTTP Live Streaming). Stitched playlists let you combine multiple media playlists into a single, unified playlist that's delivered to the client as a single stream. This stream provides a smooth playback experience to users with no breaks or stutters in the action when the interstitial content is presented.

Define the Time Ranges

AVKit in tvOS simplifies working with interstitial content delivered as part of a stitched playlist. You define the time ranges in your presentation that contain interstitial content. As the time ranges are traversed during playback, you receive callbacks when they begin and end, giving you the opportunity to enforce business rules or capture analytics.

AVPlayerItem in tvOS adds an interstitialTimeRanges property that is set with an array of AVInterstitialTimeRange objects. Each object defines a CMTimeRange marking the interstitial time range in your media’s timeline. The following code example shows how to create interstitial time ranges:

func setupPlayback() {
    ...
    playerItem.interstitialTimeRanges = makeInterstitialTimeRanges()
    ...
}

private func makeInterstitialTimeRanges() -> [AVInterstitialTimeRange] {
    // 10 second time range at beginning of video (Content Warning)
    let timeRange1 = CMTimeRange(start: kCMTimeZero,
                                 duration: CMTime(value: 10, timescale: 1))

    // 1 minute time range at 10:00 (Advertisements)
    let timeRange2 = CMTimeRange(start: CMTime(value: 600, timescale: 1),
                                 duration: CMTime(value: 60, timescale: 1))

    // Return array of AVInterstitialTimeRange objects
    return [
        AVInterstitialTimeRange(timeRange: timeRange1),
        AVInterstitialTimeRange(timeRange: timeRange2)
    ]
}

When interstitial time ranges are defined, AVPlayerViewController updates its user interface in two important ways (see Figure 1). First, any interstitial times ranges are represented as small dots on the player’s timeline. This makes it easy for users to understand where they are between interstitial breaks and helps orient them to where they are in your app. Second, the interstitial time ranges are collapsed from the player’s interface. The current time and duration presented represent only your main content, providing a better sense of the timing of the main app.

Abstract representation of a video player with a timeline along the bottom and two interstitial time ranges highlighted.

Enforce Linear Playback

By adopting the AVPlayerViewControllerDelegate protocol, you can be notified as interstitial time ranges are traversed, which is useful to help you enforce business rules. For instance, a common requirement when presenting advertisements is to prevent users from skipping past them. You can use the requiresLinearPlayback property of AVPlayerViewController to control whether users can navigate through the content using the Siri Remote. During playback, this property is normally set to false, but when presenting an advertisement, you can set it to true to prevent user navigation, as shown in the following example:

public func playerViewController(_ playerViewController: AVPlayerViewController,
                                 willPresent interstitial: AVInterstitialTimeRange) {
    playerViewController.requiresLinearPlayback = true
}

public func playerViewController(_ playerViewController: AVPlayerViewController,
                                 didPresent interstitial: AVInterstitialTimeRange) {
    playerViewController.requiresLinearPlayback = false
}

Prevent Skipping of Interstitial Content

If your app presents interstitial content, you may also want to prevent users from skipping past ads or legal text. Implement this functionality by using the playerViewController(_:timeToSeekAfterUserNavigatedFrom:to:) delegate method. This method is called whenever a user performs a seek operation using the Siri Remote, which happens either by swiping left or right on the remote touch pad or by navigating chapter markers in the Info panel. The following simple code example shows how you might implement this method to prevent users from skipping past advertisements:

public func playerViewController(_ playerViewController: AVPlayerViewController, timeToSeekAfterUserNavigatedFrom oldTime: CMTime, to targetTime: CMTime) -> CMTime {

    // Only evaluate if the user performed a forward seek
    guard !canSkipInterstitials && oldTime < targetTime else {
        return targetTime
    }

    // Define time range of the user's seek operation
    let seekRange = CMTimeRange(start: oldTime, end: targetTime)

    // Iterate over the defined interstitial time ranges...
    for interstitialRange in playerItem.interstitialTimeRanges {
        // If the current interstitial is contained within the user's
        // seek range, return the interstitial's start time
        if seekRange.containsTimeRange(interstitialRange.timeRange) {
            return interstitialRange.timeRange.start
        }
    }

    // No match, return the target time
    return targetTime
}

For any forward seeks, the example code ensures that the user can’t skip past an ad break. It attempts to find an interstitial time range within the time range of the user’s seek request. If an interstitial time range is found, the code returns its start time, forcing playback to begin at the start of the advertisement.

See Also

Media Playback

Adopting Picture in Picture in a Standard Player

Add picture-in-picture controls to your player using a player view controller.

Adopting Picture in Picture in a Custom Player

Add picture-in-picture controls to your custom player using a picture-in-picture controller.

Using AVKit in iOS

This sample code project demonstrates techniques for tracking AVPlayerViewController presentation state across multiple application scenarios, including being displayed fullscreen, embedded inline, or as Picture in Picture.

Adopting Custom Interactive Overlays, Channel Flipping, and Parental Controls in tvOS Video Playback

This sample demonstrates support for custom interactive overlays, and parental controls, using AVPlayerViewController.

Adding Information to the Info Panel (tvOS)

Add metadata information and navigation markers to the Info panel in Apple TV.

class AVPlayerViewController

An object that displays the video content from a player object along with system-supplied playback controls.

class AVPlayerView

A macOS-only object that displays the video content from a player object along with system-supplied playback controls.

class AVPictureInPictureController

A controller that responds to user-initiated playback of video in a floating, resizable window on iPad.

class AVRoutePickerView

An object that displays controls for picking playback routes.

class AVInterstitialTimeRange

A time range in an audiovisual presentation as interstitial content, such as advertisements or legal notices.

class AVNavigationMarkersGroup

A set of markers for navigating playback of an audiovisual presentation.