【swift】An AVPlayerItem cannot be associated with more than one instance of AVPlayer

【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

【Swift】SwiftでAVPlayerの動画プレイヤーを作った – きみが思い出になる前に

Swift3で動画をカメラから選択してきて描画して再生まで

iOS 複数動画同時再生

AVPlayerの再生ステータス(playing/pause/end)を取得する – kitoko552.memo