Opus para iOS, se bloquea con 16000 frecuencias de muestreo

Estoy desarrollando la aplicación Voip con Opus para iOS (Objective-C y C ++).
Funciona bien con una velocidad de muestreo de 8000, 12000, 24000 y 48000, excepto con 16000, donde la aplicación falla en el método opus_encode.

Esto es lo que estoy haciendo:

m_oAudioSession = [AVAudioSession shanetworkingInstance]; [m_oAudioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&m_oError]; [m_oAudioSession setMode:AVAudioSessionModeVoiceChat error:&m_oError]; [m_oAudioSession setPrefernetworkingSampleRate:VOIP_AUDIO_DRIVER_DEFAULT_SAMPLE_RATE error:&m_oError]; [m_oAudioSession setPrefernetworkingInputNumberOfChannels:VOIP_AUDIO_DRIVER_DEFAULT_INPUT_CHANNELS error:&m_oError]; [m_oAudioSession setPrefernetworkingOutputNumberOfChannels:VOIP_AUDIO_DRIVER_DEFAULT_OUTPUT_CHANNELS error:&m_oError]; [m_oAudioSession setPrefernetworkingIOBufferDuration:VOIP_AUDIO_DRIVER_DEFAULT_BUFFER_DURATION error:&m_oError]; [m_oAudioSession setActive:YES error:&m_oError]; 

Constantes:

 VOIP_AUDIO_DRIVER_DEFAULT_SAMPLE_RATE is 16000 VOIP_AUDIO_DRIVER_DEFAULT_INPUT_CHANNELS is 1 VOIP_AUDIO_DRIVER_DEFAULT_OUTPUT_CHANNELS is 1 VOIP_AUDIO_DRIVER_DEFAULT_BUFFER_DURATION is 0.02 VOIP_AUDIO_DRIVER_FRAMES_PER_PACKET is 1 

Después de eso, estoy usando una tasa de muestreo real y la duración del buffer de m_oAudioSession.sampleRate y m_oAudioSession.IOBufferDuration. Se configuran en variables m_fSampleRate y m_fBufferDuration.

Las configuraciones son:

 //Describes audio component: m_sAudioDescription.componentType = kAudioUnitType_Output; m_sAudioDescription.componentSubType = kAudioUnitSubType_VoiceProcessingIO/*kAudioUnitSubType_RemoteIO*/; m_sAudioDescription.componentFlags = 0; m_sAudioDescription.componentFlagsMask = 0; m_sAudioDescription.componentManufacturer = kAudioUnitManufacturer_Apple; m_sAudioFormat.mSampleRate = m_fSampleRate; m_sAudioFormat.mFormatID = kAudioFormatLinearPCM; m_sAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; m_sAudioFormat.mFramesPerPacket = VOIP_AUDIO_DRIVER_FRAMES_PER_PACKET; m_sAudioFormat.mChannelsPerFrame = VOIP_AUDIO_DRIVER_DEFAULT_INPUT_CHANNELS; m_sAudioFormat.mBitsPerChannel = (UInt32)(8 * m_iBytesPerSample); m_sAudioFormat.mBytesPerFrame = (UInt32)((m_sAudioFormat.mBitsPerChannel / 8) * m_sAudioFormat.mChannelsPerFrame); m_sAudioFormat.mBytesPerPacket = m_sAudioFormat.mBytesPerFrame * m_sAudioFormat.mFramesPerPacket; m_sAudioFormat.mReserved = 0; 

Los cálculos que hago son:

 m_iBytesPerSample = sizeof(/*AudioSampleType*/SInt16); //Calculating buffer size: int samplesPerFrame = (int)(m_fBufferDuration * m_fSampleRate) + 1; m_iBufferSizeBytes = samplesPerFrame * m_iBytesPerSample; //Allocating input buffer: UInt32 inputBufferListSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * m_sAudioFormat.mChannelsPerFrame); m_sInputBuffer = (AudioBufferList *)VoipAlloc(inputBufferListSize); m_sInputBuffer->mNumberBuffers = m_sAudioFormat.mChannelsPerFrame; //Pre-mallocating buffers for AudioBufferLists for(VoipUInt32 tmp_int1 = 0; tmp_int1 < m_sInputBuffer->mNumberBuffers; tmp_int1++) { m_sInputBuffer->mBuffers[tmp_int1].mNumberChannels = VOIP_AUDIO_DRIVER_DEFAULT_INPUT_CHANNELS; m_sInputBuffer->mBuffers[tmp_int1].mDataByteSize = (UInt32)m_iBufferSizeBytes; m_sInputBuffer->mBuffers[tmp_int1].mData = VoipAlloc(m_iBufferSizeBytes); memset(m_sInputBuffer->mBuffers[tmp_int1].mData, 0, m_iBufferSizeBytes); } 

La lectura y escritura desde la unidad de audio se realizan usando m_sInputBuffer.

Aquí está la creación del Opus:

 m_oEncoder = opus_encoder_create(m_iSampleRate, m_iNumberOfChannels, VOIP_AUDIO_CODECS_OPUS_APPLICATION_TYPE, &_error); if (_error < 0) { fprintf(stderr, "VoipAudioCodecs error: failed to create an encoder: %s\n", opus_strerror(_error)); return; } _error = opus_encoder_ctl(m_oEncoder, OPUS_SET_BITRATE(VOIP_AUDIO_CODECS_OPUS_BITRATE)); if (_error < 0) { fprintf(stderr, "VoipAudioCodecs error: failed to set the bitrate: %s\n", opus_strerror(_error)); return; } m_oDecoder = opus_decoder_create(m_iSampleRate, m_iNumberOfChannels, &_error); if (_error < 0) { fprintf(stderr, "VoipAudioCodecs error: failed to create a decoder: %s\n", opus_strerror(_error)); return; } 

Las configuraciones de Opus son:

 VOIP_AUDIO_CODECS_OPUS_BITRATE is OPUS_BITRATE_MAX //64000 //70400 //84800 //112000 VOIP_AUDIO_CODECS_OPUS_APPLICATION_TYPE is OPUS_APPLICATION_VOIP //OPUS_APPLICATION_AUDIO VOIP_AUDIO_CODECS_OPUS_MAX_FRAME_SIZE is 5760 //Minimum: (120ms; 5760 for 48kHz) VOIP_AUDIO_CODECS_OPUS_BYTES_SIZE is 960 //120, 240, 480, 960, 1920, 2880 

Cuando codifico y decode utilizo estos methods:

 Encode_Opus(VoipInt16* rawSamples, int rawSamplesSize) { unsigned char encodedData[m_iMaxPacketSize]; VoipInt32 bytesEncoded; int frameSize = rawSamplesSize / m_iBytesPerSample; bytesEncoded = opus_encode(m_oEncoder, rawSamples, frameSize, encodedData, m_iMaxPacketSize); if (bytesEncoded < 0) { fprintf(stderr, "VoipAudioCodecs error: encode failed: %s\n", opus_strerror(bytesEncoded)); return nullptr; } sVoipAudioCodecOpusEncoded* resultStruct = (sVoipAudioCodecOpusEncoded* )VoipAlloc(sizeof(sVoipAudioCodecOpusEncoded)); resultStruct->m_data = (unsigned char*)VoipAlloc(bytesEncoded); memcpy(resultStruct->m_data, encodedData, bytesEncoded); resultStruct->m_dataSize = bytesEncoded; return resultStruct; } Decode_Opus(void* encodedSamples, VoipInt32 encodedSamplesSize) { VoipInt16 decodedPacket[VOIP_AUDIO_CODECS_OPUS_MAX_FRAME_SIZE]; int _frameSize = opus_decode(m_oDecoder, (const unsigned char*)encodedSamples, encodedSamplesSize, decodedPacket, VOIP_AUDIO_CODECS_OPUS_MAX_FRAME_SIZE, 0); if (_frameSize < 0) { fprintf(stderr, "VoipAudioCodecs error: decoder failed: %s\n", opus_strerror(_frameSize)); return nullptr; } size_t frameSize = (size_t)_frameSize; sVoipAudioCodecOpusDecoded* resultStruct = (sVoipAudioCodecOpusDecoded* )VoipAlloc(sizeof(sVoipAudioCodecOpusDecoded)); resultStruct->m_data = (VoipInt16*)VoipAlloc(frameSize * m_iBytesPerSample); memcpy(resultStruct->m_data, decodedPacket, (frameSize * m_iBytesPerSample)); resultStruct->m_dataSize = frameSize * m_iBytesPerSample; return resultStruct; } 

Cuando la aplicación debe enviar datos:

 VoipUInt32 itemsForProcess = inputAudioQueue->getItemCount(); for (int tmp_queueItems = 0; tmp_queueItems < itemsForProcess; tmp_queueItems++) { sVoipQueue* tmp_samples = inputAudioQueue->popItem(); m_oCircularTempInputBuffer->writeDataToBuffer(tmp_samples->m_pData, tmp_samples->m_iDataSize); while (void* tmp_buffer = m_oCircularTempInputBuffer->readDataFromBuffer(VOIP_AUDIO_CODECS_OPUS_BYTES_SIZE)) { sVoipAudioCodecOpusEncoded* encodedSamples = Encode_Opus((VoipInt16*)tmp_buffer, VOIP_AUDIO_CODECS_OPUS_BYTES_SIZE); //Then packeting and the real sending using tcp socket… } //Rest of the code… } 

Aquí está la lectura:

 sVoipAudioCodecOpusDecoded* decodedSamples = Decode_Opus(inputPacket->m_pPacketData, (VoipInt32)inputPacket->m_iPacketSize); if (decodedSamples != nullptr) { m_oCircularTempOutputBuffer->writeDataToBuffer(decodedSamples->m_data, decodedSamples->m_dataSize); VoipFree((void**)&decodedSamples->m_data); VoipFree((void**)&decodedSamples); } while (void* tmp_buffer = m_oCircularTempOutputBuffer->readDataFromBuffer(m_iBufferSizeBytes)) { outputAudioQueue->pushItem(tmp_buffer, m_iBufferSizeBytes); } 

inputAudioQueue es queue con datos grabados de la callback de mi unidad de audio.
outputAudioQueue es una queue utilizada desde la callback de mi unidad de audio para reproducir el sonido.
m_iMaxPacketSize es lo mismo que m_iBufferSizeBytes.

Mis preguntas son:
Me preguntaba, ¿son correctos mis cálculos?
Y si no, ¿cómo puedo mejorarlos?
¿Ves algún error en el código?
¿Tiene una sugerencia para arreglar el error de locking en el método opus_encode cuando la tasa de muestreo se establece en 16000?

Gracias de antemano.

PD. Hice algunas testings con la tasa de muestreo en 16000 y encontré esto:
Si utilizo esta fórmula: frame_duration = frame_size / sample rate, y si establezco frame_duration a prefenetworkingIOBufferDuration:
120/16000 = 0.0075 // AVAudioSession establece 0.008000 – Bloqueos
240/16000 = 0.015 // AVAudioSession establece 0.016000 – Bloqueos
480/16000 = 0.03 // AVAudioSession establece 0.032000 – Bloqueos
960/16000 = 0.06 // AVAudioSession establece 0.064000 – Bloqueos
1920/16000 = 0.12 // AVAudioSession establece 0.128000 – Works
2880/16000 = 0,18 // AVAudioSession establece 0.128000 – Bloqueos
Entonces encontré que no hay locking del codificador con la tasa de muestreo 16000 y prefernetworkingIOBufferDuration 0.12 (1920), donde AVAudioSession establece 0.128000. Entonces solo funciona en este caso.

Algunas ideas ?