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

iOS音频播放器无法播放Firestore URL音频,报OSStatus错误2003334207

问题描述
  • 本地录制存储的音频可正常播放,上传至Firebase Storage后,音频链接在Chrome中能正常播放,但使用自定义AVAudioPlayer播放时报错:播放失败。操作无法完成。(OSStatus错误2003334207。)
  • 尝试为URL添加/移除.m4a后缀均无法解决问题
  • 测试URL:
    • https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/audio%2F60787BCB-DEFE-4743-BF05-A2EAF24C0F28.m4a?alt=media&token=a29c8a9a-ce16-4240-9af1-5aefcac8a824.m4a
    • https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/audio%2F60787BCB-DEFE-4743-BF05-A2EAF24C0F28.m4a?alt=media&token=a29c8a9a-ce16-4240-9af1-5aefcac8a824
问题原因

OSStatus错误2003334207对应AVAudioPlayer的核心限制:它仅支持播放本地文件或已加载到内存的音频数据,不支持直接流式播放网络URL。浏览器能正常播放是因为内置了支持网络流的播放器,而AVAudioPlayer不具备该能力。

解决方案

提供两种可行修复方案:

方案一:改用AVPlayer(推荐,支持流式播放)

替换原有的AVAudioPlayer为AVPlayer,它原生支持网络音频流播放,无需提前下载文件。修改后的代码示例:

import SwiftUI
import AVFoundation

public final class AudioPlayer: NSObject, ObservableObject {
    @Published public var isPlaying = false
    @Published public var currentTime: Int = 0
    @Published public var fileDuration: TimeInterval = 0
    private var player: AVPlayer?
    private var timeObserver: Any?
    static let shared = AudioPlayer()
    
    func startPlayback(audio: URL) {
        // 直接使用传入的原始URL,无需手动添加后缀
        let playerItem = AVPlayerItem(url: audio)
        player = AVPlayer(playerItem: playerItem)
        
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
            try AVAudioSession.sharedInstance().setActive(true)
        } catch {
            print("音频会话设置失败:\(error.localizedDescription)")
            return
        }
        
        player?.play()
        isPlaying = true
        
        // 监听播放时长和进度
        fileDuration = playerItem.duration.seconds.rounded()
        currentTime = Int(fileDuration)
        
        timeObserver = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: .main) { [weak self] time in
            guard let self = self else { return }
            let elapsed = Int(time.seconds)
            self.currentTime = Int(self.fileDuration) - elapsed
        }
        
        // 监听播放结束
        NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: playerItem)
    }
    
    @objc private func playerDidFinishPlaying() {
        stopPlayback()
    }
    
    public func stopPlayback() {
        player?.pause()
        player = nil
        if let observer = timeObserver {
            player?.removeTimeObserver(observer)
            timeObserver = nil
        }
        isPlaying = false
        currentTime = Int(fileDuration)
        NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)
    }
}

方案二:先下载音频到本地再用AVAudioPlayer播放

如果必须保留AVAudioPlayer,可以先将网络音频下载到临时目录,再读取本地文件路径播放:

func startPlayback(audio: URL) {
    let task = URLSession.shared.dataTask(with: audio) { [weak self] data, response, error in
        guard let self = self, let data = data, error == nil else {
            print("下载失败:\(error?.localizedDescription ?? "未知错误")")
            return
        }
        
        // 创建临时文件路径
        let tempDir = NSTemporaryDirectory()
        let tempFile = tempDir + "temp_audio.m4a"
        let tempURL = URL(fileURLWithPath: tempFile)
        
        do {
            try data.write(to: tempURL)
            
            DispatchQueue.main.async {
                do {
                    try AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
                    try AVAudioSession.sharedInstance().setActive(true)
                    
                    self.audioPlayer = try AVAudioPlayer(contentsOf: tempURL)
                    self.audioPlayer.volume = 1.0
                    self.audioPlayer.delegate = self
                    self.audioPlayer.play()
                    
                    withAnimation {
                        self.isPlaying = true
                    }
                    
                    self.fileDuration = self.audioPlayer.duration.rounded()
                    self.startMonitoring()
                } catch {
                    print("播放失败:\(error.localizedDescription)")
                }
            }
        } catch {
            print("保存临时文件失败:\(error.localizedDescription)")
        }
    }
    task.resume()
}
额外注意点
  • 原代码中手动给URL添加.m4a后缀是错误操作,会导致URL无效(比如第一个测试URL末尾已多了一个.m4a),直接使用传入的原始URL即可。
  • 确保App已开启网络权限(在Info.plist中确认Firebase Storage的URL为HTTPS,iOS默认允许HTTPS请求;若需HTTP请求需配置NSAppTransportSecurity)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:45:34