我有一个视频播放器,当我在本地捕获音频记录并将它们存储在设备上的某个位置时,它可以正常工作.当我try 上传录音到Firestore时,音频被正确上传,我可以将链接粘贴到Google中来收听正确的音频文件.然而,当我try 使用相同的音频播放器播放带有Firestore链接的音频时,我得到了下面的错误.我try 在URL中添加".m4a"扩展名并将其删除,但一直收到相同的错误.

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

工作URL

100

100

音频播放器

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()
        }
    }
}

推荐答案

try 从URL播放音频时遇到的OSStatus error 2003334207通常表示URL的格式或媒体文件本身有问题,在这种情况下,可能与您处理URL的方式有关.从描述来看,当try 使用AVAudioPlayer中的下载URL播放存储在Firestore中的音频文件时,似乎出现了这个问题.

startPlayback(audio: URL)函数中,您将".m4a"附加到原始URL,如果".m4a"扩展名已经存在或不需要,则可能使URL无效.Firestore URL包括访问内容所必需的访问令牌和查询参数;更改URL字符串可能会 destruct 这些参数.

参考Firebase / Upload files with Cloud Storage on Apple platforms中的示例,其中说明了,将文件上传到云存储并通过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)")
    }
}

Make sure your Firestore rules allow for public access to the files or that your app properly authenticates to access private files.
Also, confirm the URLs are publicly accessible by testing them in a web browser or using a tool like curl to fetch the headers or content.

我已经try 在没有附加m4a的情况下使用原始URL,但不起作用.音频文件可以公开访问,并且没有阻止访问的规则.

那么你还有其他可能导致这OSStatus error 2003334207人死亡的原因.

即使您已经确认URL在浏览器中工作正常,AVAudioPlayer可能在某些URL格式或参数方面仍有问题.确保URL编码正确,特别是当它包含特殊字符或空格时.

并确保在FiRestore中正确设置托管音频文件的MIME类型.尽管您提到该文件是可访问的,但预期的MIME类型和实际的MIME类型不匹配可能会导致回放问题.

作为诊断步骤,以编程方式下载音频文件并将其保存到应用程序的本地存储中.try 从那里使用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)")
    }
}

先下载音频,然后用AVAudioPlayer首作品播放.

使用AVPlayer的唯一问题是函数startMonitoring停止工作,因为AVPlayer没有计量.

由于先下载音频,然后在本地使用AVAudioPlayer(支持测光)播放,因此您可以实现一个工作流,在播放开始之前将音频临时下载到设备的存储中.这种方法允许您使用AVAudioPlayer并保留计量功能.但是,这可能不是最有效的方法,特别是对于较大的文件或需要立即播放的情况.

或者:

对于没有下载的直接音频流,您需要使用AVPlayer,但实现一个定制的计量解决方案.由于AVPlayer不提供本机音频计量,您可以使用AVAudioEngine进入音频回放信号并执行您自己的分析.

  • 创建一个AVPlayer实例用于流式传输您的音频URL.
  • 使用AVAudioEngineAVPlayer's输出上创建音频分路器.
  • 分析音频缓冲区:在音频分路器中,分析音频缓冲区以计算音频信号的电平或功率.

下面是在AVPlayerItem上设置音频抽头以执行音频电平测量的简化示例.请注意,此示例侧重于设置,不包括音频电平计算的完整实现.

import AVFoundation

// Assume player is your AVPlayer instance
let playerItem = AVPlayerItem(url: audioURL)
player.replaceCurrentItem(with: playerItem)

let audioEngine = AVAudioEngine()
let audioNode = AVAudioPlayerNode()
let audioFormat = audioNode.outputFormat(forBus: 0)

// Attach and connect nodes
audioEngine.attach(audioNode)
audioEngine.connect(audioNode, to: audioEngine.mainMixerNode, format: audioFormat)

// Install a tap on the node to capture audio data
audioNode.installTap(onBus: 0, bufferSize: 1024, format: audioFormat) { (buffer, time) in
    // Analyze the buffer to get the audio level
    // Implementation of audio analysis goes here
}

do {
    try audioEngine.start()
} catch {
    print("Error starting audio engine: \(error.localizedDescription)")
}

// Play the item with AVPlayer
player.play()

// Add audioNode to the audio signal chain
// Note: You will need to manage the timing and synchronization between AVPlayer playback and audioNode processing

Swift相关问答推荐

多个提取来计算核心数据中的记录

为什么不能使用actor作为AsyncSequence中的状态机类型?

为什么第二项任务不在第一项任务完成之前开始?(并发队列)

如何分解阿拉伯字母?

RxSwift .debounce,.throttle用于分页

仅使用@MainActor注释类的部分时的并发问题

物理主体未与 spritekit Swift 的网格图案上的纹理图像对齐

如何使用Swift宏和@Observable和@Environment?

RealityKit - 从中心zoom 模型

如何在 swift 5.0 中获取当前行

从 iPhone 中的安全飞地获取真正的随机数?

如何避免从模块导入函数

为什么 id 不能通过 struct 从 Objective-C 移植到 Swift?

自定义 DispatchQueue 服务质量

这是 Int64 Swift Decoding 多余的吗?

使用 Date.ParseStrategy 将字符串解析为日期

在 Swift 中返回实例类型

Swift 协议只能设置?

使用 ARAnchor 插入 node 和直接插入 node 有什么区别?

来自 ObservableObject 的绑定值