【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