Swift中AVAudioPlayer无法播放用户录制音频:OSStatus错误1685348671
问题描述
开发基于Swift的iOS应用时,使用AVAudioPlayer加载用户录制的音频文件出现OSStatus错误1685348671,但播放Bundle内的音频资源完全正常。
报错信息:
Failed to initialize AVAudioPlayer: The operation couldn’t be completed. (OSStatus error 1685348671.)
相关代码:
import SwiftUI import UIKit import AVFoundation struct ParentView: UIViewControllerRepresentable { typealias UIViewControllerType = UIImagePickerController func makeUIViewController(context: Context) -> UIImagePickerController { let soundSelectorButtonSize: CGFloat = 50 let soundSelectorButton = UIButton(frame: CGRect(x: 20, y: overlayView.bounds.height - soundSelectorButtonSize - 100, width: soundSelectorButtonSize, height: soundSelectorButtonSize)) if let image = UIImage(systemName: "music.note", withConfiguration: symbolConfigSmall) { soundSelectorButton.setImage(image, for: .normal) } soundSelectorButton.tintColor = .white soundSelectorButton.backgroundColor = .clear soundSelectorButton.addTarget(context.coordinator, action: #selector(Coordinator.showSoundSelector), for: .touchUpInside) let recordButton = UIButton() recordButton.addTarget(context.coordinator, action: #selector(Coordinator.startRecording), for: .touchDown) recordButton.addTarget(context.coordinator, action: #selector(Coordinator.stopRecording), for: [.touchUpInside, .touchUpOutside]) return picker } func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } } class Coordinator: NSObject, AVAudioRecorderDelegate { var parent: ParentView var audioRecorder: AVAudioRecorder? var audioURL: URL? init(_ parent: CameraView) { self.parent = parent super.init() setupAudioRecorder() } func setupAudioRecorder() { let settings = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] let documentsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] audioURL = documentsPath.appendingPathComponent("mySuperCoolApp.m4a") do { audioRecorder = try AVAudioRecorder(url: audioURL!, settings: settings) audioRecorder?.prepareToRecord() } catch { print("Error setting up the audio recorder:", error) } } @objc func startRecording(_ sender: UIButton) { print("Recording started") audioRecorder?.record() } @objc func stopRecording(_ sender: UIButton) { print("Recording ended") audioRecorder?.stop() if FileManager.default.fileExists(atPath: audioURL!.path) { print("File saved!") } else { print("File did not save.") } } @objc func showSoundSelector() { if let audioURL = audioURL, FileManager.default.fileExists(atPath: audioURL.path) { print("Custom sound file exists at \(audioURL)") } else { print("Custom sound file does not exist.") } let soundOptions = ["audio1", "audio2", "audio3", "audio4"] let alert = UIAlertController(title: "Select Sound", message: nil, preferredStyle: .actionSheet) let customAction = UIAlertAction(title: "Custom Sound", style: .default) { _ in self.loadSound(isCustom: true) } for option in soundOptions { let action = UIAlertAction(title: option, style: .default) { _ in self.loadSound(named: option) } alert.addAction(action) } if FileManager.default.fileExists(atPath: audioURL!.path) { alert.addAction(customAction) } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) alert.addAction(cancelAction) parent.imagePicker.present(alert, animated: true) } func loadSound(named name: String = "Recording", isCustom: Bool = false) { var sound: URL? if isCustom { //USER RECORDED AUDIO if let audioURL = audioURL, FileManager.default.fileExists(atPath: audioURL.path) { sound = audioURL print("Custom sound file exists at \(audioURL)") } else { print("Custom audio file not found.") return } } else { if let path = Bundle.main.path(forResource: name, ofType: "m4a") { sound = URL(fileURLWithPath: path) } else { print("Bundled audio file \(name) not found.") return } } if let soundURL = sound { do { print(soundURL) audioPlayer = try AVAudioPlayer(contentsOf: soundURL) //TODO: Error Occurring here! audioPlayer?.prepareToPlay() } catch { print("Failed to initialize AVAudioPlayer: \(error.localizedDescription)") } } else { print("Sound URL is nil.") } } }
已尝试操作:
- 在Info.plist添加必要权限描述
- 修改音频格式
- 调整音频准备逻辑
首次实现功能正常,后续无明确改动情况下出现该问题。
解决方案
OSStatus错误1685348671对应kAudioFileInvalidFileError,本质是录制的音频文件损坏或格式不被AVAudioPlayer正确识别,以下是针对性修复步骤:
1. 确保录制完成后正确收尾文件
当前stopRecording仅调用audioRecorder?.stop(),未确认音频写入完成。利用AVAudioRecorderDelegate回调确保文件写入成功:
// 在Coordinator类中实现代理回调 func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if flag { print("录制文件已成功写入磁盘") } else { print("录制文件写入失败") } } // 在setupAudioRecorder中设置代理 func setupAudioRecorder() { // ... 原有代码 do { audioRecorder = try AVAudioRecorder(url: audioURL!, settings: settings) audioRecorder?.delegate = self // 新增代理设置 audioRecorder?.prepareToRecord() } catch { print("Error setting up the audio recorder:", error) } }
2. 检查文件可读性与路径权限
仅判断文件存在不够,需确认文件可读取:
if isCustom { guard let audioURL = audioURL, FileManager.default.isReadableFile(atPath: audioURL.path) else { print("自定义音频文件不可读或不存在") return } sound = audioURL print("Custom sound file exists at \(audioURL)") }
3. 调整录制设置的兼容性
12000Hz采样率对AAC格式兼容性较差,改为标准采样率:
let settings = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100, // 修改为44100Hz AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ]
4. 避免重复录制覆盖导致文件损坏
每次录制前删除旧文件,防止覆盖过程中损坏文件:
func setupAudioRecorder() { let documentsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] audioURL = documentsPath.appendingPathComponent("mySuperCoolApp.m4a") // 新增:删除旧文件 if FileManager.default.fileExists(atPath: audioURL!.path) { do { try FileManager.default.removeItem(at: audioURL!) } catch { print("删除旧录制文件失败:\(error)") } } // ... 原有初始化代码 }
5. 验证音频文件完整性
录制完成后用AVAsset检查文件是否有效:
func checkAudioFileValidity() { guard let audioURL = audioURL else { return } let asset = AVURLAsset(url: audioURL) asset.loadValuesAsynchronously(forKeys: ["duration"]) { var error: NSError? let status = asset.statusOfValue(forKey: "duration", error: &error) switch status { case .loaded: print("音频文件有效,时长:\(asset.duration.seconds)秒") case .failed: print("音频文件损坏:\(error?.localizedDescription ?? "未知错误")") default: break } } } // 在stopRecording中调用检查 @objc func stopRecording(_ sender: UIButton) { print("Recording ended") audioRecorder?.stop() checkAudioFileValidity() // 新增检查 }
内容的提问来源于stack exchange,提问作者nellyJustNelly
相关产品推荐
相关产品推荐

