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