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

Unified Diff: content/renderer/media/webaudio_suspender.cc

Issue 2365723003: Break out WebAudio suspension code into new class. Add tests. (Closed)
Patch Set: 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
Index: content/renderer/media/webaudio_suspender.cc
diff --git a/content/renderer/media/webaudio_suspender.cc b/content/renderer/media/webaudio_suspender.cc
new file mode 100644
index 0000000000000000000000000000000000000000..16b98bf6783360b6b72ba3a09512505c5200efeb
--- /dev/null
+++ b/content/renderer/media/webaudio_suspender.cc
@@ -0,0 +1,116 @@
+// 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 "content/renderer/media/webaudio_suspender.h"
+
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace content {
+
+WebAudioSuspender::WebAudioSuspender(
+ media::AudioRendererSink::RenderCallback* callback,
+ const media::AudioParameters& params,
+ const scoped_refptr<media::AudioRendererSink>& sink,
+ const scoped_refptr<base::SingleThreadTaskRunner>& worker)
+ : callback_(callback),
+ params_(params),
+ sink_(sink),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ fake_sink_(worker, params_),
+ sink_transition_callback_(base::Bind(&WebAudioSuspender::TransitionSinks,
+ base::Unretained(this))) {
+ DCHECK(params_.IsValid());
+ DCHECK(sink_);
+ DCHECK(callback_);
+ DCHECK(task_runner_->BelongsToCurrentThread());
+}
+
+WebAudioSuspender::~WebAudioSuspender() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ fake_sink_.Stop();
+}
+
+int WebAudioSuspender::Render(media::AudioBus* dest,
+ uint32_t frames_delayed,
+ uint32_t frames_skipped) {
+ // 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(media::AudioBus::Create(params_));
+ dest = buffers_after_silence_.back().get();
+ }
+
+ // Drain any non-silent transitional buffers before queuing more audio data.
+ // Note: This may cause skew in the WebAudio clock if too many buffers occur.
+ if (!is_using_fake_sink_ && !buffers_after_silence_.empty()) {
+ 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_ > silent_timeout_) {
o1ka 2016/09/23 10:34:35 can be "else if"
DaleCurtis 2016/09/23 20:24:19 Not if silent timeout is 0 or negative (testing).
o1ka 2016/09/26 12:21:55 Acknowledged.
+ is_transition_pending_ = true;
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(sink_transition_callback_.callback(), true));
+ }
+ }
+
+ return dest->frames();
+}
+
+void WebAudioSuspender::OnRenderError() {
+ callback_->OnRenderError();
+}
+
+void WebAudioSuspender::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 && !is_using_fake_sink_)
+ return;
+
+ // It's safe to modify |is_using_fake_sink_| in between Pause() and
+ // Play() since no sink is actively calling into Render() during that time.
o1ka 2016/09/23 10:34:35 ARS interface does not guarantee that Pause() is s
DaleCurtis 2016/09/23 20:24:19 Ah thanks for pointing that out. I'd forgotten. Ad
+ if (use_fake_sink) {
+ sink_->Pause();
+ is_transition_pending_ = false;
+ is_using_fake_sink_ = true;
o1ka 2016/09/23 10:34:34 The above means that these two flags may be concur
DaleCurtis 2016/09/23 20:24:19 Locked!
+ fake_sink_.Start(base::Bind(base::IgnoreResult(&WebAudioSuspender::Render),
+ base::Unretained(this), nullptr, 0, 0));
+ } else {
+ fake_sink_.Stop();
+ is_transition_pending_ = false;
+ is_using_fake_sink_ = false;
+ sink_->Play();
+ }
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698