blob: 59e4ff38a2052c2a748e307355851b6f87412eb3 [file] [log] [blame]
// 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