Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3531)

Unified Diff: chromecast/media/audio/cast_audio_output_stream_unittest.cc

Issue 1308153005: [Chromecast] Plumbs raw audio through CMA backend. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed cast_shell compile error Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chromecast/media/audio/cast_audio_output_stream.cc ('k') | chromecast/media/media.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromecast/media/audio/cast_audio_output_stream_unittest.cc
diff --git a/chromecast/media/audio/cast_audio_output_stream_unittest.cc b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a1e491aa48b1d98e5852111e8da6266e96798620
--- /dev/null
+++ b/chromecast/media/audio/cast_audio_output_stream_unittest.cc
@@ -0,0 +1,495 @@
+// Copyright 2015 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 "chromecast/base/task_runner_impl.h"
+#include "chromecast/media/audio/cast_audio_manager.h"
+#include "chromecast/media/audio/cast_audio_output_stream.h"
+#include "chromecast/public/media/audio_pipeline_device.h"
+#include "chromecast/public/media/cast_decoder_buffer.h"
+#include "chromecast/public/media/decoder_config.h"
+#include "chromecast/public/media/decrypt_context.h"
+#include "chromecast/public/media/media_clock_device.h"
+#include "chromecast/public/media/media_pipeline_backend.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromecast {
+namespace media {
+namespace {
+const char kDefaultDeviceId[] = "";
+
+class FakeClockDevice : public MediaClockDevice {
+ public:
+ FakeClockDevice() : state_(kStateUninitialized), rate_(0.f) {}
+ ~FakeClockDevice() override {}
+
+ State GetState() const override { return state_; }
+ bool SetState(State new_state) override {
+ state_ = new_state;
+ return true;
+ }
+ bool ResetTimeline(int64_t time_microseconds) override { return true; }
+ bool SetRate(float rate) override {
+ rate_ = rate;
+ return true;
+ }
+ int64_t GetTimeMicroseconds() override { return 0; }
+
+ float rate() const { return rate_; }
+
+ private:
+ State state_;
+ float rate_;
+};
+
+class FakeAudioPipelineDevice : public AudioPipelineDevice {
+ public:
+ enum PipelineStatus {
+ PIPELINE_STATUS_OK,
+ PIPELINE_STATUS_BUSY,
+ PIPELINE_STATUS_ERROR
+ };
+
+ FakeAudioPipelineDevice()
+ : state_(kStateUninitialized),
+ volume_multiplier_(1.0f),
+ pipeline_status_(PIPELINE_STATUS_OK),
+ pushed_frame_count_(0) {}
+ ~FakeAudioPipelineDevice() override {}
+
+ // AudioPipelineDevice overrides.
+ void SetClient(Client* client) override {}
+ bool SetState(State new_state) override {
+ state_ = new_state;
+ return true;
+ }
+ State GetState() const override { return state_; }
+ bool SetStartPts(int64_t microseconds) override { return false; }
+ FrameStatus PushFrame(DecryptContext* decrypt_context,
+ CastDecoderBuffer* buffer,
+ FrameStatusCB* completion_cb) override {
+ last_frame_decrypt_context_.reset(decrypt_context);
+ last_frame_buffer_.reset(buffer);
+ last_frame_completion_cb_.reset(completion_cb);
+ ++pushed_frame_count_;
+
+ switch (pipeline_status_) {
+ case PIPELINE_STATUS_OK:
+ return kFrameSuccess;
+ case PIPELINE_STATUS_BUSY:
+ return kFramePending;
+ case PIPELINE_STATUS_ERROR:
+ return kFrameFailed;
+ }
+ NOTREACHED();
+ }
+ RenderingDelay GetRenderingDelay() const override { return RenderingDelay(); }
+ bool GetStatistics(Statistics* stats) const override { return false; }
+ bool SetConfig(const AudioConfig& config) override {
+ config_ = config;
+ return true;
+ }
+ void SetStreamVolumeMultiplier(float multiplier) override {
+ volume_multiplier_ = multiplier;
+ }
+
+ const AudioConfig& config() const { return config_; }
+ float volume_multiplier() const { return volume_multiplier_; }
+ void set_pipeline_status(PipelineStatus status) { pipeline_status_ = status; }
+ unsigned pushed_frame_count() const { return pushed_frame_count_; }
+ DecryptContext* last_frame_decrypt_context() {
+ return last_frame_decrypt_context_.get();
+ }
+ CastDecoderBuffer* last_frame_buffer() { return last_frame_buffer_.get(); }
+ FrameStatusCB* last_frame_completion_cb() {
+ return last_frame_completion_cb_.get();
+ }
+
+ private:
+ State state_;
+ AudioConfig config_;
+ float volume_multiplier_;
+
+ PipelineStatus pipeline_status_;
+ unsigned pushed_frame_count_;
+ scoped_ptr<DecryptContext> last_frame_decrypt_context_;
+ scoped_ptr<CastDecoderBuffer> last_frame_buffer_;
+ scoped_ptr<FrameStatusCB> last_frame_completion_cb_;
+};
+
+class FakeMediaPipelineBackend : public MediaPipelineBackend {
+ public:
+ ~FakeMediaPipelineBackend() override {}
+
+ MediaClockDevice* GetClock() override {
+ if (!clock_device_)
+ clock_device_.reset(new FakeClockDevice);
+ return clock_device_.get();
+ }
+ AudioPipelineDevice* GetAudio() override {
+ if (!audio_device_)
+ audio_device_.reset(new FakeAudioPipelineDevice);
+ return audio_device_.get();
+ }
+ VideoPipelineDevice* GetVideo() override {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ private:
+ scoped_ptr<FakeClockDevice> clock_device_;
+ scoped_ptr<FakeAudioPipelineDevice> audio_device_;
+};
+
+class FakeAudioSourceCallback
+ : public ::media::AudioOutputStream::AudioSourceCallback {
+ public:
+ FakeAudioSourceCallback() : error_(false) {}
+
+ bool error() const { return error_; }
+
+ // ::media::AudioOutputStream::AudioSourceCallback overrides.
+ int OnMoreData(::media::AudioBus* audio_bus,
+ uint32 total_bytes_delay) override {
+ audio_bus->Zero();
+ return audio_bus->frames();
+ }
+ void OnError(::media::AudioOutputStream* stream) override { error_ = true; }
+
+ private:
+ bool error_;
+};
+
+class FakeAudioManager : public CastAudioManager {
+ public:
+ FakeAudioManager()
+ : CastAudioManager(nullptr), media_pipeline_backend_(nullptr) {}
+ ~FakeAudioManager() override {}
+
+ // CastAudioManager overrides.
+ scoped_ptr<MediaPipelineBackend> CreateMediaPipelineBackend() override {
+ DCHECK(!media_pipeline_backend_);
+ scoped_ptr<FakeMediaPipelineBackend> backend(new FakeMediaPipelineBackend);
+ // Cache the backend locally to be used by tests.
+ media_pipeline_backend_ = backend.get();
+ return backend.Pass();
+ }
+ void ReleaseOutputStream(::media::AudioOutputStream* stream) override {
+ DCHECK(media_pipeline_backend_);
+ media_pipeline_backend_ = nullptr;
+ CastAudioManager::ReleaseOutputStream(stream);
+ }
+
+ // Returns the MediaPipelineBackend being used by the AudioOutputStream.
+ // Note: here is a valid MediaPipelineBackend only while the stream is open.
+ // Returns NULL at all other times.
+ FakeMediaPipelineBackend* media_pipeline_backend() {
+ return media_pipeline_backend_;
+ }
+
+ private:
+ FakeMediaPipelineBackend* media_pipeline_backend_;
+};
+
+class AudioOutputStreamTest : public ::testing::Test {
+ public:
+ AudioOutputStreamTest()
+ : format_(::media::AudioParameters::AUDIO_PCM_LINEAR),
+ channel_layout_(::media::CHANNEL_LAYOUT_MONO),
+ sample_rate_(::media::AudioParameters::kAudioCDSampleRate),
+ bits_per_sample_(16),
+ frames_per_buffer_(256) {}
+ ~AudioOutputStreamTest() override {}
+
+ protected:
+ void SetUp() override {
+ message_loop_.reset(new base::MessageLoop());
+ audio_manager_.reset(new FakeAudioManager);
+ }
+
+ void TearDown() override {
+ audio_manager_.reset();
+ message_loop_.reset();
+ }
+
+ ::media::AudioParameters GetAudioParams() {
+ return ::media::AudioParameters(format_, channel_layout_, sample_rate_,
+ bits_per_sample_, frames_per_buffer_);
+ }
+ ::media::AudioOutputStream* CreateStream() {
+ return audio_manager_->MakeAudioOutputStream(GetAudioParams(),
+ kDefaultDeviceId);
+ }
+ FakeClockDevice* GetClock() {
+ MediaPipelineBackend* backend = audio_manager_->media_pipeline_backend();
+ return backend ? static_cast<FakeClockDevice*>(backend->GetClock())
+ : nullptr;
+ }
+ FakeAudioPipelineDevice* GetAudio() {
+ MediaPipelineBackend* backend = audio_manager_->media_pipeline_backend();
+ return backend ? static_cast<FakeAudioPipelineDevice*>(backend->GetAudio())
+ : nullptr;
+ }
+
+ scoped_ptr<base::MessageLoop> message_loop_;
+ scoped_ptr<FakeAudioManager> audio_manager_;
+ scoped_ptr<TaskRunnerImpl> audio_task_runner_;
+
+ // AudioParameters used to create AudioOutputStream.
+ // Tests can modify these parameters before calling CreateStream.
+ ::media::AudioParameters::Format format_;
+ ::media::ChannelLayout channel_layout_;
+ int sample_rate_;
+ int bits_per_sample_;
+ int frames_per_buffer_;
+};
+
+TEST_F(AudioOutputStreamTest, Format) {
+ ::media::AudioParameters::Format format[] = {
+ ::media::AudioParameters::AUDIO_PCM_LINEAR,
+ ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY};
+ for (size_t i = 0; i < arraysize(format); ++i) {
+ format_ = format[i];
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ const AudioConfig& audio_config = audio_device->config();
+ EXPECT_EQ(kCodecPCM, audio_config.codec);
+ EXPECT_EQ(kSampleFormatS16, audio_config.sample_format);
+ EXPECT_FALSE(audio_config.is_encrypted);
+
+ stream->Close();
+ }
+}
+
+TEST_F(AudioOutputStreamTest, ChannelLayout) {
+ ::media::ChannelLayout layout[] = {::media::CHANNEL_LAYOUT_MONO,
+ ::media::CHANNEL_LAYOUT_STEREO};
+ for (size_t i = 0; i < arraysize(layout); ++i) {
+ channel_layout_ = layout[i];
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ const AudioConfig& audio_config = audio_device->config();
+ EXPECT_EQ(::media::ChannelLayoutToChannelCount(channel_layout_),
+ audio_config.channel_number);
+
+ stream->Close();
+ }
+}
+
+TEST_F(AudioOutputStreamTest, SampleRate) {
+ sample_rate_ = ::media::AudioParameters::kAudioCDSampleRate;
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ const AudioConfig& audio_config = audio_device->config();
+ EXPECT_EQ(sample_rate_, audio_config.samples_per_second);
+
+ stream->Close();
+}
+
+TEST_F(AudioOutputStreamTest, BitsPerSample) {
+ bits_per_sample_ = 16;
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ const AudioConfig& audio_config = audio_device->config();
+ EXPECT_EQ(bits_per_sample_ / 8, audio_config.bytes_per_channel);
+
+ stream->Close();
+}
+
+TEST_F(AudioOutputStreamTest, DeviceState) {
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_FALSE(GetAudio());
+
+ EXPECT_TRUE(stream->Open());
+ AudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ FakeClockDevice* clock_device = GetClock();
+ ASSERT_TRUE(clock_device);
+ EXPECT_EQ(AudioPipelineDevice::kStateIdle, audio_device->GetState());
+ EXPECT_EQ(MediaClockDevice::kStateIdle, clock_device->GetState());
+ EXPECT_EQ(1.f, clock_device->rate());
+
+ scoped_ptr<FakeAudioSourceCallback> source_callback(
+ new FakeAudioSourceCallback);
+ stream->Start(source_callback.get());
+ EXPECT_EQ(AudioPipelineDevice::kStateRunning, audio_device->GetState());
+ EXPECT_EQ(MediaClockDevice::kStateRunning, clock_device->GetState());
+ EXPECT_EQ(1.f, clock_device->rate());
+
+ stream->Stop();
+ EXPECT_EQ(AudioPipelineDevice::kStatePaused, audio_device->GetState());
+ EXPECT_EQ(MediaClockDevice::kStateIdle, clock_device->GetState());
+ EXPECT_EQ(0.f, clock_device->rate());
+
+ stream->Close();
+ EXPECT_FALSE(GetAudio());
+}
+
+TEST_F(AudioOutputStreamTest, PushFrame) {
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ scoped_ptr<FakeAudioSourceCallback> source_callback(
+ new FakeAudioSourceCallback);
+ stream->Start(source_callback.get());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+
+ EXPECT_EQ(0u, audio_device->pushed_frame_count());
+ EXPECT_FALSE(audio_device->last_frame_decrypt_context());
+ EXPECT_FALSE(audio_device->last_frame_buffer());
+ EXPECT_FALSE(audio_device->last_frame_completion_cb());
+
+ // Let the stream push frames.
+ message_loop_->RunUntilIdle();
+
+ EXPECT_LT(0u, audio_device->pushed_frame_count());
+ // DecryptContext is always NULL becuase of "raw" audio.
+ EXPECT_FALSE(audio_device->last_frame_decrypt_context());
+ EXPECT_TRUE(audio_device->last_frame_buffer());
+ EXPECT_TRUE(audio_device->last_frame_completion_cb());
+
+ // Verify decoder buffer.
+ ::media::AudioParameters audio_params = GetAudioParams();
+ const size_t expected_frame_size =
+ static_cast<size_t>(audio_params.GetBytesPerBuffer());
+ const CastDecoderBuffer* buffer = audio_device->last_frame_buffer();
+ EXPECT_TRUE(buffer->data());
+ EXPECT_EQ(expected_frame_size, buffer->data_size());
+ EXPECT_FALSE(buffer->decrypt_config()); // Null because of raw audio.
+ EXPECT_FALSE(buffer->end_of_stream());
+
+ // No error must be reported to source callback.
+ EXPECT_FALSE(source_callback->error());
+
+ stream->Stop();
+ stream->Close();
+}
+
+TEST_F(AudioOutputStreamTest, DeviceBusy) {
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ audio_device->set_pipeline_status(
+ FakeAudioPipelineDevice::PIPELINE_STATUS_BUSY);
+
+ scoped_ptr<FakeAudioSourceCallback> source_callback(
+ new FakeAudioSourceCallback);
+ stream->Start(source_callback.get());
+
+ // Let the stream push frames.
+ message_loop_->RunUntilIdle();
+
+ // Make sure that one frame was pushed.
+ EXPECT_EQ(1u, audio_device->pushed_frame_count());
+ // No error must be reported to source callback.
+ EXPECT_FALSE(source_callback->error());
+
+ // Sleep for a few frames so that when the message loop is drained
+ // AudioOutputStream would have the opportunity to push more frames.
+ ::media::AudioParameters audio_params = GetAudioParams();
+ base::TimeDelta pause = audio_params.GetBufferDuration() * 5;
+ base::PlatformThread::Sleep(pause);
+
+ // Let the stream attempt to push more frames.
+ message_loop_->RunUntilIdle();
+ // But since the device was busy, it must not push more frames.
+ EXPECT_EQ(1u, audio_device->pushed_frame_count());
+
+ // Unblock the pipeline and verify that PushFrame resumes.
+ audio_device->set_pipeline_status(
+ FakeAudioPipelineDevice::PIPELINE_STATUS_OK);
+ audio_device->last_frame_completion_cb()->Run(
+ MediaComponentDevice::kFrameSuccess);
+ base::PlatformThread::Sleep(pause);
+ message_loop_->RunUntilIdle();
+ EXPECT_EQ(2u, audio_device->pushed_frame_count());
+ EXPECT_FALSE(source_callback->error());
+
+ // Make the pipeline busy again, but this time send kFrameFailed.
+ audio_device->set_pipeline_status(
+ FakeAudioPipelineDevice::PIPELINE_STATUS_BUSY);
+ base::PlatformThread::Sleep(pause);
+ message_loop_->RunUntilIdle();
+ EXPECT_EQ(3u, audio_device->pushed_frame_count());
+ EXPECT_FALSE(source_callback->error());
+
+ audio_device->last_frame_completion_cb()->Run(
+ MediaComponentDevice::kFrameFailed);
+ EXPECT_TRUE(source_callback->error());
+
+ stream->Stop();
+ stream->Close();
+}
+
+TEST_F(AudioOutputStreamTest, DeviceError) {
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ EXPECT_TRUE(stream->Open());
+
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+ audio_device->set_pipeline_status(
+ FakeAudioPipelineDevice::PIPELINE_STATUS_ERROR);
+
+ scoped_ptr<FakeAudioSourceCallback> source_callback(
+ new FakeAudioSourceCallback);
+ stream->Start(source_callback.get());
+
+ // Let the stream push frames.
+ message_loop_->RunUntilIdle();
+
+ // Make sure that AudioOutputStream attempted to push the initial frame.
+ EXPECT_LT(0u, audio_device->pushed_frame_count());
+ // AudioOutputStream must report error to source callback.
+ EXPECT_TRUE(source_callback->error());
+
+ stream->Stop();
+ stream->Close();
+}
+
+TEST_F(AudioOutputStreamTest, Volume) {
+ ::media::AudioOutputStream* stream = CreateStream();
+ ASSERT_TRUE(stream);
+ ASSERT_TRUE(stream->Open());
+ FakeAudioPipelineDevice* audio_device = GetAudio();
+ ASSERT_TRUE(audio_device);
+
+ double volume = 0.0;
+ stream->GetVolume(&volume);
+ EXPECT_EQ(1.0, volume);
+ EXPECT_EQ(1.0f, audio_device->volume_multiplier());
+
+ stream->SetVolume(0.5);
+ stream->GetVolume(&volume);
+ EXPECT_EQ(0.5, volume);
+ EXPECT_EQ(0.5f, audio_device->volume_multiplier());
+
+ stream->Close();
+}
+
+} // namespace
+} // namespace media
+} // namespace chromecast
« no previous file with comments | « chromecast/media/audio/cast_audio_output_stream.cc ('k') | chromecast/media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698