| 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
|
|
|