尝试通过多点连接将音频从麦克风流式传输到另一部手机

Posted

技术标签:

【中文标题】尝试通过多点连接将音频从麦克风流式传输到另一部手机【英文标题】:Trying to stream audio from microphone to another phone via multipeer connectivity 【发布时间】:2017-01-23 04:07:17 【问题描述】:

我正在尝试通过 Apple 的 Multipeer Connectivity 框架将音频从麦克风流式传输到另一部 iPhone。为了进行音频捕获和播放,我使用了 AVAudioEngine(非常感谢 Rhythmic Fistman's 回答 here)。

我通过在输入端安装一个水龙头从麦克风接收数据,从中我得到一个 AVAudioPCMBuffer,然后我将其转换为 UInt8 数组,然后我将其流式传输到另一部手机。

但是当我将数组转换回 AVAudioPCMBuffer 时,我得到一个 EXC_BAD_ACCESS 异常,编译器指向我再次将字节数组转换为 AVAudioPCMBuffer 的方法。

这是我在哪里获取、转换和流式传输输入的代码:

input.installTap(onBus: 0, bufferSize: 2048, format: input.inputFormat(forBus: 0), block: 
                (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in

                let audioBuffer = self.typetobinary(buffer)
                stream.write(audioBuffer, maxLength: audioBuffer.count)
            )

我的两个数据转换函数(取自Martin.R的答案here):

func binarytotype <T> (_ value: [UInt8], _: T.Type) -> T 
    return value.withUnsafeBufferPointer 
        UnsafeRawPointer($0.baseAddress!).load(as: T.self)
    



func typetobinary<T>(_ value: T) -> [UInt8] 
    var data = [UInt8](repeating: 0, count: MemoryLayout<T>.size)
    data.withUnsafeMutableBufferPointer 
        UnsafeMutableRawPointer($0.baseAddress!).storeBytes(of: value, as: T.self)
    
    return data

在接收端:

func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) 
    if streamName == "voice" 

        stream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
        stream.open()

        var bytes = [UInt8](repeating: 0, count: 8)
        stream.read(&bytes, maxLength: bytes.count)

        let audioBuffer = self.binarytotype(bytes, AVAudioPCMBuffer.self) //Here is where the app crashes

        do 
            try engine.start()

            audioPlayer.scheduleBuffer(audioBuffer, completionHandler: nil)
            audioPlayer.play()
       catch let error 
            print(error.localizedDescription)

        
    

问题是我可以在流式传输它(在同一部手机中)之前来回转换字节数组并从中播放声音,但不能在接收端创建 AVAudioPCMBuffer。有谁知道为什么转换在接收端不起作用?这是正确的方法吗?

对此的任何帮助、想法/意见将不胜感激。

【问题讨论】:

您能否提供一些参考如何使用音频队列或任何示例项目? 不,我不敢。 【参考方案1】:

您的AVAudioPCMBuffer 序列化/反序列化错误。

Swift3 的演员阵容发生了很大变化,似乎比 Swift2 需要更多的复制。

[UInt8]AVAudioPCMBuffers 之间转换的方法如下:

注意:此代码假定单浮点数据为 44.1kHz。 你可能想改变它。

func copyAudioBufferBytes(_ audioBuffer: AVAudioPCMBuffer) -> [UInt8] 
    let srcLeft = audioBuffer.floatChannelData![0]
    let bytesPerFrame = audioBuffer.format.streamDescription.pointee.mBytesPerFrame
    let numBytes = Int(bytesPerFrame * audioBuffer.frameLength)

    // initialize bytes to 0 (how to avoid?)
    var audioByteArray = [UInt8](repeating: 0, count: numBytes)

    // copy data from buffer
    srcLeft.withMemoryRebound(to: UInt8.self, capacity: numBytes)  srcByteData in
        audioByteArray.withUnsafeMutableBufferPointer 
            $0.baseAddress!.initialize(from: srcByteData, count: numBytes)
        
    

    return audioByteArray


func bytesToAudioBuffer(_ buf: [UInt8]) -> AVAudioPCMBuffer 
    // format assumption! make this part of your protocol?
    let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
    let frameLength = UInt32(buf.count) / fmt.streamDescription.pointee.mBytesPerFrame

    let audioBuffer = AVAudioPCMBuffer(pcmFormat: fmt, frameCapacity: frameLength)
    audioBuffer.frameLength = frameLength

    let dstLeft = audioBuffer.floatChannelData![0]
    // for stereo
    // let dstRight = audioBuffer.floatChannelData![1]

    buf.withUnsafeBufferPointer 
        let src = UnsafeRawPointer($0.baseAddress!).bindMemory(to: Float.self, capacity: Int(frameLength))
        dstLeft.initialize(from: src, count: Int(frameLength))
    

    return audioBuffer

【讨论】:

再次感谢节奏拳手!这似乎有效,但我遇到了一些问题。当我在 iPhone 5s 中录制时,录制以 8kHz 完成,而在 iPhone 6s 中则为 16kHz。当我发送 16kHz 流并使用该方法(需要 8kHz 流)时,这会使程序崩溃。在我发送之前,您对如何转换流有任何提示吗?此外,如果我将流从 5s 发送到 6s 并设置接收 8kHz 流的方法,则声音不会通过。它只播放几毫秒的白噪声,暂停和重复。有什么想法吗? 我建议选择一种采样率并在任何地方使用它。您可以通过在引擎中添加AVAudioMixer 或使用AVAudioConverter 来转换为它。 嗨@RhythmicFistman,我有一个问题希望你能回答。我在上面使用你的算法,我想知道 numBytes 变量是什么。基于这个变量,当我尝试将这些数据写入流时,每次写入发送超过 17000 个字节,这看起来非常高。最终写入失败,因为其他设备流缓冲区已满... 这个答案向您展示了如何 [de] 序列化 AVAudioPCMBuffer,但问题假设多对等体有足够的带宽通过它发送未压缩的。现在回想起来,这可能是一个错误的假设。 @RhythmicFistman 好吧,我想这是以比特为单位的,大约只有 2 KB,这应该是可以管理的。但是,我有一个问题,我播放的音频现在是静态的。我不确定为什么会这样,有什么想法吗?

以上是关于尝试通过多点连接将音频从麦克风流式传输到另一部手机的主要内容,如果未能解决你的问题,请参考以下文章

通过 WiFi 在 Android 手机之间流式传输语音

我可以使用 nodejs 将麦克风音频从客户端流式传输到客户端吗?

Android:使用 AudioTrack 和 Socket 手动有效地流式传输音频

将音频流式传输到其他 iOS 设备(使用多点)并通过本地设备播放音频

在 Android 上使用 OpenSL ES 通过套接字通信流式传输 MP3 音频

如何通过套接字或框架将音频从 iPhone 的麦克风流式传输到 Mac/PC?