Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to encode and decode audio using opus

I am trying integrate opus into my application, the encode and decode function returns positive value which means successfully, but the output audio can't play. Raw audio data can play as well. Here is how I encode data. I use 4 bytes prefix to separate from each packet.

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];
}

And here is decode function:

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];
}

Please help me to point out what I am missing. An example would be great.

like image 849
sahara108 Avatar asked May 26 '15 01:05

sahara108


People also ask

How do I encode Opus?

Since Opus is a stateful codec, the encoding process starts with creating an encoder state. This can be done with: int error; OpusEncoder *enc; enc = opus_encoder_create(Fs, channels, application, &error); From this point, enc can be used for encoding an audio stream.

What audio format is Opus?

An OPUS file is an audio file created in the Opus format (also called “Ogg Opus“), a lossy audio format developed for Internet streaming. It uses both SILK (used by Skype) and CELT (from Xiph.Org) codecs and supports variable bit rates from 6 kb/s to 510 kb/s.

Why is Opus codec so good?

Opus is a totally open, royalty-free, highly versatile audio codec. Opus is unmatched for interactive speech and music transmission over the Internet, but is also intended for storage and streaming applications. Open, highly-versatile… and royalty-free.

What bitrate should I use for Opus?

Opus only supports bitrates down to 6 Kb/s. Codec 2 handles ultra low bitrate speech at 0.7 - 3.2 Kb/s. 10 Kb/s will deliver narrowband most of the time, 24 Kb/s should give fullband.


2 Answers

One thing I notice is that you're treating the return value of opus_encode() as a number of samples encoded, when it's the number of bytes in the compressed packet. that means you're writing 50% or 75% garbage data from the end of _encoderOutputBuffer into your encoded stream.

Also make sure _encoderOutputBuffer has room for the hard-coded 1000 byte packet-length limit you're passing in.

like image 162
Ralph Giles Avatar answered Sep 28 '22 11:09

Ralph Giles


I found what the problem is. I have set the audio format is float kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;. I should use opus_encode_float and opus_decode_float instead of opus_encode opus_decode. As @Ralph says, we should use fec = 0 in opus_decode. Thanks to @Ralph.

like image 34
sahara108 Avatar answered Sep 28 '22 13:09

sahara108