Обрезка звука с опусом на 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) или больше.

Другие вопросы по тегам