You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVPlayer播放本地下载音频失败但流媒体正常问题求助

问题分析与解决思路

先直接说核心问题:你的音频播放失败,90%的概率是因为下载完成的回调时机完全错了,导致AVPlayer拿到的是还没下载好的空文件或者无效路径。下面一步步拆解问题和修复方案:

核心问题:异步下载的回调逻辑错误

你的downloadSongAsynch方法里,URLSession.shared.downloadTask是异步执行的——也就是说,调用resume()后,代码会立刻往下走,而此时文件还在下载中,你却直接调用了completion(destinationUrl)。这时候destinationUrl指向的要么是个空文件,要么根本还没被创建,AVPlayer自然无法播放无效的资源。

分步修复方案

1. 修正下载方法的回调时机

把completion回调移到文件成功移动到目标路径之后,确保只有当文件完全就绪时,才通知上层初始化AVPlayer。同时补充错误处理和重复下载判断:

func downloadSongAsynch(audioUrl: URL, completion: @escaping (URL?, Error?) -> ()) {
    let fileManager = FileManager.default
    let documentsDirectoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("podcasts/")
    
    // 创建目录失败直接回调错误
    do {
        try fileManager.createDirectory(atPath: documentsDirectoryURL.path, withIntermediateDirectories: true, attributes: nil)
    } catch {
        completion(nil, error)
        return
    }
    
    let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
    
    // 如果文件已存在,直接返回路径,避免重复下载
    if fileManager.fileExists(atPath: destinationUrl.path) {
        completion(destinationUrl, nil)
        return
    }
    
    URLSession.shared.downloadTask(with: audioUrl) { location, response, error in
        guard let location = location, error == nil else {
            completion(nil, error)
            return
        }
        
        do {
            // 如果目标路径已有旧文件,先删除再移动
            if fileManager.fileExists(atPath: destinationUrl.path) {
                try fileManager.removeItem(at: destinationUrl)
            }
            try fileManager.moveItem(at: location, to: destinationUrl)
            // 只有文件移动成功后,才回调通知上层
            completion(destinationUrl, nil)
        } catch {
            completion(nil, error)
        }
    }.resume()
}

2. 修正按钮点击的调用逻辑

注意你代码里SetUpBotton中调用的downloadPodcastAsynch是笔误,应该是downloadSongAsynch,同时补充弱引用避免循环引用,以及错误处理:

@IBAction func SetUpBotton(_ sender: Any) {
    getLatestPodcastURL { [weak self] podcastUrl in
        guard let self = self else { return }
        self.downloadSongAsynch(audioUrl: podcastUrl) { localUrl, error in
            guard let localUrl = localUrl, error == nil else {
                print("下载失败:\(error?.localizedDescription ?? "未知错误")")
                return
            }
            self.player = AVPlayer(url: localUrl)
            print("AVPlayer初始化完成,currentItem: \(self.player.currentItem)")
        }
    }
}

3. 确保音频会话被激活

你只设置了AVAudioSession的category,但没有激活会话,这可能导致播放权限问题,在viewDidLoad里补充激活代码:

override func viewDidLoad() {
    super.viewDidLoad()
    do {
        try AVAudioSession.sharedInstance().setCategory(.playback)
        try AVAudioSession.sharedInstance().activate() // 新增激活会话
    } catch {
        print("音频会话设置失败:\(error.localizedDescription)")
    }
}

4. 给播放按钮加安全校验

播放前先检查player和currentItem是否有效,避免空指针或无效资源:

@IBAction func PlayButton(_ sender: Any) {
    guard let player = player, player.currentItem != nil else {
        print("Player未初始化或资源无效")
        return
    }
    player.play()
    print("开始播放,currentItem: \(player.currentItem)")
}

关于BoringSSL错误

你提到的SSL_ERROR_ZERO_RETURN确实是iOS新版本的一个日志bug,它只是记录了连接正常关闭的状态,完全不会影响下载或播放功能,直接忽略这个日志就行。

额外排查建议

如果修复后还是有问题,可以加个AVPlayerItem状态监听,排查资源是否能正常加载:

// 在初始化player的时候替换成这个
let playerItem = AVPlayerItem(url: localUrl)
playerItem.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
self.player = AVPlayer(playerItem: playerItem)

// 然后实现监听方法
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "status", let playerItem = object as? AVPlayerItem {
        switch playerItem.status {
        case .readyToPlay:
            print("资源已准备好播放")
        case .failed:
            print("资源加载失败:\(playerItem.error?.localizedDescription ?? "未知错误")")
        case .unknown:
            print("资源加载中")
        @unknown default:
            break
        }
    }
}

内容的提问来源于stack exchange,提问作者RMoore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:24