diff --git a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index 1cfc30219e..358647f539 100644 --- a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -37,7 +37,10 @@ int16_t CastInt16(size_t x) { } // namespace AudioEncoderOpus::Config::Config() - : frame_size_ms(20), num_channels(1), payload_type(120) { + : frame_size_ms(20), + num_channels(1), + payload_type(120), + application(kVoip) { } bool AudioEncoderOpus::Config::IsOk() const { @@ -52,10 +55,11 @@ AudioEncoderOpus::AudioEncoderOpus(const Config& config) : num_10ms_frames_per_packet_(DivExact(config.frame_size_ms, 10)), num_channels_(config.num_channels), payload_type_(config.payload_type), + application_(config.application), samples_per_10ms_frame_(DivExact(kSampleRateHz, 100) * num_channels_) { CHECK(config.IsOk()); input_buffer_.reserve(num_10ms_frames_per_packet_ * samples_per_10ms_frame_); - CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, num_channels_)); + CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, num_channels_, application_)); } AudioEncoderOpus::~AudioEncoderOpus() { diff --git a/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h b/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h index f44627dc48..e9d0fb8632 100644 --- a/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h +++ b/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h @@ -20,12 +20,18 @@ namespace webrtc { class AudioEncoderOpus : public AudioEncoder { public: + enum ApplicationMode { + kVoip = 0, + kAudio = 1, + }; + struct Config { Config(); bool IsOk() const; int frame_size_ms; int num_channels; int payload_type; + ApplicationMode application; }; explicit AudioEncoderOpus(const Config& config); @@ -47,6 +53,7 @@ class AudioEncoderOpus : public AudioEncoder { const int num_10ms_frames_per_packet_; const int num_channels_; const int payload_type_; + const ApplicationMode application_; const int samples_per_10ms_frame_; std::vector input_buffer_; OpusEncInst* inst_; diff --git a/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h b/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h index d788af7ed7..27009a86af 100644 --- a/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h +++ b/webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h @@ -21,7 +21,29 @@ extern "C" { typedef struct WebRtcOpusEncInst OpusEncInst; typedef struct WebRtcOpusDecInst OpusDecInst; -int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst, int32_t channels); +/**************************************************************************** + * WebRtcOpus_EncoderCreate(...) + * + * This function create an Opus encoder. + * + * Input: + * - channels : number of channels. + * - application : 0 - VOIP applications. + * Favor speech intelligibility. + * 1 - Audio applications. + * Favor faithfulness to the original input. + * + * Output: + * - inst : a pointer to Encoder context that is created + * if success. + * + * Return value : 0 - Success + * -1 - Error + */ +int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst, + int32_t channels, + int32_t application); + int16_t WebRtcOpus_EncoderFree(OpusEncInst* inst); /**************************************************************************** diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc b/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc index 35f39729fa..b91aa4542a 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc +++ b/webrtc/modules/audio_coding/codecs/opus/opus_fec_test.cc @@ -103,8 +103,11 @@ void OpusFecTest::SetUp() { out_data_.reset(new int16_t[2 * block_length_sample_ * channels_]); bit_stream_.reset(new uint8_t[max_bytes_]); + // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode. + int app = channels_ == 1 ? 0 : 1; + // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app)); EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // Set bitrate. EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c index 1b99864490..955fb00a96 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_interface.c +++ b/webrtc/modules/audio_coding/codecs/opus/opus_interface.c @@ -31,17 +31,31 @@ enum { kWebRtcOpusDefaultFrameSize = 960, }; -int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst, int32_t channels) { +int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst, + int32_t channels, + int32_t application) { OpusEncInst* state; if (inst != NULL) { state = (OpusEncInst*) calloc(1, sizeof(OpusEncInst)); if (state) { - int error; - /* Default to VoIP application for mono, and AUDIO for stereo. */ - int application = (channels == 1) ? OPUS_APPLICATION_VOIP : - OPUS_APPLICATION_AUDIO; + int opus_app; + switch (application) { + case 0: { + opus_app = OPUS_APPLICATION_VOIP; + break; + } + case 1: { + opus_app = OPUS_APPLICATION_AUDIO; + break; + } + default: { + free(state); + return -1; + } + } - state->encoder = opus_encoder_create(48000, channels, application, + int error; + state->encoder = opus_encoder_create(48000, channels, opus_app, &error); state->in_dtx_mode = 0; if (error == OPUS_OK && state->encoder != NULL) { diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc b/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc index e7811e3498..ebe4a35361 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc +++ b/webrtc/modules/audio_coding/codecs/opus/opus_speed_test.cc @@ -41,8 +41,10 @@ OpusSpeedTest::OpusSpeedTest() void OpusSpeedTest::SetUp() { AudioCodecSpeedTest::SetUp(); + // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode. + int app = channels_ == 1 ? 0 : 1; /* Create encoder memory. */ - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app)); EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); /* Set bitrate. */ EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); diff --git a/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc b/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc index 5439df4090..e93cd7c511 100644 --- a/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc +++ b/webrtc/modules/audio_coding/codecs/opus/opus_unittest.cc @@ -18,6 +18,9 @@ namespace webrtc { using test::AudioLoop; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::Combine; // Maximum number of bytes in output bitstream. const size_t kMaxBytes = 1000; @@ -28,11 +31,10 @@ const int kOpus20msFrameSamples = kOpusRateKhz * 20; // Number of samples-per-channel in a 10 ms frame, sampled at 48 kHz. const int kOpus10msFrameSamples = kOpusRateKhz * 10; -class OpusTest : public ::testing::Test { +class OpusTest : public TestWithParam<::testing::tuple> { protected: OpusTest(); - void TestSetMaxPlaybackRate(opus_int32 expect, int32_t set); void TestDtxEffect(bool dtx); // Prepare |speech_data_| for encoding, read from a hard-coded file. @@ -48,27 +50,33 @@ class OpusTest : public ::testing::Test { int16_t* output_audio, int16_t* audio_type); - WebRtcOpusEncInst* opus_mono_encoder_; - WebRtcOpusEncInst* opus_stereo_encoder_; - WebRtcOpusDecInst* opus_mono_decoder_; - WebRtcOpusDecInst* opus_stereo_decoder_; + void SetMaxPlaybackRate(WebRtcOpusEncInst* encoder, + opus_int32 expect, int32_t set); + + WebRtcOpusEncInst* opus_encoder_; + WebRtcOpusDecInst* opus_decoder_; AudioLoop speech_data_; uint8_t bitstream_[kMaxBytes]; int encoded_bytes_; + int channels_; + int application_; }; OpusTest::OpusTest() - : opus_mono_encoder_(NULL), - opus_stereo_encoder_(NULL), - opus_mono_decoder_(NULL), - opus_stereo_decoder_(NULL) { + : opus_encoder_(NULL), + opus_decoder_(NULL), + encoded_bytes_(0), + channels_(::testing::get<0>(GetParam())), + application_(::testing::get<1>(GetParam())) { } void OpusTest::PrepareSpeechData(int channel, int block_length_ms, int loop_length_ms) { const std::string file_name = - webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"); + webrtc::test::ResourcePath((channel == 1) ? + "audio_coding/testfile32kHz" : + "audio_coding/teststereo32kHz", "pcm"); if (loop_length_ms < block_length_ms) { loop_length_ms = block_length_ms; } @@ -77,16 +85,12 @@ void OpusTest::PrepareSpeechData(int channel, int block_length_ms, block_length_ms * kOpusRateKhz * channel)); } -void OpusTest::TestSetMaxPlaybackRate(opus_int32 expect, int32_t set) { +void OpusTest::SetMaxPlaybackRate(WebRtcOpusEncInst* encoder, + opus_int32 expect, + int32_t set) { opus_int32 bandwidth; - // Test mono encoder. - EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_mono_encoder_, set)); - opus_encoder_ctl(opus_mono_encoder_->encoder, - OPUS_GET_MAX_BANDWIDTH(&bandwidth)); - EXPECT_EQ(expect, bandwidth); - // Test stereo encoder. - EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_stereo_encoder_, set)); - opus_encoder_ctl(opus_stereo_encoder_->encoder, + EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_encoder_, set)); + opus_encoder_ctl(opus_encoder_->encoder, OPUS_GET_MAX_BANDWIDTH(&bandwidth)); EXPECT_EQ(expect, bandwidth); } @@ -109,105 +113,109 @@ int OpusTest::EncodeDecode(WebRtcOpusEncInst* encoder, // Test if encoder/decoder can enter DTX mode properly and do not enter DTX when // they should not. This test is signal dependent. void OpusTest::TestDtxEffect(bool dtx) { - PrepareSpeechData(1, 20, 2000); + PrepareSpeechData(channels_, 20, 2000); // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // Set bitrate. - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, 32000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, + channels_ == 1 ? 32000 : 64000)); // Set input audio as silence. - int16_t silence[kOpus20msFrameSamples] = {0}; + int16_t* silence = new int16_t[kOpus20msFrameSamples * channels_]; + memset(silence, 0, sizeof(int16_t) * kOpus20msFrameSamples * channels_); // Setting DTX. - EXPECT_EQ(0, dtx ? WebRtcOpus_EnableDtx(opus_mono_encoder_) : - WebRtcOpus_DisableDtx(opus_mono_encoder_)); + EXPECT_EQ(0, dtx ? WebRtcOpus_EnableDtx(opus_encoder_) : + WebRtcOpus_DisableDtx(opus_encoder_)); int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples]; + int16_t* output_data_decode = new int16_t[kOpus20msFrameSamples * channels_]; for (int i = 0; i < 100; ++i) { EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); // If not DTX, it should never enter DTX mode. If DTX, we do not care since // whether it enters DTX depends on the signal type. if (!dtx) { EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. } } // We input some silent segments. In DTX mode, the encoder will stop sending. // However, DTX may happen after a while. - for (int i = 0; i < 22; ++i) { + for (int i = 0; i < 30; ++i) { EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, silence, - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, silence, + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); if (!dtx) { EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. } else if (1 == encoded_bytes_) { - EXPECT_EQ(1, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(1, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(1, opus_encoder_->in_dtx_mode); + EXPECT_EQ(1, opus_decoder_->in_dtx_mode); EXPECT_EQ(2, audio_type); // Comfort noise. break; } } // DTX mode is maintained 400 ms. - for (int i = 0; i < 20; ++i) { + for (int i = 0; i < 19; ++i) { EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, silence, - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, silence, + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); if (dtx) { EXPECT_EQ(0, encoded_bytes_) // Send 0 byte. << "Opus should have entered DTX mode."; - EXPECT_EQ(1, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(1, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(1, opus_encoder_->in_dtx_mode); + EXPECT_EQ(1, opus_decoder_->in_dtx_mode); EXPECT_EQ(2, audio_type); // Comfort noise. } else { EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. } } // Quit DTX after 400 ms EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, silence, - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, silence, + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. // Enters DTX again immediately. EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, silence, - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, silence, + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); if (dtx) { EXPECT_EQ(1, encoded_bytes_); // Send 1 byte. - EXPECT_EQ(1, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(1, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(1, opus_encoder_->in_dtx_mode); + EXPECT_EQ(1, opus_decoder_->in_dtx_mode); EXPECT_EQ(2, audio_type); // Comfort noise. } else { EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. } @@ -215,386 +223,340 @@ void OpusTest::TestDtxEffect(bool dtx) { if (dtx) { // Verify that encoder/decoder can jump out from DTX mode. EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, silence, - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, silence, + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); EXPECT_GT(encoded_bytes_, 1); - EXPECT_EQ(0, opus_mono_encoder_->in_dtx_mode); - EXPECT_EQ(0, opus_mono_decoder_->in_dtx_mode); + EXPECT_EQ(0, opus_encoder_->in_dtx_mode); + EXPECT_EQ(0, opus_decoder_->in_dtx_mode); EXPECT_EQ(0, audio_type); // Speech. } // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_mono_decoder_)); + delete[] output_data_decode; + delete[] silence; + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } // Test failing Create. -TEST_F(OpusTest, OpusCreateFail) { +TEST(OpusTest, OpusCreateFail) { + WebRtcOpusEncInst* opus_encoder; + WebRtcOpusDecInst* opus_decoder; + // Test to see that an invalid pointer is caught. - EXPECT_EQ(-1, WebRtcOpus_EncoderCreate(NULL, 1)); - EXPECT_EQ(-1, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 3)); + EXPECT_EQ(-1, WebRtcOpus_EncoderCreate(NULL, 1, 0)); + // Invalid channel number. + EXPECT_EQ(-1, WebRtcOpus_EncoderCreate(&opus_encoder, 3, 0)); + // Invalid applciation mode. + EXPECT_EQ(-1, WebRtcOpus_EncoderCreate(&opus_encoder, 1, 2)); + EXPECT_EQ(-1, WebRtcOpus_DecoderCreate(NULL, 1)); - EXPECT_EQ(-1, WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 3)); + // Invalid channel number. + EXPECT_EQ(-1, WebRtcOpus_DecoderCreate(&opus_decoder, 3)); } // Test failing Free. -TEST_F(OpusTest, OpusFreeFail) { +TEST(OpusTest, OpusFreeFail) { // Test to see that an invalid pointer is caught. EXPECT_EQ(-1, WebRtcOpus_EncoderFree(NULL)); EXPECT_EQ(-1, WebRtcOpus_DecoderFree(NULL)); } // Test normal Create and Free. -TEST_F(OpusTest, OpusCreateFree) { - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); - EXPECT_TRUE(opus_mono_encoder_ != NULL); - EXPECT_TRUE(opus_mono_decoder_ != NULL); - EXPECT_TRUE(opus_stereo_encoder_ != NULL); - EXPECT_TRUE(opus_stereo_decoder_ != NULL); +TEST_P(OpusTest, OpusCreateFree) { + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); + EXPECT_TRUE(opus_encoder_ != NULL); + EXPECT_TRUE(opus_decoder_ != NULL); // Free encoder and decoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_mono_decoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } -TEST_F(OpusTest, OpusEncodeDecodeMono) { - PrepareSpeechData(1, 20, 20); +TEST_P(OpusTest, OpusEncodeDecode) { + PrepareSpeechData(channels_, 20, 20); // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, + channels_)); // Set bitrate. - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, 32000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, + channels_ == 1 ? 32000 : 64000)); // Check number of channels for decoder. - EXPECT_EQ(1, WebRtcOpus_DecoderChannels(opus_mono_decoder_)); + EXPECT_EQ(channels_, WebRtcOpus_DecoderChannels(opus_decoder_)); + + // Check application mode. + opus_int32 app; + opus_encoder_ctl(opus_encoder_->encoder, + OPUS_GET_APPLICATION(&app)); + EXPECT_EQ(application_ == 0 ? OPUS_APPLICATION_VOIP : OPUS_APPLICATION_AUDIO, + app); // Encode & decode. int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples]; + int16_t* output_data_decode = new int16_t[kOpus20msFrameSamples * channels_]; EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_mono_decoder_)); + delete[] output_data_decode; + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } -TEST_F(OpusTest, OpusEncodeDecodeStereo) { - PrepareSpeechData(2, 20, 20); - - // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); - - // Set bitrate. - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, 64000)); - - // Check number of channels for decoder. - EXPECT_EQ(2, WebRtcOpus_DecoderChannels(opus_stereo_decoder_)); - - // Encode & decode. - int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples * 2]; - EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_stereo_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_stereo_decoder_, - output_data_decode, &audio_type)); - - // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_)); -} - -TEST_F(OpusTest, OpusSetBitRate) { +TEST_P(OpusTest, OpusSetBitRate) { // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_SetBitRate(opus_mono_encoder_, 60000)); - EXPECT_EQ(-1, WebRtcOpus_SetBitRate(opus_stereo_encoder_, 60000)); + EXPECT_EQ(-1, WebRtcOpus_SetBitRate(opus_encoder_, 60000)); // Create encoder memory, try with different bitrates. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, 30000)); - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, 60000)); - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, 300000)); - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, 600000)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, 30000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, 60000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, 300000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, 600000)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); } -TEST_F(OpusTest, OpusSetComplexity) { +TEST_P(OpusTest, OpusSetComplexity) { // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_mono_encoder_, 9)); - EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 9)); + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_encoder_, 9)); // Create encoder memory, try with different complexities. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); - EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, 0)); - EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 0)); - EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_mono_encoder_, 10)); - EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 10)); - EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_mono_encoder_, 11)); - EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_stereo_encoder_, 11)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, 0)); + EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, 10)); + EXPECT_EQ(-1, WebRtcOpus_SetComplexity(opus_encoder_, 11)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); } -// Encode and decode one frame (stereo), initialize the decoder and +// Encode and decode one frame, initialize the decoder and // decode once more. -TEST_F(OpusTest, OpusDecodeInit) { - PrepareSpeechData(2, 20, 20); +TEST_P(OpusTest, OpusDecodeInit) { + PrepareSpeechData(channels_, 20, 20); // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // Encode & decode. int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples * 2]; + int16_t* output_data_decode = new int16_t[kOpus20msFrameSamples * channels_]; EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_stereo_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_stereo_decoder_, + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); - EXPECT_EQ(0, WebRtcOpus_DecoderInit(opus_stereo_decoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderInit(opus_decoder_)); EXPECT_EQ(kOpus20msFrameSamples, - WebRtcOpus_Decode(opus_stereo_decoder_, bitstream_, + WebRtcOpus_Decode(opus_decoder_, bitstream_, encoded_bytes_, output_data_decode, &audio_type)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_)); + delete[] output_data_decode; + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } -TEST_F(OpusTest, OpusEnableDisableFec) { +TEST_P(OpusTest, OpusEnableDisableFec) { // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_EnableFec(opus_mono_encoder_)); - EXPECT_EQ(-1, WebRtcOpus_DisableFec(opus_stereo_encoder_)); + EXPECT_EQ(-1, WebRtcOpus_EnableFec(opus_encoder_)); + EXPECT_EQ(-1, WebRtcOpus_DisableFec(opus_encoder_)); - // Create encoder memory, try with different bitrates. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + // Create encoder memory. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); - EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); } -TEST_F(OpusTest, OpusEnableDisableDtx) { +TEST_P(OpusTest, OpusEnableDisableDtx) { // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_EnableDtx(opus_mono_encoder_)); - EXPECT_EQ(-1, WebRtcOpus_DisableDtx(opus_stereo_encoder_)); + EXPECT_EQ(-1, WebRtcOpus_EnableDtx(opus_encoder_)); + EXPECT_EQ(-1, WebRtcOpus_DisableDtx(opus_encoder_)); - // Create encoder memory, try with different bitrates. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); + // Create encoder memory. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); opus_int32 dtx; // DTX is off by default. - opus_encoder_ctl(opus_mono_encoder_->encoder, - OPUS_GET_DTX(&dtx)); - EXPECT_EQ(0, dtx); - - opus_encoder_ctl(opus_stereo_encoder_->encoder, + opus_encoder_ctl(opus_encoder_->encoder, OPUS_GET_DTX(&dtx)); EXPECT_EQ(0, dtx); // Test to enable DTX. - EXPECT_EQ(0, WebRtcOpus_EnableDtx(opus_mono_encoder_)); - opus_encoder_ctl(opus_mono_encoder_->encoder, - OPUS_GET_DTX(&dtx)); - EXPECT_EQ(1, dtx); - - EXPECT_EQ(0, WebRtcOpus_EnableDtx(opus_stereo_encoder_)); - opus_encoder_ctl(opus_stereo_encoder_->encoder, + EXPECT_EQ(0, WebRtcOpus_EnableDtx(opus_encoder_)); + opus_encoder_ctl(opus_encoder_->encoder, OPUS_GET_DTX(&dtx)); EXPECT_EQ(1, dtx); // Test to disable DTX. - EXPECT_EQ(0, WebRtcOpus_DisableDtx(opus_mono_encoder_)); - opus_encoder_ctl(opus_mono_encoder_->encoder, + EXPECT_EQ(0, WebRtcOpus_DisableDtx(opus_encoder_)); + opus_encoder_ctl(opus_encoder_->encoder, OPUS_GET_DTX(&dtx)); EXPECT_EQ(0, dtx); - EXPECT_EQ(0, WebRtcOpus_DisableDtx(opus_stereo_encoder_)); - opus_encoder_ctl(opus_stereo_encoder_->encoder, - OPUS_GET_DTX(&dtx)); - EXPECT_EQ(0, dtx); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); } -TEST_F(OpusTest, OpusDtxOff) { +TEST_P(OpusTest, OpusDtxOff) { TestDtxEffect(false); } -TEST_F(OpusTest, OpusDtxOn) { +TEST_P(OpusTest, OpusDtxOn) { + if (application_ == 1) { + // We do not check DTX under OPUS_APPLICATION_AUDIO mode. + return; + } TestDtxEffect(true); } -TEST_F(OpusTest, OpusSetPacketLossRate) { +TEST_P(OpusTest, OpusSetPacketLossRate) { // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 50)); - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 50)); - - // Create encoder memory, try with different bitrates. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - - EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 50)); - EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 50)); - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, -1)); - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, -1)); - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_mono_encoder_, 101)); - EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_stereo_encoder_, 101)); - - // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); -} - -TEST_F(OpusTest, OpusSetMaxPlaybackRate) { - // Test without creating encoder memory. - EXPECT_EQ(-1, WebRtcOpus_SetMaxPlaybackRate(opus_mono_encoder_, 20000)); - EXPECT_EQ(-1, WebRtcOpus_SetMaxPlaybackRate(opus_stereo_encoder_, 20000)); - - // Create encoder memory, try with different bitrates. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_FULLBAND, 48000); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_FULLBAND, 24001); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_SUPERWIDEBAND, 24000); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_SUPERWIDEBAND, 16001); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_WIDEBAND, 16000); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_WIDEBAND, 12001); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_MEDIUMBAND, 12000); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_MEDIUMBAND, 8001); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_NARROWBAND, 8000); - TestSetMaxPlaybackRate(OPUS_BANDWIDTH_NARROWBAND, 4000); - - // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); -} - -// PLC in mono mode. -TEST_F(OpusTest, OpusDecodePlcMono) { - PrepareSpeechData(1, 20, 20); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_encoder_, 50)); // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, 50)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_encoder_, -1)); + EXPECT_EQ(-1, WebRtcOpus_SetPacketLossRate(opus_encoder_, 101)); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); +} + +TEST_P(OpusTest, OpusSetMaxPlaybackRate) { + // Test without creating encoder memory. + EXPECT_EQ(-1, WebRtcOpus_SetMaxPlaybackRate(opus_encoder_, 20000)); + + // Create encoder memory. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_FULLBAND, 48000); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_FULLBAND, 24001); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_SUPERWIDEBAND, 24000); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_SUPERWIDEBAND, 16001); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_WIDEBAND, 16000); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_WIDEBAND, 12001); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_MEDIUMBAND, 12000); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_MEDIUMBAND, 8001); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_NARROWBAND, 8000); + SetMaxPlaybackRate(opus_encoder_, OPUS_BANDWIDTH_NARROWBAND, 4000); + + // Free memory. + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); +} + +// Test PLC. +TEST_P(OpusTest, OpusDecodePlc) { + PrepareSpeechData(channels_, 20, 20); + + // Create encoder memory. + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // Set bitrate. - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_mono_encoder_, 32000)); + EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, + channels_== 1 ? 32000 : 64000)); // Check number of channels for decoder. - EXPECT_EQ(1, WebRtcOpus_DecoderChannels(opus_mono_decoder_)); + EXPECT_EQ(channels_, WebRtcOpus_DecoderChannels(opus_decoder_)); // Encode & decode. int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples]; + int16_t* output_data_decode = new int16_t[kOpus20msFrameSamples * channels_]; EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_mono_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_mono_decoder_, + EncodeDecode(opus_encoder_, speech_data_.GetNextBlock(), + kOpus20msFrameSamples, opus_decoder_, output_data_decode, &audio_type)); // Call decoder PLC. - int16_t plc_buffer[kOpus20msFrameSamples]; + int16_t* plc_buffer = new int16_t[kOpus20msFrameSamples * channels_]; EXPECT_EQ(kOpus20msFrameSamples, - WebRtcOpus_DecodePlc(opus_mono_decoder_, plc_buffer, 1)); + WebRtcOpus_DecodePlc(opus_decoder_, plc_buffer, 1)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_mono_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_mono_decoder_)); -} - -// PLC in stereo mode. -TEST_F(OpusTest, OpusDecodePlcStereo) { - PrepareSpeechData(2, 20, 20); - - // Create encoder memory. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); - - // Set bitrate. - EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_stereo_encoder_, 64000)); - - // Check number of channels for decoder. - EXPECT_EQ(2, WebRtcOpus_DecoderChannels(opus_stereo_decoder_)); - - // Encode & decode. - int16_t audio_type; - int16_t output_data_decode[kOpus20msFrameSamples * 2]; - EXPECT_EQ(kOpus20msFrameSamples, - EncodeDecode(opus_stereo_encoder_, speech_data_.GetNextBlock(), - kOpus20msFrameSamples, opus_stereo_decoder_, - output_data_decode, &audio_type)); - - // Call decoder PLC. - int16_t plc_buffer[kOpus20msFrameSamples * 2]; - EXPECT_EQ(kOpus20msFrameSamples, - WebRtcOpus_DecodePlc(opus_stereo_decoder_, plc_buffer, 1)); - - // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_)); + delete[] plc_buffer; + delete[] output_data_decode; + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } // Duration estimation. -TEST_F(OpusTest, OpusDurationEstimation) { - PrepareSpeechData(2, 20, 20); +TEST_P(OpusTest, OpusDurationEstimation) { + PrepareSpeechData(channels_, 20, 20); // Create. - EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2)); - EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_stereo_decoder_, 2)); + EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, + channels_, + application_)); + EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // 10 ms. We use only first 10 ms of a 20 ms block. - encoded_bytes_ = WebRtcOpus_Encode(opus_stereo_encoder_, + encoded_bytes_ = WebRtcOpus_Encode(opus_encoder_, speech_data_.GetNextBlock(), kOpus10msFrameSamples, kMaxBytes, bitstream_); EXPECT_EQ(kOpus10msFrameSamples, - WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, + WebRtcOpus_DurationEst(opus_decoder_, bitstream_, encoded_bytes_)); // 20 ms - encoded_bytes_ = WebRtcOpus_Encode(opus_stereo_encoder_, + encoded_bytes_ = WebRtcOpus_Encode(opus_encoder_, speech_data_.GetNextBlock(), kOpus20msFrameSamples, kMaxBytes, bitstream_); EXPECT_EQ(kOpus20msFrameSamples, - WebRtcOpus_DurationEst(opus_stereo_decoder_, bitstream_, + WebRtcOpus_DurationEst(opus_decoder_, bitstream_, encoded_bytes_)); // Free memory. - EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_stereo_encoder_)); - EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_stereo_decoder_)); + EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); + EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } +INSTANTIATE_TEST_CASE_P(VariousMode, + OpusTest, + Combine(Values(1, 2), Values(0, 1))); + + } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc index b1c4214304..6b1809566f 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.cc @@ -999,6 +999,12 @@ int16_t ACMGenericCodec::REDPayloadISAC(const int32_t /* isac_rate */, return -1; } +int ACMGenericCodec::SetOpusApplication(OpusApplicationMode /*application*/) { + WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, + "The send-codec is not Opus, failed to set application."); + return -1; +} + int ACMGenericCodec::SetOpusMaxPlaybackRate(int /* frequency_hz */) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, unique_id_, "The send-codec is not Opus, failed to set maximum playback rate."); diff --git a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h index d55d91e6c6..b13719fb2e 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h +++ b/webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h @@ -525,6 +525,20 @@ class ACMGenericCodec { uint8_t* payload, int16_t* payload_len_bytes); + /////////////////////////////////////////////////////////////////////////// + // int SetOpusApplication() + // Sets the intended application for the Opus encoder. Opus uses this to + // optimize the encoding for applications like VOIP and music. + // + // Input: + // - application : intended application. + // + // Return value: + // -1 if failed or on codecs other than Opus. + // 0 if succeeded. + // + virtual int SetOpusApplication(OpusApplicationMode /*application*/); + /////////////////////////////////////////////////////////////////////////// // int SetOpusMaxPlaybackRate() // Sets maximum playback rate the receiver will render, if the codec is Opus. @@ -636,8 +650,8 @@ class ACMGenericCodec { // See InitEncoder() for the description of function, input(s)/output(s) // and return value. // - int16_t InitEncoderSafe(WebRtcACMCodecParams* codec_params, - bool force_initialization) + virtual int16_t InitEncoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization) EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); /////////////////////////////////////////////////////////////////////////// diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus.cc b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc index 8991dae875..005e91046c 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_opus.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc @@ -68,7 +68,8 @@ ACMOpus::ACMOpus(int16_t codec_id) sample_freq_(32000), // Default sampling frequency. bitrate_(20000), // Default bit-rate. channels_(1), // Default mono. - packet_loss_rate_(0) { // Initial packet loss rate. + packet_loss_rate_(0), // Initial packet loss rate. + application_(kVoip) { // Initial application mode. codec_id_ = codec_id; // Opus has internal DTX, but we dont use it for now. has_internal_dtx_ = false; @@ -113,6 +114,16 @@ int16_t ACMOpus::InternalEncode(uint8_t* bitstream, return *bitstream_len_byte; } +int16_t ACMOpus::InitEncoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization) { + // Determine target application if codec is not initialized or a forced + // initialization is requested. + if (!encoder_initialized_ || force_initialization) { + application_ = (codec_params->codec_inst.channels == 1) ? kVoip : kAudio; + } + return ACMGenericCodec::InitEncoderSafe(codec_params, force_initialization); +} + int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { int16_t ret; if (encoder_inst_ptr_ != NULL) { @@ -120,7 +131,8 @@ int16_t ACMOpus::InternalInitEncoder(WebRtcACMCodecParams* codec_params) { encoder_inst_ptr_ = NULL; } ret = WebRtcOpus_EncoderCreate(&encoder_inst_ptr_, - codec_params->codec_inst.channels); + codec_params->codec_inst.channels, + application_); // Store number of channels. channels_ = codec_params->codec_inst.channels; @@ -251,6 +263,13 @@ int ACMOpus::SetOpusMaxPlaybackRate(int frequency_hz) { return WebRtcOpus_SetMaxPlaybackRate(encoder_inst_ptr_, frequency_hz); } +int ACMOpus::SetOpusApplication(OpusApplicationMode application) { + WriteLockScoped lockCodec(codec_wrapper_lock_); + application_ = application; + // Set Opus application invokes a reset of the encoder. + return InternalResetEncoder(); +} + #endif // WEBRTC_CODEC_OPUS } // namespace acm2 diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus.h b/webrtc/modules/audio_coding/main/acm2/acm_opus.h index 8ef6406767..3c7aea65fb 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_opus.h +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus.h @@ -29,23 +29,29 @@ class ACMOpus : public ACMGenericCodec { ACMGenericCodec* CreateInstance(void); int16_t InternalEncode(uint8_t* bitstream, - int16_t* bitstream_len_byte) OVERRIDE + int16_t* bitstream_len_byte) override + EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); + + int16_t InitEncoderSafe(WebRtcACMCodecParams* codec_params, + bool force_initialization) override EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); int16_t InternalInitEncoder(WebRtcACMCodecParams *codec_params); - virtual int SetFEC(bool enable_fec) OVERRIDE; + int SetFEC(bool enable_fec) override; - virtual int SetPacketLossRate(int loss_rate) OVERRIDE; + int SetOpusApplication(OpusApplicationMode mode) override; - virtual int SetOpusMaxPlaybackRate(int frequency_hz) OVERRIDE; + int SetPacketLossRate(int loss_rate) override; + + int SetOpusMaxPlaybackRate(int frequency_hz) override; protected: void DestructEncoderSafe(); int16_t InternalCreateEncoder(); - int16_t SetBitRateSafe(const int32_t rate) OVERRIDE + int16_t SetBitRateSafe(const int32_t rate) override EXCLUSIVE_LOCKS_REQUIRED(codec_wrapper_lock_); WebRtcOpusEncInst* encoder_inst_ptr_; @@ -54,6 +60,8 @@ class ACMOpus : public ACMGenericCodec { int channels_; int packet_loss_rate_; + + OpusApplicationMode application_; }; } // namespace acm2 diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc b/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc index e33c9d3829..421bbe595e 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc @@ -35,6 +35,11 @@ class AcmOpusTest : public ACMOpus { : ACMOpus(codec_id) {} ~AcmOpusTest() {} int packet_loss_rate() { return packet_loss_rate_; } + OpusApplicationMode application() { return application_; } + bool encoder_initialized() { + ReadLockScoped cs(codec_wrapper_lock_); + return encoder_initialized_; + } void TestSetPacketLossRate(int from, int to, int expected_return); }; @@ -82,6 +87,54 @@ TEST(AcmOpusTest, PacketLossRateOptimized) { kPacketLossRate1); opus.TestSetPacketLossRate(0, 0, 0); } + +TEST(AcmOpusTest, DefaultApplicationMode) { + AcmOpusTest opus(ACMCodecDB::kOpus); + WebRtcACMCodecParams params; + memcpy(&(params.codec_inst), &kOpusCodecInst, sizeof(CodecInst)); + + params.codec_inst.channels = 2; + // Codec is not initialized, and hence without force initialization (2nd + // argument being false), an initialization will take place. + EXPECT_FALSE(opus.encoder_initialized()); + EXPECT_EQ(0, opus.InitEncoder(¶ms, false)); + EXPECT_EQ(kAudio, opus.application()); + + params.codec_inst.channels = 1; + EXPECT_EQ(0, opus.InitEncoder(¶ms, true)); + EXPECT_EQ(kVoip, opus.application()); +} + +TEST(AcmOpusTest, ChangeApplicationMode) { + AcmOpusTest opus(ACMCodecDB::kOpus); + WebRtcACMCodecParams params; + memcpy(&(params.codec_inst), &kOpusCodecInst, sizeof(CodecInst)); + + params.codec_inst.channels = 2; + // Codec is not initialized, and hence without force initialization (2nd + // argument being false), an initialization will take place. + EXPECT_EQ(0, opus.InitEncoder(¶ms, false)); + EXPECT_EQ(kAudio, opus.application()); + + opus.SetOpusApplication(kVoip); + EXPECT_EQ(kVoip, opus.application()); +} + +TEST(AcmOpusTest, ResetWontChangeApplicationMode) { + AcmOpusTest opus(ACMCodecDB::kOpus); + WebRtcACMCodecParams params; + memcpy(&(params.codec_inst), &kOpusCodecInst, sizeof(CodecInst)); + + params.codec_inst.channels = 2; + // Codec is not initialized, and hence without force initialization (2nd + // argument being false), an initialization will take place. + EXPECT_EQ(0, opus.InitEncoder(¶ms, false)); + EXPECT_EQ(kAudio, opus.application()); + + opus.ResetEncoder(); + EXPECT_EQ(kAudio, opus.application()); +} + #else void AcmOpusTest:TestSetPacketLossRate(int /* from */, int /* to */, int /* expected_return */) { diff --git a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h index e9902842f8..5953cab2ba 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h +++ b/webrtc/modules/audio_coding/main/acm2/acm_send_test_oldapi.h @@ -54,6 +54,8 @@ class AcmSendTestOldApi : public AudioPacketizationCallback, size_t payload_len_bytes, const RTPFragmentationHeader* fragmentation) OVERRIDE; + AudioCodingModule* acm() { return acm_.get(); } + private: static const int kBlockSizeMs = 10; diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc index fe2b5474f6..c9139bdbec 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.cc @@ -1544,6 +1544,14 @@ int AudioCodingModuleImpl::ConfigISACBandwidthEstimator( frame_size_ms, rate_bit_per_sec, enforce_frame_size); } +int AudioCodingModuleImpl::SetOpusApplication(OpusApplicationMode application) { + CriticalSectionScoped lock(acm_crit_sect_); + if (!HaveValidEncoder("SetOpusApplication")) { + return -1; + } + return codecs_[current_send_codec_idx_]->SetOpusApplication(application); +} + // Informs Opus encoder of the maximum playback rate the receiver will render. int AudioCodingModuleImpl::SetOpusMaxPlaybackRate(int frequency_hz) { CriticalSectionScoped lock(acm_crit_sect_); diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h index df9e334773..d393427342 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h @@ -224,6 +224,8 @@ class AudioCodingModuleImpl : public AudioCodingModule { int rate_bit_per_sec, bool enforce_frame_size = false) OVERRIDE; + int SetOpusApplication(OpusApplicationMode application) override; + // If current send codec is Opus, informs it about the maximum playback rate // the receiver will render. virtual int SetOpusMaxPlaybackRate(int frequency_hz) OVERRIDE; diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc index 0151568fe4..84d55585ca 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc @@ -977,6 +977,22 @@ TEST_F(AcmSenderBitExactnessOldApi, MAYBE_Opus_stereo_20ms) { test::AcmReceiveTestOldApi::kStereoOutput); } +TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms_voip) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 2, 120, 960, 960)); + // If not set, default will be kAudio in case of stereo. + send_test_->acm()->SetOpusApplication(kVoip); + Run(AcmReceiverBitExactnessOldApi::PlatformChecksum( + "9b9e12bc3cc793740966e11cbfa8b35b", + "57412a4b5771d19ff03ec35deffe7067", + "9b9e12bc3cc793740966e11cbfa8b35b"), + AcmReceiverBitExactnessOldApi::PlatformChecksum( + "c7340b1189652ab6b5e80dade7390cb4", + "cdfe85939c411d12b61701c566e22d26", + "c7340b1189652ab6b5e80dade7390cb4"), + 50, + test::AcmReceiveTestOldApi::kStereoOutput); +} + // This test fixture is implemented to run ACM and change the desired output // frequency during the call. The input packets are simply PCM16b-wb encoded // payloads with a constant value of |kSampleValue|. The test fixture itself diff --git a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h index a16b005897..7de0a12070 100644 --- a/webrtc/modules/audio_coding/main/interface/audio_coding_module.h +++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module.h @@ -874,6 +874,20 @@ class AudioCodingModule: public Module { int init_rate_bps, bool enforce_frame_size = false) = 0; + /////////////////////////////////////////////////////////////////////////// + // int SetOpusApplication() + // Sets the intended application for the Opus encoder. Opus uses this to + // optimize the encoding for applications like VOIP and music. + // + // Input: + // - application : intended application. + // + // Return value: + // -1 if failed or on codecs other than Opus. + // 0 if succeeded. + // + virtual int SetOpusApplication(OpusApplicationMode /*application*/) = 0; + /////////////////////////////////////////////////////////////////////////// // int SetOpusMaxPlaybackRate() // If current send codec is Opus, informs it about maximum playback rate the diff --git a/webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h b/webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h index 199be963a5..dce5352a77 100644 --- a/webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h +++ b/webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h @@ -200,6 +200,18 @@ enum ACMBackgroundNoiseMode { Off }; +/////////////////////////////////////////////////////////////////////////// +// +// Enumeration of Opus mode for intended application. +// +// kVoip : optimized for voice signals. +// kAudio : optimized for non-voice signals like music. +// +enum OpusApplicationMode { + kVoip = 0, + kAudio = 1, +}; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_INTERFACE_AUDIO_CODING_MODULE_TYPEDEFS_H_ diff --git a/webrtc/modules/audio_coding/main/test/opus_test.cc b/webrtc/modules/audio_coding/main/test/opus_test.cc index ecc056a365..8fda4b386a 100644 --- a/webrtc/modules/audio_coding/main/test/opus_test.cc +++ b/webrtc/modules/audio_coding/main/test/opus_test.cc @@ -79,8 +79,8 @@ void OpusTest::Perform() { in_file_mono_.ReadStereo(false); // Create Opus encoders for mono and stereo. - ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1), -1); - ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2), -1); + ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_mono_encoder_, 1, 0), -1); + ASSERT_GT(WebRtcOpus_EncoderCreate(&opus_stereo_encoder_, 2, 1), -1); // Create Opus decoders for mono and stereo for stand-alone testing of Opus. ASSERT_GT(WebRtcOpus_DecoderCreate(&opus_mono_decoder_, 1), -1); diff --git a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc index 0f058d025d..bcbc4c29ee 100644 --- a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc +++ b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc @@ -447,6 +447,7 @@ class AudioDecoderOpusTest : public AudioDecoderTest { AudioEncoderOpus::Config config; config.frame_size_ms = static_cast(frame_size_) / 48; config.payload_type = payload_type_; + config.application = AudioEncoderOpus::kVoip; audio_encoder_.reset(new AudioEncoderOpus(config)); } }; @@ -461,6 +462,7 @@ class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest { config.frame_size_ms = static_cast(frame_size_) / 48; config.num_channels = 2; config.payload_type = payload_type_; + config.application = AudioEncoderOpus::kAudio; audio_encoder_.reset(new AudioEncoderOpus(config)); } }; diff --git a/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc b/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc index eddbffd89a..e1f53af8a9 100644 --- a/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc +++ b/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc @@ -145,8 +145,10 @@ NetEqOpusFecQualityTest::NetEqOpusFecQualityTest() } void NetEqOpusFecQualityTest::SetUp() { + // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode. + int app = channels_ == 1 ? 0 : 1; // Create encoder memory. - WebRtcOpus_EncoderCreate(&opus_encoder_, channels_); + WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app); ASSERT_TRUE(opus_encoder_ != NULL); // Set bitrate. EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_kbps_ * 1000));