gpt4 book ai didi

ios - 如何使用 opus 编码和解码音频

转载 作者:可可西里 更新时间:2023-11-01 05:11:44 77 4
gpt4 key购买 nike

我正在尝试将 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];
}

请帮我指出我遗漏了什么。一个例子会很棒。

最佳答案

我认为问题出在解码端:

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

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

  • OPUS_SET_SIGNAL 不会在 opus_decoder_ctl() 中执行任何操作,但它只会返回 OPUS_UNIMPLEMENTED 而不会影响行为。

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

关于ios - 如何使用 opus 编码和解码音频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30448006/

77 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com