我有一个视频播放器,当我在本地捕获音频记录并将其存储在设备上的某个位置时,该视频播放器可以工作。当我尝试将录音上传到 firestore 时,音频已正确上传,我可以将链接粘贴到 google 中以收听正确的音频文件。但是,当我尝试使用相同的音频播放器通过 firestore 的链接播放音频时,出现以下错误。我尝试在 URL 中添加“.m4a”扩展名并将其删除,但我不断收到相同的错误。
播放失败。操作无法完成。 (操作系统状态错误 2003334207。)
工作网址
static func uploadAudioToFirebaseStorage(localURL: URL, completion: @escaping (String?) -> Void) {
let storage = Storage.storage()
let storageRef = storage.reference()
//even tried adding .m4a after the UUID
let videoRef = storageRef.child("audio/\(UUID().uuidString)")//.m4a
videoRef.putFile(from: localURL, metadata: nil) { metadata, error in
if metadata == nil {
completion(nil)
return
}
videoRef.downloadURL { url, error in
if let downloadURL = url {
completion(downloadURL.absoluteString)
} else {
completion(nil)
}
}
}
}
音频播放器
import SwiftUI
import AVFoundation
public final class AudioPlayer: NSObject, ObservableObject, AVAudioPlayerDelegate {
@Published public var soundSamples: [SampleModel] = []
@Published public var isPlaying = false
var audioPlayer = AVAudioPlayer()
private var timer: Timer?
private var currentSample: Float = 0
private let numberOfSamples: Int
private var durationTimer: Timer?
var fileDuration: TimeInterval = 0
var currentTime: Int = 0
static let shared = AudioPlayer(numberOfSamples: 15)
public init(isPlaying: Bool = false, audioPlayer: AVAudioPlayer = AVAudioPlayer(), timer: Timer? = nil, numberOfSamples: Int) {
self.isPlaying = isPlaying
self.audioPlayer = audioPlayer
self.timer = timer
self.numberOfSamples = numberOfSamples
}
func playSystemSound(soundID: SystemSoundID) {
AudioServicesPlaySystemSound(soundID)
}
func startPlayback(audio: URL) {
let original = audio.absoluteString
print(original)
let updatedURL = original + ".m4a"
print(updatedURL)
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
try AVAudioSession.sharedInstance().setActive(true)
audioPlayer = try AVAudioPlayer(contentsOf: URL(string: updatedURL)!)
audioPlayer.volume = 1.0
audioPlayer.delegate = self
audioPlayer.play()
withAnimation {
isPlaying = true
}
fileDuration = audioPlayer.duration.rounded()
startMonitoring()
} catch let error {
print("Playback failed.\(error.localizedDescription)")
}
}
func startMonitoring() {
audioPlayer.isMeteringEnabled = true
currentTime = Int(fileDuration)
timer = Timer.scheduledTimer(withTimeInterval: 0.03, repeats: true) { [weak self] _ in
guard let this = self else { return }
this.audioPlayer.updateMeters()
this.currentSample = this.audioPlayer.peakPower(forChannel: 0)
this.soundSamples.append(SampleModel(sample: this.currentSample))
}
durationTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
guard let this = self else { return }
this.currentTime -= 1
}
}
private func stopMonitoring() {
soundSamples = []
audioPlayer.isMeteringEnabled = false
timer?.invalidate()
durationTimer?.invalidate()
currentTime = Int(fileDuration)
}
public func stopPlayback() {
audioPlayer.stop()
stopMonitoring()
withAnimation {
isPlaying = false
}
}
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if flag {
stopPlayback()
}
}
}
像这样从本地 URL 播放可以与同一个播放器一起使用
let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let audioFilename = documentPath.appendingPathComponent("\(UUID().uuidString).m4a")
UserDefaults.standard.set(audioFilename.absoluteString, forKey: "tempUrl")
尝试从 URL 播放音频时遇到的
OSStatus error 2003334207
通常表示 URL 或媒体文件本身的格式存在问题,在这种情况下,可能与您处理 URL 的方式有关。从描述来看,当尝试使用 AVAudioPlayer
中的下载 URL 播放存储在 Firestore中的音频文件时,似乎会出现问题。
在您的
startPlayback(audio: URL)
函数中,您将“.m4a”附加到原始 URL,如果“.m4a”扩展名已存在或不需要,这可能会使 URL 无效。 Firestore URL 包括访问令牌和访问内容所必需的查询参数;更改 URL 字符串可能会破坏这些参数。
参见“firebase / Apple 平台上使用 Cloud Storage 上传文件”中的示例,该示例说明,一旦您将文件上传到 Cloud Storage 并通过
downloadURL
方法获取了下载 URL,就应该使用此 URL按原样访问文件。
您的
startPlayback(audio: URL)
函数(不附加“.m4a”)将是:
func startPlayback(audio: URL) {
print(audio.absoluteString) // Verify the URL is correct
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
try AVAudioSession.sharedInstance().setActive(true)
// Use the URL directly without appending ".m4a"
audioPlayer = try AVAudioPlayer(contentsOf: audio)
audioPlayer.volume = 1.0
audioPlayer.delegate = self
audioPlayer.play()
withAnimation {
isPlaying = true
}
fileDuration = audioPlayer.duration.rounded()
startMonitoring()
} catch let error {
print("Playback failed.\(error.localizedDescription)")
}
}
确保您的 Firestore 规则 允许公共访问文件,或者您的应用程序经过正确身份验证以访问私有文件。
另外,通过在网络浏览器中测试 URL 或使用
curl
等工具来获取标头或内容来确认 URL 可以公开访问。
我已经尝试过使用原始 URL 而不附加 m4a,但没有成功。音频文件可以公开访问,并且没有任何规则阻止访问。
那么您还有其他潜在原因导致
OSStatus error 2003334207
。
即使您已确认该 URL 在浏览器中有效,
AVAudioPlayer
可能仍会遇到某些 URL 格式或参数的问题。确保 URL 编码正确,尤其是包含特殊字符或空格时。
并确保在 Firestore 中正确设置托管音频文件的 MIME 类型。尽管您提到该文件是可访问的,但预期 MIME 类型与实际 MIME 类型不匹配可能会导致播放问题。
作为诊断步骤,以编程方式下载音频文件并将其保存到应用程序的本地存储中。尝试使用
AVAudioPlayer
从那里播放它。这可以帮助确定问题是出在文件本身还是来自 URL 的流。
AVAudioPlayer
一般是为了播放本地文件而设计的。对于来自 URL 的流式音频,请考虑使用 AVPlayer
而不是 AVAudioPlayer
。 AVPlayer
更适合通过网络流媒体内容。
import AVFoundation
import SwiftUI
func startPlayback(audio: URL) {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .duckOthers)
try AVAudioSession.sharedInstance().setActive(true)
// Use AVPlayer for network resources
let playerItem = AVPlayerItem(url: audio)
let player = AVPlayer(playerItem: playerItem)
player.play()
withAnimation {
isPlaying = true
}
} catch let error {
print("Playback failed: \(error.localizedDescription)")
}
}