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

Unified Diff: media/base/silent_sink_suspender.cc

Issue 2382473002: Merge M54: "Break out WebAudio suspension code into new class. Add tests." (Closed)
Patch Set: Fix conflicts. Created 4 years, 3 months 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 | « media/base/silent_sink_suspender.h ('k') | media/base/silent_sink_suspender_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/base/silent_sink_suspender.cc
diff --git a/media/base/silent_sink_suspender.cc b/media/base/silent_sink_suspender.cc
new file mode 100644
index 0000000000000000000000000000000000000000..70b9266ee8abcbef21e860f73dd1554eb45333df
--- /dev/null
+++ b/media/base/silent_sink_suspender.cc
@@ -0,0 +1,143 @@
+// Copyright 2016 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 "media/base/silent_sink_suspender.h"
+
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace media {
+
+SilentSinkSuspender::SilentSinkSuspender(
+ AudioRendererSink::RenderCallback* callback,
+ base::TimeDelta silence_timeout,
+ const AudioParameters& params,
+ const scoped_refptr<AudioRendererSink>& sink,
+ const scoped_refptr<base::SingleThreadTaskRunner>& worker)
+ : callback_(callback),
+ params_(params),
+ sink_(sink),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ silence_timeout_(silence_timeout),
+ fake_sink_(worker, params_),
+ sink_transition_callback_(
+ base::Bind(&SilentSinkSuspender::TransitionSinks,
+ base::Unretained(this))) {
+ DCHECK(params_.IsValid());
+ DCHECK(sink_);
+ DCHECK(callback_);
+ DCHECK(task_runner_->BelongsToCurrentThread());
+}
+
+SilentSinkSuspender::~SilentSinkSuspender() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ fake_sink_.Stop();
+}
+
+int SilentSinkSuspender::Render(AudioBus* dest,
+ uint32_t frames_delayed,
+ uint32_t frames_skipped) {
+ // Lock required since AudioRendererSink::Pause() is not synchronous, we need
+ // to discard these calls during the transition to the fake sink.
+ base::AutoLock al(transition_lock_);
+ if (is_using_fake_sink_ && dest) {
+ // Audio should be silent at this point, if not, it will be handled once the
+ // transition to the fake sink is complete.
+ dest->Zero();
+ return dest->frames();
+ }
+
+ // When we're using the |fake_sink_| a null destination will be sent; we store
+ // the audio data for a future transition out of silence.
+ if (!dest) {
+ DCHECK(is_using_fake_sink_);
+ DCHECK_EQ(frames_delayed, 0u);
+ DCHECK_EQ(frames_skipped, 0u);
+
+ // If we have no buffers or a transition is pending, one or more extra
+ // Render() calls have occurred in before TransitionSinks() can run, so we
+ // store this data for the eventual transition.
+ if (buffers_after_silence_.empty() || is_transition_pending_)
+ buffers_after_silence_.push_back(AudioBus::Create(params_));
+ dest = buffers_after_silence_.back().get();
+ } else if (!buffers_after_silence_.empty()) {
+ // Drain any non-silent transitional buffers before queuing more audio data.
+ // Note: These do not skew clocks derived from frame count since we don't
+ // issue Render() to the client when returning these buffers.
+ DCHECK(!is_using_fake_sink_);
+ buffers_after_silence_.front()->CopyTo(dest);
+ buffers_after_silence_.pop_front();
+ return dest->frames();
+ }
+
+ // Pass-through to client and request rendering.
+ callback_->Render(dest, frames_delayed, frames_skipped);
+
+ // Check for silence or real audio data and transition if necessary.
+ if (!dest->AreFramesZero()) {
+ first_silence_time_ = base::TimeTicks();
+ if (is_using_fake_sink_) {
+ is_transition_pending_ = true;
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(sink_transition_callback_.callback(), false));
+ return dest->frames();
+ }
+ } else if (!is_using_fake_sink_) {
+ const base::TimeTicks now = base::TimeTicks::Now();
+ if (first_silence_time_.is_null())
+ first_silence_time_ = now;
+ if (now - first_silence_time_ > silence_timeout_) {
+ is_transition_pending_ = true;
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(sink_transition_callback_.callback(), true));
+ }
+ }
+
+ return dest->frames();
+}
+
+void SilentSinkSuspender::OnRenderError() {
+ callback_->OnRenderError();
+}
+
+void SilentSinkSuspender::TransitionSinks(bool use_fake_sink) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ // Ignore duplicate requests which can occur if the transition takes too long
+ // and multiple Render() events occur.
+ if (use_fake_sink == is_using_fake_sink_)
+ return;
+
+ if (use_fake_sink) {
+ sink_->Pause();
+
+ // |sink_| may still be executing Render() at this point or even sometime
+ // after this point, so we must acquire the lock to make sure we don't have
+ // concurrent Render() execution. Once |is_using_fake_sink_| is set to true,
+ // calls from |sink_| will be dropped.
+ {
+ base::AutoLock al(transition_lock_);
+ is_transition_pending_ = false;
+ is_using_fake_sink_ = true;
+ }
+ fake_sink_.Start(
+ base::Bind(base::IgnoreResult(&SilentSinkSuspender::Render),
+ base::Unretained(this), nullptr, 0, 0));
+ } else {
+ fake_sink_.Stop();
+
+ // Despite the fake sink having a synchronous Stop(), if this transition
+ // occurs too soon after pausing the real sink, we may have pending Render()
+ // calls from before the transition to the fake sink. As such, we need to
+ // hold the lock here to avoid any races.
+ {
+ base::AutoLock al(transition_lock_);
+ is_transition_pending_ = false;
+ is_using_fake_sink_ = false;
+ }
+ sink_->Play();
+ }
+}
+
+} // namespace content
« no previous file with comments | « media/base/silent_sink_suspender.h ('k') | media/base/silent_sink_suspender_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698