Cómo codificar y decodificar audio usando opus

Estoy intentando integrar opus en mi aplicación, la function de encoding y deencoding devuelve un valor positivo que significa con éxito, pero el audio de salida no puede reproducirse. Los datos de audio sin procesar también pueden reproducirse. Aquí es cómo codifico los datos. Utilizo el prefijo de 4 bytes para separar de cada package.

self.encoder = opus_encoder_create(24000, 1, OPUS_APPLICATION_VOIP, &opusError); opus_encoder_ctl(self.encoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); - (void) encodeBufferList:(AudioBufferList *)bufferList { BOOL success = TPCircularBufferProduceBytes(_circularBuffer, bufferList->mBuffers[0].mData, bufferList->mBuffers[0].mDataByteSize); if (!success) { NSLog(@"insufficient space in circular buffer!"); } if (!_encoding) { _encoding = YES; dispatch_async(self.processingQueue, ^{ [self startEncodingLoop]; }); } } -(void)startEncodingLoop { int32_t availableBytes = 0; opus_int16 *data = (opus_int16*)TPCircularBufferTail(_circularBuffer, &availableBytes); int availableSamples = availableBytes / _inputASBD.mBytesPerFrame; /*! * Use dynamic duration */ // int validSamples[6] = {2.5, 5, 10, 20, 40, 60}; // in milisecond // int esample = validSamples[0] * self.sampleRate / 1000; // for (int i = 0; i < 6; i++) { // int32_t samp = validSamples[i] * self.sampleRate / 1000; // if (availableSamples < samp) { // break; // } // esample = samp; // } /*! * Use 20ms */ int esample = 20 * self.sampleRate / 1000; if (availableSamples < esample) { /*! * Out of data. Finish encoding */ self.encoding = NO; [self.eDelegate didFinishEncode]; return; } // printf("raw input value for packet \n"); // for (int i = 0; i < esample * self.numberOfChannels; i++) { // printf("%d :", data[i]); // } int returnValue = opus_encode(_encoder, data, esample, _encoderOutputBuffer, 1000); TPCircularBufferConsume(_circularBuffer, esample * sizeof(opus_int16) * self.numberOfChannels); // printf("output encode \n"); // for (int i = 0; i < returnValue; i++) { // printf("%d :", _encoderOutputBuffer[i]); // } NSMutableData *outputData = [NSMutableData new]; NSError *error = nil; if (returnValue <= 0) { error = [OKUtilities errorForOpusErrorCode:returnValue]; }else { [outputData appendBytes:_encoderOutputBuffer length:returnValue * sizeof(unsigned char)]; unsigned char int_field[4]; int_to_char(returnValue , int_field); NSData *header = [NSData dataWithBytes:&int_field[0] length:4 * sizeof(unsigned char)]; if (self.eDelegate) { [self.eDelegate didEncodeWithData:header]; } } if (self.eDelegate) { [self.eDelegate didEncodeWithData:outputData]; } [self startEncodingLoop]; } 

Y aquí está la function de deencoding:

 self.decoder = opus_decoder_create(24000, 1, &opusError); opus_decoder_ctl(self.decoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); opus_decoder_ctl(self.decoder, OPUS_SET_GAIN(10)); -(void)startParseData:(unsigned char*)data remainingLen:(int)len { if (len <= 0) { [self.dDelegate didFinishDecode]; return; } int headLen = sizeof(unsigned char) * 4; unsigned char h[4]; h[0] = data[0]; h[1] = data[1]; h[2] = data[2]; h[3] = data[3]; int packetLen = char_to_int(h); data += headLen; packetLen = packetLen * sizeof(unsigned char) * self.numberOfChannels; [self decodePacket:data length:packetLen remainingLen:len - headLen]; } -(void)decodePacket:(unsigned char*)inputData length:(int)len remainingLen:(int)rl { int bw = opus_packet_get_bandwidth(inputData); //TEST: return OPUS_BANDWIDTH_SUPERWIDEBAND here int32_t decodedSamples = 0; // int validSamples[6] = {2.5, 5, 10, 20, 40, 60}; // in milisecond /*! * Use 60ms */ int esample = 60 * self.sampleRate / 1000; // printf("input decode \n"); // for (int i = 0; i < len; i++) { // printf("%d :", inputData[i]); // } _decoderBufferLength = esample * self.numberOfChannels * sizeof(opus_int16); int returnValue = opus_decode(_decoder, inputData, len, _outputBuffer, esample, 1); if (returnValue < 0) { NSError *error = [OKUtilities errorForOpusErrorCode:returnValue]; NSLog(@"decode error %@", error); inputData += len; [self startParseData:inputData remainingLen:rl - len]; return; } decodedSamples = returnValue; NSUInteger length = decodedSamples * self.numberOfChannels; // printf("raw decoded data \n"); // for (int i = 0; i < length; i++) { // printf("%d :", _outputBuffer[i]); // } NSData *audioData = [NSData dataWithBytes:_outputBuffer length:length * sizeof(opus_int16)]; if (self.dDelegate) { [self.dDelegate didDecodeData:audioData]; } inputData += len; [self startParseData:inputData remainingLen:rl - len]; } 

Ayúdame a señalar lo que me falta. Un ejemplo sería genial.

    Creo que el problema está en el lado de la deencoding:

    • fec 1 como argumento opus_decode() a opus_decode() . Esto le pide al decodificador que genere el valor total de la duración del package de datos a partir de los datos de corrección de errores en el package actual. No veo ningún seguimiento de packages perdidos en su código, por lo que debe pasarse 0 su lugar. Con ese cambio, su input y la duración de la salida deben coincidir.

    • Configura el decodificador para salida mono, pero luego usa self.numberOfChannels en cálculos de longitud. Esos deben coincidir o puede tener un comportamiento inesperado.

    • OPUS_SET_SIGNAL no hace nada en opus_decoder_ctl () pero simplemente devolverá OPUS_UNIMPLEMENTED sin afectar el comportamiento.

    • Los packages de Opus pueden tener una duración de hasta 120 ms, por lo que su límite de 60 ms no puede descifrar algunas secuencias. Si solo estás hablando con tu propia aplicación que no causará ningún problema de la forma en que lo has configurado, ya que libopus tiene un valor pnetworkingeterminado de 20 ms.

    Una cosa que noto es que está tratando el valor de retorno de opus_encode () como una cantidad de muestras codificadas, cuando es el número de bytes en el package comprimido. eso significa que está escribiendo datos de basura del 50% o del 75% desde el final de _encoderOutputBuffer en su flujo codificado.

    También asegúrese de que _encoderOutputBuffer tenga espacio para el límite de longitud de package de 1000 bytes codificados que está pasando.

    Encontré cuál es el problema. He configurado el formatting de audio flotante kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat; . Debería usar opus_encode_float y opus_decode_float lugar de opus_encode opus_decode . Como dice @Ralph, deberíamos usar fec = 0 en opus_decode . Gracias a @Ralph.