1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/client/audio_player.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10
11 namespace {
12
13 const int kAudioSamplesPerFrame = 25;
14 const int kAudioSampleBytes = 4;
15 const int kAudioFrameBytes = kAudioSamplesPerFrame * kAudioSampleBytes;
16 const int kPaddingBytes = 16;
17
18 // TODO(garykac): Generate random audio data in the tests rather than having
19 // a single constant value.
20 const uint8 kDefaultBufferData = 0x5A;
21 const uint8 kDummyAudioData = 0x8B;
22
23 } // namespace
24
25 namespace remoting {
26
27 class FakeAudioPlayer : public AudioPlayer {
28 public:
FakeAudioPlayer()29 FakeAudioPlayer() {
30 }
31
ResetAudioPlayer(AudioPacket::SamplingRate)32 virtual bool ResetAudioPlayer(AudioPacket::SamplingRate) OVERRIDE {
33 return true;
34 }
35
GetSamplesPerFrame()36 virtual uint32 GetSamplesPerFrame() OVERRIDE {
37 return kAudioSamplesPerFrame;
38 }
39 };
40
41 class AudioPlayerTest : public ::testing::Test {
42 protected:
SetUp()43 virtual void SetUp() {
44 audio_.reset(new FakeAudioPlayer());
45 buffer_.reset(new char[kAudioFrameBytes + kPaddingBytes]);
46 }
47
TearDown()48 virtual void TearDown() {
49 }
50
ConsumeAudioFrame()51 void ConsumeAudioFrame() {
52 uint8* buffer = reinterpret_cast<uint8*>(buffer_.get());
53 memset(buffer, kDefaultBufferData, kAudioFrameBytes + kPaddingBytes);
54 AudioPlayer::AudioPlayerCallback(reinterpret_cast<void*>(buffer_.get()),
55 kAudioFrameBytes,
56 reinterpret_cast<void*>(audio_.get()));
57 // Verify we haven't written beyond the end of the buffer.
58 for (int i = 0; i < kPaddingBytes; i++)
59 ASSERT_EQ(kDefaultBufferData, *(buffer + kAudioFrameBytes + i));
60 }
61
62 // Check that the first |num_bytes| bytes are filled with audio data and
63 // the rest of the buffer is zero-filled.
CheckAudioFrameBytes(int num_bytes)64 void CheckAudioFrameBytes(int num_bytes) {
65 uint8* buffer = reinterpret_cast<uint8*>(buffer_.get());
66 int i = 0;
67 for (; i < num_bytes; i++) {
68 ASSERT_EQ(kDummyAudioData, *(buffer + i));
69 }
70 // Rest of audio frame must be filled with '0's.
71 for (; i < kAudioFrameBytes; i++) {
72 ASSERT_EQ(0, *(buffer + i));
73 }
74 }
75
GetNumQueuedSamples()76 int GetNumQueuedSamples() {
77 return audio_->queued_bytes_ / kAudioSampleBytes;
78 }
79
GetNumQueuedPackets()80 int GetNumQueuedPackets() {
81 return static_cast<int>(audio_->queued_packets_.size());
82 }
83
GetBytesConsumed()84 int GetBytesConsumed() {
85 return static_cast<int>(audio_->bytes_consumed_);
86 }
87
88 scoped_ptr<AudioPlayer> audio_;
89 scoped_ptr<char[]> buffer_;
90 };
91
CreatePacketWithSamplingRate(AudioPacket::SamplingRate rate,int samples)92 scoped_ptr<AudioPacket> CreatePacketWithSamplingRate(
93 AudioPacket::SamplingRate rate, int samples) {
94 scoped_ptr<AudioPacket> packet(new AudioPacket());
95 packet->set_encoding(AudioPacket::ENCODING_RAW);
96 packet->set_sampling_rate(rate);
97 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
98 packet->set_channels(AudioPacket::CHANNELS_STEREO);
99
100 // The data must be a multiple of 4 bytes (channels x bytes_per_sample).
101 std::string data;
102 data.resize(samples * kAudioSampleBytes, kDummyAudioData);
103 packet->add_data(data);
104
105 return packet.Pass();
106 }
107
CreatePacket44100Hz(int samples)108 scoped_ptr<AudioPacket> CreatePacket44100Hz(int samples) {
109 return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_44100,
110 samples);
111 }
112
CreatePacket48000Hz(int samples)113 scoped_ptr<AudioPacket> CreatePacket48000Hz(int samples) {
114 return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_48000,
115 samples);
116 }
117
TEST_F(AudioPlayerTest,Init)118 TEST_F(AudioPlayerTest, Init) {
119 ASSERT_EQ(0, GetNumQueuedPackets());
120
121 scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(10));
122 audio_->ProcessAudioPacket(packet.Pass());
123 ASSERT_EQ(1, GetNumQueuedPackets());
124 }
125
TEST_F(AudioPlayerTest,MultipleSamples)126 TEST_F(AudioPlayerTest, MultipleSamples) {
127 scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10));
128 audio_->ProcessAudioPacket(packet1.Pass());
129 ASSERT_EQ(10, GetNumQueuedSamples());
130 ASSERT_EQ(1, GetNumQueuedPackets());
131
132 scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(20));
133 audio_->ProcessAudioPacket(packet2.Pass());
134 ASSERT_EQ(30, GetNumQueuedSamples());
135 ASSERT_EQ(2, GetNumQueuedPackets());
136 }
137
TEST_F(AudioPlayerTest,ChangeSampleRate)138 TEST_F(AudioPlayerTest, ChangeSampleRate) {
139 scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10));
140 audio_->ProcessAudioPacket(packet1.Pass());
141 ASSERT_EQ(10, GetNumQueuedSamples());
142 ASSERT_EQ(1, GetNumQueuedPackets());
143
144 // New packet with different sampling rate causes previous samples to
145 // be removed.
146 scoped_ptr<AudioPacket> packet2(CreatePacket48000Hz(20));
147 audio_->ProcessAudioPacket(packet2.Pass());
148 ASSERT_EQ(20, GetNumQueuedSamples());
149 ASSERT_EQ(1, GetNumQueuedPackets());
150 }
151
TEST_F(AudioPlayerTest,ExceedLatency)152 TEST_F(AudioPlayerTest, ExceedLatency) {
153 // Push about 4 seconds worth of samples.
154 for (int i = 0; i < 100; ++i) {
155 scoped_ptr<AudioPacket> packet1(CreatePacket48000Hz(2000));
156 audio_->ProcessAudioPacket(packet1.Pass());
157 }
158
159 // Verify that we don't have more than 0.5s.
160 EXPECT_LT(GetNumQueuedSamples(), 24000);
161 }
162
163 // Incoming packets: 100
164 // Consume: 25 (w/ 75 remaining, offset 25 into packet)
TEST_F(AudioPlayerTest,ConsumePartialPacket)165 TEST_F(AudioPlayerTest, ConsumePartialPacket) {
166 int total_samples = 0;
167 int bytes_consumed = 0;
168
169 // Process 100 samples.
170 int packet1_samples = 100;
171 scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(packet1_samples));
172 total_samples += packet1_samples;
173 audio_->ProcessAudioPacket(packet.Pass());
174 ASSERT_EQ(total_samples, GetNumQueuedSamples());
175 ASSERT_EQ(1, GetNumQueuedPackets());
176 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
177
178 // Consume one frame (=25) of samples.
179 ConsumeAudioFrame();
180 total_samples -= kAudioSamplesPerFrame;
181 bytes_consumed += kAudioFrameBytes;
182 ASSERT_EQ(total_samples, GetNumQueuedSamples());
183 ASSERT_EQ(1, GetNumQueuedPackets());
184 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
185 CheckAudioFrameBytes(kAudioFrameBytes);
186
187 // Remaining samples.
188 ASSERT_EQ(75, total_samples);
189 ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed);
190 }
191
192 // Incoming packets: 20, 70
193 // Consume: 25, 25 (w/ 40 remaining, offset 30 into packet)
TEST_F(AudioPlayerTest,ConsumeAcrossPackets)194 TEST_F(AudioPlayerTest, ConsumeAcrossPackets) {
195 int total_samples = 0;
196 int bytes_consumed = 0;
197
198 // Packet 1.
199 int packet1_samples = 20;
200 scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
201 total_samples += packet1_samples;
202 audio_->ProcessAudioPacket(packet1.Pass());
203 ASSERT_EQ(total_samples, GetNumQueuedSamples());
204
205 // Packet 2.
206 int packet2_samples = 70;
207 scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples));
208 total_samples += packet2_samples;
209 audio_->ProcessAudioPacket(packet2.Pass());
210 ASSERT_EQ(total_samples, GetNumQueuedSamples());
211 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
212
213 // Consume 1st frame of 25 samples.
214 // This will consume the entire 1st packet.
215 ConsumeAudioFrame();
216 total_samples -= kAudioSamplesPerFrame;
217 bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes);
218 ASSERT_EQ(total_samples, GetNumQueuedSamples());
219 ASSERT_EQ(1, GetNumQueuedPackets());
220 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
221 CheckAudioFrameBytes(kAudioFrameBytes);
222
223 // Consume 2nd frame of 25 samples.
224 ConsumeAudioFrame();
225 total_samples -= kAudioSamplesPerFrame;
226 bytes_consumed += kAudioFrameBytes;
227 ASSERT_EQ(total_samples, GetNumQueuedSamples());
228 ASSERT_EQ(1, GetNumQueuedPackets());
229 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
230 CheckAudioFrameBytes(kAudioFrameBytes);
231
232 // Remaining samples.
233 ASSERT_EQ(40, total_samples);
234 ASSERT_EQ(30 * kAudioSampleBytes, bytes_consumed);
235 }
236
237 // Incoming packets: 50, 30
238 // Consume: 25, 25, 25 (w/ 5 remaining, offset 25 into packet)
TEST_F(AudioPlayerTest,ConsumeEntirePacket)239 TEST_F(AudioPlayerTest, ConsumeEntirePacket) {
240 int total_samples = 0;
241 int bytes_consumed = 0;
242
243 // Packet 1.
244 int packet1_samples = 50;
245 scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
246 total_samples += packet1_samples;
247 audio_->ProcessAudioPacket(packet1.Pass());
248 ASSERT_EQ(total_samples, GetNumQueuedSamples());
249 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
250
251 // Packet 2.
252 int packet2_samples = 30;
253 scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples));
254 total_samples += packet2_samples;
255 audio_->ProcessAudioPacket(packet2.Pass());
256 ASSERT_EQ(total_samples, GetNumQueuedSamples());
257 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
258
259 // Consume 1st frame of 25 samples.
260 ConsumeAudioFrame();
261 total_samples -= kAudioSamplesPerFrame;
262 bytes_consumed += kAudioFrameBytes;
263 ASSERT_EQ(total_samples, GetNumQueuedSamples());
264 ASSERT_EQ(2, GetNumQueuedPackets());
265 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
266 CheckAudioFrameBytes(kAudioFrameBytes);
267
268 // Consume 2nd frame of 25 samples.
269 // This will consume the entire first packet (exactly), but the entry for
270 // this packet will stick around (empty) until the next audio chunk is
271 // consumed.
272 ConsumeAudioFrame();
273 total_samples -= kAudioSamplesPerFrame;
274 bytes_consumed += kAudioFrameBytes;
275 ASSERT_EQ(total_samples, GetNumQueuedSamples());
276 ASSERT_EQ(2, GetNumQueuedPackets());
277 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
278 CheckAudioFrameBytes(kAudioFrameBytes);
279
280 // Consume 3rd frame of 25 samples.
281 ConsumeAudioFrame();
282 total_samples -= kAudioSamplesPerFrame;
283 bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes);
284 ASSERT_EQ(total_samples, GetNumQueuedSamples());
285 ASSERT_EQ(1, GetNumQueuedPackets());
286 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
287 CheckAudioFrameBytes(kAudioFrameBytes);
288
289 // Remaining samples.
290 ASSERT_EQ(5, total_samples);
291 ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed);
292 }
293
294 // Incoming packets: <none>
295 // Consume: 25
TEST_F(AudioPlayerTest,NoDataToConsume)296 TEST_F(AudioPlayerTest, NoDataToConsume) {
297 // Attempt to consume a frame of 25 samples.
298 ConsumeAudioFrame();
299 ASSERT_EQ(0, GetNumQueuedSamples());
300 ASSERT_EQ(0, GetNumQueuedPackets());
301 ASSERT_EQ(0, GetBytesConsumed());
302 CheckAudioFrameBytes(0);
303 }
304
305 // Incoming packets: 10
306 // Consume: 25
TEST_F(AudioPlayerTest,NotEnoughDataToConsume)307 TEST_F(AudioPlayerTest, NotEnoughDataToConsume) {
308 int total_samples = 0;
309 int bytes_consumed = 0;
310
311 // Packet 1.
312 int packet1_samples = 10;
313 scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
314 total_samples += packet1_samples;
315 audio_->ProcessAudioPacket(packet1.Pass());
316 ASSERT_EQ(total_samples, GetNumQueuedSamples());
317 ASSERT_EQ(bytes_consumed, GetBytesConsumed());
318
319 // Attempt to consume a frame of 25 samples.
320 ConsumeAudioFrame();
321 ASSERT_EQ(0, GetNumQueuedSamples());
322 ASSERT_EQ(0, GetNumQueuedPackets());
323 ASSERT_EQ(0, GetBytesConsumed());
324 CheckAudioFrameBytes(packet1_samples * kAudioSampleBytes);
325 }
326
327 } // namespace remoting
328