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