2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
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.
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h"
13 #include "webrtc/test/testsupport/fileutils.h"
14 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
17 using ::std::tr1::tuple;
18 using ::std::tr1::make_tuple;
19 using ::std::tr1::get;
20 using ::testing::TestWithParam;
21 using ::testing::ValuesIn;
25 // Define coding parameter as <channels, bit_rate, filename, extension>.
26 typedef tuple<int, int, string, string> coding_param;
27 typedef struct mode mode;
31 uint8_t target_packet_loss_rate;
34 const int kOpusBlockDurationMs = 20;
35 const int kOpusSamplingKhz = 48;
37 class OpusFecTest : public TestWithParam<coding_param> {
42 virtual void TearDown();
44 virtual void EncodeABlock();
46 virtual void DecodeABlock(bool lost_previous, bool lost_current);
48 int block_duration_ms_;
50 int block_length_sample_;
56 size_t loop_length_samples_;
60 WebRtcOpusEncInst* opus_encoder_;
61 WebRtcOpusDecInst* opus_decoder_;
65 scoped_ptr<int16_t[]> in_data_;
66 scoped_ptr<int16_t[]> out_data_;
67 scoped_ptr<uint8_t[]> bit_stream_;
70 void OpusFecTest::SetUp() {
71 channels_ = get<0>(GetParam());
72 bit_rate_ = get<1>(GetParam());
73 printf("Coding %d channel signal at %d bps.\n", channels_, bit_rate_);
75 in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam()));
77 FILE* fp = fopen(in_filename_.c_str(), "rb");
78 ASSERT_FALSE(fp == NULL);
81 fseek(fp, 0, SEEK_END);
82 loop_length_samples_ = ftell(fp) / sizeof(int16_t);
85 // Allocate memory to contain the whole file.
86 in_data_.reset(new int16_t[loop_length_samples_ +
87 block_length_sample_ * channels_]);
89 // Copy the file into the buffer.
90 ASSERT_EQ(fread(&in_data_[0], sizeof(int16_t), loop_length_samples_, fp),
91 loop_length_samples_);
94 // The audio will be used in a looped manner. To ease the acquisition of an
95 // audio frame that crosses the end of the excerpt, we add an extra block
96 // length of samples to the end of the array, starting over again from the
97 // beginning of the array. Audio frames cross the end of the excerpt always
98 // appear as a continuum of memory.
99 memcpy(&in_data_[loop_length_samples_], &in_data_[0],
100 block_length_sample_ * channels_ * sizeof(int16_t));
102 // Maximum number of bytes in output bitstream.
103 max_bytes_ = block_length_sample_ * channels_ * sizeof(int16_t);
105 out_data_.reset(new int16_t[2 * block_length_sample_ * channels_]);
106 bit_stream_.reset(new uint8_t[max_bytes_]);
108 // Create encoder memory.
109 EXPECT_EQ(0, WebRtcOpus_EncoderCreate(&opus_encoder_, channels_));
110 EXPECT_EQ(0, WebRtcOpus_DecoderCreate(&opus_decoder_, channels_));
112 EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_));
115 void OpusFecTest::TearDown() {
117 EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
118 EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_));
121 OpusFecTest::OpusFecTest()
122 : block_duration_ms_(kOpusBlockDurationMs),
123 sampling_khz_(kOpusSamplingKhz),
124 block_length_sample_(block_duration_ms_ * sampling_khz_),
129 opus_decoder_(NULL) {
132 void OpusFecTest::EncodeABlock() {
133 int16_t value = WebRtcOpus_Encode(opus_encoder_,
134 &in_data_[data_pointer_],
135 block_length_sample_,
136 max_bytes_, &bit_stream_[0]);
139 encoded_bytes_ = value;
142 void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) {
144 int16_t value_1 = 0, value_2 = 0;
147 // Decode previous frame.
149 WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_) == 1) {
150 value_1 = WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0],
151 encoded_bytes_, &out_data_[0],
154 value_1 = WebRtcOpus_DecodePlc(opus_decoder_, &out_data_[0], 1);
156 EXPECT_EQ(block_length_sample_, value_1);
160 // Decode current frame.
161 value_2 = WebRtcOpus_DecodeNew(opus_decoder_, &bit_stream_[0],
163 &out_data_[value_1 * channels_],
165 EXPECT_EQ(block_length_sample_, value_2);
169 TEST_P(OpusFecTest, RandomPacketLossTest) {
170 const int kDurationMs = 200000;
171 int time_now_ms, fec_frames;
172 int actual_packet_loss_rate;
173 bool lost_current, lost_previous;
174 mode mode_set[3] = {{true, 0},
178 lost_current = false;
179 for (int i = 0; i < 3; i++) {
180 if (mode_set[i].fec) {
181 EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_));
182 EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_,
183 mode_set[i].target_packet_loss_rate));
184 printf("FEC is ON, target at packet loss rate %d percent.\n",
185 mode_set[i].target_packet_loss_rate);
187 EXPECT_EQ(0, WebRtcOpus_DisableFec(opus_encoder_));
188 printf("FEC is OFF.\n");
190 // In this test, we let the target packet loss rate match the actual rate.
191 actual_packet_loss_rate = mode_set[i].target_packet_loss_rate;
192 // Run every mode a certain time.
195 while (time_now_ms < kDurationMs) {
199 // Check if payload has FEC.
200 int16_t fec = WebRtcOpus_PacketHasFec(&bit_stream_[0], encoded_bytes_);
202 // If FEC is disabled or the target packet loss rate is set to 0, there
203 // should be no FEC in the bit stream.
204 if (!mode_set[i].fec || mode_set[i].target_packet_loss_rate == 0) {
206 } else if (fec == 1) {
210 lost_previous = lost_current;
211 lost_current = rand() < actual_packet_loss_rate * (RAND_MAX / 100);
212 DecodeABlock(lost_previous, lost_current);
214 time_now_ms += block_duration_ms_;
216 // |data_pointer_| is incremented and wrapped across
217 // |loop_length_samples_|.
218 data_pointer_ = (data_pointer_ + block_length_sample_ * channels_) %
219 loop_length_samples_;
221 if (mode_set[i].fec) {
222 printf("%.2f percent frames has FEC.\n",
223 static_cast<float>(fec_frames) * block_duration_ms_ / 2000);
228 const coding_param param_set[] =
229 {make_tuple(1, 64000, string("audio_coding/testfile32kHz"),
231 make_tuple(1, 32000, string("audio_coding/testfile32kHz"),
233 make_tuple(2, 64000, string("audio_coding/teststereo32kHz"),
237 INSTANTIATE_TEST_CASE_P(AllTest, OpusFecTest,
238 ValuesIn(param_set));
240 } // namespace webrtc