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 "webrtc/modules/audio_coding/codecs/opus/opus_interface.h"
12 #include "webrtc/modules/audio_coding/codecs/opus/opus_inst.h"
13 #include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h"
14
15 using google::RegisterFlagValidator;
16 using google::ParseCommandLineFlags;
17 using std::string;
18 using testing::InitGoogleTest;
19
20 namespace webrtc {
21 namespace test {
22 namespace {
23
24 static const int kOpusBlockDurationMs = 20;
25 static const int kOpusSamplingKhz = 48;
26
27 // Define switch for bit rate.
ValidateBitRate(const char * flagname,int32_t value)28 static bool ValidateBitRate(const char* flagname, int32_t value) {
29 if (value >= 6 && value <= 510)
30 return true;
31 printf("Invalid bit rate, should be between 6 and 510 kbps.");
32 return false;
33 }
34
35 DEFINE_int32(bit_rate_kbps, 32, "Target bit rate (kbps).");
36
37 static const bool bit_rate_dummy =
38 RegisterFlagValidator(&FLAGS_bit_rate_kbps, &ValidateBitRate);
39
40 // Define switch for complexity.
ValidateComplexity(const char * flagname,int32_t value)41 static bool ValidateComplexity(const char* flagname, int32_t value) {
42 if (value >= -1 && value <= 10)
43 return true;
44 printf("Invalid complexity setting, should be between 0 and 10.");
45 return false;
46 }
47
48 DEFINE_int32(complexity, 10, "Complexity: 0 ~ 10 -- defined as in Opus"
49 "specification.");
50
51 static const bool complexity_dummy =
52 RegisterFlagValidator(&FLAGS_complexity, &ValidateComplexity);
53
54 // Define switch for maxplaybackrate
55 DEFINE_int32(maxplaybackrate, 48000, "Maximum playback rate (Hz).");
56
57 // Define switch for application mode.
ValidateApplication(const char * flagname,int32_t value)58 static bool ValidateApplication(const char* flagname, int32_t value) {
59 if (value != 0 && value != 1) {
60 printf("Invalid application mode, should be 0 or 1.");
61 return false;
62 }
63 return true;
64 }
65
66 DEFINE_int32(application, 0, "Application mode: 0 -- VOIP, 1 -- Audio.");
67
68 static const bool application_dummy =
69 RegisterFlagValidator(&FLAGS_application, &ValidateApplication);
70
71 // Define switch for reported packet loss rate.
ValidatePacketLossRate(const char * flagname,int32_t value)72 static bool ValidatePacketLossRate(const char* flagname, int32_t value) {
73 if (value >= 0 && value <= 100)
74 return true;
75 printf("Invalid packet loss percentile, should be between 0 and 100.");
76 return false;
77 }
78
79 DEFINE_int32(reported_loss_rate, 10, "Reported percentile of packet loss.");
80
81 static const bool reported_loss_rate_dummy =
82 RegisterFlagValidator(&FLAGS_reported_loss_rate, &ValidatePacketLossRate);
83
84 DEFINE_bool(fec, false, "Enable FEC for encoding (-nofec to disable).");
85
86 DEFINE_bool(dtx, false, "Enable DTX for encoding (-nodtx to disable).");
87
88 // Define switch for number of sub packets to repacketize.
ValidateSubPackets(const char * flagname,int32_t value)89 static bool ValidateSubPackets(const char* flagname, int32_t value) {
90 if (value >= 1 && value <= 3)
91 return true;
92 printf("Invalid number of sub packets, should be between 1 and 3.");
93 return false;
94 }
95 DEFINE_int32(sub_packets, 1, "Number of sub packets to repacketize.");
96 static const bool sub_packets_dummy =
97 RegisterFlagValidator(&FLAGS_sub_packets, &ValidateSubPackets);
98
99 } // namepsace
100
101 class NetEqOpusQualityTest : public NetEqQualityTest {
102 protected:
103 NetEqOpusQualityTest();
104 void SetUp() override;
105 void TearDown() override;
106 virtual int EncodeBlock(int16_t* in_data, size_t block_size_samples,
107 uint8_t* payload, size_t max_bytes);
108 private:
109 WebRtcOpusEncInst* opus_encoder_;
110 OpusRepacketizer* repacketizer_;
111 size_t sub_block_size_samples_;
112 int bit_rate_kbps_;
113 bool fec_;
114 bool dtx_;
115 int complexity_;
116 int maxplaybackrate_;
117 int target_loss_rate_;
118 int sub_packets_;
119 int application_;
120 };
121
NetEqOpusQualityTest()122 NetEqOpusQualityTest::NetEqOpusQualityTest()
123 : NetEqQualityTest(kOpusBlockDurationMs * FLAGS_sub_packets,
124 kOpusSamplingKhz,
125 kOpusSamplingKhz,
126 NetEqDecoder::kDecoderOpus),
127 opus_encoder_(NULL),
128 repacketizer_(NULL),
129 sub_block_size_samples_(
130 static_cast<size_t>(kOpusBlockDurationMs * kOpusSamplingKhz)),
131 bit_rate_kbps_(FLAGS_bit_rate_kbps),
132 fec_(FLAGS_fec),
133 dtx_(FLAGS_dtx),
134 complexity_(FLAGS_complexity),
135 maxplaybackrate_(FLAGS_maxplaybackrate),
136 target_loss_rate_(FLAGS_reported_loss_rate),
137 sub_packets_(FLAGS_sub_packets) {
138 // Redefine decoder type if input is stereo.
139 if (channels_ > 1) {
140 decoder_type_ = NetEqDecoder::kDecoderOpus_2ch;
141 }
142 application_ = FLAGS_application;
143 }
144
SetUp()145 void NetEqOpusQualityTest::SetUp() {
146 // Create encoder memory.
147 WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, application_);
148 ASSERT_TRUE(opus_encoder_);
149
150 // Create repacketizer.
151 repacketizer_ = opus_repacketizer_create();
152 ASSERT_TRUE(repacketizer_);
153
154 // Set bitrate.
155 EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_kbps_ * 1000));
156 if (fec_) {
157 EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_));
158 }
159 if (dtx_) {
160 EXPECT_EQ(0, WebRtcOpus_EnableDtx(opus_encoder_));
161 }
162 EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, complexity_));
163 EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_encoder_, maxplaybackrate_));
164 EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_,
165 target_loss_rate_));
166 NetEqQualityTest::SetUp();
167 }
168
TearDown()169 void NetEqOpusQualityTest::TearDown() {
170 // Free memory.
171 EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
172 opus_repacketizer_destroy(repacketizer_);
173 NetEqQualityTest::TearDown();
174 }
175
EncodeBlock(int16_t * in_data,size_t block_size_samples,uint8_t * payload,size_t max_bytes)176 int NetEqOpusQualityTest::EncodeBlock(int16_t* in_data,
177 size_t block_size_samples,
178 uint8_t* payload, size_t max_bytes) {
179 EXPECT_EQ(block_size_samples, sub_block_size_samples_ * sub_packets_);
180 int16_t* pointer = in_data;
181 int value;
182 opus_repacketizer_init(repacketizer_);
183 for (int idx = 0; idx < sub_packets_; idx++) {
184 value = WebRtcOpus_Encode(opus_encoder_, pointer, sub_block_size_samples_,
185 max_bytes, payload);
186 Log() << "Encoded a frame with Opus mode "
187 << (value == 0 ? 0 : payload[0] >> 3)
188 << std::endl;
189 if (OPUS_OK != opus_repacketizer_cat(repacketizer_, payload, value)) {
190 opus_repacketizer_init(repacketizer_);
191 // If the repacketization fails, we discard this frame.
192 return 0;
193 }
194 pointer += sub_block_size_samples_ * channels_;
195 }
196 value = opus_repacketizer_out(repacketizer_, payload,
197 static_cast<opus_int32>(max_bytes));
198 EXPECT_GE(value, 0);
199 return value;
200 }
201
TEST_F(NetEqOpusQualityTest,Test)202 TEST_F(NetEqOpusQualityTest, Test) {
203 Simulate();
204 }
205
206 } // namespace test
207 } // namespace webrtc
208