스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
미디어 메타데이터 게시 및 재생 상호 작용 살펴보기
모든 플랫폼에서 앱의 '지금 재생 중' 정보를 강조하는 방법을 알아보세요. 미디어 메타데이터의 개요를 소개하고, 잠금 화면 및 제어 센터와 같은 영역에 표시하는 방법을 알아보며, 콘텐츠에 효과적인 미디어 메타데이터를 작성 및 게시하는 방법을 보여드립니다. 또한 앱이 HomePod과 같은 다른 기기의 명령에 응답할 수 있는 방법을 알아보겠습니다.
리소스
관련 비디오
WWDC22
-
다운로드
♪ ♪ 안녕하세요 비디오 팀의 Nik입니다 미디어 메타데이터 게시와 재생 상호작용에 대해 말씀드리게 되어 기쁩니다 먼저 이들의 정확한 의미가 무엇일까요? Apple 기기에는 수많은 곳에서 재생 정보가 표시되거나 재생이 제어됩니다 예컨데 제어 센터의 Now Playing 섹션에선 현재 기기에서 재생 중인 미디어의 표지, 제목과 미디어 재생 현황을 표시하며 재생, 일시정지, 앞 혹은 뒤로 건너뛸 수도 있습니다 이 타일을 확장하면 표지나 재생 현황 등 상세 내용이 보이며 재생 중인 곳을 이동하거나 볼륨을 조절할 수도 있습니다 잠금 화면에서도 같은 정보와 제어를 보여줘 사용자들이 편하게 잠금을 해제 없이 재생 현황을 확인하거나 일시정지를 할 수 있고 AirPlay로 다른 기기에서 재생할 수도 있습니다
어떤 기기에서 재생 중이어도 Apple Watch의 Now Playing 앱에서 같은 경험을 제공합니다 Apple TV Remote도 가능합니다
tvOS에서 AVKit를 사용할 때 정보 오버레이에 제어가 표시될 때 제목과 챕터가 표시됩니다 아래로 쓸어넘겨 정보 창을 보면 표지나 설명 등과 같은 상세 정보가 표시됩니다
Apple TV Remote의 TV 버튼을 길게 누르면 제어 센터가 보이며 iOS의 Now Playing 타일처럼 확장할 수 있습니다 리모컨에서 재생 버튼을 누르거나 다른 기기의 음악 앱에서 곡을 선택해 tvOS의 백그라운드에서 오디오 컨텐츠가 재생되면 Now Playing 정보에 대한 알림이 표시됩니다 또한 오디오가 재생 중일 때 tvOS에서 잠시 동안 활동이 없으면 현재 재생 중인 것을 보여주는 전체 화면 오버레이가 표시됩니다
마지막으로 iOS에서 Control Other Speakers and TVs 버튼으로 모든 기기에서 Now Playing 정보를 보고 재생을 제어할 수 있습니다
Now Playing 정보가 표시되며 재생을 제어할 수 있는 기기와 UI의 수가 많아짐에 따라 Now Playing 정보를 알맞게 게시하는 것과 원격 명령을 바르게 하는 것이 전보다 훨씬 중요해졌습니다 이 세션의 남은 부분 전반에서 원격 명령, 자동 메타데이터 게시 AVKit를 이용한 게시와 수동 게시의 형태로 재생 상호작용에 대응하는 법을 다룹니다 미디어 재생을 위해 AVFoundation을 사용할 때 Now Playing 메타데이터 게시 후 재생 상호작용에 대응하는 최적의 방법은 MPNowPlayingSession 클래스를 이용하는 것입니다
지금까지 이 클래스는 tvOS에서만 사용 가능했지만 이제 iOS 16에서도 사용 가능합니다
이 클래스는 별개의 재생 세션을 표현하고 앱이 여러 개의 활성 세션을 포함할 때 Now Playing 상태를 제어하는데 사용됩니다 이 클래스는 메타데이터 수동 게시를 지원하며 iOS 16과 tvOS 16에서는 새롭게 자동 게시를 지원합니다
AVKit을 사용 할 때 tvOS에서는 고유한 자동 메타데이터 게시 메커니즘이 있으므로 MPNowPlayingSession를 사용하면 안됩니다 이는 나중에 다시 다루겠습니다 ‘Now Playing' 앱이 된다는 것은 여러분의 앱이 제어 센터, 잠금 화면 등에 표시되며 사용자가 이 인터페이스들 중 하나에서 일시정지를 누르는 것과 같은 재생 제어를 수신할 수 있게 되죠 MPNowPlayingSession으로 하나의 앱에서 여러 동시 재생 세션을 표시할 수 있습니다 하지만 여러 세션에서 여러분의 앱을 원격 제어할 때 여러분의 앱은 전체 시스템에서 하나의 앱만 활성화해야 합니다 예를 들어 화면 속 화면에서 두 개의 동시 재생 세션이 있다면 전체 화면에서 재생되는 세션이 지금 재생 중인 활성 세션이 되어야 합니다 시스템은 앱을 지금 재생 중으로 정하는 몇 가지 휴리스틱이 있죠 먼저 최소 한 개 이상 원격 명령에 대한 핸들러를 등록해야 하죠 어떠한 재생 상호작용에도 응답하지 않는 앱은 Now Playing 앱으로 보여주기에 최적의 앱이 아닐 가능성이 큽니다 두 번째로 여러분 앱의 AVAudioSession은 섞이지 않는 카테고리와 카테고리 설정으로 구성돼야 해요 섞일 수 있는 재생 카테고리와 설정은 보통 알림을 재생할 때 사용되므로 무엇이 재생 중이든 Now Playing에 적합한 앱은 아닙니다 재생 세션의 이해를 돕는 예시를 보겠습니다 이 예시에서는 하나의 콘텐츠가 재생 중이므로 하나의 MPNowPlayingSession으로 표현됩니다 앱이 화면 속 화면을 지원하면 두 개의 MPNowPlayingSession이 되는데 메인 플레이어와 화면 속 화면 재생에 대해 각각 하나씩입니다 더 복잡한 경우로는 하나의 MPNowPlayingSession가 여러 플레이어를 갖는 경우입니다 이 예시에서는 각각의 사분면에 한 개씩 네 개의 플레이어가 각각 경주의 다른 시점을 보여주고 있습니다 같은 MPNowPlayingSession에 추가된 플레이어들은 항상 같은 콘텐츠의 일부여야 합니다 다음은 이 각각의 예제 세션들이 인스턴스화 되는 방법입니다 먼저 콘텐츠의 한 부분만 재생하므로 하나의 플레이어에 하나의 세션을 가집니다 두 번째 예제는 화면 속 화면을 사용하는 것으로 각각의 플레이어마다 하나씩 두 개의 세션을 갖게 됩니다 첫 번째는 전체 화면 콘텐츠 두 번째는 화면 속 화면 콘텐츠죠 마지막 예제는 하나의 세션에서 네 개의 플레이어로 경주의 여러 시점을 보는 것입니다 앱이 여러 개의 세션을 갖고 있으면 가능한 한 앱에서 지정된 하나의 세션을 활성 상태로 만들어야 합니다 예를 들어 미디어가 화면 속 화면에서 재생 중일 때 사용자가 이를 전체 화면으로 확장하면 이전의 전체화면 세션은 더 이상 활성 세션이나 지금 재생 중이 아니어야 하고 전체 화면이 된 화면 속 화면의 세션이 활성 세션이 돼야 합니다 이는 MPNowPlayingSession의 becomeActiveIfPossible를 호출해 전환할 수 있습니다 지금까지 MPNowPlayingSession의 인스턴스를 세팅하는 법과 Now Playing 세션을 제어하는 기본적인 방법을 다뤘으니 이제 잠금 화면이나 다른 방에 있는 HomePod으로부터 원격 명령을 수신하고 반응하는 법을 보겠습니다 재생과 일시정지 명령을 등록하는 기본적인 예제부터 보겠습니다 이렇게 함으로써 사용자가 다른 기기에서 재생이나 일시정지 버튼을 누르거나 Siri로 명령할 때 앱이 콜백을 받을 수 있게 됩니다 먼저 MPNowPlayingSession을 인스턴스화 합니다 하나의 세션만 있으므로 becomeActiveIfPossible 메소드를 호출할 필요가 없습니다 하나의 세션만 있을 때는 앱이 Now Playing 앱일 때 기본 세션이 됩니다 각각의 MPNowPlayingSession 인스턴스는 재생 세션이 응답할 수 있는 원격 명령을 선언하는데 사용되는 자체적인 MPRemoteCommandCenter 인스턴스를 가집니다 다음으로 플레이어에서 play 메소드를 호출하는 playCommand에 대한 핸들러를 추가하고 .success를 반환합니다 pauseCommand에 대해서도 같은 작업을 합니다 앱이 지원하고 적용 가능한 모든 명령에 대해 핸들러를 추가해야합니다 다음 예제는 앞, 뒤로 건너뛰기 명령입니다 이 명령은 대부분의 콘텐츠에 적용되어야 하지만 앞으로 이동할 수 없는 라이브 스트림과 같은 곳에는 적용하는 것이 불가능합니다 첫 번째로 각 방향으로 건너뛸 초나 간격을 정해야 합니다 이 경우에는 15초로 하겠습니다 다음으로 앞서 재생과 일시정지 명령에 대해서 했던 것과 비슷하게 사용자가 앞으로 건너뛰기 버튼을 누르거나 Siri로 명령했을 때 호출될 핸들러를 추가합니다 이 핸들러에서는 MPSkipIntervalCommandEvent를 수신해서 해당 타입으로 이벤트를 보냅니다 그러고 나서 현재 시간으로부터 경과한 시간을 계산하고 MPSkipIntervalCommandEvent에서 제공된 간격을 찾은 뒤 새로운 위치로 건너뛰었음을 알리기 위해 success를 반환합니다 또한 광고 중일 때와 같이 앞으로 건너뛰기가 일시적으로 허용되지 않은 경우도 있습니다 이 경우에는 skipForwardCommand가 비활성화 될 수 있습니다 이제 원격 명령에 응답하게 됐으니 자동 메타데이터 게시를 보겠습니다 자동 게시는 전체 시간과 현재 경과 시간 재생 상태 및 재생 진행과 같이 플레이어에서 직접 얻어지는 메타데이터 속성을 자동으로 유지해서 이를 정확하게 유지하는 수고를 덜어줍니다 콘텐츠에 전체 시간이나 경과 시간에 포함되면 안되는 광고가 포함되어 있는 경우 이를 제외한 시간을 계산해 보고할 수도 있습니다 제목, 설명, 표지 등 다른 메타데이터들은 nowPlayingInfo 속성으로 AVPlayerItems에 직접적으로 추가될 수 있죠 이 예제에서는 자동 게시를 이용해 대부분의 작업을 수행하고 제목과 표지를 직접 설정해보겠습니다 첫 번째로 MPMediaItemArtwork 인스턴스를 만들어 표지 이미지를 전달합니다 대부분의 앱은 이를 가져오기 위해 네트워크 요청을 수행합니다 다음으로 콘텐츠의 문자열 제목을 설정합니다 이 표지와 제목을 MPMediaItemPropertyTitle과 MPMediaItemPropertyArtwork로 현재 플레이어 항목의 nowPlayingInfo 딕셔너리로 설정합니다 Now Playing 메타데이터는 MPMediaItemProperty와 MPNowPlayingInfoProperty 둘 다로 구성될 수 있습니다 마지막으로 MPNowPlayingSession 인스턴스를 만들어 플레이어를 전달하고 automaticallyPublish NowPlayingInfo를 true로 설정해요 automaticallyPublish NowPlayingInfo가 true로 설정되면 MPNowPlayingSession 인스턴스는 플레이어에서 재생 중인 곳이 이동되거나 재생/일시정지 등의 이벤트 혹은 재생중인 항목의 변화 등을 관찰하기 시작합니다 이번엔 콘텐츠에 광고가 포함되는데 전체 시간이나 경과 시간에 광고 시간이 포함되지 않기를 바랄 때 인스턴스에 대한 자동 게시를 사용하는 법에 대한 예제입니다 이를 위해 포함된 모든 광고에 대해 MPAdTimeRange 인스턴스를 만들어줍니다 이 예제에서는 시작할 때 30초짜리 광고 한 개가 포함되어 있습니다 따라서 시작점은 0초, 지속시간은 30초로 인스턴스를 만들어줍니다 제목과 표지에 대해 했던 것과 비슷하게 MPAdTimeRange 배열을 MPNowPlayingInfoProperty AdTimeRanges로 플레이어 항목의 nowPlayingInfo 딕셔너리에 추가합니다 그리고 앞서 했던 것과 같이 MPNowPlayingSession를 만들고 자동 게시를 허용합니다 다음은 AVKit를 이용한 메타데이터 게시입니다 tvOS에서 AVKit로 Now Playing 메타데이터를 게시하는 것은 MPNowPlayingSession과 매우 비슷합니다 메타데이터는 AVPlayerItem에 바로 추가되며 진행 시간, 전체 시간, 재생 상태 등의 값들은 게시되고 최신으로 유지됩니다 플레이어로와 콘텐츠로부터 직접적으로 얻어진 메타데이터는 AVPlayerItem상의 앱에 의해 제공되는 메타데이터와 결합되어 플레이어 UI의 정보 창에 표시됩니다 AVKit는 원격 명령 응답에 대한 등록도 관리하죠 AVKit를 사용하는 건 지금까지 본 AirPlay나 화면 속 화면 등의 다른 플랫폼 기능과 통합하는 제일 좋고 쉬운 방법입니다 AVKit으로 메타데이터를 설정하는 것은 콘텐츠를 설명하는 AVPlayerItem에서 externalMetadata 배열을 사용해 끝나며 이는 AVMetadataItem 인스턴스로 구성됩니다 보통 AVMetadataItem마다 세 가지 값을 설정합니다 첫 번째는 identifier로 AVMetadataItem이 나타내는 메타데이터를 식별하는 키 입니다 예를 들어 제목에 대해서는 AVMeta dataCommonIdentifierTitle이 표지에 대해서는 AVMetadata CommonIdentifierArtwork이 있습니다 두 번째는 value입니다 제목의 경우에는 문자열로 된 콘텐츠 제목이 포함되고 표지의 경우에는 이미지 데이터를 포함하는 NSData 인스턴스가 되죠 dataType은 제공된 표지의 포맷이 무엇인지 식별하는데 사용됩니다 만일 JPEG 데이터라면 kCMMetadatabaseDataType_JPEG이 사용됩니다 마지막으로 extendedLanguageTag는 제목이나 설명 등에 사용된 언어를 식별합니다 대부분의 경우 모든 시청자가 같은 값을 볼 수 있도록 하기 위해 'und'가 사용돼야 합니다 값이 영어로 되어있는 경우 'en-us'를 사용하고 싶으시겠지만 그렇게 하면 스페인어 등 타 언어로 설정되어 있는 기기에선 메타데이터가 보이지 않을 수 있습니다
여기 표지와 제목을 설정하는 예제가 있습니다 먼저 Bundle에서 표지 이미지 데이터를 가져옵니다 대부분의 앱은 리소스로부터 이를 가져올 것입니다 그러고 나서 변경 가능한 AVMeta dataItem을 인스턴스화합니다 identifier를 .commonIdentifier Artwork로 설정합니다 value는 NSData로 원시 표지 이미지 데이터를 설정해요 이미지 데이터는 JPEG이므로 dataType을 kCMMetadata BaseDataType_JPEG로 설정합니다 만일 PNG라면 kCMMetadataBaseDataType_PNG를 사용합니다 이 메타데이터를 사용자 기기의 언어 설정에 관계없이 표시하기 위해 extendedLanguageTag를 'und' 또는 'undefined'로 설정합니다 이 과정을 .commonIdentifier Title을 써 제목에 반복합니다 value는 문자열 제목이 되고 extendedLanguageTag는 여기서도 'und'입니다 모든 메타데이터 항목을 설정한 뒤 이 둘을 배열로 묶어 AVPlayerItem의 externalMetadata 속성으로 설정합니다 이제 표지와 제목이 플레이어 항목에 추가됐으니 iOS의 잠금 화면과 제어 센터에서 보일 항목을 매핑합니다 표지와 마찬가지로 설명이나 자막 정보, 콘텐츠 점수와 같은 다른 메타데이터들도 설정할 수있습니다 사용자에게 최대한 풍부한 경험을 제공하려면 여러분의 앱은 최대한 많은 메타데이터들을 설정해야 합니다 지금까지 MPNowPlayingSession을 이용한 자동 게시와 AVKit을 이용한 게시를 봤습니다 하지만 MPNowPlayingSession과 자동 게시를 위해서는 AVPlayer 인스턴스를 이에 전달해야 합니다 어떤 앱에서는 이것이 불가능해도 수동 게시는 가능합니다 수동 게시는 모든 메타데이터 값을 제공해야 합니다 자동 게시와는 달리 경과시간과 같은 정보나 재생 속도와 같은 정보는 시스템에서 자동 결정되지 않죠 따라서 낮은 수준의 재생 상태를 수동으로 미세 조절하며 재생 상태가 시간에 따라 변해도 앱이 이를 정확히 유지해야 합니다 이 때도 원격 명령에 대한 등록과 응답이 필요하며 MPNowPlayingSession을 사용하지 않기 때문에 반드시 MPRemoteCommandCenter의 공유 인스턴스를 사용해야 합니다 Now Playing 정보 딕셔너리를 업데이트 하는 법에 대한 예입니다 먼저 자동 게시할 때와 비슷하게 이미지를 포함하는 MPMediaItem Artwork 인스턴스를 만듭니다 그 다음 사용 가능한 메타데이터가 포함된 딕셔너리를 만듭니다 여기서는 제목, 표지, 전체 시간 경과 시간, 재생 속도입니다 그리고 MPNowPlayingInfoCenter를 기본 인스턴스로 설정합니다 이 메타데이터는 재생, 일시정지 앞이나 뒤로 이동하는 것 새 콘텐츠 재생과 같이 중요한 변화가 있을 때마다 업데이트 돼야 합니다 경과 시간을 지속적으로 업데이트 해줄 필요는 없습니다 마지막 업데이트 이후 경과 시간을 바탕으로 올바른 경과 시간이 시스템에서 항상 추정됩니다 이제 Now Playing 메타데이터를 게시하는 다양한 방법과 다른 기기나 인터페이스로부터의 원격 명령에 응답하는 방법에 익숙해지셨으니 이를 통합해 사용자 경험을 극대화하는 것이 그 어느때보다 쉬워졌습니다 기존의 통합도 자동 게시로 전환하면 나중에 퇴보하는 것을 손쉽게 방지하고 유지 및 관리할 코드를 최소로 유지할 수 있는 이점이 있죠 더 많은 정보는 developer.apple.com의 MediaPlayer에서 확인해보세요 시청해주셔서 감사합니다
-
-
4:34 - 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])
-
4:58 - 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 }
-
5:32 - 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 }
-
6:22 - 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
-
7:48 - 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
-
8:38 - 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
-
11:02 - 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]
-
12:59 - 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
-
13:25 - 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
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.