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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:44:51