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

Side by Side 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, 2 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/renderer/media/webaudio_suspender.h"
6
7 #include "base/single_thread_task_runner.h"
8 #include "base/threading/thread_task_runner_handle.h"
9
10 namespace content {
11
12 WebAudioSuspender::WebAudioSuspender(
13 media::AudioRendererSink::RenderCallback* callback,
14 const media::AudioParameters& params,
15 const scoped_refptr<media::AudioRendererSink>& sink,
16 const scoped_refptr<base::SingleThreadTaskRunner>& worker)
17 : callback_(callback),
18 params_(params),
19 sink_(sink),
20 task_runner_(base::ThreadTaskRunnerHandle::Get()),
21 fake_sink_(worker, params_),
22 sink_transition_callback_(base::Bind(&WebAudioSuspender::TransitionSinks,
23 base::Unretained(this))) {
24 DCHECK(params_.IsValid());
25 DCHECK(sink_);
26 DCHECK(callback_);
27 DCHECK(task_runner_->BelongsToCurrentThread());
28 }
29
30 WebAudioSuspender::~WebAudioSuspender() {
31 DCHECK(task_runner_->BelongsToCurrentThread());
32 fake_sink_.Stop();
33 }
34
35 int WebAudioSuspender::Render(media::AudioBus* dest,
36 uint32_t frames_delayed,
37 uint32_t frames_skipped) {
38 // When we're using the |fake_sink_| a null destination will be sent; we store
39 // the audio data for a future transition out of silence.
40 if (!dest) {
41 DCHECK(is_using_fake_sink_);
42 DCHECK_EQ(frames_delayed, 0u);
43 DCHECK_EQ(frames_skipped, 0u);
44 // If we have no buffers or a transition is pending, one or more extra
45 // Render() calls have occurred in before TransitionSinks() can run, so we
46 // store this data for the eventual transition.
47 if (buffers_after_silence_.empty() || is_transition_pending_)
48 buffers_after_silence_.push_back(media::AudioBus::Create(params_));
49 dest = buffers_after_silence_.back().get();
50 }
51
52 // Drain any non-silent transitional buffers before queuing more audio data.
53 // Note: This may cause skew in the WebAudio clock if too many buffers occur.
54 if (!is_using_fake_sink_ && !buffers_after_silence_.empty()) {
55 buffers_after_silence_.front()->CopyTo(dest);
56 buffers_after_silence_.pop_front();
57 return dest->frames();
58 }
59
60 // Pass-through to client and request rendering.
61 callback_->Render(dest, frames_delayed, frames_skipped);
62
63 // Check for silence or real audio data and transition if necessary.
64 if (!dest->AreFramesZero()) {
65 first_silence_time_ = base::TimeTicks();
66 if (is_using_fake_sink_) {
67 is_transition_pending_ = true;
68 task_runner_->PostTask(
69 FROM_HERE, base::Bind(sink_transition_callback_.callback(), false));
70 return dest->frames();
71 }
72 } else if (!is_using_fake_sink_) {
73 const base::TimeTicks now = base::TimeTicks::Now();
74 if (first_silence_time_.is_null())
75 first_silence_time_ = now;
76 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.
77 is_transition_pending_ = true;
78 task_runner_->PostTask(
79 FROM_HERE, base::Bind(sink_transition_callback_.callback(), true));
80 }
81 }
82
83 return dest->frames();
84 }
85
86 void WebAudioSuspender::OnRenderError() {
87 callback_->OnRenderError();
88 }
89
90 void WebAudioSuspender::TransitionSinks(bool use_fake_sink) {
91 DCHECK(task_runner_->BelongsToCurrentThread());
92
93 // Ignore duplicate requests which can occur if the transition takes too long
94 // and multiple Render() events occur.
95 if (use_fake_sink && is_using_fake_sink_)
96 return;
97 if (!use_fake_sink && !is_using_fake_sink_)
98 return;
99
100 // It's safe to modify |is_using_fake_sink_| in between Pause() and
101 // 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
102 if (use_fake_sink) {
103 sink_->Pause();
104 is_transition_pending_ = false;
105 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!
106 fake_sink_.Start(base::Bind(base::IgnoreResult(&WebAudioSuspender::Render),
107 base::Unretained(this), nullptr, 0, 0));
108 } else {
109 fake_sink_.Stop();
110 is_transition_pending_ = false;
111 is_using_fake_sink_ = false;
112 sink_->Play();
113 }
114 }
115
116 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698