/* * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "rtc_base/format_macros.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" using std::string; using std::tuple; using std::get; using ::testing::TestWithParam; namespace webrtc { // Define coding parameter as . typedef tuple coding_param; typedef struct mode mode; struct mode { bool fec; uint8_t target_packet_loss_rate; }; const int kOpusBlockDurationMs = 20; const int kOpusSamplingKhz = 48; class OpusFecTest : public TestWithParam { protected: OpusFecTest(); void SetUp() override; void TearDown() override; virtual void EncodeABlock(); virtual void DecodeABlock(bool lost_previous, bool lost_current); int block_duration_ms_; int sampling_khz_; size_t block_length_sample_; size_t channels_; int bit_rate_; size_t data_pointer_; size_t loop_length_samples_; size_t max_bytes_; size_t encoded_bytes_; WebRtcOpusEncInst* opus_encoder_; WebRtcOpusDecInst* opus_decoder_; string in_filename_; std::unique_ptr in_data_; std::unique_ptr out_data_; std::unique_ptr bit_stream_; }; void OpusFecTest::SetUp() { channels_ = get<0>(GetParam()); bit_rate_ = get<1>(GetParam()); printf("Coding %" PRIuS " channel signal at %d bps.\n", channels_, bit_rate_); in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam())); FILE* fp = fopen(in_filename_.c_str(), "rb"); ASSERT_FALSE(fp == NULL); // Obtain file size. fseek(fp, 0, SEEK_END); loop_length_samples_ = ftell(fp) / sizeof(int16_t); rewind(fp); // Allocate memory to contain the whole file. in_data_.reset( new int16_t[loop_length_samples_ + block_length_sample_ * channels_]); // Copy the file into the buffer. ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp), loop_length_samples_); fclose(fp); // The audio will be used in a looped manner. To ease the acquisition of an // audio frame that crosses the end of the excerpt, we add an extra block // length of samples to the end of the array, starting over again from the // beginning of the array. Audio frames cross the end of the excerpt always // appear as a continuum of memory. memcpy(&in_data_[loop_length_samples_], &in_data_[0], block_length_sample_ * channels_ * sizeof(int16_t)); // Maximum number of bytes in output bitstream. max_bytes_ = block_length_sample_ * channels_ * sizeof(int16_t); 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_, app)); EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_)); // Set bitrate. EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); } void OpusFecTest::TearDown() { // Free memory. EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); } OpusFecTest::OpusFecTest() : block_duration_ms_(kOpusBlockDurationMs), sampling_khz_(kOpusSamplingKhz), block_length_sample_( static_cast(block_duration_ms_ * sampling_khz_)), data_pointer_(0), max_bytes_(0), encoded_bytes_(0), opus_encoder_(NULL), opus_decoder_(NULL) {} void OpusFecTest::EncodeABlock() { int value = WebRtcOpus_Encode(opus_encoder_, &in_data_[data_pointer_], block_length_sample_, max_bytes_, &bit_stream_[0]); EXPECT_GT(value, 0); encoded_bytes_ = static_cast(value); } void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) { int16_t audio_type; int value_1 = 0, value_2 = 0; if (lost_previous) { // Decode previous frame. if (!lost_current && WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_) == 1) { value_1 = WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0], encoded_bytes_, &out_data_[0], &audio_type); } else { value_1 = WebRtcOpus_DecodePlc(opus_decoder_, &out_data_[0], 1); } EXPECT_EQ(static_cast(block_length_sample_), value_1); } if (!lost_current) { // Decode current frame. value_2 = WebRtcOpus_Decode(opus_decoder_, &bit_stream_[0], encoded_bytes_, &out_data_[value_1 * channels_], &audio_type); EXPECT_EQ(static_cast(block_length_sample_), value_2); } } TEST_P(OpusFecTest, RandomPacketLossTest) { const int kDurationMs = 200000; int time_now_ms, fec_frames; int actual_packet_loss_rate; bool lost_current, lost_previous; mode mode_set[3] = {{true, 0}, {false, 0}, {true, 50}}; lost_current = false; for (int i = 0; i < 3; i++) { if (mode_set[i].fec) { EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate( opus_encoder_, mode_set[i].target_packet_loss_rate)); printf("FEC is ON, target at packet loss rate %d percent.\n", mode_set[i].target_packet_loss_rate); } else { EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_)); printf("FEC is OFF.\n"); } // In this test, we let the target packet loss rate match the actual rate. actual_packet_loss_rate = mode_set[i].target_packet_loss_rate; // Run every mode a certain time. time_now_ms = 0; fec_frames = 0; while (time_now_ms < kDurationMs) { // Encode & decode. EncodeABlock(); // Check if payload has FEC. int fec = WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_); // If FEC is disabled or the target packet loss rate is set to 0, there // should be no FEC in the bit stream. if (!mode_set[i].fec || mode_set[i].target_packet_loss_rate == 0) { EXPECT_EQ(fec, 0); } else if (fec == 1) { fec_frames++; } lost_previous = lost_current; lost_current = rand() < actual_packet_loss_rate * (RAND_MAX / 100); DecodeABlock(lost_previous, lost_current); time_now_ms += block_duration_ms_; // |data_pointer_| is incremented and wrapped across // |loop_length_samples_|. data_pointer_ = (data_pointer_ + block_length_sample_ * channels_) % loop_length_samples_; } if (mode_set[i].fec) { printf("%.2f percent frames has FEC.\n", static_cast(fec_frames) * block_duration_ms_ / 2000); } } } const coding_param param_set[] = { std::make_tuple(1, 64000, string("audio_coding/testfile32kHz"), string("pcm")), std::make_tuple(1, 32000, string("audio_coding/testfile32kHz"), string("pcm")), std::make_tuple(2, 64000, string("audio_coding/teststereo32kHz"), string("pcm"))}; // 64 kbps, stereo INSTANTIATE_TEST_SUITE_P(AllTest, OpusFecTest, ::testing::ValuesIn(param_set)); } // namespace webrtc