Воспроизведение/Пауза и Истекшее время не обновляются в командном центре iOS

У меня есть видеоплеер, который может играть из командного центра iOS и экрана блокировки. Когда я переключаю кнопку воспроизведения/паузы в своем приложении, она должна обновить кнопку воспроизведения/паузы в командном центре (MPRemoteCommandCenter), обновив nowPlayingInfo (MPNowPlayingInfoCenter). Я не уверен, почему он не обновляется.

Например, если я приостанавливаю видео с помощью настраиваемой кнопки в своем приложении, в командном центре по-прежнему отображается кнопка паузы (это означает, что видео по-прежнему играет неправильно).

введите описание изображения здесь

Вот как я обновляю nowPlayingInfo:

func updateMPNowPlayingInforCenterMetadata() {
    guard video != nil else {
        nowPlayingInfoCenter.nowPlayingInfo = nil
        return
    }

    var nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo ?? [String: Any]()

    let image: UIImage
    if let placeholderLocalURL = video.placeholderLocalURL, let placeholderImage = UIImage(contentsOfFile: placeholderLocalURL.path) {
        image = placeholderImage
    } else {
        image = UIImage()
    }

    let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { _ -> UIImage in
        return image
    })

    nowPlayingInfo[MPMediaItemPropertyTitle] = video.title
    nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = video.creator?.name ?? " "
    nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork

    nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = Float(video.duration)
    nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = Float(currentTime) // CMTimeGetSeconds(player.currentItem!.currentTime())
    nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player.rate
    nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = player.rate

    nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo

    if player.rate == 0.0 {
        state = .paused
    } else {
        state = .playing
    }
}

С KVO, когда игрок rate изменяется, я вызываю эту функцию:

// MARK: - Key-Value Observing Method

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard context == &assetPlaybackManagerKVOContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }

    } else if keyPath == #keyPath(AVPlayer.rate) {
        updateMPNowPlayingInforCenterMetadata()
    }
}

Любые мысли?

UPDATE

Я нашел решение, хотя и не идеально в моем случае. Так что в моем приложении у меня есть 2 контроллера вида. Назовем их FeedVC и PlayerVC. Итак, FeedVC имеет AVPlayer, которые всегда играют, но приглушены. Если вы нажмете на один из них, создайте PlayerVC и воспроизведите полное видео. Если я приостанавливаю AVPlayer в FeedVC перед тем, как перейти к PlayerVC, тогда кнопка "play/pause" в NowPlayingInfoCenter отлично работает!

Есть ли способ сделать эту работу без необходимости приостанавливать видео в FeedVC?

Другая проблема заключается в том, что elapsed time продолжает подсчитывать, если я не приостанавливаю игроков в FeedVC. Кажется, что если играют несколько игроков, кнопка воспроизведения/паузы и прошедшее время неверны.

Ответ 1

Когда вы устанавливаете словарь для nowPlayingInfo, вам нужно установить значение MPNowPlayingInfoPropertyPlaybackRate соответствующим образом. MPNowPlayingInfoCenter ожидает либо значение 1.0 (воспроизведение), либо 0.0 (не воспроизводящее) значение как Double, завернутое в объект NSNumber. Ниже вы найдете код, как я устанавливаю nowPlayingInfo в моем проекте.

func setNowPlayingMediaWith(song: SUSong, currentTime: Double?) {

    var playingInfo:[String: Any] = [:]

    if let title = song.title {
        playingInfo[MPMediaItemPropertyTitle] = title
    }

    if let songID = song.id {
        playingInfo[MPMediaItemPropertyPersistentID] = songID
    }
    if let artist = song.artist, let artistName = artist.name {
        playingInfo[MPMediaItemPropertyArtist] = artistName
    }

    if let album = song.album, let albumTitle = album.title {
        var artwork:MPMediaItemArtwork? = nil
        if let album = song.album, let artworkData = album.artwork {
            artwork = MPMediaItemArtwork(boundsSize: Constants.Library.Albums.thumbSize, requestHandler: { (size) -> UIImage in
                return UIImage(data: artworkData as Data)!
            })
        }
        if artwork != nil {
            playingInfo[MPMediaItemPropertyArtwork] = artwork!
        }
        playingInfo[MPMediaItemPropertyAlbumTitle] = albumTitle
    }

    var rate:Double = 0.0
    if let time = currentTime {
        playingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = time
        playingInfo[MPMediaItemPropertyPlaybackDuration] = song.duration
        rate = 1.0
    }
    playingInfo[MPNowPlayingInfoPropertyPlaybackRate] = NSNumber(value: rate)
    playingInfo[MPNowPlayingInfoPropertyMediaType] = NSNumber(value: MPNowPlayingInfoMediaType.audio.rawValue)
    MPNowPlayingInfoCenter.default().nowPlayingInfo = playingInfo
}

В этом методе я передаю song, который загружен моим игроком. Всякий раз, когда пользователь решает играть или приостанавливать, я вызываю setNowPlayingMediaWith(song:currentTime:) для обновления консоли управления устройством.

Я отслеживаю currentTime (Double) как свойство моего плеера. Если передано currentTime, значит, мы должны играть, поэтому установите MPNowPlayingInfoPropertyPlaybackRate на 1.0 и установите MPNowPlayingInfoPropertyElapsedPlaybackTime на currentTime. Это приведет к тому, что текущее время начнет автоматически воспроизводиться на консоли управления устройством.

Аналогично, если нет currentTime, то мы остановились или остановились. В этом случае мы устанавливаем MPNowPlayingInfoPropertyPlaybackRate в 0.0, и мы не включаем MPNowPlayingInfoPropertyElapsedPlaybackTime.

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


ИЗМЕНИТЬ (ответ на комментарии)

"Есть ли способ сделать эту работу без необходимости приостанавливать видео в FeedVC"

Не видя своего кода, трудно дать вам определенный ответ. Было бы разумно приостановить любые текущие носители перед запуском вашего носителя PlayerVC.


"прошедшее время продолжает считать"

Истекшее время будет отсчет прошедшего времени на основе свойства NSTimer на NowPlayingInfoCenter. Этот таймер остановится только тогда, когда значение таймера достигнет значения, установленного в MPMediaItemPropertyPlaybackDuration, или при обновлении MPNowPlayingInfoPropertyElapsedPlaybackTime.

Мое предложение - написать метод, который "очищает" NowPlayingInfoCenter. Этот метод должен установить значение, задающее все ключевые значения как 0.0, так и nil соответственно. Вызовите этот метод "очистить" каждый раз, прежде чем воспроизводить медиафайлы в PlayerVC. Затем, как только вы играете из PlayerVC, установите значения ключей NowPlayingInfoCenter, как и в методе, который я вставил в свой ответ, чтобы установить новые значения для нового воспроизводимого носителя.