Ios 如果URL失败,如何重新加载AVPlayer的数据

Ios 如果URL失败,如何重新加载AVPlayer的数据,ios,swift,multithreading,avplayer,Ios,Swift,Multithreading,Avplayer,我有一个json文件,其中包含一些单词的URL(带.mp3)。一些URL无效(或有效,但返回错误,因此我无法获取数据) 这个URL我用来播放单词的发音。因此,我将抛出3个步骤: 查找特定单词的URL。如果找不到,就什么事也没有发生 使用此URL初始化AVPlayerItem并准备AVPlayer。而不仅仅是等待,当用户按下 当用户按下word时播放声音 所以,首先,我正在准备我的AVPlayer,以避免延迟播放 我对多线程有点困惑,我不知道应该在哪里检查我是否能够播放这个声音,我应该使用下一个U

我有一个json文件,其中包含一些单词的URL(带.mp3)。一些URL无效(或有效,但返回错误,因此我无法获取数据)

这个URL我用来播放单词的发音。因此,我将抛出3个步骤:

  • 查找特定单词的URL。如果找不到,就什么事也没有发生
  • 使用此URL初始化AVPlayerItem并准备AVPlayer。而不仅仅是等待,当用户按下
  • 当用户按下word时播放声音
  • 所以,首先,我正在准备我的AVPlayer,以避免延迟播放

    我对多线程有点困惑,我不知道应该在哪里检查我是否能够播放这个声音,我应该使用下一个URL

    代码:

    }

    这是一个json文件,每个单词有几个URL

    "abel": [
        "http://static.sfdict.com/staticrep/dictaudio/A00/A0015900.mp3",
        "http://img2.tfd.com/pron/mp3/en/US/d5/d5djdgdyslht.mp3",
        "http://img2.tfd.com/pron/mp3/en/UK/d5/d5djdgdyslht.mp3",
        "http://www.yourdictionary.com/audio/a/ab/abel.mp3"
    ],
    "abele": [
        "http://www.yourdictionary.com/audio/a/ab/abele.mp3",
        "http://static.sfdict.com/staticrep/dictaudio/A00/A0016300.mp3",
        "http://www.oxforddictionaries.com/media/english/uk_pron/a/abe/abele/abele__gb_2_8.mp3",
        "http://s3.amazonaws.com/audio.vocabulary.com/1.0/us/A/1B3JGI7ALNB2K.mp3",
        "http://www.oxforddictionaries.com/media/english/uk_pron/a/abe/abele/abele__gb_1_8.mp3"
    ],
    
    所以,我需要获取第一个URL并检查它。如果失败,则再进行一次检查。。。等等,当URL结束时,或者找到一些有效的URL。 所有这些都必须在AVPlayer尝试播放声音之前完成

    如何实施,在哪里实施


    请用简单的话告诉并描述解析,因为我是swift和多线程的初学者。

    我会使用
    AVPlayerItem.Status
    属性查看解析失败的时间。在当前代码中,您在创建项目后立即检查状态,该项目将始终产生与您初始化
    AVPlayerItem
    时相同的结果。默认情况下,
    状态
    未知

    一旦您与播放器关联,
    AVPlayerItem
    就会进入队列。为了能够跟踪状态更改,您需要设置一个观察者

    文档仍然建议使用
    addObserver
    使用“旧样式”,但根据您的偏好,我会选择较新的块样式

    // make sure to keep a strong reference to the observer (e.g. in your controller) otherwise the observer will be de-initialised and no changes updates will occur
    var observerStatus: NSKeyValueObservation?
    
    
    // in your method where you setup your player item
    observerStatus = playerItem.observe(\.status, changeHandler: { (item, value) in
        debugPrint("status: \(item.status.rawValue)")
        if item.status == .failed {
            // enqueue new asset with diff url
        }
    })
    
    您还可以在
    AVPlayer
    实例上设置类似的观察者


    更新了完整示例 -这段代码远非完美,但它展示了observer的优点

    import UIKit
    import AVFoundation
    
    class ViewController: UIViewController {
        var observerStatus: NSKeyValueObservation?
        var currentTrack = -1
        let urls = [
            "https://sample-videos.com/audio/mp3/crowd-cheerin.mp3", // "https://sample-videos.com/audio/mp3/crowd-cheering.mp3"
            "https://sample-videos.com/audio/mp3/wave.mp3"
        ]
        var player: AVPlayer? {
            didSet {
                guard let p = player else { return debugPrint("no player") }
                debugPrint("status: \(p.currentItem?.status == .unknown)") // this is set before status changes from unknown
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            nextTrack()
        }
    
        func nextTrack() {
            currentTrack += 1
            guard let url = URL(string: urls[currentTrack]) else { return }
            let item = AVPlayerItem(url: url)
    
            observerStatus = item.observe(\.status, changeHandler: { [weak self] (item, value) in
                switch item.status {
                case .unknown:
                    debugPrint("status: unknown")
                case .readyToPlay:
                    debugPrint("status: ready to play")
                case .failed:
                    debugPrint("playback failed")
                    self?.nextTrack()
                }
            })
    
            if player == nil {
                player = AVPlayer(playerItem: item)
            } else {
                player?.replaceCurrentItem(with: item)
            }
            player?.play()
        }
    }
    

    我将使用
    AVPlayerItem.Status
    属性查看它何时失败。在当前代码中,您在创建项目后立即检查状态,该项目将始终产生与您初始化
    AVPlayerItem
    时相同的结果。默认情况下,
    状态
    未知

    一旦您与播放器关联,
    AVPlayerItem
    就会进入队列。为了能够跟踪状态更改,您需要设置一个观察者

    文档仍然建议使用
    addObserver
    使用“旧样式”,但根据您的偏好,我会选择较新的块样式

    // make sure to keep a strong reference to the observer (e.g. in your controller) otherwise the observer will be de-initialised and no changes updates will occur
    var observerStatus: NSKeyValueObservation?
    
    
    // in your method where you setup your player item
    observerStatus = playerItem.observe(\.status, changeHandler: { (item, value) in
        debugPrint("status: \(item.status.rawValue)")
        if item.status == .failed {
            // enqueue new asset with diff url
        }
    })
    
    您还可以在
    AVPlayer
    实例上设置类似的观察者


    更新了完整示例 -这段代码远非完美,但它展示了observer的优点

    import UIKit
    import AVFoundation
    
    class ViewController: UIViewController {
        var observerStatus: NSKeyValueObservation?
        var currentTrack = -1
        let urls = [
            "https://sample-videos.com/audio/mp3/crowd-cheerin.mp3", // "https://sample-videos.com/audio/mp3/crowd-cheering.mp3"
            "https://sample-videos.com/audio/mp3/wave.mp3"
        ]
        var player: AVPlayer? {
            didSet {
                guard let p = player else { return debugPrint("no player") }
                debugPrint("status: \(p.currentItem?.status == .unknown)") // this is set before status changes from unknown
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            nextTrack()
        }
    
        func nextTrack() {
            currentTrack += 1
            guard let url = URL(string: urls[currentTrack]) else { return }
            let item = AVPlayerItem(url: url)
    
            observerStatus = item.observe(\.status, changeHandler: { [weak self] (item, value) in
                switch item.status {
                case .unknown:
                    debugPrint("status: unknown")
                case .readyToPlay:
                    debugPrint("status: ready to play")
                case .failed:
                    debugPrint("playback failed")
                    self?.nextTrack()
                }
            })
    
            if player == nil {
                player = AVPlayer(playerItem: item)
            } else {
                player?.replaceCurrentItem(with: item)
            }
            player?.play()
        }
    }
    
    解决方案:

     private var player: AVPlayer? {
        didSet{
            if player?.currentItem?.status == .failed {
                if indexOfURL >= 0 {
                    prepareForPronunciation(indexOfURL)
                }
            }
        }
    }
    
    如果URL已结束,请将indexOfURL设置为-1,否则将其递增以在下一个函数调用中使用下一个URL

    解决方案:

     private var player: AVPlayer? {
        didSet{
            if player?.currentItem?.status == .failed {
                if indexOfURL >= 0 {
                    prepareForPronunciation(indexOfURL)
                }
            }
        }
    }
    

    如果URL已结束,请将indexOfURL设置为-1,否则将其递增以在下一个函数调用中使用下一个URL

    谢谢您的解决方案。事实上,在我的知识中有一些空白的代码。因此,我将尝试更多地了解观察。我通过观察AVPlayer解决了这个问题。我会把我的解决方案写在下面。谢谢你的解决方案。事实上,在我的知识中有一些空白的代码。因此,我将尝试更多地了解观察。我通过观察AVPlayer解决了这个问题。我会在下面写下我的解决方案。我相信你的解决方案不会像你期望的那样有效。在使用现场文件时可能不复制,但使用远程URL需要考虑延迟。当
    AVPlayerItem
    的状态改变时,您的播放器将已经设置好。此外,除非您有理由这样做,否则没有理由为每个项目重新创建
    AVPlayer
    ,而是重复使用它。我已经更新了我的答案。我相信你的解决方案不会像你期望的那样有效。在使用现场文件时可能不复制,但使用远程URL需要考虑延迟。当
    AVPlayerItem
    的状态改变时,您的播放器将已经设置好。此外,除非您有理由这样做,否则没有理由为每个项目重新创建
    AVPlayer
    ,而是重复使用它。我已经更新了我的答案。