Index: services/media/audio/audio_output_manager.cc |
diff --git a/services/media/audio/audio_output_manager.cc b/services/media/audio/audio_output_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..59e4ff38a2052c2a748e307355851b6f87412eb3 |
--- /dev/null |
+++ b/services/media/audio/audio_output_manager.cc |
@@ -0,0 +1,150 @@ |
+// 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 <string> |
+ |
+#include "services/media/audio/audio_output.h" |
+#include "services/media/audio/audio_output_manager.h" |
+#include "services/media/audio/audio_server_impl.h" |
+#include "services/media/audio/audio_track_to_output_link.h" |
+#include "services/media/audio/platform/generic/throttle_output.h" |
+ |
+namespace mojo { |
+namespace media { |
+namespace audio { |
+ |
+static constexpr size_t THREAD_POOL_SZ = 2; |
+static const std::string THREAD_PREFIX("AudioMixer"); |
+ |
+AudioOutputManager::AudioOutputManager(AudioServerImpl* server) |
+ : server_(server) { |
+} |
+ |
+AudioOutputManager::~AudioOutputManager() { |
+ Shutdown(); |
+ DCHECK_EQ(outputs_.size(), 0u); |
+ DCHECK(!thread_pool_); |
+} |
+ |
+MediaResult AudioOutputManager::Init() { |
+ // Step #1: Initialize the mixing thread pool. |
+ // |
+ // TODO(johngro): make the thread pool size proportional to the maximum |
+ // number of cores available in the system. |
+ // |
+ // TODO(johngro): make sure that the threads are executed at an elevated |
+ // priority, not the default priority. |
+ thread_pool_ = new base::SequencedWorkerPool(THREAD_POOL_SZ, THREAD_PREFIX); |
+ |
+ // Step #2: Instantiate all of the built-in audio output devices. |
+ // |
+ // TODO(johngro): Come up with a better way of doing this based on our |
+ // platform. Right now, we just create some hardcoded default outputs and |
+ // leave it at that. |
+ outputs_.emplace(audio::ThrottleOutput::New(this)); |
+ |
+ // Step #3: Being monitoring for plug/unplug events for pluggable audio |
+ // output devices. |
+ // |
+ // TODO(johngro): Implement step #3. Right now, the details are behind |
+ // hot-plug monitoring are TBD, so the feature is not implemented. |
+ |
+ // Step #4: Attempt to initialize each of the audio outputs we have created, |
+ // then kick off the callback engine for each of them. |
+ for (auto iter = outputs_.begin(); iter != outputs_.end(); ) { |
+ const AudioOutputPtr& output = *iter; |
+ auto tmp = iter++; |
+ DCHECK(output); |
+ |
+ // Create a sequenced task runner for this output. It will be used by the |
+ // output to schedule jobs (such as mixing) on the thread pool. |
+ scoped_refptr<base::SequencedTaskRunner> task_runner = |
+ thread_pool_->GetSequencedTaskRunnerWithShutdownBehavior( |
+ thread_pool_->GetSequenceToken(), |
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
+ |
+ MediaResult res = output->Init(output, task_runner); |
+ if (res != MediaResult::OK) { |
+ // TODO(johngro): Probably should log something about this, assuming that |
+ // the output has not already. |
+ outputs_.erase(tmp); |
+ } |
+ } |
+ |
+ return MediaResult::OK; |
+} |
+ |
+void AudioOutputManager::Shutdown() { |
+ // Are we already shutdown (or were we never successfully initialized?) |
+ if (thread_pool_ == nullptr) { |
+ DCHECK_EQ(outputs_.size(), 0u); |
+ return; |
+ } |
+ |
+ // Step #1: Stop monitoringing plug/unplug events. We are shutting down and |
+ // no longer care about outputs coming and going. |
+ // |
+ // TODO(johngro): Implement step #1. Right now, the details are behind |
+ // hot-plug monitoring are TBD, so the feature is not implemented. |
+ |
+ // Step #2: Shut down each currently active output in the system. It is |
+ // possible for this to take a bit of time as outputs release their hardware, |
+ // but it should not take long. |
+ for (const auto& output_ptr : outputs_) { |
+ output_ptr->Shutdown(); |
+ } |
+ outputs_.clear(); |
+ |
+ // Step #3: Shutdown and release our thread pool. Since we have shut down all |
+ // of our outputs, any pending tasks left in the task runner are now no-ops, |
+ // so it does not matter that the task runner is going to cancel them all |
+ // (instead of blocking) when we shut it down. |
+ thread_pool_->Shutdown(); |
+ thread_pool_ = nullptr; |
+} |
+ |
+void AudioOutputManager::ShutdownOutput(AudioOutputPtr output) { |
+ // No one should be calling this method if we have been shut down (or never |
+ // successfully started). |
+ DCHECK(thread_pool_); |
+ |
+ auto iter = outputs_.find(output); |
+ if (iter != outputs_.end()) { |
+ output->Shutdown(); |
+ outputs_.erase(iter); |
+ } |
+} |
+ |
+void AudioOutputManager::SelectOutputsForTrack(AudioTrackImplPtr track) { |
+ // TODO(johngro): Someday, base this on policy. For now, every track gets |
+ // assigned to every output in the system. |
+ DCHECK(track); |
+ |
+ // TODO(johngro): Add some way to assert that we are executing on the main |
+ // message loop thread. |
+ |
+ for (auto output : outputs_) { |
+ auto link = AudioTrackToOutputLink::New(track, output); |
+ DCHECK(output); |
+ DCHECK(link); |
+ |
+ // If we cannot add this link to the output, it's because the output is in |
+ // the process of shutting down (we didn't want to hang out with that guy |
+ // anyway) |
+ if (output->AddTrackLink(link) == MediaResult::OK) { |
+ track->AddOutput(link); |
+ } |
+ } |
+} |
+ |
+void AudioOutputManager::ScheduleMessageLoopTask( |
+ const tracked_objects::Location& from_here, |
+ const base::Closure& task) { |
+ DCHECK(server_); |
+ server_->ScheduleMessageLoopTask(from_here, task); |
+} |
+ |
+} // namespace audio |
+} // namespace media |
+} // namespace mojo |