我在记录时崩溃:“required condition is false: format.sampleRate == hwFormat.sampleRate”afterweb rtc call

fam*_*fam 6 record avaudiosession swift

我的记录工作正常,但问题是在 WebRTC 调用之后,我崩溃了

所需条件为 false:format.sampleRate == hwFormat.sampleRate

这是我开始崩溃和 installTap 的方式:

func startRecord() {
        self.filePath = nil
        
        print("last format: \(audioEngine.inputNode.inputFormat(forBus: 0).sampleRate)")
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setCategory(.playAndRecord, options: .mixWithOthers)
        } catch {
            print("======== Error setting setCategory \(error.localizedDescription)")
        }
        do {
            try session.setPreferredSampleRate(44100.0)
        } catch {
            print("======== Error setting rate \(error.localizedDescription)")
        }
        do {
            try session.setPreferredIOBufferDuration(0.005)
        } catch {
            print("======== Error IOBufferDuration \(error.localizedDescription)")
        }
        do {
            try session.setActive(true, options: .notifyOthersOnDeactivation)
        } catch {
            print("========== Error starting session \(error.localizedDescription)")
        }
        let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16,
            sampleRate: 44100.0,
//            sampleRate: audioEngine.inputNode.inputFormat(forBus: 0).sampleRate,
            channels: 1,
            interleaved: true)
        audioEngine.connect(audioEngine.inputNode, to: mixer, format: format)
        audioEngine.connect(mixer, to: audioEngine.mainMixerNode, format: format)

        let dir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as String
        filePath =  dir.appending("/\(UUID.init().uuidString).wav")

        _ = ExtAudioFileCreateWithURL(URL(fileURLWithPath: filePath!) as CFURL,
            kAudioFileWAVEType,(format?.streamDescription)!,nil,AudioFileFlags.eraseFile.rawValue,&outref)

        mixer.installTap(onBus: 0, bufferSize: AVAudioFrameCount((format?.sampleRate)!), format: format, block: { (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in

            let audioBuffer : AVAudioBuffer = buffer
            _ = ExtAudioFileWrite(self.outref!, buffer.frameLength, audioBuffer.audioBufferList)
        })

        try! audioEngine.start()
        startMP3Rec(path: filePath!, rate: 128)
    }

    func stopRecord() {

        self.audioFilePlayer.stop()
        self.audioEngine.stop()
        self.mixer.removeTap(onBus: 0)

        self.stopMP3Rec()
        ExtAudioFileDispose(self.outref!)

        try? AVAudioSession.sharedInstance().setActive(false)
    }
    
    func startMP3Rec(path: String, rate: Int32) {

        self.isMP3Active = true
        var total = 0
        var read = 0
        var write: Int32 = 0

        let mp3path = path.replacingOccurrences(of: "wav", with: "mp3")
        var pcm: UnsafeMutablePointer<FILE> = fopen(path, "rb")
        fseek(pcm, 4*1024, SEEK_CUR)
        let mp3: UnsafeMutablePointer<FILE> = fopen(mp3path, "wb")
        let PCM_SIZE: Int = 8192
        let MP3_SIZE: Int32 = 8192
        let pcmbuffer = UnsafeMutablePointer<Int16>.allocate(capacity: Int(PCM_SIZE*2))
        let mp3buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(MP3_SIZE))

        let lame = lame_init()
        lame_set_num_channels(lame, 1)
        lame_set_mode(lame, MONO)
        lame_set_in_samplerate(lame, 44100)
        lame_set_brate(lame, rate)
        lame_set_VBR(lame, vbr_off)
        lame_init_params(lame)

        DispatchQueue.global(qos: .default).async {
            while true {
                pcm = fopen(path, "rb")
                fseek(pcm, 4*1024 + total, SEEK_CUR)
                read = fread(pcmbuffer, MemoryLayout<Int16>.size, PCM_SIZE, pcm)
                if read != 0 {
                    write = lame_encode_buffer(lame, pcmbuffer, nil, Int32(read), mp3buffer, MP3_SIZE)
                    fwrite(mp3buffer, Int(write), 1, mp3)
                    total += read * MemoryLayout<Int16>.size
                    fclose(pcm)
                } else if !self.isMP3Active {
                    _ = lame_encode_flush(lame, mp3buffer, MP3_SIZE)
                    _ = fwrite(mp3buffer, Int(write), 1, mp3)
                    break
                } else {
                    fclose(pcm)
                    usleep(50)
                }
            }
            lame_close(lame)
            fclose(mp3)
            fclose(pcm)
            self.filePathMP3 = mp3path
        }
    }
    
    func stopMP3Rec() {
        self.isMP3Active = false
    }
Run Code Online (Sandbox Code Playgroud)

作为第一次运行应用程序,我使用记录最后的格式

print("last format: \(audioEngine.inputNode.inputFormat(forBus: 0).sampleRate)")
Run Code Online (Sandbox Code Playgroud)

--> 返回 0 -> 下次正常记录 返回 44100 -> 正常记录

但是在 webrtc 调用之后,我得到了 48000,然后它使这一行崩溃

self.audioEngine.connect(self.audioEngine.inputNode, to: self.mixer, format: format)
Run Code Online (Sandbox Code Playgroud)

我在 stackoverflow 上花了 4 个小时,但没有解决方案适合我。

我不需要 48000 格式,因为我已将示例设置为

sampleRate: audioEngine.inputNode.inputFormat(forBus: 0).sampleRate,
Run Code Online (Sandbox Code Playgroud)

-> 我的输出很难听清,我可以认出我的声音:(

所以我认为44100是最好的

有人可以给我一些建议吗?谢谢

den*_*T30 1

这条线有bug。

let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, ...
Run Code Online (Sandbox Code Playgroud)

AVAudioCommonFormat.pcmFormatInt16默认情况下不起作用。

你应该使用.pcmFormatFloat32


xcode 的提示很明显,

崩溃线

self.audioEngine.connect(self.audioEngine.inputNode, to: self.mixer, format: format)
Run Code Online (Sandbox Code Playgroud)

你通过打印就知道了mixer.inputFormat(forBus: 0 )


那么你在实际设备上得到的采样率为 48000。兑换即可获得44100


只是用来AVAudioConverter做下采样音频缓冲区。

let input = engine.inputNode
let bus = 0
let inputFormat = input.outputFormat(forBus: bus )

 guard let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true), let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else{
        return
    }

if let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate)){
            var error: NSError?
            let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
            assert(status != .error)
            print(convertedBuffer.format)
        }
Run Code Online (Sandbox Code Playgroud)