iOS - 如何从流中读取音频并播放音频

Posted

技术标签:

【中文标题】iOS - 如何从流中读取音频并播放音频【英文标题】:iOS - How to read audio from a stream and play the audio 【发布时间】:2017-06-20 07:38:40 【问题描述】:

感谢所有花时间阅读问题的人!

所以我使用 MultipeerConnectivity 制作了一个流。我能够将音频录制到 CMSampleBuffer 并将该缓冲区转换为 UInt8 数据。然后使用以下方法将该数据发送给对等点:

outputStream!.write(u8ptr, maxLength: Int(buffer.mDataByteSize))

然后当数据显示在 inputStream 上时,会调用以下方法:

func stream(_ aStream: Stream, handle eventCode: Stream.Event) 

我有打印语句,所以这部分运行良好。当数据实际出现时,我调用我的函数

func readFromStream() 

我知道我需要调用 inputStream.read 方法来实际从流中读取,但我不确定如何实际读取数据然后将其转换为 NSData 以便可以使用 AVAudioPlayer 播放。

(除非您知道更好、更有效的方法)

这是我目前所拥有的,但我还没有测试过它,我认为会有问题。

func readFromStream() 
    var buffer = [UInt8](repeating: 0, count: 1024)

    while (inputStream!.hasBytesAvailable) 
        let length = inputStream!.read(&buffer, maxLength: buffer.count)

        if (length > 0) 

            if (audioEngine!.isRunning) 
                audioEngine!.stop()
                audioEngine!.reset()
            

            print("\(#file) > \(#function) > \(length) bytes read")

            let audioBuffer = bytesToAudioBuffer(buffer)
            let mainMixer = audioEngine!.mainMixerNode

            audioEngine!.connect(audioPlayer!, to: mainMixer, format: audioBuffer.format)

            audioPlayer!.scheduleBuffer(audioBuffer, completionHandler: nil)

            do 
                try audioEngine!.start()
            
            catch let error as NSError 
                print("\(#file) > \(#function) > error: \(error.localizedDescription)")
            

            audioPlayer!.play()
        
    

根据我所拥有的,没有音频播放。这是静音,但我可以看到其中一台设备正在接收音频。

所以基本上,我的问题是,如何将此缓冲区转换为正确的数据类型以便可以实时播放?

感谢您的帮助!如果您需要更多信息,请告诉我。

【问题讨论】:

【参考方案1】:

将 UInt8 转换为 NSData > NSData from UInt8

完成后,只需使用 AVAudioPlayer

let player = AVAudioPlayer(data: data)
player.play()

【讨论】:

【参考方案2】:

我没有使用 CMSampleBuffers,而是使用了 AVAudioPCMBuffers。这些可以通过从 AVAudioEngine 录制来创建。基本上这里是我如何将 AVAudioPCMBuffer 转换为 NSData 并返回。

func audioBufferToNSData(PCMBuffer: AVAudioPCMBuffer) -> NSData 
    let channelCount = 1  // given PCMBuffer channel count is 1
    let channels = UnsafeBufferPointer(start: PCMBuffer.floatChannelData, count: channelCount)
    let data = NSData(bytes: channels[0], length:Int(PCMBuffer.frameLength * PCMBuffer.format.streamDescription.pointee.mBytesPerFrame))

    return data


func dataToPCMBuffer(format: AVAudioFormat, data: NSData) -> AVAudioPCMBuffer 

    let audioBuffer = AVAudioPCMBuffer(pcmFormat: format,
                                       frameCapacity: UInt32(data.length) / format.streamDescription.pointee.mBytesPerFrame)

    audioBuffer.frameLength = audioBuffer.frameCapacity
    let channels = UnsafeBufferPointer(start: audioBuffer.floatChannelData, count: Int(audioBuffer.format.channelCount))
    data.getBytes(UnsafeMutableRawPointer(channels[0]) , length: data.length)
    return audioBuffer

【讨论】:

以上是关于iOS - 如何从流中读取音频并播放音频的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C - 将流数据传递到音频队列

如何从实时流中播放音频

如何播放 RTMP 流中的音频?

如何在 iOS 中使用原始数据播放音频?

如何在三星智能电视上提取嵌入在 Icecast 音频(广播)流中的流式“正在播放”数据

读取音频文件而不播放它