【发布时间】:2020-03-14 19:21:36
【问题描述】:
我正在从 audioUnit 在 IOS 中录制音频,使用 opus 对字节进行编码并通过 UDP 将其发送到 android 端。 问题是播放的声音有点剪辑。我还通过将原始数据从 IOS 发送到 Android 来测试声音,它播放完美。
我的 AudioSession 代码是
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker])
try audioSession.setPreferredIOBufferDuration(0.02)
try audioSession.setActive(true)
我的录音回调代码是:
func performRecording(
_ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp: UnsafePointer<AudioTimeStamp>,
inBufNumber: UInt32,
inNumberFrames: UInt32,
ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus
{
var err: OSStatus = noErr
err = AudioUnitRender(audioUnit!, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData)
if let mData = ioData[0].mBuffers.mData {
let ptrData = mData.bindMemory(to: Int16.self, capacity: Int(inNumberFrames))
let bufferPtr = UnsafeBufferPointer(start: ptrData, count: Int(inNumberFrames))
count += 1
addedBuffer += Array(bufferPtr)
if count == 2 {
let _ = TPCircularBufferProduceBytes(&circularBuffer, addedBuffer, UInt32(addedBuffer.count * 2))
count = 0
addedBuffer = []
let buffer = TPCircularBufferTail(&circularBuffer, &availableBytes)
memcpy(&targetBuffer, buffer, Int(min(bytesToCopy, Int(availableBytes))))
TPCircularBufferConsume(&circularBuffer, UInt32(min(bytesToCopy, Int(availableBytes))))
self.audioRecordingDelegate(inTimeStamp.pointee.mSampleTime / Double(16000), targetBuffer)
}
}
return err;
}
这里我得到 inNumberOfFrames 几乎 341,我将 2 个数组附加在一起以获得更大的 Android 帧大小(需要 640),但我仅在 TPCircularBuffer 的帮助下编码 640。
func gotSomeAudio(timeStamp: Double, samples: [Int16]) {
samples.count))
let encodedData = opusHelper?.encodeStream(of: samples)
OPUS_SET_BITRATE_REQUEST)
let myData = encodedData!.withUnsafeBufferPointer {
Data(buffer: $0)
}
var protoModel = ProtoModel()
seqNumber += 1
protoModel.sequenceNumber = seqNumber
protoModel.timeStamp = Date().currentTimeInMillis()
protoModel.payload = myData
DispatchQueue.global().async {
do {
try self.tcpClient?.send(data: protoModel)
} catch {
print(error.localizedDescription)
}
}
let diff = CFAbsoluteTimeGetCurrent() - start
print("Time diff is \(diff)")
}
在上面的代码中,我将 opus 编码为 640 帧大小并将其添加到 ProtoBuf 有效负载并通过 UDP 发送。
在 Android 端,我正在解析 Protobuf 并解码 640 帧大小并使用 AudioTrack 播放它。Android 端没有问题,因为我仅使用 Android 录制和播放了声音,但是当我通过 IOS 录制声音时出现问题并通过 Android 端播放。
请不要建议通过设置 Preferred IO Buffer Duration 来增加 frameSize。我想这样做而不改变它。
https://stackoverflow.com/a/57873492/12020007 很有帮助。
https://stackoverflow.com/a/58947295/12020007 我已根据您的建议更新了我的代码,删除了委托和数组串联,但在 android 端仍有剪辑。我还计算了编码字节所需的时间,大约为 2-3 毫秒。
更新的回调代码是
var err: OSStatus = noErr
// we are calling AudioUnitRender on the input bus of AURemoteIO
// this will store the audio data captured by the microphone in ioData
err = AudioUnitRender(audioUnit!, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData)
if let mData = ioData[0].mBuffers.mData {
_ = TPCircularBufferProduceBytes(&circularBuffer, mData, inNumberFrames * 2)
print("mDataByteSize: \(ioData[0].mBuffers.mDataByteSize)")
count += 1
if count == 2 {
count = 0
let buffer = TPCircularBufferTail(&circularBuffer, &availableBytes)
memcpy(&targetBuffer, buffer, min(bytesToCopy, Int(availableBytes)))
TPCircularBufferConsume(&circularBuffer, UInt32(min(bytesToCopy, Int(availableBytes))))
let encodedData = opusHelper?.encodeStream(of: targetBuffer)
let myData = encodedData!.withUnsafeBufferPointer {
Data(buffer: $0)
}
var protoModel = ProtoModel()
seqNumber += 1
protoModel.sequenceNumber = seqNumber
protoModel.timeStamp = Date().currentTimeInMillis()
protoModel.payload = myData
do {
try self.udpClient?.send(data: protoModel)
} catch {
print(error.localizedDescription)
}
}
}
return err;
【问题讨论】:
-
您应该监听应用程序/硬件状态的变化,并且您可以为每个状态设置自定义帧大小。
-
试试什么? @AbdulRahmanAyub 请坚持主题。
-
@shyayan 请增加frameSize,如果问题解决了告诉我
标签: ios swift core-audio audiounit opus