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
17 using ::std::string;
18 using ::std::tr1::tuple;
19 using ::std::tr1::get;
20 using ::testing::TestWithParam;
21
22 namespace webrtc {
23
24 // Define coding parameter as <channels, bit_rate, filename, extension>.
25 typedef tuple<size_t, int, string, string> coding_param;
26 typedef struct mode mode;
27
28 struct mode {
29 bool fec;
30 uint8_t target_packet_loss_rate;
31 };
32
33 const int kOpusBlockDurationMs = 20;
34 const int kOpusSamplingKhz = 48;
35
36 class 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
SetUp()69 void 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
TearDown()117 void OpusFecTest::TearDown() {
118 // Free memory.
119 EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
120 EXPECT_EQ(0, WebRtcOpus_DecoderFree(opus_decoder_));
121 }
122
OpusFecTest()123 OpusFecTest::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
EncodeABlock()135 void 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
DecodeABlock(bool lost_previous,bool lost_current)145 void 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
TEST_P(OpusFecTest,RandomPacketLossTest)170 TEST_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
229 const 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
238 INSTANTIATE_TEST_CASE_P(AllTest, OpusFecTest,
239 ::testing::ValuesIn(param_set));
240
241 } // namespace webrtc
242