AVPlayerにおけるバックグラウンド再生
やりたいこと
最低限必要な作業
Capability の追加
iOS アプリの Lifecycle では、Background に移行したアプリは iOS によって適宜 Suspend されてしまう。が、いくつかのタスクについては特別に Background での継続した実行を許可することができる。 Apps don’t normally receive any extra execution time after they enter the background. However, UIKit does grant execution time to apps that support any of the following time-sensitive capabilities
どのタスクについて Background での実行を許可するか?については、アプリケーションの Capability として設定されている必要がある。動画/音声の再生の場合は、Audio タスクについての許可が必要となる。
https://docs-assets.developer.apple.com/published/f27f0815f6/d865bb39-a4c6-40ef-ba0b-7823cea46e83.png
AudioSessionに適切なCategoryを設定する
AVAudioSession に適切な Category を設定し、バックグランドに入る前に有効にすることが必要。具体的にはカテゴリには playback を設定するとよい。 動画再生の場合の追加作業
音声のみの再生であれば上記の作業だけで済むが、動画再生の場合は追加の作業が必要になる。AVPlayer の現在の AVPlayerItem がデバイス上に映像を表示していた場合、AVPlayer インスタンスの再生は、バックグラウンド移動時に自動的に一時停止してしまう。これを避けるためには、AVPlayer のインスタンスを、バックグラウンドへの移動の直前に View から削除し、フォアグラウンドに戻ってきたときに繋げ直す必要がある。 これを達成できれば場所はどこでも良いが、Apple のドキュメントだと UIApplicationDelegate で行われる例が記述されている。
code:swift
func applicationDidEnterBackground(_ application: UIApplication) {
// Disconnect the AVPlayer from the presentation when entering background
// If presenting video with AVPlayerViewController
playerViewController.player = nil
// If presenting video with AVPlayerLayer
playerLayer.player = nil
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Reconnect the AVPlayer to the presentation when returning to foreground
// If presenting video with AVPlayerViewController
playerViewController.player = player
// If presenting video with AVPlayerLayer
playerLayer.player = player
}
code:swift
// background event
// foreground event
// add these 2 notifications to prevent freeze on long Home button press and back
制御する
割り込み後に再開する
音声の再生中に、電話の着信があったり、他のアプリケーション上で短い音声や動画の再生を行ってしまったり等の様々な理由で、再生に対する割り込みが発生する可能性がある。
この割り込み動作を監視するためには AVAudioSession が利用できる。AVAudioSession は、割り込みの開始, 終了時に、登録された observer にそれらの割り込みイベントを通知し、observer 側はイベントに応じて適切な処理が実行できるようになっている。 code:swift
func setupNotifications() {
NotificationCenter.default.addObserver(self,
name: AVAudioSession.interruptionNotification,
object: nil)
}
@objc func handleInterruption(notification: Notification) {
// To be implemented.
}
さらに、割り込み終了時には、resume 再生すべきかどうかのオプションを参照し、この中に shouldResume が含まれているかどうかをチェックできる。このオプションはユーザ入力を待たずに音声の再生を再開すべきかどうかを表す。
If the interruption type is AVAudioSession.InterruptionType.ended, check for this value in the AVAudioSessionInterruptionOptionKey key in the userInfo dictionary of the interruptionNotification notification. It serves as a hint that it’s appropriate for your app to resume audio playback without waiting for user input.
このオプションは、例えば電話の着信による割り込み終了時などには true となるが、例えば他のアプリケーションを開いて別のメディアの再生を開始してしまった際などには false となる。
code:swift
@objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
switch type {
case .began:
// 割り込み開始
case .ended:
// 割り込み終了
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// 再生再開すべき
} else {
// 再生再開すべきでない
}
default: ()
}
}