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
11namespace {
12
13const int kAudioSamplesPerFrame = 25;
14const int kAudioSampleBytes = 4;
15const int kAudioFrameBytes = kAudioSamplesPerFrame * kAudioSampleBytes;
16const int kPaddingBytes = 16;
17
18// TODO(garykac): Generate random audio data in the tests rather than having
19// a single constant value.
20const uint8 kDefaultBufferData = 0x5A;
21const uint8 kDummyAudioData = 0x8B;
22
23}  // namespace
24
25namespace remoting {
26
27class FakeAudioPlayer : public AudioPlayer {
28 public:
29  FakeAudioPlayer() {
30  }
31
32  virtual bool ResetAudioPlayer(AudioPacket::SamplingRate) OVERRIDE {
33    return true;
34  }
35
36  virtual uint32 GetSamplesPerFrame() OVERRIDE {
37    return kAudioSamplesPerFrame;
38  }
39};
40
41class AudioPlayerTest : public ::testing::Test {
42 protected:
43  virtual void SetUp() {
44    audio_.reset(new FakeAudioPlayer());
45    buffer_.reset(new char[kAudioFrameBytes + kPaddingBytes]);
46  }
47
48  virtual void TearDown() {
49  }
50
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.
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
76  int GetNumQueuedSamples() {
77    return audio_->queued_bytes_ / kAudioSampleBytes;
78  }
79
80  int GetNumQueuedPackets() {
81    return static_cast<int>(audio_->queued_packets_.size());
82  }
83
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
92scoped_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
108scoped_ptr<AudioPacket> CreatePacket44100Hz(int samples) {
109  return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_44100,
110                                      samples);
111}
112
113scoped_ptr<AudioPacket> CreatePacket48000Hz(int samples) {
114  return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_48000,
115                                      samples);
116}
117
118TEST_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
126TEST_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
138TEST_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
152TEST_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)
165TEST_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)
194TEST_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)
239TEST_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
296TEST_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
307TEST_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