Currently, web players aren’t supported for TV App integrations.
AVKit tvOS Integrations
Implementing Now Playing Info when you are using AVKit (AVPlayerViewController) on tvOS is easy — AVKit has done the bulk of the heavy lifting for you, providing the majority of the metadata properties.
You are still responsible for providing the Content Identifier or Service Identifier and Playback Progress (if applicable).
Property | Constant |
---|
Service Identifier | AVKitMetadataIdentifierServiceIdentifier |
Content Identifier | AVKitMetadataIdentifierExternalContentIdentifier |
Playback Progress | AVKitMetadataIdentifierPlaybackProgress |
If you intend to provide additional metadata (for example, to populate the HUD), see the AVPlayerItem.h header file for common metadata properties.
Below is an example of adding metadata properties for the VOD content type:
let videoURL = NSURL(string: “https://example.com/video_manifest.m3u8")
let playerItem = AVPlayerItem(url: videoURL)
let metaTitle = AVMutableMetadataItem()
metaTitle.identifier = AVMetadataCommonIdentifierTitle
metaTitle.extendedLanguageTag = "und"
metaTitle.value = “Example Title"
let metaContentId = AVMutableMetadataItem()
metaContentId.extendedLanguageTag = "und"
metaContentId.value = “example_content_id”
let metaPlaybackProgress = AVMutableMetadataItem()
metaPlaybackProgress.extendedLanguageTag = "und"
metaPlaybackProgress.value = 0
if #available(tvOS 10.1, *) {
metaContentId.identifier = AVMetadataIdentifier(AVKitMetadataIdentifierExternalContentIdentifier)
metaPlaybackProgress.identifier = AVMetadataIdentifier(AVKitMetadataIdentifierPlaybackProgress)
} else {
metaContentId.identifier = AVMetadataIdentifier("NPI/_MPNowPlayingInfoPropertyExternalContentIdentifier")
}
playerItem.externalMetadata = [metaTitle, metaContentId, metaPlaybackProgress]
Once the metadata properties have been added to your AVPlayerItem’s externalMetadata property, AVKit handles updating the Now Playing Info for you.
iOS and non-AVKit tvOS Integration
Implementing Now Playing Info on iOS and on tvOS when you are not using AVKit requires four pieces:
1. Registration of remote commands via MPRemoteCommandCenter In order for iOS and tvOS to recognize the Now Playing Info and to properly support external remotes and accessories (lock screen, Control Center, AirPods, Apple Watch), your app must register for remote commands, and handle the commands appropriately.
2. Disable AVKit Now Playing Info Updates If you are using AVKit on iOS, you must disable AVKit from sending Now Playing Info updates, otherwise it will interfere with your manual updates.
3. Sending Now Playing Info updates on playback start and future playback events The playback activity data provided using the MPNowPlayingInfoCenter class is the key to allowing playback tracking in the Apple TV App. Updated Now Playing Info metadata must be provided on key playback events such as play, pause, and seek. If your content contains interstitial content (e.g. advertisements, bumpers, promos, previews, etc), set the Playback Rate to 0 and do not update the Elapsed Time during interstitial content playback. Once interstitial content playback is complete, resume updating Now Playing Info as applicable for your asset.
4. Unregister for remote commands When your player is torn down, you must unregister for remote commands, otherwise the system will assume that playback is still eligible to be resumed.
Registering for Remote Commands
As noted above, registering for remote commands is required in order for iOS and tvOS to recognize Now Playing Info updates. You must register for at least one remote command, but we recommend registering for as many of the available remote commands as your player, and the respective content type, supports. It is also the mechanism to receive commands such as pause, resume, and seek from the lock screen, Control Center, and external accessories.
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.pauseCommand.isEnabled = true
commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
// pause your player
return .success
}
commandCenter.seekForwardCommand.isEnabled = true
commandCenter.seekForwardCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
// seek forward
return .success
}
commandCenter.seekBackwardCommand.isEnabled = true
commandCenter.seekBackwardCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
// seek backward
return .success
}
Disable AVKit Now Playing Updates
AVKit on iOS does not allow you to provide the extended metadata properties required by the Apple TV App. Therefore, you must disable AVKit from sending Now Playing Info updates to prevent it from interfering with your manual updates.
self.playerViewController.updatesNowPlayingInfoCenter = false
Send Now Playing Info Updates
Once playback has been initiated, you must update the Now Playing Info with the required metadata properties for the content type.
Note: To aid in development, we strongly recommend using the NowPlayingLogger class during debugging, which allows you to see the same Now Playing Info changes that Apple will see during validation. You can find the NowPlayingLogger class in the Search & Discovery Portal on App Store Connect.
Event | Property Updates |
---|
Video Start | MPNowPlayingInfoPropertyExternalContentIdentifier set to the content identifier that matches the identifier provided in the availability feed MPMediaItemPropertyPlaybackDuration set to the net duration of the content in seconds MPNowPlayingInfoPropertyPlaybackRate set to 1.0 MPNowPlayingInfoPropertyElapsedPlaybackTime set to the playback offset in seconds |
Play | MPNowPlayingInfoPropertyPlaybackRate updated to 1.0 |
Pause | MPNowPlayingInfoPropertyPlaybackRate updated to 0 MPNowPlayingInfoPropertyElapsedPlaybackTime updated to the playback offset in seconds |
Seek | MPNowPlayingInfoPropertyElapsedPlaybackTime updated to the playback offset in seconds |
Video completion or player dismissal | nowPlayingInfo dictionary set to nil |
Now Playing Info updates should only be sent on the events listed above, and not at an arbitrary interval.
For example, when playback begins for a VOD asset, you might send an initial Now Playing Info update with the content ID:
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "WWDC 2016"
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = durationInSeconds
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTimeInSeconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = 1.0
if #available(iOS 10.0, *, tvOS 10.0, *) {
nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] = contentId
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackProgress] = 0.0
}
nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo
When the user pauses the player, you would send another update using the existing dictionary, and updating the values for elapsed time and playback rate:
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
var nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTimeInSeconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = 0.0
nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo
Unregister for Remote Commands
Once the player is dismissed, you must unregister for the remote commands that were registering before playback started.
commandCenter.pauseCommand.isEnabled = false
commandCenter.pauseCommand.removeTarget(nil)
commandCenter.seekBackwardCommand.isEnabled = false
commandCenter.seekBackwardCommand.removeTarget(nil)
commandCenter.seekForwardCommand.isEnabled = false
commandCenter.seekForwardCommand.removeTarget(nil)
TVML
Implementing Now Playing Info in TVML apps is just as easy as AVKit:
player.showsResumeMenu = false;
var myMediaItem = new MediaItem(‘video’, ‘https://example.com/video_manifest.m3u8’);
myMediaItem.title = ‘Example Title’;
myMediaItem.externalID = “content_id”; // contentId from availability feed
myMediaItem.metadata = {
playbackProgress: 0.0
};
Note: the MediaItem.metadata dictionary should only be set if you are setting playbackProgress. See Expected Metadata above for more information
Provide the external Service Identifier and playback progress (if applicable):
Event | Constant |
---|
External Service Identifier | externalServiceIdentifier |
Playback Progress | playbackProgress |