'AVPlayerItem无法与多个AVPlayer实例关联'

时间:2015-06-05 02:34:16

标签: ios swift avqueueplayer avplayeritem

我知道这个问题已经遍布Stack Overflow了,但是大部分都是旧的,与我在这里要问的内容无关。

所以我有一个包含AVPlayerItemsAVQueuePlayer的数组。首先我设置AVQueuePlayer来播放数组项:

func mediaPicker(mediaPicker: MPMediaPickerController!, didPickMediaItems mediaItemCollection: MPMediaItemCollection!) {

    mediaItems = mediaItemCollection.items

    for thisItem in mediaItems as [MPMediaItem] {

        var itemUrl = thisItem.valueForProperty(MPMediaItemPropertyAssetURL) as? NSURL
        var playerItem = AVPlayerItem(URL: itemUrl)
        mediaArray.append(playerItem)           

    }

    updateMetadata()

    audioPlayer = AVQueuePlayer(items: mediaArray)


    self.dismissViewControllerAnimated(true, completion: nil)

}

但是,例如,当用户添加新项目或删除一个项目时,我尝试更新播放器(与我在第一时间设置它的方式相同),但它给了我错误。我想知道它是否有任何解决方法或任何解决方案。这是用户删除歌曲的方式:

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == UITableViewCellEditingStyle.Delete{
            mediaArray.removeAtIndex(indexPath.row)
            tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
            audioPlayer = AVQueuePlayer(items: mediaArray) //here gives the error
        }
    }

我正在为此而自杀,所以,如果有人可以帮助我,我会很感激。

6 个答案:

答案 0 :(得分:4)

问题在于,当您使用此行更新项目数组时,您正在创建新的播放器对象(在tableView中:commitEditingStyle:forRowAtIndexPath :),

audioPlayer = AVQueuePlayer(items: mediaArray)

当您执行此操作时,似乎玩家项目仍与原始玩家相关联。您应该能够删除该行,以使其正常工作。从mediaArray插入或删除项目应该足以做你想做的事情;你不需要创建一个新玩家。

答案 1 :(得分:2)

我解决这个问题的方法是,我在数组中保留对AVAsset个对象的引用,然后map将其转换为[AVPlayerItem]AVQUeuePlayer init的类型接受)。

详细说明,我们需要制作一个可以暂停的录音机(code on github),所以我使用了AVQueuePlayer,但遇到了与OP相同的问题,也是唯一一个干净的解决方案是用新数组重新创建这个玩家对象。

Swift 4.1中的相关代码:

class RecordViewController: UIViewController {

    var audioRecorder: AVAudioRecorder?

    var audioChunks = [AVURLAsset]()
    var queuePlayer:  AVQueuePlayer?

// irrelevant code omitted

    func stopRecorder() {
        self.audioRecorder?.stop()
        let assetURL  = self.audioRecorder!.url

        self.audioRecorder = nil

        let asset = AVURLAsset(url: assetURL)
        self.audioChunks.append(asset)

        let assetKeys = ["playable"]
        let playerItems = self.audioChunks.map {
            AVPlayerItem(asset: $0, automaticallyLoadedAssetKeys: assetKeys)
        }

        self.queuePlayer = AVQueuePlayer(items: playerItems)
        self.queuePlayer?.actionAtItemEnd = .advance
    }

    func startPlayer() {
        self.queuePlayer?.play()
    }

    func stopPlayer() {
        self.queuePlayer?.pause()
        self.queuePlayer = nil
    }
}

(最后,AVAssetExportSession is used to stitch the chunks of audio back together,因此最后存储AVAsset AVPlayerItemparted的内容更为有利。)

答案 2 :(得分:1)

来自Apple documentation

在添加项目之前:

  

canInsertItem(_:afterItem:)

     

返回指示的布尔值   是否可以将给定的玩家项目插入到玩家的队列中。

func canInsertItem(_ item: AVPlayerItem!, afterItem afterItem: AVPlayerItem!) -> Bool

添加项目:

insertItem(_:afterItem:)
     

在队列中指定的项目之后放置给定的玩家项目。

 func insertItem(_ item: AVPlayerItem!, afterItem afterItem: AVPlayerItem!)

删除项目:

  

removeItem(_:)

     

从队列中删除给定的玩家项目。

     

func removeItem(_ item: AVPlayerItem!)

答案 3 :(得分:0)

需要检查播放器项目是否可以插入播放器队列中。然后根据以下条件添加,删除或替换播放器项目:

if player.canInsert(playerItem, after: nil) == true {

            player = AVQueuePlayer(playerItem: playerItem)

        } else {

            player.remove(self.playerItem)

            player.replaceCurrentItem(with: playerItem)
}

答案 4 :(得分:0)

使用此代码为我解决了问题:

player?.pause()                     
let playerItem = AVPlayerItem(asset: myAsset) 
player = nil 
player = AVPlayer()
player?.replaceCurrentItem(with: playerItem)

代替:

player?.pause()                     
player = nil player = AVPlayer()
player?.replaceCurrentItem(with: availablePlayerItem) //
availablePlayerItem is a playerItem which has been created before and
played once with this AVPlayer

注意: 在这两个代码中,“玩家”是我班上的一个全局变量,它是:

var player:AVPlayer? =零

答案 5 :(得分:0)

我最初在单元格内初始化了一个AVPlayerItem并将其添加到AVPlayer(也在单元格内)。后来我提出了一个新的vc,并将同一单元格的AVPlayerItem传递到新vc内的另一个AVPlayer上,并导致崩溃。要修复它,我只是使用.copy() as? AVPlayerItem复制了playerItem。副本为新副本提供了与原始副本不同的内存地址。

单元格:

var cellPlayer: AVPlayer?
var cellsPlayerItem: AVPlayerItem?

cellsPlayerItem = AVPlayerItem(asset: AVAsset(url: url)) // cellsPlayerItem's memory address 0x1111111
cellPlayer = AVPlayer(playerItem: cellsPlayerItem!)

展示的vc:

var vcPlayer: AVPlayer?

let playerItemCopy = cellsPlayerItem.copy() as? AVPlayerItem // playerItemCopy's memory address 0x2222222
let vcPlayer = AVPlayer(playerItem: playerItemCopy)