Adding Opus stereo support to WebRTC

This CL adds support for sending and receiving stereo using the Opus codec.

BUG=issue1013

Review URL: https://webrtc-codereview.appspot.com/930008

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3050 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
tina.legrand@webrtc.org
2012-11-07 08:07:29 +00:00
parent 6dddfe9c35
commit 0ad3c1af0a
14 changed files with 276 additions and 36 deletions

View File

@ -74,6 +74,7 @@ int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst);
* -1 - Error * -1 - Error
*/ */
int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst); int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst);
int16_t WebRtcOpus_DecoderInitSlave(OpusDecInst* inst);
/**************************************************************************** /****************************************************************************
* WebRtcOpus_Decode(...) * WebRtcOpus_Decode(...)
@ -98,7 +99,9 @@ int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst);
int16_t WebRtcOpus_Decode(OpusDecInst* inst, int16_t* encoded, int16_t WebRtcOpus_Decode(OpusDecInst* inst, int16_t* encoded,
int16_t encoded_bytes, int16_t* decoded, int16_t encoded_bytes, int16_t* decoded,
int16_t* audio_type); int16_t* audio_type);
int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, int16_t* encoded,
int16_t encoded_bytes, int16_t* decoded,
int16_t* audio_type);
/**************************************************************************** /****************************************************************************
* WebRtcOpus_DecodePlc(...) * WebRtcOpus_DecodePlc(...)
* *

View File

@ -29,8 +29,8 @@ enum {
*/ */
kWebRtcOpusMaxDecodeFrameSizeMs = 120, kWebRtcOpusMaxDecodeFrameSizeMs = 120,
/* Sample count is 48 kHz * samples per frame. */ /* Sample count is 48 kHz * samples per frame * stereo. */
kWebRtcOpusMaxFrameSize = 48 * kWebRtcOpusMaxDecodeFrameSizeMs, kWebRtcOpusMaxFrameSize = 48 * kWebRtcOpusMaxDecodeFrameSizeMs * 2,
}; };
struct WebRtcOpusEncInst { struct WebRtcOpusEncInst {
@ -82,18 +82,25 @@ int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) {
} }
struct WebRtcOpusDecInst { struct WebRtcOpusDecInst {
int16_t state_48_32[8]; int16_t state_48_32_left[8];
OpusDecoder* decoder; int16_t state_48_32_right[8];
OpusDecoder* decoder_left;
OpusDecoder* decoder_right;
int channels;
}; };
int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) { int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) {
OpusDecInst* state; OpusDecInst* state;
state = (OpusDecInst*) calloc(1, sizeof(OpusDecInst)); state = (OpusDecInst*) calloc(1, sizeof(OpusDecInst));
if (state) { if (state) {
int error; int error_l;
int error_r;
// Always create a 48000 Hz Opus decoder. // Always create a 48000 Hz Opus decoder.
state->decoder = opus_decoder_create(48000, channels, &error); state->decoder_left = opus_decoder_create(48000, channels, &error_l);
if (error == OPUS_OK && state->decoder != NULL ) { state->decoder_right = opus_decoder_create(48000, channels, &error_r);
if (error_l == OPUS_OK && error_r == OPUS_OK &&
state->decoder_left != NULL && state->decoder_right != NULL) {
state->channels = channels;
*inst = state; *inst = state;
return 0; return 0;
} }
@ -104,27 +111,37 @@ int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, int channels) {
} }
int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst) { int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst) {
opus_decoder_destroy(inst->decoder); opus_decoder_destroy(inst->decoder_left);
opus_decoder_destroy(inst->decoder_right);
free(inst); free(inst);
return 0; return 0;
} }
int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst) { int16_t WebRtcOpus_DecoderInit(OpusDecInst* inst) {
int error = opus_decoder_ctl(inst->decoder, OPUS_RESET_STATE); int error = opus_decoder_ctl(inst->decoder_left, OPUS_RESET_STATE);
if (error == OPUS_OK) { if (error == OPUS_OK) {
memset(inst->state_48_32, 0, sizeof(inst->state_48_32)); memset(inst->state_48_32_left, 0, sizeof(inst->state_48_32_left));
return 0; return 0;
} }
return -1; return -1;
} }
static int DecodeNative(OpusDecInst* inst, int16_t* encoded, int16_t WebRtcOpus_DecoderInitSlave(OpusDecInst* inst) {
int error = opus_decoder_ctl(inst->decoder_right, OPUS_RESET_STATE);
if (error == OPUS_OK) {
memset(inst->state_48_32_right, 0, sizeof(inst->state_48_32_right));
return 0;
}
return -1;
}
static int DecodeNative(OpusDecoder* inst, int16_t* encoded,
int16_t encoded_bytes, int16_t* decoded, int16_t encoded_bytes, int16_t* decoded,
int16_t* audio_type) { int16_t* audio_type) {
unsigned char* coded = (unsigned char*) encoded; unsigned char* coded = (unsigned char*) encoded;
opus_int16* audio = (opus_int16*) decoded; opus_int16* audio = (opus_int16*) decoded;
int res = opus_decode(inst->decoder, coded, encoded_bytes, audio, int res = opus_decode(inst, coded, encoded_bytes, audio,
kWebRtcOpusMaxFrameSize, 0); kWebRtcOpusMaxFrameSize, 0);
/* TODO(tlegrand): set to DTX for zero-length packets? */ /* TODO(tlegrand): set to DTX for zero-length packets? */
*audio_type = 0; *audio_type = 0;
@ -148,16 +165,82 @@ int16_t WebRtcOpus_Decode(OpusDecInst* inst, int16_t* encoded,
int16_t output_samples; int16_t output_samples;
int i; int i;
/* If mono case, just do a regular call to the decoder.
* If stereo, call to WebRtcOpus_Decode() gives left channel as output, and
* calls to WebRtcOpus_Decode_slave() give right channel as output.
* This is to make stereo work with the current setup of NetEQ, which
* requires two calls to the decoder to produce stereo. */
/* Decode to a temporary buffer. */ /* Decode to a temporary buffer. */
decoded_samples = DecodeNative(inst, encoded, encoded_bytes, buffer16, decoded_samples = DecodeNative(inst->decoder_left, encoded, encoded_bytes,
audio_type); buffer16, audio_type);
if (decoded_samples < 0) { if (decoded_samples < 0) {
return -1; return -1;
} }
if (inst->channels == 2) {
/* The parameter |decoded_samples| holds the number of samples pairs, in
* case of stereo. Number of samples in |buffer16| equals |decoded_samples|
* times 2. */
for (i = 0; i < decoded_samples; i++) {
/* Take every second sample, starting at the first sample. This gives
* the left channel. */
buffer16[i] = buffer16[i * 2];
}
}
/* Resample from 48 kHz to 32 kHz. */
for (i = 0; i < 7; i++) {
buffer32[i] = inst->state_48_32_left[i];
inst->state_48_32_left[i] = buffer16[decoded_samples - 7 + i];
}
for (i = 0; i < decoded_samples; i++) {
buffer32[7 + i] = buffer16[i];
}
/* Resampling 3 samples to 2. Function divides the input in |blocks| number
* of 3-sample groups, and output is |blocks| number of 2-sample groups. */
blocks = decoded_samples / 3;
WebRtcSpl_Resample48khzTo32khz(buffer32, buffer32, blocks);
output_samples = (int16_t) (blocks * 2);
WebRtcSpl_VectorBitShiftW32ToW16(decoded, output_samples, buffer32, 15);
return output_samples;
}
int16_t WebRtcOpus_DecodeSlave(OpusDecInst* inst, int16_t* encoded,
int16_t encoded_bytes, int16_t* decoded,
int16_t* audio_type) {
/* Enough for 120 ms (the largest Opus packet size) of mono audio at 48 kHz
* and resampler overlap. This will need to be enlarged for stereo decoding.
*/
int16_t buffer16[kWebRtcOpusMaxFrameSize];
int32_t buffer32[kWebRtcOpusMaxFrameSize + 7];
int decoded_samples;
int blocks;
int16_t output_samples;
int i;
/* Decode to a temporary buffer. */
decoded_samples = DecodeNative(inst->decoder_right, encoded, encoded_bytes,
buffer16, audio_type);
if (decoded_samples < 0) {
return -1;
}
if (inst->channels == 2) {
/* The parameter |decoded_samples| holds the number of samples pairs, in
* case of stereo. Number of samples in |buffer16| equals |decoded_samples|
* times 2. */
for (i = 0; i < decoded_samples; i++) {
/* Take every second sample, starting at the second sample. This gives
* the right channel. */
buffer16[i] = buffer16[i * 2 + 1];
}
} else {
/* Decode slave should never be called for mono packets. */
return -1;
}
/* Resample from 48 kHz to 32 kHz. */ /* Resample from 48 kHz to 32 kHz. */
for (i = 0; i < 7; i++) { for (i = 0; i < 7; i++) {
buffer32[i] = inst->state_48_32[i]; buffer32[i] = inst->state_48_32_right[i];
inst->state_48_32[i] = buffer16[decoded_samples -7 + i]; inst->state_48_32_right[i] = buffer16[decoded_samples - 7 + i];
} }
for (i = 0; i < decoded_samples; i++) { for (i = 0; i < decoded_samples; i++) {
buffer32[7 + i] = buffer16[i]; buffer32[7 + i] = buffer16[i];

View File

@ -107,10 +107,10 @@ namespace webrtc {
// codecs. Note! There are a limited number of payload types. If more codecs // codecs. Note! There are a limited number of payload types. If more codecs
// are defined they will receive reserved fixed payload types (values 69-95). // are defined they will receive reserved fixed payload types (values 69-95).
const int kDynamicPayloadtypes[ACMCodecDB::kMaxNumCodecs] = { const int kDynamicPayloadtypes[ACMCodecDB::kMaxNumCodecs] = {
105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 121, 105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 92,
92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68,
68, 67 67, 66
}; };
// Creates database with all supported codecs at compile time. // Creates database with all supported codecs at compile time.
@ -189,8 +189,11 @@ const CodecInst ACMCodecDB::database_[] = {
{3, "GSM", 8000, 160, 1, 13200}, {3, "GSM", 8000, 160, 1, 13200},
#endif #endif
#ifdef WEBRTC_CODEC_OPUS #ifdef WEBRTC_CODEC_OPUS
// Opus supports 48, 24, 16, 12, 8 kHz. // Opus internally supports 48, 24, 16, 12, 8 kHz.
// Mono
{120, "opus", 48000, 960, 1, 32000}, {120, "opus", 48000, 960, 1, 32000},
// Stereo
{121, "opus", 48000, 960, 2, 32000},
#endif #endif
#ifdef WEBRTC_CODEC_SPEEX #ifdef WEBRTC_CODEC_SPEEX
{kDynamicPayloadtypes[count_database++], "speex", 8000, 160, 1, 11000}, {kDynamicPayloadtypes[count_database++], "speex", 8000, 160, 1, 11000},
@ -282,6 +285,9 @@ const ACMCodecDB::CodecSettings ACMCodecDB::codec_settings_[] = {
#ifdef WEBRTC_CODEC_OPUS #ifdef WEBRTC_CODEC_OPUS
// Opus supports frames shorter than 10ms, // Opus supports frames shorter than 10ms,
// but it doesn't help us to use them. // but it doesn't help us to use them.
// Mono
{1, {960}, 0, 2},
// Stereo
{1, {960}, 0, 2}, {1, {960}, 0, 2},
#endif #endif
#ifdef WEBRTC_CODEC_SPEEX #ifdef WEBRTC_CODEC_SPEEX
@ -369,7 +375,10 @@ const WebRtcNetEQDecoder ACMCodecDB::neteq_decoders_[] = {
kDecoderGSMFR, kDecoderGSMFR,
#endif #endif
#ifdef WEBRTC_CODEC_OPUS #ifdef WEBRTC_CODEC_OPUS
// Mono
kDecoderOpus, kDecoderOpus,
// Stereo
kDecoderOpus_2ch,
#endif #endif
#ifdef WEBRTC_CODEC_SPEEX #ifdef WEBRTC_CODEC_SPEEX
kDecoderSPEEX_8, kDecoderSPEEX_8,
@ -758,7 +767,11 @@ ACMGenericCodec* ACMCodecDB::CreateCodecInstance(const CodecInst* codec_inst) {
#endif #endif
} else if (!STR_CASE_CMP(codec_inst->plname, "opus")) { } else if (!STR_CASE_CMP(codec_inst->plname, "opus")) {
#ifdef WEBRTC_CODEC_OPUS #ifdef WEBRTC_CODEC_OPUS
return new ACMOpus(kOpus); if (codec_inst->channels == 1) {
return new ACMOpus(kOpus);
} else {
return new ACMOpus(kOpus_2ch);
}
#endif #endif
} else if (!STR_CASE_CMP(codec_inst->plname, "speex")) { } else if (!STR_CASE_CMP(codec_inst->plname, "speex")) {
#ifdef WEBRTC_CODEC_SPEEX #ifdef WEBRTC_CODEC_SPEEX

View File

@ -92,7 +92,10 @@ class ACMCodecDB {
, kGSMFR , kGSMFR
#endif #endif
#ifdef WEBRTC_CODEC_OPUS #ifdef WEBRTC_CODEC_OPUS
// Mono
, kOpus , kOpus
// Stereo
, kOpus_2ch
#endif #endif
#ifdef WEBRTC_CODEC_SPEEX #ifdef WEBRTC_CODEC_SPEEX
, kSPEEX8 , kSPEEX8
@ -175,7 +178,10 @@ class ACMCodecDB {
enum {kSPEEX16 = -1}; enum {kSPEEX16 = -1};
#endif #endif
#ifndef WEBRTC_CODEC_OPUS #ifndef WEBRTC_CODEC_OPUS
// Mono
enum {kOpus = -1}; enum {kOpus = -1};
// Stereo
enum {kOpus_2ch = -1};
#endif #endif
#ifndef WEBRTC_CODEC_AVT #ifndef WEBRTC_CODEC_AVT
enum {kAVT = -1}; enum {kAVT = -1};

View File

@ -29,7 +29,8 @@ ACMOpus::ACMOpus(int16_t /* codecID */)
: _encoderInstPtr(NULL), : _encoderInstPtr(NULL),
_decoderInstPtr(NULL), _decoderInstPtr(NULL),
_sampleFreq(0), _sampleFreq(0),
_bitrate(0) { _bitrate(0),
_channels(1) {
return; return;
} }
@ -91,18 +92,26 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) {
return -1; return -1;
} }
bool ACMOpus::IsTrueStereoCodec() {
return true;
}
void ACMOpus::SplitStereoPacket(uint8_t* /*payload*/,
int32_t* /*payload_length*/) {}
#else //===================== Actual Implementation ======================= #else //===================== Actual Implementation =======================
ACMOpus::ACMOpus(int16_t codecID) ACMOpus::ACMOpus(int16_t codecID)
: _encoderInstPtr(NULL), : _encoderInstPtr(NULL),
_decoderInstPtr(NULL), _decoderInstPtr(NULL),
_sampleFreq(32000), // Default sampling frequency. _sampleFreq(32000), // Default sampling frequency.
_bitrate(20000) { // Default bit-rate. _bitrate(20000), // Default bit-rate.
_channels(1) { // Default mono
_codecID = codecID; _codecID = codecID;
// Opus has internal DTX, but we dont use it for now. // Opus has internal DTX, but we dont use it for now.
_hasInternalDTX = false; _hasInternalDTX = false;
if (_codecID != ACMCodecDB::kOpus) { if ((_codecID != ACMCodecDB::kOpus) && (_codecID != ACMCodecDB::kOpus_2ch)) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID,
"Wrong codec id for Opus."); "Wrong codec id for Opus.");
_sampleFreq = -1; _sampleFreq = -1;
@ -140,7 +149,7 @@ int16_t ACMOpus::InternalEncode(uint8_t* bitStream, int16_t* bitStreamLenByte) {
// Increment the read index. This tells the caller how far // Increment the read index. This tells the caller how far
// we have gone forward in reading the audio buffer. // we have gone forward in reading the audio buffer.
_inAudioIxRead += _frameLenSmpl; _inAudioIxRead += _frameLenSmpl * _channels;
return *bitStreamLenByte; return *bitStreamLenByte;
} }
@ -159,6 +168,9 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) {
} }
ret = WebRtcOpus_EncoderCreate(&_encoderInstPtr, ret = WebRtcOpus_EncoderCreate(&_encoderInstPtr,
codecParams->codecInstant.channels); codecParams->codecInstant.channels);
// Store number of channels.
_channels = codecParams->codecInstant.channels;
if (ret < 0) { if (ret < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID,
"Encoder creation failed for Opus"); "Encoder creation failed for Opus");
@ -170,6 +182,10 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) {
"Setting initial bitrate failed for Opus"); "Setting initial bitrate failed for Opus");
return ret; return ret;
} }
// Store bitrate.
_bitrate = codecParams->codecInstant.rate;
return 0; return 0;
} }
@ -182,7 +198,14 @@ int16_t ACMOpus::InternalInitDecoder(WebRtcACMCodecParams* codecParams) {
codecParams->codecInstant.channels) < 0) { codecParams->codecInstant.channels) < 0) {
return -1; return -1;
} }
return WebRtcOpus_DecoderInit(_decoderInstPtr);
if (WebRtcOpus_DecoderInit(_decoderInstPtr) < 0) {
return -1;
}
if (WebRtcOpus_DecoderInitSlave(_decoderInstPtr) < 0) {
return -1;
}
return 0;
} }
int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef, int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef,
@ -198,12 +221,26 @@ int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef,
// TODO(tlegrand): Decoder is registered in NetEQ as a 32 kHz decoder, which // TODO(tlegrand): Decoder is registered in NetEQ as a 32 kHz decoder, which
// is true until we have a full 48 kHz system, and remove the downsampling // is true until we have a full 48 kHz system, and remove the downsampling
// in the Opus decoder wrapper. // in the Opus decoder wrapper.
SET_CODEC_PAR((codecDef), kDecoderOpus, codecInst.pltype, _decoderInstPtr, if (codecInst.channels == 1) {
32000); SET_CODEC_PAR(codecDef, kDecoderOpus, codecInst.pltype, _decoderInstPtr,
SET_OPUS_FUNCTIONS((codecDef)); 32000);
} else {
SET_CODEC_PAR(codecDef, kDecoderOpus_2ch, codecInst.pltype,
_decoderInstPtr, 32000);
}
// If this is the master of NetEQ, regular decoder will be added, otherwise
// the slave decoder will be used.
if (_isMaster) {
SET_OPUS_FUNCTIONS(codecDef);
} else {
SET_OPUSSLAVE_FUNCTIONS(codecDef);
}
return 0; return 0;
} }
ACMGenericCodec* ACMOpus::CreateInstance(void) { ACMGenericCodec* ACMOpus::CreateInstance(void) {
return NULL; return NULL;
} }
@ -258,6 +295,23 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t rate) {
return -1; return -1;
} }
bool ACMOpus::IsTrueStereoCodec() {
return true;
}
// Copy the stereo packet so that NetEq will insert into both master and slave.
void ACMOpus::SplitStereoPacket(uint8_t* payload, int32_t* payload_length) {
// Check for valid inputs.
assert(payload != NULL);
assert(*payload_length > 0);
// Duplicate the payload.
memcpy(&payload[*payload_length], &payload[0],
sizeof(uint8_t) * (*payload_length));
// Double the size of the packet.
*payload_length *= 2;
}
#endif // WEBRTC_CODEC_OPUS #endif // WEBRTC_CODEC_OPUS
} // namespace webrtc } // namespace webrtc

View File

@ -50,10 +50,15 @@ class ACMOpus : public ACMGenericCodec {
int16_t SetBitRateSafe(const int32_t rate); int16_t SetBitRateSafe(const int32_t rate);
bool IsTrueStereoCodec();
void SplitStereoPacket(uint8_t* payload, int32_t* payload_length);
WebRtcOpusEncInst* _encoderInstPtr; WebRtcOpusEncInst* _encoderInstPtr;
WebRtcOpusDecInst* _decoderInstPtr; WebRtcOpusDecInst* _decoderInstPtr;
uint16_t _sampleFreq; uint16_t _sampleFreq;
uint16_t _bitrate; uint16_t _bitrate;
int _channels;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -120,6 +120,9 @@
'<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/testing/gtest.gyp:gtest',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
], ],
'defines': [
'<@(audio_coding_defines)',
],
'sources': [ 'sources': [
'../test/ACMTest.cc', '../test/ACMTest.cc',
'../test/APITest.cc', '../test/APITest.cc',

View File

@ -121,6 +121,7 @@ TestStereo::TestStereo(int test_mode)
pcma_pltype_(-1), pcma_pltype_(-1),
pcmu_pltype_(-1), pcmu_pltype_(-1),
celt_pltype_(-1), celt_pltype_(-1),
opus_pltype_(-1),
cn_8khz_pltype_(-1), cn_8khz_pltype_(-1),
cn_16khz_pltype_(-1), cn_16khz_pltype_(-1),
cn_32khz_pltype_(-1) { cn_32khz_pltype_(-1) {
@ -432,6 +433,29 @@ void TestStereo::Perform() {
celt_pltype_); celt_pltype_);
Run(channel_a2b_, audio_channels, codec_channels); Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close(); out_file_.Close();
#endif
#ifdef WEBRTC_CODEC_OPUS
if(test_mode_ != 0) {
printf("===========================================================\n");
printf("Test number: %d\n",test_cntr_ + 1);
printf("Test type: Stereo-to-stereo\n");
}
channel_a2b_->set_codec_mode(kStereo);
audio_channels = 2;
codec_channels = 2;
test_cntr_++;
OpenOutFile(test_cntr_);
char codec_opus[] = "opus";
RegisterSendCodec('A', codec_opus, 48000, 40000, 960, codec_channels,
opus_pltype_);
Run(channel_a2b_, audio_channels, codec_channels);
RegisterSendCodec('A', codec_opus, 48000, 64000, 960, codec_channels,
opus_pltype_);
Run(channel_a2b_, audio_channels, codec_channels);
RegisterSendCodec('A', codec_opus, 48000, 510000, 960, codec_channels,
opus_pltype_);
Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close();
#endif #endif
// //
// Test Mono-To-Stereo for all codecs. // Test Mono-To-Stereo for all codecs.
@ -520,6 +544,20 @@ void TestStereo::Perform() {
Run(channel_a2b_, audio_channels, codec_channels); Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close(); out_file_.Close();
#endif #endif
#ifdef WEBRTC_CODEC_OPUS
if(test_mode_ != 0) {
printf("===============================================================\n");
printf("Test number: %d\n",test_cntr_ + 1);
printf("Test type: Mono-to-stereo\n");
}
test_cntr_++;
channel_a2b_->set_codec_mode(kStereo);
OpenOutFile(test_cntr_);
RegisterSendCodec('A', codec_opus, 48000, 64000, 960, codec_channels,
opus_pltype_);
Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close();
#endif
// //
// Test Stereo-To-Mono for all codecs. // Test Stereo-To-Mono for all codecs.
@ -615,6 +653,19 @@ void TestStereo::Perform() {
Run(channel_a2b_, audio_channels, codec_channels); Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close(); out_file_.Close();
#endif #endif
#ifdef WEBRTC_CODEC_OPUS
if(test_mode_ != 0) {
printf("===============================================================\n");
printf("Test number: %d\n",test_cntr_ + 1);
printf("Test type: Stereo-to-mono\n");
}
test_cntr_++;
OpenOutFile(test_cntr_);
RegisterSendCodec('A', codec_opus, 48000, 32000, 960, codec_channels,
opus_pltype_);
Run(channel_a2b_, audio_channels, codec_channels);
out_file_.Close();
#endif
// Print out which codecs were tested, and which were not, in the run. // Print out which codecs were tested, and which were not, in the run.
if (test_mode_ != 0) { if (test_mode_ != 0) {
@ -696,6 +747,8 @@ void TestStereo::RegisterSendCodec(char side, char* codec_name,
my_codec_param.rate = rate; my_codec_param.rate = rate;
my_codec_param.pacsize = pack_size; my_codec_param.pacsize = pack_size;
CHECK_ERROR(my_acm->RegisterSendCodec(my_codec_param)); CHECK_ERROR(my_acm->RegisterSendCodec(my_codec_param));
send_codec_name_ = codec_name;
} }
void TestStereo::Run(TestPackStereo* channel, int in_channels, int out_channels, void TestStereo::Run(TestPackStereo* channel, int in_channels, int out_channels,
@ -741,11 +794,13 @@ void TestStereo::Run(TestPackStereo* channel, int in_channels, int out_channels,
// Verify that the received packet size matches the settings // Verify that the received packet size matches the settings
rec_size = channel->payload_size(); rec_size = channel->payload_size();
if ((0 < rec_size) & (rec_size < 65535)) { if ((0 < rec_size) & (rec_size < 65535)) {
if ((rec_size != pack_size_bytes_ * out_channels) // Opus is variable rate, skip this test.
&& (pack_size_bytes_ < 65535)) { if (strcmp(send_codec_name_, "opus")) {
error_count++; if ((rec_size != pack_size_bytes_ * out_channels)
&& (pack_size_bytes_ < 65535)) {
error_count++;
}
} }
// Verify that the timestamp is updated with expected length // Verify that the timestamp is updated with expected length
time_stamp_diff = channel->timestamp_diff(); time_stamp_diff = channel->timestamp_diff();
if ((counter_ > 10) && (time_stamp_diff != pack_size_samp_)) { if ((counter_ > 10) && (time_stamp_diff != pack_size_samp_)) {

View File

@ -97,6 +97,7 @@ class TestStereo : public ACMTest {
WebRtc_UWord16 pack_size_samp_; WebRtc_UWord16 pack_size_samp_;
WebRtc_UWord16 pack_size_bytes_; WebRtc_UWord16 pack_size_bytes_;
int counter_; int counter_;
char* send_codec_name_;
// Payload types for stereo codecs and CNG // Payload types for stereo codecs and CNG
int g722_pltype_; int g722_pltype_;
@ -106,6 +107,7 @@ class TestStereo : public ACMTest {
int pcma_pltype_; int pcma_pltype_;
int pcmu_pltype_; int pcmu_pltype_;
int celt_pltype_; int celt_pltype_;
int opus_pltype_;
int cn_8khz_pltype_; int cn_8khz_pltype_;
int cn_16khz_pltype_; int cn_16khz_pltype_;
int cn_32khz_pltype_; int cn_32khz_pltype_;

View File

@ -116,6 +116,7 @@ int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec,
#endif #endif
#ifdef NETEQ_OPUS_CODEC #ifdef NETEQ_OPUS_CODEC
case kDecoderOpus : case kDecoderOpus :
case kDecoderOpus_2ch :
#endif #endif
#ifdef NETEQ_G722_CODEC #ifdef NETEQ_G722_CODEC
case kDecoderG722 : case kDecoderG722 :
@ -463,6 +464,7 @@ int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecI
#endif #endif
#ifdef NETEQ_OPUS_CODEC #ifdef NETEQ_OPUS_CODEC
case kDecoderOpus: case kDecoderOpus:
case kDecoderOpus_2ch :
#endif #endif
#ifdef NETEQ_ARBITRARY_CODEC #ifdef NETEQ_ARBITRARY_CODEC
case kDecoderArbitrary: case kDecoderArbitrary:

View File

@ -63,6 +63,7 @@ enum WebRtcNetEQDecoder
kDecoderG722_1C_32, kDecoderG722_1C_32,
kDecoderG722_1C_48, kDecoderG722_1C_48,
kDecoderOpus, kDecoderOpus,
kDecoderOpus_2ch,
kDecoderSPEEX_8, kDecoderSPEEX_8,
kDecoderSPEEX_16, kDecoderSPEEX_16,
kDecoderCELT_32, kDecoderCELT_32,

View File

@ -327,6 +327,17 @@
inst.funcUpdBWEst=NULL; \ inst.funcUpdBWEst=NULL; \
inst.funcGetErrorCode=NULL; inst.funcGetErrorCode=NULL;
#define SET_OPUSSLAVE_FUNCTIONS(inst) \
inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcOpus_DecodeSlave; \
inst.funcDecodeRCU=NULL; \
inst.funcDecodePLC=NULL; \
inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcOpus_DecoderInitSlave; \
inst.funcAddLatePkt=NULL; \
inst.funcGetMDinfo=NULL; \
inst.funcGetPitch=NULL; \
inst.funcUpdBWEst=NULL; \
inst.funcGetErrorCode=NULL;
#define SET_SPEEX_FUNCTIONS(inst) \ #define SET_SPEEX_FUNCTIONS(inst) \
inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcSpeex_Decode; \ inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcSpeex_Decode; \
inst.funcDecodeRCU=NULL; \ inst.funcDecodeRCU=NULL; \

View File

@ -578,7 +578,8 @@ int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID,
codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */ codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */
codecBuffers = 8; codecBuffers = 8;
} }
else if (codecID[i] == kDecoderOpus) else if ((codecID[i] == kDecoderOpus) ||
(codecID[i] == kDecoderOpus_2ch))
{ {
codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */ codecBytes = 15300; /* 240ms @ 510kbps (60ms frames) */
codecBuffers = 30; /* Replicating the value for PCMu/a */ codecBuffers = 30; /* Replicating the value for PCMu/a */

View File

@ -378,6 +378,7 @@ int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType)
break; break;
} }
case kDecoderOpus: case kDecoderOpus:
case kDecoderOpus_2ch:
{ {
/* We resample Opus internally to 32 kHz, but timestamps /* We resample Opus internally to 32 kHz, but timestamps
* are counted at 48 kHz. So there are two output samples * are counted at 48 kHz. So there are two output samples