【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で再生するデモを作っていて、ハマったのでメモ。

ビデオの再生して、もう一度同じビデオを再生しようとするとエラーになってアプリが落ちてしまうという現象に遭遇。表示されるエラーがこちら。

1
'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'

翻訳すると「AVPlayerItemを複数のAVPlayerインスタンスに関連付けることはできません」とのこと

何のことか理解できずにいろいろ試してみたところ、AVPlayerItemをそのままAVPlayerに使ってはいけないらしい。

・エラーになったコード(AVPlayerItemをそのままAVPlayerに渡す)

1
2
3
4
5
6
7
8
9
10
11
12
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に渡す)

1
2
3
4
5
6
7
8
9
10
11
12
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に渡す)

1
2
3
4
5
6
7
8
9
10
11
12
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は何らかの方法で初期化してから渡せ」と理解しました。勘違いがあったらご指摘いただけると喜びます。

【補足】
ちなみにカメラロールから動画を取得する処理はこんな感じ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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に移動

1
2
3
4
5
6
7
8
9
10
11
12
@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