«AVPlayerItem не может быть связан более чем с одним экземпляром AVPlayer»

Я знаю, что этот вопрос есть во всем Stack Overflow, но, тем не менее, большинство из них старые и не связаны с тем, что я собираюсь задать здесь.

Итак, у меня есть массив с AVPlayerItems и AVQueuePlayer. Сначала я установил 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
        }
    }

Я убиваю себя из-за этого, поэтому, если кто-то может мне помочь, я был бы признателен.


person Gabriel Bitencourt    schedule 05.06.2015    source источник
comment
Вам нужно показать свой код.   -  person rdelmar    schedule 05.06.2015
comment
Этого кода недостаточно. Вам нужно показать целые методы, а не отдельные строки. Покажите также, что вы делаете, когда пользователь добавляет или удаляет элемент.   -  person rdelmar    schedule 05.06.2015


Ответы (6)


Проблема в том, что вы создаете новый объект игрока, когда обновляете массив элементов этой строкой (в tableView:commitEditingStyle:forRowAtIndexPath:),

audioPlayer = AVQueuePlayer(items: mediaArray)

Кажется, что элементы игрока все еще связаны с исходным игроком, когда вы это делаете. Вы должны иметь возможность удалить эту строку, чтобы она работала правильно. Вставки или удаления элементов из mediaArray должно быть достаточно, чтобы сделать то, что вы хотите; вам не нужно создавать нового игрока.

person rdelmar    schedule 05.06.2015
comment
но к сожалению это не так, кажется, что когда вы впервые связываете массив с проигрывателем, независимо от того, что происходит с массивом, проигрыватель все равно будет воспроизводить первый заданный массив. Самое смешное, что если снова вызвать mediaPicker и выбрать ту же музыку, то все будет нормально. Мне было интересно, почему Apple это делает, это просто не кажется необходимым. В любом случае, я думаю, мне придется изменить свой подход, спасибо за помощь - person Gabriel Bitencourt; 05.06.2015

Из Документация Apple:

Прежде чем добавить элемент:

canInsertItem(_:afterItem:)

Возвращает логическое значение, указывающее, может ли данный элемент игрока быть вставлен в очередь игрока.

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

Добавление элемента:

insertItem(_:afterItem:)

Помещает данный предмет игрока после указанного предмета в очередь.

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

Удаление элемента:

removeItem(_:)

Удаляет данный предмет игрока из очереди.

func removeItem(_ item: AVPlayerItem!)

person Icaro    schedule 05.06.2015
comment
да, это я знаю, но, если я пытаюсь добавить предмет, который уже был сыгран, выдает ошибку, например - person Gabriel Bitencourt; 05.06.2015
comment
Вам нужно проверить, можете ли вы вставить, прежде чем сделать это, я добавлю это к ответу, надеюсь, это поможет вам - person Icaro; 05.06.2015
comment
Я уже знал, что это тоже неразрешимая проблема благодаря Apple. Но все равно спасибо - person Gabriel Bitencourt; 05.06.2015
comment
Добавьте немного больше вашего кода. Я уверен, что мы сможем вам помочь, если мы увидим немного больше того, что вы делаете. - person Icaro; 05.06.2015

Я решил эту проблему следующим образом: я сохранил ссылку на AVAsset объекты в массиве, а затем map преобразовал ее в [AVPlayerItem] (тип, который принимает AVQUeuePlayer init).

Чтобы уточнить, нам нужно было сделать аудиозаписывающее устройство (код на 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 используется для объединения фрагментов аудио. , так что хранить AVAssets вместо AVPlayerItems в конце концов было еще выгоднее.)

person toraritte    schedule 20.04.2018

Использование этого кода решило проблему для меня:

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? = ноль

person Marjan Basiri    schedule 09.02.2020

Требуется проверить, что предмет игрока может быть вставлен в очередь игрока. Затем добавьте, удалите или замените элемент игрока в соответствии с указанными ниже условиями:

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

            player = AVQueuePlayer(playerItem: playerItem)

        } else {

            player.remove(self.playerItem)

            player.replaceCurrentItem(with: playerItem)
}
person FRIDDAY    schedule 11.01.2020

Сначала я инициализировал AVPlayerItem внутри ячейки и добавил его в AVPlayer (тоже внутри ячейки). Позже я представил новый vc и передал AVPlayerItem той же ячейки другому AVPlayer внутри нового vc и получил сбой. Чтобы исправить это, я просто сделал копию playerItem, используя .copy() as? AVPlayerItem. Копия дает новой копии адрес памяти, отличный от оригинала.

клетка:

var cellPlayer: AVPlayer?
var cellsPlayerItem: AVPlayerItem?

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

представил вк:

var vcPlayer: AVPlayer?

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