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

PCM转G711 μlaw原始格式遇缓冲无数据问题求助

问题分析与解决方案

你的核心误区在于对G711 uLaw格式的缓冲区访问方式错误,以及缓冲区长度设置的问题,具体修正如下:

1. 为什么floatChannelData是nil?

G711 uLaw是压缩音频格式,不属于PCM格式范畴。AVAudioPCMBuffer的floatChannelData仅对PCM格式(比如.pcmFormatFloat32)有效,对于压缩格式,这个属性必然是nil。你需要通过缓冲区的data属性来访问压缩后的uLaw字节数据。

2. 缓冲区长度设置错误

你直接把frameLength设为frameCapacity,但转换后的实际帧数可能和计算的容量不一致,应该让转换器自动设置实际的frameLength,不要手动赋值。

3. 修正后的代码示例

var audioEngine = AVAudioEngine()

var convertFormatBasicDescription = AudioStreamBasicDescription(
    mSampleRate: 8000,
    mFormatID: kAudioFormatULaw,
    mFormatFlags: AudioFormatFlags(kAudioFormatULaw),
    mBytesPerPacket: 1,
    mFramesPerPacket: 1,
    mBytesPerFrame: 1,
    mChannelsPerFrame: 1,
    mBitsPerChannel: 8,
    mReserved: 0
)

func convert() {
    let inputNode = audioEngine.inputNode
    // 建议用输入节点的实际格式,避免强制指定不匹配的格式
    guard let micInputFormat = inputNode.inputFormat(forBus: 0) else {
        print("无法获取麦克风输入格式")
        return
    }
    
    guard let convertFormat = AVAudioFormat(streamDescription: &convertFormatBasicDescription) else {
        print("创建转换格式失败")
        return
    }
    
    guard let converter = AVAudioConverter(from: micInputFormat, to: convertFormat) else {
        print("创建转换器失败")
        return
    }
    
    do {
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: micInputFormat) { [weak self] (buffer, time) in
            var newBufferAvailable = true
            
            let inputCallback: AVAudioConverterInputBlock = { inNumPackets, outStatus in
                if newBufferAvailable {
                    outStatus.pointee = .haveData
                    newBufferAvailable = false
                    return buffer
                } else {
                    outStatus.pointee = .noDataNow
                    return nil
                }
            }
            
            // 计算转换后的预期帧数
            let expectedFrameCount = AVAudioFrameCount(convertFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate)
            guard let convertedBuffer = AVAudioPCMBuffer(pcmFormat: convertFormat, frameCapacity: expectedFrameCount) else {
                print("创建转换缓冲区失败")
                return
            }
            
            var error: NSError?
            let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
            
            // 检查转换状态
            switch status {
            case .success:
                // 访问uLaw压缩数据:通过data属性获取字节
                if let data = convertedBuffer.data {
                    let byteCount = Int(convertedBuffer.frameLength) * Int(convertFormat.streamDescription.pointee.mBytesPerFrame)
                    let uLawBytes = Data(bytes: data, count: byteCount)
                    print("转换成功,uLaw数据长度:\(uLawBytes.count)")
                    // 这里可以处理uLawBytes,比如发送或存储
                }
            case .error:
                print("转换失败:\(error?.localizedDescription ?? "未知错误")")
            case .inputRanDry:
                print("输入数据不足")
            @unknown default:
                print("未知转换状态")
            }
        }
        
        audioEngine.prepare()
        try audioEngine.start()
    } catch {
        print("启动音频引擎失败:\(error.localizedDescription)")
    }
}

额外注意事项

  • 不要强制指定麦克风输入格式为.pcmFormatFloat32 + 44100Hz,不同设备的麦克风默认格式可能不同,直接用inputNode.inputFormat(forBus: 0)获取实际格式更可靠。
  • 转换后的uLaw数据是8bit单字节,每帧对应一个字节,所以byteCount等于转换后的帧数。

内容的提问来源于stack exchange,提问作者zones

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:50:23