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

Unified Diff: services/media/audio/audio_output.cc

Issue 1424933002: Add an initial revision of an audio server. (Closed) Base URL: https://github.com/domokit/mojo.git@change4
Patch Set: fix issues discovered with initial preflight Created 5 years, 1 month 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 | « services/media/audio/audio_output.h ('k') | services/media/audio/audio_output_manager.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
+
« no previous file with comments | « services/media/audio/audio_output.h ('k') | services/media/audio/audio_output_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698