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:
@ -107,10 +107,10 @@ namespace webrtc {
|
||||
// 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).
|
||||
const int kDynamicPayloadtypes[ACMCodecDB::kMaxNumCodecs] = {
|
||||
105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 121,
|
||||
92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81,
|
||||
80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69,
|
||||
68, 67
|
||||
105, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 92,
|
||||
91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
|
||||
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68,
|
||||
67, 66
|
||||
};
|
||||
|
||||
// Creates database with all supported codecs at compile time.
|
||||
@ -189,8 +189,11 @@ const CodecInst ACMCodecDB::database_[] = {
|
||||
{3, "GSM", 8000, 160, 1, 13200},
|
||||
#endif
|
||||
#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},
|
||||
// Stereo
|
||||
{121, "opus", 48000, 960, 2, 32000},
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_SPEEX
|
||||
{kDynamicPayloadtypes[count_database++], "speex", 8000, 160, 1, 11000},
|
||||
@ -282,6 +285,9 @@ const ACMCodecDB::CodecSettings ACMCodecDB::codec_settings_[] = {
|
||||
#ifdef WEBRTC_CODEC_OPUS
|
||||
// Opus supports frames shorter than 10ms,
|
||||
// but it doesn't help us to use them.
|
||||
// Mono
|
||||
{1, {960}, 0, 2},
|
||||
// Stereo
|
||||
{1, {960}, 0, 2},
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_SPEEX
|
||||
@ -369,7 +375,10 @@ const WebRtcNetEQDecoder ACMCodecDB::neteq_decoders_[] = {
|
||||
kDecoderGSMFR,
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_OPUS
|
||||
// Mono
|
||||
kDecoderOpus,
|
||||
// Stereo
|
||||
kDecoderOpus_2ch,
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_SPEEX
|
||||
kDecoderSPEEX_8,
|
||||
@ -758,7 +767,11 @@ ACMGenericCodec* ACMCodecDB::CreateCodecInstance(const CodecInst* codec_inst) {
|
||||
#endif
|
||||
} else if (!STR_CASE_CMP(codec_inst->plname, "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
|
||||
} else if (!STR_CASE_CMP(codec_inst->plname, "speex")) {
|
||||
#ifdef WEBRTC_CODEC_SPEEX
|
||||
|
||||
@ -92,7 +92,10 @@ class ACMCodecDB {
|
||||
, kGSMFR
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_OPUS
|
||||
// Mono
|
||||
, kOpus
|
||||
// Stereo
|
||||
, kOpus_2ch
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_SPEEX
|
||||
, kSPEEX8
|
||||
@ -175,7 +178,10 @@ class ACMCodecDB {
|
||||
enum {kSPEEX16 = -1};
|
||||
#endif
|
||||
#ifndef WEBRTC_CODEC_OPUS
|
||||
// Mono
|
||||
enum {kOpus = -1};
|
||||
// Stereo
|
||||
enum {kOpus_2ch = -1};
|
||||
#endif
|
||||
#ifndef WEBRTC_CODEC_AVT
|
||||
enum {kAVT = -1};
|
||||
|
||||
@ -29,7 +29,8 @@ ACMOpus::ACMOpus(int16_t /* codecID */)
|
||||
: _encoderInstPtr(NULL),
|
||||
_decoderInstPtr(NULL),
|
||||
_sampleFreq(0),
|
||||
_bitrate(0) {
|
||||
_bitrate(0),
|
||||
_channels(1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -91,18 +92,26 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t /*rate*/) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ACMOpus::IsTrueStereoCodec() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ACMOpus::SplitStereoPacket(uint8_t* /*payload*/,
|
||||
int32_t* /*payload_length*/) {}
|
||||
|
||||
#else //===================== Actual Implementation =======================
|
||||
|
||||
ACMOpus::ACMOpus(int16_t codecID)
|
||||
: _encoderInstPtr(NULL),
|
||||
_decoderInstPtr(NULL),
|
||||
_sampleFreq(32000), // Default sampling frequency.
|
||||
_bitrate(20000) { // Default bit-rate.
|
||||
_bitrate(20000), // Default bit-rate.
|
||||
_channels(1) { // Default mono
|
||||
_codecID = codecID;
|
||||
// Opus has internal DTX, but we dont use it for now.
|
||||
_hasInternalDTX = false;
|
||||
|
||||
if (_codecID != ACMCodecDB::kOpus) {
|
||||
if ((_codecID != ACMCodecDB::kOpus) && (_codecID != ACMCodecDB::kOpus_2ch)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID,
|
||||
"Wrong codec id for Opus.");
|
||||
_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
|
||||
// we have gone forward in reading the audio buffer.
|
||||
_inAudioIxRead += _frameLenSmpl;
|
||||
_inAudioIxRead += _frameLenSmpl * _channels;
|
||||
|
||||
return *bitStreamLenByte;
|
||||
}
|
||||
@ -159,6 +168,9 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) {
|
||||
}
|
||||
ret = WebRtcOpus_EncoderCreate(&_encoderInstPtr,
|
||||
codecParams->codecInstant.channels);
|
||||
// Store number of channels.
|
||||
_channels = codecParams->codecInstant.channels;
|
||||
|
||||
if (ret < 0) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID,
|
||||
"Encoder creation failed for Opus");
|
||||
@ -170,6 +182,10 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codecParams) {
|
||||
"Setting initial bitrate failed for Opus");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Store bitrate.
|
||||
_bitrate = codecParams->codecInstant.rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -182,7 +198,14 @@ int16_t ACMOpus::InternalInitDecoder(WebRtcACMCodecParams* codecParams) {
|
||||
codecParams->codecInstant.channels) < 0) {
|
||||
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,
|
||||
@ -198,12 +221,26 @@ int32_t ACMOpus::CodecDef(WebRtcNetEQ_CodecDef& codecDef,
|
||||
// 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
|
||||
// in the Opus decoder wrapper.
|
||||
SET_CODEC_PAR((codecDef), kDecoderOpus, codecInst.pltype, _decoderInstPtr,
|
||||
32000);
|
||||
SET_OPUS_FUNCTIONS((codecDef));
|
||||
if (codecInst.channels == 1) {
|
||||
SET_CODEC_PAR(codecDef, kDecoderOpus, codecInst.pltype, _decoderInstPtr,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
ACMGenericCodec* ACMOpus::CreateInstance(void) {
|
||||
return NULL;
|
||||
}
|
||||
@ -258,6 +295,23 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t rate) {
|
||||
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
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -50,10 +50,15 @@ class ACMOpus : public ACMGenericCodec {
|
||||
|
||||
int16_t SetBitRateSafe(const int32_t rate);
|
||||
|
||||
bool IsTrueStereoCodec();
|
||||
|
||||
void SplitStereoPacket(uint8_t* payload, int32_t* payload_length);
|
||||
|
||||
WebRtcOpusEncInst* _encoderInstPtr;
|
||||
WebRtcOpusDecInst* _decoderInstPtr;
|
||||
uint16_t _sampleFreq;
|
||||
uint16_t _bitrate;
|
||||
int _channels;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -120,6 +120,9 @@
|
||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
|
||||
],
|
||||
'defines': [
|
||||
'<@(audio_coding_defines)',
|
||||
],
|
||||
'sources': [
|
||||
'../test/ACMTest.cc',
|
||||
'../test/APITest.cc',
|
||||
|
||||
Reference in New Issue
Block a user