2020/09/23:追記
頂いたコメントを元に再調査を実施、新しい対策方法を追記しました。
@imamurhさん、ありがとうございます!
2020/10/01:追記
「iOS14.2 beta2」で検証したところ、このバージョンでは対策しなくてもBG再生が出来ることを確認。
この件、iOS14側のバグであった可能性が高まってきました。
ただ、まだベータ版なので今後もチェックしていきたいと思います。
@a26364177jp, thanks for your comment!
概要
現在、リリースしているメディア管理系アプリで行ったiOS14に関する不具合調査とその対策です。
経緯とバグ調査
アプリを使って頂いているユーザーさんから**「iOS14でバックグラウンド再生が出来ない」**
との問い合わせがありました。
早速、手元の実機で調査してみると
動画ファイルを再生中 → アプリをバックグラウンドへ移行 → 再生が一時停止されてしまう
という現象を確認出来ました。
これが、**iOS14の仕様(であればアプリ側のバグ)**なのか
iOS14側の不具合なのかは今のところ分かりません。
ただ、音声ファイルの再生時にこの現象は起きないので
(バックグラウンドでも再生は継続される)
おそらく、iOS14の新しい仕様の可能性が高いのかなと思います。
ちなみに一時停止してしまった動画は、
コントロールセンターからプレイバックボタンを押す事で、再び再生が可能でした。
(追記:「iOS14.2 beta2」での実機検証では、BG再生可能な事からiOS14.0のバグだった可能性あり)
対策(追記しました)
バックグラウンドで動画再生させるケースはあまりない?と思われがちですが
実際、弊アプリのユーザーさんでは音楽動画などをBG再生して聴かれるケースも多いです。
症状は動画ファイル再生時のみですが、取り急ぎでも対応が必要です。
まずAVAudioSessionで色々やってみる
AVFoundation FrameworkのAVAudioSessionのカテゴリーを調整してみます。
AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback, options: [])
ここのmodeとoptionsを調整してみます。
extension AVAudioSession.Mode {
public static let `default`: AVAudioSession.Mode
public static let voiceChat: AVAudioSession.Mode
public static let gameChat: AVAudioSession.Mode
public static let videoRecording: AVAudioSession.Mode
public static let measurement: AVAudioSession.Mode
public static let moviePlayback: AVAudioSession.Mode
public static let videoChat: AVAudioSession.Mode
public static let spokenAudio: AVAudioSession.Mode
public static let voicePrompt: AVAudioSession.Mode
}
public struct CategoryOptions : OptionSet {
public init(rawValue: UInt)
public static var mixWithOthers: AVAudioSession.CategoryOptions { get }
public static var duckOthers: AVAudioSession.CategoryOptions { get }
public static var allowBluetooth: AVAudioSession.CategoryOptions { get }
public static var defaultToSpeaker: AVAudioSession.CategoryOptions { get }
public static var interruptSpokenAudioAndMixWithOthers: AVAudioSession.CategoryOptions { get }
public static var allowBluetoothA2DP: AVAudioSession.CategoryOptions { get }
public static var allowAirPlay: AVAudioSession.CategoryOptions { get }
}
色々と組み合わせてみるが、効果無し。
仕方がないので、少し強引な対策で対処
根本的な対策方法については、引き続き調べていくとして
とりあえずの落としどころが欲しかったので、
-
まずAVPlayerItemのstatusを監視
-
システムから再生が停止されてしまった際 → playbackLikelyToKeepUpの変更を検知
isPlaybackLikelyToKeepUp -
そのタイミングでバックグラウンド再生中であれば、再び動画を再生させる。
と言う方法で対処することにしました。
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?){
if let item = object as? AVPlayerItem, let keyPath = keyPath {
if item == self.playerItem {
switch keyPath {
// ...略
case #keyPath(AVPlayerItem.playbackLikelyToKeepUp):
debugPrint("PlaybackLikelyToKeepUp is changed")
// ** BG状態かつ、再生ステータスなのにAVPlayerが停止という状態をチェック
if isInBackground, isInconsistentPlaybackState {
play()
debugPrint("Background Playback Forced Resuming >> Did Execute")
}
default:
break
}
}
}
}
2020/09/23追記: 再生中アイテムのビデオトラックを無効化
こちらのQAをコメントにて指摘頂き、実際に検証してみました。
Playing media while in the background using AV Foundation on iOS
ここで挙げられている2つのうち、AVPlayerLayerを削除する方法は既に実施していましたが
ビデオトラックをDisableにする方法は実施していませんでした。
早速試してみると、見事にバックグラウンドに移行した際も再生が継続する様になりました。
現在再生しているアイテムのビデオトラックを探して無効化
func changeVideoTrackState(isEnabled: Bool){
if let tracks = currentPlayerItem.tracks {
for playerItemTrack in tracks {
// Find the video tracks.
if let assetTrack = playerItemTrack.assetTrack, assetTrack.hasMediaCharacteristic(.visual) {
// Enable/Disable the track.
playerItemTrack.isEnabled = isEnabled
}
}
}
}
アプリがバックグラウンドへ移行したタイミングで呼び出して、ビデオトラックを無効化します。
@objc func applicationDidEnterBackground(_ notifiaction: Notification){
changeVideoTrackState(isEnabled: true)
}
アプリに戻ってきた際に再び有効化。元に戻します。
@objc func applicationDidBecomeActive(_ notifiaction: Notification){
changeVideoTrackState(isEnabled: true)
}
どちらにしても、iOS13まではBG再生出来ていたので、iOS14から何かしらの仕様が変わった可能性があります。
この対策で、再び様子を見ていきたいと思います。