root/remoting/client/audio_player_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ResetAudioPlayer
  2. GetSamplesPerFrame
  3. SetUp
  4. TearDown
  5. ConsumeAudioFrame
  6. CheckAudioFrameBytes
  7. GetNumQueuedSamples
  8. GetNumQueuedPackets
  9. GetBytesConsumed
  10. CreatePacketWithSamplingRate
  11. CreatePacket44100Hz
  12. CreatePacket48000Hz
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/client/audio_player.h"

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const int kAudioSamplesPerFrame = 25;
const int kAudioSampleBytes = 4;
const int kAudioFrameBytes = kAudioSamplesPerFrame * kAudioSampleBytes;
const int kPaddingBytes = 16;

// TODO(garykac): Generate random audio data in the tests rather than having
// a single constant value.
const uint8 kDefaultBufferData = 0x5A;
const uint8 kDummyAudioData = 0x8B;

}  // namespace

namespace remoting {

class FakeAudioPlayer : public AudioPlayer {
 public:
  FakeAudioPlayer() {
  }

  virtual bool ResetAudioPlayer(AudioPacket::SamplingRate) OVERRIDE {
    return true;
  }

  virtual uint32 GetSamplesPerFrame() OVERRIDE {
    return kAudioSamplesPerFrame;
  }
};

class AudioPlayerTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    audio_.reset(new FakeAudioPlayer());
    buffer_.reset(new char[kAudioFrameBytes + kPaddingBytes]);
  }

  virtual void TearDown() {
  }

  void ConsumeAudioFrame() {
    uint8* buffer = reinterpret_cast<uint8*>(buffer_.get());
    memset(buffer, kDefaultBufferData, kAudioFrameBytes + kPaddingBytes);
    AudioPlayer::AudioPlayerCallback(reinterpret_cast<void*>(buffer_.get()),
                                     kAudioFrameBytes,
                                     reinterpret_cast<void*>(audio_.get()));
    // Verify we haven't written beyond the end of the buffer.
    for (int i = 0; i < kPaddingBytes; i++)
      ASSERT_EQ(kDefaultBufferData, *(buffer + kAudioFrameBytes + i));
  }

  // Check that the first |num_bytes| bytes are filled with audio data and
  // the rest of the buffer is zero-filled.
  void CheckAudioFrameBytes(int num_bytes) {
    uint8* buffer = reinterpret_cast<uint8*>(buffer_.get());
    int i = 0;
    for (; i < num_bytes; i++) {
      ASSERT_EQ(kDummyAudioData, *(buffer + i));
    }
    // Rest of audio frame must be filled with '0's.
    for (; i < kAudioFrameBytes; i++) {
      ASSERT_EQ(0, *(buffer + i));
    }
  }

  int GetNumQueuedSamples() {
    return audio_->queued_bytes_ / kAudioSampleBytes;
  }

  int GetNumQueuedPackets() {
    return static_cast<int>(audio_->queued_packets_.size());
  }

  int GetBytesConsumed() {
    return static_cast<int>(audio_->bytes_consumed_);
  }

  scoped_ptr<AudioPlayer> audio_;
  scoped_ptr<char[]> buffer_;
};

scoped_ptr<AudioPacket> CreatePacketWithSamplingRate(
      AudioPacket::SamplingRate rate, int samples) {
  scoped_ptr<AudioPacket> packet(new AudioPacket());
  packet->set_encoding(AudioPacket::ENCODING_RAW);
  packet->set_sampling_rate(rate);
  packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
  packet->set_channels(AudioPacket::CHANNELS_STEREO);

  // The data must be a multiple of 4 bytes (channels x bytes_per_sample).
  std::string data;
  data.resize(samples * kAudioSampleBytes, kDummyAudioData);
  packet->add_data(data);

  return packet.Pass();
}

scoped_ptr<AudioPacket> CreatePacket44100Hz(int samples) {
  return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_44100,
                                      samples);
}

scoped_ptr<AudioPacket> CreatePacket48000Hz(int samples) {
  return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_48000,
                                      samples);
}

TEST_F(AudioPlayerTest, Init) {
  ASSERT_EQ(0, GetNumQueuedPackets());

  scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(10));
  audio_->ProcessAudioPacket(packet.Pass());
  ASSERT_EQ(1, GetNumQueuedPackets());
}

TEST_F(AudioPlayerTest, MultipleSamples) {
  scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10));
  audio_->ProcessAudioPacket(packet1.Pass());
  ASSERT_EQ(10, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());

  scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(20));
  audio_->ProcessAudioPacket(packet2.Pass());
  ASSERT_EQ(30, GetNumQueuedSamples());
  ASSERT_EQ(2, GetNumQueuedPackets());
}

TEST_F(AudioPlayerTest, ChangeSampleRate) {
  scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10));
  audio_->ProcessAudioPacket(packet1.Pass());
  ASSERT_EQ(10, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());

  // New packet with different sampling rate causes previous samples to
  // be removed.
  scoped_ptr<AudioPacket> packet2(CreatePacket48000Hz(20));
  audio_->ProcessAudioPacket(packet2.Pass());
  ASSERT_EQ(20, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
}

TEST_F(AudioPlayerTest, ExceedLatency) {
  // Push about 4 seconds worth of samples.
  for (int i = 0; i < 100; ++i) {
    scoped_ptr<AudioPacket> packet1(CreatePacket48000Hz(2000));
    audio_->ProcessAudioPacket(packet1.Pass());
  }

  // Verify that we don't have more than 0.5s.
  EXPECT_LT(GetNumQueuedSamples(), 24000);
}

// Incoming packets: 100
// Consume: 25 (w/ 75 remaining, offset 25 into packet)
TEST_F(AudioPlayerTest, ConsumePartialPacket) {
  int total_samples = 0;
  int bytes_consumed = 0;

  // Process 100 samples.
  int packet1_samples = 100;
  scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(packet1_samples));
  total_samples += packet1_samples;
  audio_->ProcessAudioPacket(packet.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());

  // Consume one frame (=25) of samples.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes;
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Remaining samples.
  ASSERT_EQ(75, total_samples);
  ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed);
}

// Incoming packets: 20, 70
// Consume: 25, 25 (w/ 40 remaining, offset 30 into packet)
TEST_F(AudioPlayerTest, ConsumeAcrossPackets) {
  int total_samples = 0;
  int bytes_consumed = 0;

  // Packet 1.
  int packet1_samples = 20;
  scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
  total_samples += packet1_samples;
  audio_->ProcessAudioPacket(packet1.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());

  // Packet 2.
  int packet2_samples = 70;
  scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples));
  total_samples += packet2_samples;
  audio_->ProcessAudioPacket(packet2.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());

  // Consume 1st frame of 25 samples.
  // This will consume the entire 1st packet.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes);
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Consume 2nd frame of 25 samples.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes;
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Remaining samples.
  ASSERT_EQ(40, total_samples);
  ASSERT_EQ(30 * kAudioSampleBytes, bytes_consumed);
}

// Incoming packets: 50, 30
// Consume: 25, 25, 25 (w/ 5 remaining, offset 25 into packet)
TEST_F(AudioPlayerTest, ConsumeEntirePacket) {
  int total_samples = 0;
  int bytes_consumed = 0;

  // Packet 1.
  int packet1_samples = 50;
  scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
  total_samples += packet1_samples;
  audio_->ProcessAudioPacket(packet1.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());

  // Packet 2.
  int packet2_samples = 30;
  scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples));
  total_samples += packet2_samples;
  audio_->ProcessAudioPacket(packet2.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());

  // Consume 1st frame of 25 samples.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes;
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(2, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Consume 2nd frame of 25 samples.
  // This will consume the entire first packet (exactly), but the entry for
  // this packet will stick around (empty) until the next audio chunk is
  // consumed.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes;
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(2, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Consume 3rd frame of 25 samples.
  ConsumeAudioFrame();
  total_samples -= kAudioSamplesPerFrame;
  bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes);
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(1, GetNumQueuedPackets());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());
  CheckAudioFrameBytes(kAudioFrameBytes);

  // Remaining samples.
  ASSERT_EQ(5, total_samples);
  ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed);
}

// Incoming packets: <none>
// Consume: 25
TEST_F(AudioPlayerTest, NoDataToConsume) {
  // Attempt to consume a frame of 25 samples.
  ConsumeAudioFrame();
  ASSERT_EQ(0, GetNumQueuedSamples());
  ASSERT_EQ(0, GetNumQueuedPackets());
  ASSERT_EQ(0, GetBytesConsumed());
  CheckAudioFrameBytes(0);
}

// Incoming packets: 10
// Consume: 25
TEST_F(AudioPlayerTest, NotEnoughDataToConsume) {
  int total_samples = 0;
  int bytes_consumed = 0;

  // Packet 1.
  int packet1_samples = 10;
  scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples));
  total_samples += packet1_samples;
  audio_->ProcessAudioPacket(packet1.Pass());
  ASSERT_EQ(total_samples, GetNumQueuedSamples());
  ASSERT_EQ(bytes_consumed, GetBytesConsumed());

  // Attempt to consume a frame of 25 samples.
  ConsumeAudioFrame();
  ASSERT_EQ(0, GetNumQueuedSamples());
  ASSERT_EQ(0, GetNumQueuedPackets());
  ASSERT_EQ(0, GetBytesConsumed());
  CheckAudioFrameBytes(packet1_samples * kAudioSampleBytes);
}

}  // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */