音频播放器无法播放 url 音频(OSStatus 错误 2003334207。)

问题描述 投票:0回答:1

我有一个视频播放器,当我在本地捕获音频记录并将其存储在设备上的某个位置时,该视频播放器可以工作。当我尝试将录音上传到 firestore 时,音频已正确上传,我可以将链接粘贴到 google 中以收听正确的音频文件。但是,当我尝试使用相同的音频播放器通过 firestore 的链接播放音频时,出现以下错误。我尝试在 URL 中添加“.m4a”扩展名并将其删除,但我不断收到相同的错误。

播放失败。操作无法完成。 (操作系统状态错误 2003334207。)

工作网址

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

    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")
swift google-cloud-firestore swiftui audio
1个回答
1
投票

尝试从 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)")
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.