| Index: services/media/audio/audio_output.cc
|
| diff --git a/services/media/audio/audio_output.cc b/services/media/audio/audio_output.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..be2b89556d7ffb242000b18660b01bf3c97c15b3
|
| --- /dev/null
|
| +++ b/services/media/audio/audio_output.cc
|
| @@ -0,0 +1,215 @@
|
| +// 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 "base/bind.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| +
|
| +#include "services/media/audio/audio_output.h"
|
| +#include "services/media/audio/audio_output_manager.h"
|
| +#include "services/media/audio/audio_track_to_output_link.h"
|
| +
|
| +namespace mojo {
|
| +namespace media {
|
| +namespace audio {
|
| +
|
| +// Function used in a base::Closure to defer the final part of a shutdown task
|
| +// to the main event loop.
|
| +static void FinishShutdownSelf(AudioOutputManager* manager,
|
| + AudioOutputWeakPtr weak_output) {
|
| + auto output = weak_output.lock();
|
| + if (output) {
|
| + manager->ShutdownOutput(output);
|
| + }
|
| +}
|
| +
|
| +AudioOutput::AudioOutput(AudioOutputManager* manager)
|
| + : manager_(manager) {
|
| + DCHECK(manager_);
|
| +}
|
| +
|
| +AudioOutput::~AudioOutput() {
|
| + DCHECK(!task_runner_ && shutting_down_);
|
| +}
|
| +
|
| +MediaResult AudioOutput::AddTrackLink(AudioTrackToOutputLinkPtr link) {
|
| + MediaResult res = InitializeLink(link);
|
| +
|
| + if (res == MediaResult::OK) {
|
| + base::AutoLock lock(processing_lock_);
|
| +
|
| + // Assert that we are the output in this link.
|
| + DCHECK(this == link->GetOutput().get());
|
| +
|
| + if (shutting_down_) {
|
| + return MediaResult::SHUTTING_DOWN;
|
| + }
|
| +
|
| + auto insert_result = links_.emplace(link);
|
| + DCHECK(insert_result.second);
|
| + } else {
|
| + // TODO(johngro): Output didn't like this track for some reason... Should
|
| + // probably log something about this.
|
| + }
|
| +
|
| + return res;
|
| +}
|
| +
|
| +MediaResult AudioOutput::RemoveTrackLink(
|
| + const AudioTrackToOutputLinkPtr& link) {
|
| + base::AutoLock lock(processing_lock_);
|
| +
|
| + if (shutting_down_) {
|
| + return MediaResult::SHUTTING_DOWN;
|
| + }
|
| +
|
| + auto iter = links_.find(link);
|
| + if (iter == links_.end()) {
|
| + return MediaResult::NOT_FOUND;
|
| + }
|
| +
|
| + links_.erase(iter);
|
| + return MediaResult::OK;
|
| +}
|
| +
|
| +MediaResult AudioOutput::Init() {
|
| + return MediaResult::OK;
|
| +}
|
| +
|
| +void AudioOutput::Cleanup() {
|
| +}
|
| +
|
| +MediaResult AudioOutput::InitializeLink(const AudioTrackToOutputLinkPtr& link) {
|
| + DCHECK(link);
|
| + return MediaResult::OK;
|
| +}
|
| +
|
| +void AudioOutput::ScheduleCallback(LocalTime when) {
|
| + base::AutoLock lock(shutdown_lock_);
|
| +
|
| + // If we are in the process of shutting down, then we are no longer permitted
|
| + // to schedule callbacks.
|
| + if (shutting_down_) {
|
| + DCHECK(!task_runner_);
|
| + return;
|
| + }
|
| + DCHECK(task_runner_);
|
| +
|
| + // TODO(johngro): Someday, if there is ever a way to schedule delayed tasks
|
| + // with absolute time, or with resolution better than microseconds, do so.
|
| + // Until then figure out the relative time for scheduling the task and do so.
|
| + LocalTime now = LocalClock::now();
|
| + base::TimeDelta sched_time = (now > when)
|
| + ? base::TimeDelta::FromMicroseconds(0)
|
| + : base::TimeDelta::FromMicroseconds(
|
| + local_time::to_usec<int64_t>(when - now));
|
| +
|
| + task_runner_->PostNonNestableDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&ProcessThunk, weak_self_),
|
| + sched_time);
|
| +}
|
| +
|
| +void AudioOutput::ShutdownSelf() {
|
| + // If we are not already in the process of shutting down, send a message to
|
| + // the main message loop telling it to complete the shutdown process.
|
| + if (!BeginShutdown()) {
|
| + DCHECK(manager_);
|
| + manager_->ScheduleMessageLoopTask(
|
| + FROM_HERE,
|
| + base::Bind(&FinishShutdownSelf, manager_, weak_self_));
|
| + }
|
| +}
|
| +
|
| +void AudioOutput::ProcessThunk(AudioOutputWeakPtr weak_output) {
|
| + // If we are still around by the time this callback fires, enter the procesing
|
| + // lock and dispatch to our derived class's implementation.
|
| + auto output = weak_output.lock();
|
| + if (output) {
|
| + base::AutoLock lock(output->processing_lock_);
|
| + output->Process();
|
| + }
|
| +}
|
| +
|
| +MediaResult AudioOutput::Init(
|
| + const AudioOutputPtr& self,
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner) {
|
| + DCHECK(this == self.get());
|
| + DCHECK(task_runner);
|
| +
|
| + // If our derived class failed to initialize, don't bother to hold onto the
|
| + // state we will need drive our callback engine. Begin the process of
|
| + // shutting ourselves down, the output manager will eventually finish the job
|
| + // for us.
|
| + MediaResult res = Init();
|
| + if (res != MediaResult::OK) {
|
| + ShutdownSelf();
|
| + return res;
|
| + }
|
| +
|
| + // Stash our callback state and schedule an immediate callback to get things
|
| + // running.
|
| + task_runner_ = task_runner;
|
| + weak_self_ = self;
|
| + task_runner_->PostNonNestableTask(FROM_HERE,
|
| + base::Bind(&ProcessThunk, weak_self_));
|
| +
|
| + return MediaResult::OK;
|
| +}
|
| +
|
| +bool AudioOutput::BeginShutdown() {
|
| + // Start the process of shutting down if we have not already. This method may
|
| + // be called from either a processing context, or from the audio output
|
| + // manager. After it finishes, any pending processing callbacks will have
|
| + // been nerfed, although there may still be callbacks in flight.
|
| + base::AutoLock lock(shutdown_lock_);
|
| +
|
| + if (shutting_down_) { return true; }
|
| +
|
| + shutting_down_ = true;
|
| + task_runner_ = nullptr;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void AudioOutput::Shutdown() {
|
| + if (shut_down_) { return; }
|
| +
|
| + // TODO(johngro): Assert that we are running on the audio server's main
|
| + // message loop thread.
|
| +
|
| + // Make sure no new callbacks can be generated, and that pending callbacks
|
| + // have been nerfed.
|
| + BeginShutdown();
|
| +
|
| + // Synchronize with any callbacks in flight. By acquiring and releasing the
|
| + // processing lock, we are guaranteed that we have no callbacks which are in
|
| + // the middle of processing, and that any pending callbacks will be nerfed.
|
| + // It is safe to destroy this audio output at any point in time after this.
|
| + processing_lock_.Acquire();
|
| + processing_lock_.Release();
|
| +
|
| + // Unlink ourselves from all of our tracks. Then go ahead and clear the track
|
| + // set.
|
| + for (const auto& link : links_) {
|
| + DCHECK(link);
|
| + AudioTrackImplPtr track = link->GetTrack();
|
| + if (track) {
|
| + track->RemoveOutput(link);
|
| + }
|
| + }
|
| + links_.clear();
|
| +
|
| + // Give our derived class a chance to clean up its resources.
|
| + Cleanup();
|
| +
|
| + // We are now completely shut down. The only reason we have this flag is to
|
| + // make sure that Shutdown is idempotent.
|
| + shut_down_ = true;
|
| +}
|
| +
|
| +} // namespace audio
|
| +} // namespace media
|
| +} // namespace mojo
|
| +
|
|
|