iOS Swift 从网络流中播放音频(aac)
Posted
技术标签:
【中文标题】iOS Swift 从网络流中播放音频(aac)【英文标题】:iOS Swift playing audio (aac) from network stream 【发布时间】:2018-06-26 15:56:13 【问题描述】:我正在开发一个 ios 应用程序,而且我对 iOS 开发还很陌生。到目前为止,我已经使用 VideoToolbox 从网络流中实现了一个 h264 解码器,这非常困难。
现在我需要播放来自网络的音频流,但不涉及文件,只是直接从套接字读取的原始 AAC 流。此流来自 ffmpeg 实例的输出。
问题是我不知道如何开始,似乎关于这个主题的信息很少。我已经尝试过AVAudioPlayer
,但发现只是沉默。我想我首先需要从流中解压缩数据包,就像使用 h264 解码器一样。
我也一直在尝试使用AVAudioEngine
和AVAudioPlayerNode
,但没有成功,与AVAudioPlayer
相同。有人可以给我一些指导吗?也许AudioToolbox
? AudioQueue
?
非常感谢您的帮助:)
编辑:
我正在玩AVAudioCompressedBuffer
并且使用AVAudioEngine
和AVAudioNode
没有错误。但是,我不知道这个输出是什么意思:
inBuffer: <AVAudioCompressedBuffer@0x6040004039f0: 0/1024 bytes>
这是否意味着缓冲区为空?我一直在尝试以多种方式提供此缓冲区,但总是返回类似 0/1024 的内容。我认为我做的不对:
compressedBuffer.mutableAudioBufferList.pointee = audioBufferList
有什么想法吗?
谢谢!
编辑 2: 我正在编辑以反映我用于解压缩缓冲区的代码。也许有人可以指出我正确的方向。 注意:此函数摄取的数据包实际上是在没有 ADTS 标头(9 个字节)的情况下传递的,但我也尝试将它与标头一起传递。
func decodeCompressedPacket(packet: Data) -> AVAudioPCMBuffer
var packetCopy = packet
var streamDescription: AudioStreamBasicDescription = AudioStreamBasicDescription.init(mSampleRate: 44100, mFormatID: kAudioFormatMPEG4AAC, mFormatFlags: UInt32(MPEG4ObjectID.AAC_LC.rawValue), mBytesPerPacket: 0, mFramesPerPacket: 1024, mBytesPerFrame: 0, mChannelsPerFrame: 1, mBitsPerChannel: 0, mReserved: 0)
let audioFormat = AVAudioFormat.init(streamDescription: &streamDescription)
let compressedBuffer = AVAudioCompressedBuffer.init(format: audioFormat!, packetCapacity: 1, maximumPacketSize: 1024)
print("packetCopy count: \(packetCopy.count)")
var audioBuffer: AudioBuffer = AudioBuffer.init(mNumberChannels: 1, mDataByteSize: UInt32(packetCopy.count), mData: &packetCopy)
var audioBufferList: AudioBufferList = AudioBufferList.init(mNumberBuffers: 1, mBuffers: audioBuffer)
var mNumberBuffers = 1
var packetSize = packetCopy.count
// memcpy(&compressedBuffer.mutableAudioBufferList[0].mBuffers, &audioBuffer, MemoryLayout<AudioBuffer>.size)
// memcpy(&compressedBuffer.mutableAudioBufferList[0].mBuffers.mDataByteSize, &packetSize, MemoryLayout<Int>.size)
// memcpy(&compressedBuffer.mutableAudioBufferList[0].mNumberBuffers, &mNumberBuffers, MemoryLayout<UInt32>.size)
// compressedBuffer.mutableAudioBufferList.pointee = audioBufferList
var bufferPointer = compressedBuffer.data
for byte in packetCopy
memset(compressedBuffer.mutableAudioBufferList[0].mBuffers.mData, Int32(byte), MemoryLayout<UInt8>.size)
print("mBuffers: \(compressedBuffer.audioBufferList[0].mBuffers.mNumberChannels)")
print("mBuffers: \(compressedBuffer.audioBufferList[0].mBuffers.mDataByteSize)")
print("mBuffers: \(compressedBuffer.audioBufferList[0].mBuffers.mData)")
var uncompressedBuffer = uncompress(inBuffer: compressedBuffer)
print("uncompressedBuffer: \(uncompressedBuffer)")
return uncompressedBuffer
【问题讨论】:
我也一样。你有什么线索吗? 【参考方案1】:所以您认为您将(很可能)需要解压缩从流中接收到的数据包是正确的。这个想法是让它们成为原始 PCM 格式,以便可以直接发送到音频输出。这样,您还可以对音频流应用任何您想要的 DSP/音频操作。
正如您所提到的,您可能需要研究 AudioQueue
方向,Apple Docs 提供了一个很好的 streaming audio in realtime 示例,尽管这是在 obj-c 中(在这种情况下,我认为它可能是在 obj-c 中执行此操作的好主意)。这可能是最好的开始(将 obj-c 连接到 swift 是 super simple)。
在 Swift 中再次查看它,有一个类 AVAudioCompressedBuffer 似乎为您的情况处理 AAC(如果您让它工作,则不需要解码 AAC),但是没有设置缓冲区的直接方法因为它的目的只是作为一个存储容器,我相信。 Here's 一个使用 AVAudioCompressedBuffer 和 AVAudioFile 的工作示例(也许您可以将所有内容缓冲到后台线程中的文件中?我认为这将是太多的 IO 开销)。
但是,如果您在 obj-c 中解决此问题,则有一个 post 说明如何直接通过 memset
设置 AVAudioPCMBuffer(可能与 AVAudioCompressedBuffer 一起使用?)(有点令人厌烦,但同时又像嵌入式一样可爱程序员本人)。
// make a silent stereo buffer
AVAudioChannelLayout *chLayout = [[AVAudioChannelLayout alloc] initWithLayoutTag:kAudioChannelLayoutTag_Stereo];
AVAudioFormat *chFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:44100.0
interleaved:NO
channelLayout:chLayout];
AVAudioPCMBuffer *thePCMBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:chFormat frameCapacity:1024];
thePCMBuffer.frameLength = thePCMBuffer.frameCapacity;
for (AVAudioChannelCount ch = 0; ch < chFormat.channelCount; ++ch)
memset(thePCMBuffer.floatChannelData[ch], 0, thePCMBuffer.frameLength * chFormat.streamDescription->mBytesPerFrame);
我知道这有很多事情要做,而且似乎不是一个简单的解决方案,但我认为 obj-c AudioQueue
技术将是我的第一站!
希望这会有所帮助!
【讨论】:
非常感谢@WoodyDev。事实上,我一直在尝试各种可能性。例如,使用 AVAudioConverter 对我不起作用,因为我遇到了各种我无法解决的AudioConverterFillComplexBuffer
错误。另外,我一直在玩AVAudioCompressedBuffer
。问题是我似乎无法将数据加载到AVAudioCompressedBuffer
,我正在编辑我的原始问题以反映这一点。但是输出没有错误。这件事快把我逼疯了。我认为用 VideoToolbox 解码 h264 很难,但是这个...
您是否尝试过将 memset 与从 [AVAudioCompressedBuffer getData] 返回的指针一起使用? (link here). 看起来它的工作方式与我在答案中发布的示例相同(对于 AVAudioPCMBuffer)。我还没有测试过,但是如果逻辑对我有用,那么它应该全部拼凑起来:-)
我尝试使用memcpy
,因为您的示例用0(静音)填充缓冲区,以复制整个AudioBuffer
。我也尝试过像您的示例一样创建一个循环,每个字节复制一个字节,但输出总是相同的。像<AVAudioCompressedBuffer@0x6040000152f0: 0/1024 bytes>
这样的东西。我尝试搜索 0/1024 字节的含义,但似乎 Internet 上没有文档。我将更新我的原始帖子以发布整个代码。谢谢@WoodyDev以上是关于iOS Swift 从网络流中播放音频(aac)的主要内容,如果未能解决你的问题,请参考以下文章
在 iOS 上播放 MP3 或 AAC 文件都有哪些音频播放选项?
aac 流的 HTML5 音频播放在 type="audio/mp4" 的 iOS 11 中不起作用