Обрезка звука с опусом на Android, отправленная с IOS
Я записываю звук в IOS из audioUnit, кодирую байты с помощью opus и отправляю его через UDP на сторону Android. Проблема в том, что звук воспроизводится немного срезанным. Я также протестировал звук, отправив необработанные данные с IOS на Android, и он отлично играет.
Мой код аудиосессии:
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 массива вместе, чтобы получить больший размер кадра (требуется 640) для Android, но я кодирую только 640 с помощью TPCircularBuffer.
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)")
}
В приведенном выше коде я кодирую 640 frameSize, добавляю его в полезную нагрузку ProtoBuf и отправляю через UDP.
На стороне Android я разбираю Protobuf, декодирую размер кадра 640 и воспроизводю его с помощью AudioTrack. На стороне Android нет проблем, поскольку я записывал и воспроизводил звук только с помощью Android, но проблема возникает, когда я записываю звук через IOS и проигрываю Сторона Android.
Пожалуйста, не предлагайте увеличивать frameSize, устанавливая Preferred IO Buffer Duration. Я хочу сделать это, не меняя этого.
/questions/51975269/audiounit-inputcallback-s-audiounitrender-and-gt-nesootvetstvie-mezhdu-audiobuff/51975278#51975278 Это было полезно.
/questions/52115451/obrezka-zvuka-s-opusom-na-android-otpravlennaya-s-ios/52115458#52115458 Я обновил свой код в соответствии с вашим предложением, удалил делегат и объединение массива, но на стороне 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;
1 ответ
Ваш код выполняет выделение памяти Swift (объединение массивов) и вызовы методов Swift (ваш делегат записи) внутри обратного вызова аудио. Apple (в сеансе WWDC на Audio) рекомендует не выделять память или вызывать методы внутри контекста обратного вызова звука в реальном времени (особенно при запросе коротких предпочтительных длительностей буфера ввода-вывода). Придерживайтесь вызовов функций C, таких как memcpy и TPCircularBuffer.
Добавлено: Также не выбрасывайте образцы. Если вы получили 680 отсчетов, но для пакета требуется только 640, оставьте 40 "оставшихся" отсчетов и используйте их перед последующим пакетом. Круговой буфер сохранит их для вас. Промыть и повторить. Отправьте все образцы, которые вы получаете от обратного вызова аудио, когда вы накопили достаточно для пакета, или еще один пакет, когда вы в конечном итоге накопите 1280 (2*640) или больше.