如何在完成Que中的全部播放后将AVQuePlayer设置为开头

时间:2017-08-06 22:52:08

标签: ios swift firebase avqueueplayer

我有一个AVQuePlayer正在下载用户发布到firebase的视频,然后通过AVQuePlayer按顺序播放它们。一切正常,直到视频队列播放所有视频,在这种情况下,如果您尝试重播该队列,则会发生SIGABRT 1错误。我相信这是因为它正在寻找可播放的视频,但没有一个存在。我已经尝试在que完成后重置playerItem数组,但它没有运气。这是我到目前为止所拥有的。

从Firebase加载数据:

func loadStory() {
    DataService.ds.REF_POSTS.observe(.value, with: { (snapshot) in
        self.posts = []
        if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
            for snap in snapshot {
                if let dict = snap.value as? Dictionary<String, Any> {
                    let key = snap.key
                    let post = HotPosts(postID: key, postData: dict)
                    self.posts.append(post)
                    let media = post.mediaURL
                    let ref = Storage.storage().reference(forURL: media)
                    ref.downloadURL(completion: { (url, error) in
                        if error != nil {
                            print(error!)
                        } else {
                            self.videos.append(url!)
                        }
                    })

                }
            }
        }
    })
}

下载网址:

 func playStory() {


    for vid in videos{


        asset = AVAsset(url: vid)
        let assetKeys = ["playable", "hasProtectedContent"]

        let item = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: assetKeys)

        item.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: &myContext)

        self.playerItem.append(item)

    }

    player = AVQueuePlayer(items: playerItem)
    print(playerItem.count)
    let playerLayer = AVPlayerLayer(player: player)
    playerLayer.frame = self.view.bounds
    self.view.layer.addSublayer(playerLayer)


  }

处理缓冲并在准备好后播放视频:

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("playing1")
    guard context == &myContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    print("playing2")
    if keyPath == #keyPath(AVPlayerItem.status) {
        let status: AVPlayerItemStatus
        print("playing3")
        if let statusNumber = change?[.newKey] as? NSNumber {
            status = AVPlayerItemStatus(rawValue: statusNumber.intValue)!
        } else {
            status = .unknown
        }

        switch status {
        case .readyToPlay:
            print("We ARE IN THE CLEAR BOIIIIII")
            player.play()
            break

        case .failed:
            print("ITem Fialed")
            break

        case .unknown:
            print("Could Not play the video")
            break

        }

    }
}

2 个答案:

答案 0 :(得分:0)

尝试订阅此通知,看看它是否适合您的情况:

NotificationCenter.default.addObserver(self, selector: #selector(endOfAudio), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)

我在一段Apple代码中也发现了这一点,它说明了在avQueuePlayer上使用一些KVO(https://developer.apple.com/library/content/samplecode/avloopplayer/Listings/Projects_VideoLooper_VideoLooper_QueuePlayerLooper_swift.html

/*
 Copyright (C) 2016 Apple Inc. All Rights Reserved.
 See LICENSE.txt for this sample’s licensing information

 Abstract:
 An object that uses AVQueuePlayer to loop a video.
 */

 // MARK: Convenience

    private func startObserving() {
        guard let player = player, !isObserving else { return }

        player.addObserver(self, forKeyPath: ObserverContexts.playerStatusKey, options: .new, context: &ObserverContexts.playerStatus)
        player.addObserver(self, forKeyPath: ObserverContexts.currentItemKey, options: .old, context: &ObserverContexts.currentItem)
        player.addObserver(self, forKeyPath: ObserverContexts.currentItemStatusKey, options: .new, context: &ObserverContexts.currentItemStatus)

        isObserving = true
    }

    private func stopObserving() {
        guard let player = player, isObserving else { return }

        player.removeObserver(self, forKeyPath: ObserverContexts.playerStatusKey, context: &ObserverContexts.playerStatus)
        player.removeObserver(self, forKeyPath: ObserverContexts.currentItemKey, context: &ObserverContexts.currentItem)
        player.removeObserver(self, forKeyPath: ObserverContexts.currentItemStatusKey, context: &ObserverContexts.currentItemStatus)

        isObserving = false
    }

    // MARK: KVO

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &ObserverContexts.playerStatus {
            guard let newPlayerStatus = change?[.newKey] as? AVPlayerStatus else { return }

            if newPlayerStatus == AVPlayerStatus.failed {
                print("End looping since player has failed with error: \(player?.error)")
                stop()
            }
        }
        else if context == &ObserverContexts.currentItem {
            guard let player = player else { return }

            if player.items().isEmpty {
                print("Play queue emptied out due to bad player item. End looping")
                stop()
            }
            else {
                // If `loopCount` has been set, check if looping needs to stop.
                if numberOfTimesToPlay > 0 {
                    numberOfTimesPlayed = numberOfTimesPlayed + 1

                    if numberOfTimesPlayed >= numberOfTimesToPlay {
                        print("Looped \(numberOfTimesToPlay) times. Stopping.");
                        stop()
                    }
                }

                /*
                 Append the previous current item to the player's queue. An initial
                 change from a nil currentItem yields NSNull here. Check to make
                 sure the class is AVPlayerItem before appending it to the end
                 of the queue.
                 */
                if let itemRemoved = change?[.oldKey] as? AVPlayerItem {
                    itemRemoved.seek(to: kCMTimeZero)

                    stopObserving()
                    player.insert(itemRemoved, after: nil)
                    startObserving()
                }
            }
        }
        else if context == &ObserverContexts.currentItemStatus {
            guard let newPlayerItemStatus = change?[.newKey] as? AVPlayerItemStatus else { return }

            if newPlayerItemStatus == .failed {
                print("End looping since player item has failed with error: \(player?.currentItem?.error)")
                stop()
            }
        }
        else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
}

答案 1 :(得分:0)

AVQueuePlayer的问题在于它删除了已经播放完毕的AVPlayerItem。一个选项可能是在QueuePlayer完成播放时创建AVQueuePlayer的副本,然后您可以只播放副本并制作新副本,以便在完成后再次播放。

您还可以在阵列中维护所有AVPlayerItem,并在需要时加载新的AVQueuePlayer。

github上有一个项目可能也很有帮助,它扩展了AVQueuePlayer并维护了以前在Queue Player上播放的playerItems。可以通过以下方式找到它:https://github.com/dgiovann/AVQueuePlayerPrevious