opus_fec_test.cc (8128B)
1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <cstddef> 12 #include <cstdint> 13 #include <cstdio> 14 #include <cstdlib> 15 #include <cstring> 16 #include <memory> 17 #include <string> 18 #include <tuple> 19 20 #include "modules/audio_coding/codecs/opus/opus_interface.h" 21 #include "test/gtest.h" 22 #include "test/testsupport/file_utils.h" 23 24 using std::get; 25 using std::string; 26 using std::tuple; 27 using ::testing::TestWithParam; 28 29 namespace webrtc { 30 31 // Define coding parameter as <channels, bit_rate, filename, extension>. 32 typedef tuple<size_t, int, string, string> coding_param; 33 typedef struct mode mode; 34 35 struct mode { 36 bool fec; 37 uint8_t target_packet_loss_rate; 38 }; 39 40 constexpr int kOpusBlockDurationMs = 20; 41 constexpr int kOpusSamplingKhz = 48; 42 43 class OpusFecTest : public TestWithParam<coding_param> { 44 protected: 45 OpusFecTest(); 46 47 void SetUp() override; 48 void TearDown() override; 49 50 virtual void EncodeABlock(); 51 52 virtual void DecodeABlock(bool lost_previous, bool lost_current); 53 54 int block_duration_ms_; 55 int sampling_khz_; 56 size_t block_length_sample_; 57 58 size_t channels_; 59 int bit_rate_; 60 61 size_t data_pointer_; 62 size_t loop_length_samples_; 63 size_t max_bytes_; 64 size_t encoded_bytes_; 65 66 WebRtcOpusEncInst* opus_encoder_; 67 WebRtcOpusDecInst* opus_decoder_; 68 69 string in_filename_; 70 71 std::unique_ptr<int16_t[]> in_data_; 72 std::unique_ptr<int16_t[]> out_data_; 73 std::unique_ptr<uint8_t[]> bit_stream_; 74 }; 75 76 void OpusFecTest::SetUp() { 77 channels_ = get<0>(GetParam()); 78 bit_rate_ = get<1>(GetParam()); 79 printf("Coding %zu channel signal at %d bps.\n", channels_, bit_rate_); 80 81 in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam())); 82 83 FILE* fp = fopen(in_filename_.c_str(), "rb"); 84 ASSERT_FALSE(fp == nullptr); 85 86 // Obtain file size. 87 fseek(fp, 0, SEEK_END); 88 loop_length_samples_ = ftell(fp) / sizeof(int16_t); 89 rewind(fp); 90 91 // Allocate memory to contain the whole file. 92 in_data_.reset( 93 new int16_t[loop_length_samples_ + block_length_sample_ * channels_]); 94 95 // Copy the file into the buffer. 96 ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp), 97 loop_length_samples_); 98 fclose(fp); 99 100 // The audio will be used in a looped manner. To ease the acquisition of an 101 // audio frame that crosses the end of the excerpt, we add an extra block 102 // length of samples to the end of the array, starting over again from the 103 // beginning of the array. Audio frames cross the end of the excerpt always 104 // appear as a continuum of memory. 105 memcpy(&in_data_[loop_length_samples_], &in_data_[0], 106 block_length_sample_ * channels_ * sizeof(int16_t)); 107 108 // Maximum number of bytes in output bitstream. 109 max_bytes_ = block_length_sample_ * channels_ * sizeof(int16_t); 110 111 out_data_.reset(new int16_t[2 * block_length_sample_ * channels_]); 112 bit_stream_.reset(new uint8_t[max_bytes_]); 113 114 // If channels_ == 1, use Opus VOIP mode, otherwise, audio mode. 115 int app = channels_ == 1 ? 0 : 1; 116 117 // Create encoder memory. 118 EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, app, 48000)); 119 EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_, 48000)); 120 // Set bitrate. 121 EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_)); 122 } 123 124 void OpusFecTest::TearDown() { 125 // Free memory. 126 EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_)); 127 EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_)); 128 } 129 130 OpusFecTest::OpusFecTest() 131 : block_duration_ms_(kOpusBlockDurationMs), 132 sampling_khz_(kOpusSamplingKhz), 133 block_length_sample_( 134 static_cast<size_t>(block_duration_ms_ * sampling_khz_)), 135 data_pointer_(0), 136 max_bytes_(0), 137 encoded_bytes_(0), 138 opus_encoder_(nullptr), 139 opus_decoder_(nullptr) {} 140 141 void OpusFecTest::EncodeABlock() { 142 int value = 143 WebRtcOpus_Encode(opus_encoder_, &in_data_[data_pointer_], 144 block_length_sample_, max_bytes_, &bit_stream_[0]); 145 EXPECT_GT(value, 0); 146 147 encoded_bytes_ = static_cast<size_t>(value); 148 } 149 150 void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) { 151 int16_t audio_type; 152 int value_1 = 0, value_2 = 0; 153 154 if (lost_previous) { 155 // Decode previous frame. 156 if (!lost_current && 157 WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_) == 1) { 158 value_1 = 159 WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0], encoded_bytes_, 160 &out_data_[0], &audio_type); 161 } else { 162 // Call decoder PLC. 163 while (value_1 < static_cast<int>(block_length_sample_)) { 164 int ret = WebRtcOpus_Decode(opus_decoder_, nullptr, 0, 165 &out_data_[value_1], &audio_type); 166 EXPECT_EQ(ret, sampling_khz_ * 10); // Should return 10 ms of samples. 167 value_1 += ret; 168 } 169 } 170 EXPECT_EQ(static_cast<int>(block_length_sample_), value_1); 171 } 172 173 if (!lost_current) { 174 // Decode current frame. 175 value_2 = WebRtcOpus_Decode(opus_decoder_, &bit_stream_[0], encoded_bytes_, 176 &out_data_[value_1 * channels_], &audio_type); 177 EXPECT_EQ(static_cast<int>(block_length_sample_), value_2); 178 } 179 } 180 181 TEST_P(OpusFecTest, RandomPacketLossTest) { 182 const int kDurationMs = 200000; 183 int time_now_ms, fec_frames; 184 int actual_packet_loss_rate; 185 bool lost_current, lost_previous; 186 mode mode_set[3] = {{true, 0}, {false, 0}, {true, 50}}; 187 188 lost_current = false; 189 for (int i = 0; i < 3; i++) { 190 if (mode_set[i].fec) { 191 EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); 192 EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate( 193 opus_encoder_, mode_set[i].target_packet_loss_rate)); 194 printf("FEC is ON, target at packet loss rate %d percent.\n", 195 mode_set[i].target_packet_loss_rate); 196 } else { 197 EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_)); 198 printf("FEC is OFF.\n"); 199 } 200 // In this test, we let the target packet loss rate match the actual rate. 201 actual_packet_loss_rate = mode_set[i].target_packet_loss_rate; 202 // Run every mode a certain time. 203 time_now_ms = 0; 204 fec_frames = 0; 205 while (time_now_ms < kDurationMs) { 206 // Encode & decode. 207 EncodeABlock(); 208 209 // Check if payload has FEC. 210 int fec = WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_); 211 212 // If FEC is disabled or the target packet loss rate is set to 0, there 213 // should be no FEC in the bit stream. 214 if (!mode_set[i].fec || mode_set[i].target_packet_loss_rate == 0) { 215 EXPECT_EQ(fec, 0); 216 } else if (fec == 1) { 217 fec_frames++; 218 } 219 220 lost_previous = lost_current; 221 lost_current = rand() < actual_packet_loss_rate * (RAND_MAX / 100); 222 DecodeABlock(lost_previous, lost_current); 223 224 time_now_ms += block_duration_ms_; 225 226 // `data_pointer_` is incremented and wrapped across 227 // `loop_length_samples_`. 228 data_pointer_ = (data_pointer_ + block_length_sample_ * channels_) % 229 loop_length_samples_; 230 } 231 if (mode_set[i].fec) { 232 printf("%.2f percent frames has FEC.\n", 233 static_cast<float>(fec_frames) * block_duration_ms_ / 2000); 234 } 235 } 236 } 237 238 const coding_param param_set[] = { 239 std::make_tuple(1, 240 64000, 241 string("audio_coding/testfile32kHz"), 242 string("pcm")), 243 std::make_tuple(1, 244 32000, 245 string("audio_coding/testfile32kHz"), 246 string("pcm")), 247 std::make_tuple(2, 248 64000, 249 string("audio_coding/teststereo32kHz"), 250 string("pcm"))}; 251 252 // 64 kbps, stereo 253 INSTANTIATE_TEST_SUITE_P(AllTest, OpusFecTest, ::testing::ValuesIn(param_set)); 254 255 } // namespace webrtc