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