5

我正在尝试将opus集成到我的应用程序中,编码和解码函数返回正值,表示成功,但输出音频无法播放。原始音频数据也可以播放。这是我对数据进行编码的方式。我使用 4 字节前缀与每个数据包分开。

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

这是解码功能:

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

请帮我指出我所缺少的。一个例子会很棒。

4

3 回答 3

5

我认为问题出在解码方面:

  • 1作为fec参数传递给opus_decode(). 这要求解码器从当前数据包中的纠错数据生成完整数据包持续时间的数据。我在您的代码中没有看到任何丢失的数据包跟踪,因此0应该通过。通过这种更改,您的输入和输出持续时间应该匹配。

  • 您将解码器配置为单声道输出,但稍后用于self.numberOfChannels长度计算。这些应该匹配,否则您可能会遇到意外行为。

  • OPUS_SET_SIGNAL在 opus_decoder_ctl() 中没有做任何事情,但它只会返回 OPUS_UNIMPLEMENTED 而不会影响行为。

  • Opus 数据包的持续时间最长可达 120 毫秒,因此您的 60 毫秒限制可能无法解码某些流。如果您只是在与您自己的应用程序交谈,那么您配置它的方式不会导致问题,因为 libopus 默认为 20ms 帧。

于 2015-05-29T18:19:32.363 回答
3

我发现了问题所在。我已将音频格式设置为 float kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;。我应该使用opus_encode_floatandopus_decode_float而不是opus_encode opus_decode. 正如@Ralph 所说,我们应该fecopus_decode. 感谢@Ralph。

于 2015-06-01T02:46:47.753 回答
2

我注意到的一件事是,当它是压缩数据包中的字节数时,您将opus_encode()的返回值视为编码的样本数。这意味着您正在将 _encoderOutputBuffer 末尾的 50% 或 75% 垃圾数据写入编码流。

还要确保 _encoderOutputBuffer 有空间容纳您传入的硬编码 1000 字节数据包长度限制。

于 2015-05-26T18:33:50.820 回答