【macOS High Sierra 10.13.5+Xcode 9.3+iPhoneSE(iOS 11.1.2)】
カメラロールからビデオを検索して、AVPlayerで再生するデモを作っていて、ハマったのでメモ。
ビデオの再生して、もう一度同じビデオを再生しようとするとエラーになってアプリが落ちてしまうという現象に遭遇。表示されるエラーがこちら。
'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
翻訳すると「AVPlayerItemを複数のAVPlayerインスタンスに関連付けることはできません」とのこと
何のことか理解できずにいろいろ試してみたところ、AVPlayerItemをそのままAVPlayerに使ってはいけないらしい。
・エラーになったコード(AVPlayerItemをそのままAVPlayerに渡す)
var avPlayerItem: AVPlayerItem! var player:AVPlayer! var playerItem:AVPlayerItem? var playerController:AVPlayerViewController? func initItem(item:AVPlayerItem) { playerItem = item player = AVPlayer(playerItem:playerItem) playerController = AVPlayerViewController() playerController?.player = player }
・正しく動いたコード1(AVPlayerItemをurlで初期化して、AVPlayerに渡す)
var avPlayerItem: AVPlayerItem! var player:AVPlayer! var playerItem:AVPlayerItem? var playerController:AVPlayerViewController? func initItem(item:AVPlayerItem) { playerItem = AVPlayerItem.init(url: (item.asset.value(forKey: "URL") as! NSURL) as URL) player = AVPlayer(playerItem:playerItem) playerController = AVPlayerViewController() playerController?.player = player }
・正しく動いたコード2(AVPlayerItemをassetで初期化して、AVPlayerに渡す)
var avPlayerItem: AVPlayerItem! var player:AVPlayer! var playerItem:AVPlayerItem? var playerController:AVPlayerViewController? func initItem(item:AVPlayerItem) { playerItem = AVPlayerItem.init(asset: item.asset) player = AVPlayer(playerItem:playerItem) playerController = AVPlayerViewController() playerController?.player = player }
もう一つの初期化の方法として、
init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?)
Initializes a player item with the specified asset and the asset keys to be automatically
てのがあったんだけど、「automaticallyLoadedAssetKeys」がよくわからず割愛。
要するに「AVPlayerに使うAVPlayerItemは何らかの方法で初期化してから渡せ」と理解しました。勘違いがあったらご指摘いただけると喜びます。
【補足】
ちなみにカメラロールから動画を取得する処理はこんな感じ
var videos:NSMutableArray? //カメラロールから動画を読み込む func loadVideos() { //メディアタイプをビデオに絞って取得 let assets:PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.video, options: nil) //取得したアセットを変換 assets.enumerateObjects({(obj, index, stop) -> Void in //PHImageManagerを使ってplayerItemに self.phmov.requestPlayerItem(forVideo: assets[index], options: nil, resultHandler: {(playerItem, info) -> Void in //配列に追加 videos!.add(playerItem!) //最後の処理が終わったらメインスレッドでコレクションビューをリロード if index == assets.count-1{ //終了時に呼び出す処理はココに書く }) }) }
ボタンが押されたら配列(videos)からAVPlayerItemを取りだして、再生するViewに移動
@IBAction func tapBtn1(_ sender: UIButton) { gotoViewView(item: videos![0] as! AVPlayerItem) } func gotoViewView(item:AVPlayerItem) { let sb = UIStoryboard.init(name: "Main", bundle: nil) let vc = sb.instantiateViewController(withIdentifier: "videoVC") as! VideoViewController vc.initItem(item: item) self.present(vc, animated:false, completion:nil) }
【参考】
ios – 自作アルバムの動画をAVPlayerで再生するとsignal SIGABRT発生(iPad) – スタック・オーバーフロー
AVPlayerItem – AVFoundation | Apple Developer Documentation