2022 Audio & Video
WWDC22 · 14 min · Audio & Video
Explore media metadata publishing and playback interactions
Learn how you can highlight your app’s Now Playing information on every platform. We’ll take you through an overview of media metadata, learn how it gets represented in areas like the Lock Screen and Control Center, and show you how to write and publish effective media metadata for your content. We’ll also explore how your app can respond to commands from other devices such as HomePod.
Watch at developer.apple.com ↗Code shown on screen · 9 snippets
Instantiation examples
// playing Magnificent
self.session = MPNowPlayingSession(players: [player])
// Playing different WWDC sessions, one full screen and one in PiP
self.session = MPNowPlayingSession(players: [player])
self.pipSession = MPNowPlayingSession(players: [pipPlayer])
// playing multi-view race
self.session = MPNowPlayingSession(players: [topLeft, topRight, bottomLeft, bottomRight]) Promoting and demoting sessions as Now Playing
// Promoting and demoting sessions as Now Playing
self.session = MPNowPlayingSession(players: [player])
self.pipSession = MPNowPlayingSession(players: [pipPlayer])
// if the content in PiP is promoted to full screen, swap active
self.pipSession.becomeActiveIfPossible { becameActive in
// if success, pipSession data populates lock screen, etc, and
// controls from lock screen, etc are routed to pipSession
} Responding to play and pause commands
// Example of responding to play and pause commands
self.session = MPNowPlayingSession(players: [player])
// respond to play commands
self.session.remoteCommandCenter.playCommand.addTarget { event in
player.play()
return .success
}
// respond to pause commands
self.session.remoteCommandCenter.pauseCommand.addTarget { event in
player.pause()
return .success
} Responding to skip forward commands
// Example responding to skip forward commands
self.session.remoteCommandCenter.skipForwardCommand.preferredIntervals = [15.0]
self.session.remoteCommandCenter.skipForwardCommand.addTarget { event in
let skipCommand = event as! MPSkipIntervalCommandEvent
player.seek(to: CMTimeAdd(player.currentTime(), CMTimeMakeWithSeconds(skipCommand.interval, preferredTimescale: 1)))
return .success
}
// commands can also be disabled. for example, during an ad:
self.session.remoteCommandCenter.skipForwardCommand.isEnabled = false
// add handlers for all commands that are applicable to the content
// https://developer.apple.com/documentation/mediaplayer/mpremotecommandcenter Setting artwork metadata with automatic publishing
// Example of setting artwork metadata
let artwork = MPMediaItemArtwork(image: image)
let title = "Magnificent"
playerItem.nowPlayingInfo = [
MPMediaItemPropertyTitle: title,
MPMediaItemPropertyArtwork: artwork,
// …
]
self.session = MPNowPlayingSession(players: [player])
self.session.automaticallyPublishNowPlayingInfo = true Setting ad time ranges for automatic publishing
// Example with ads that should not contribute to elapsed time and duration
let preroll = MPAdTimeRange(timeRange: CMTimeRange(start: CMTime.zero, duration: CMTimeMakeWithSeconds(30, preferredTimescale: 1)))
playerItem.nowPlayingInfo = [
…
MPNowPlayingInfoPropertyAdTimeRanges: [preroll]
…
]
self.session = MPNowPlayingSession(players: [player])
self.session.automaticallyPublishNowPlayingInfo = true Setting artwork and title metadata for AVKit
// Example of setting artwork metadata
let path = Bundle.main.path(forResource: "poster", ofType: "jpg")
let posterData = FileManager.default.contents(atPath: path!)!
let artwork = AVMutableMetadataItem()
artwork.identifier = .commonIdentifierArtwork
artwork.value = posterData as NSData
artwork.dataType = kCMMetadataBaseDataType_JPEG as String
artwork.extendedLanguageTag = "und"
let title = AVMutableMetadataItem()
title.identifier = .commonIdentifierTitle
title.value = "Magnificent" as NSString
title.extendedLanguageTag = "und"
playerItem.externalMetadata = [artwork, title] Manually publishing Now Playing
// Example of setting Now Playing information
let artwork = MPMediaItemArtwork(image: image)
let nowPlayingInfo = [
MPMediaItemPropertyTitle: title,
MPMediaItemPropertyArtwork: artwork,
MPMediaItemPropertyPlaybackDuration: playerItem.duration,
MPNowPlayingInfoPropertyElapsedPlaybackTime: player.currentTime().seconds,
MPNowPlayingInfoPropertyPlaybackRate: player.rate
]
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo Updating Now Playing metadata
// On any non-linear time change, playback rate change, or play/pause
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player.currentTime().seconds
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate Resources
Related sessions
-
24 min