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

Side by Side Diff: content/renderer/media/renderer_webaudiodevice_impl.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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/renderer_webaudiodevice_impl.h" 5 #include "content/renderer/media/renderer_webaudiodevice_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <string> 9 #include <string>
10 10
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "base/time/time.h" 13 #include "base/time/time.h"
16 #include "build/build_config.h" 14 #include "build/build_config.h"
17 #include "content/renderer/media/audio_device_factory.h" 15 #include "content/renderer/media/audio_device_factory.h"
16 #include "content/renderer/media/webaudio_suspender.h"
18 #include "content/renderer/render_frame_impl.h" 17 #include "content/renderer/render_frame_impl.h"
19 #include "media/audio/null_audio_sink.h" 18 #include "content/renderer/render_thread_impl.h"
20 #include "media/base/media_switches.h"
21 #include "third_party/WebKit/public/web/WebLocalFrame.h" 19 #include "third_party/WebKit/public/web/WebLocalFrame.h"
22 #include "third_party/WebKit/public/web/WebView.h" 20 #include "third_party/WebKit/public/web/WebView.h"
23 21
24 using blink::WebAudioDevice; 22 using blink::WebAudioDevice;
25 using blink::WebLocalFrame; 23 using blink::WebLocalFrame;
26 using blink::WebVector; 24 using blink::WebVector;
27 using blink::WebView; 25 using blink::WebView;
28 26
29 namespace content { 27 namespace content {
30 28
31 #if defined(OS_ANDROID)
32 static const int kSilenceInSecondsToEnterIdleMode = 30;
33 #endif
34
35 RendererWebAudioDeviceImpl::RendererWebAudioDeviceImpl( 29 RendererWebAudioDeviceImpl::RendererWebAudioDeviceImpl(
36 const media::AudioParameters& params, 30 const media::AudioParameters& params,
37 WebAudioDevice::RenderCallback* callback, 31 WebAudioDevice::RenderCallback* callback,
38 int session_id, 32 int session_id,
39 const url::Origin& security_origin) 33 const url::Origin& security_origin)
40 : params_(params), 34 : params_(params),
41 client_callback_(callback), 35 client_callback_(callback),
42 sink_is_running_(static_cast<base::AtomicRefCount>(0)),
43 session_id_(session_id), 36 session_id_(session_id),
44 task_runner_(base::ThreadTaskRunnerHandle::Get()),
45 null_audio_sink_(new media::NullAudioSink(task_runner_)),
46 is_using_null_audio_sink_(false),
47 first_buffer_after_silence_(media::AudioBus::Create(params_)),
48 is_first_buffer_after_silence_(false),
49 security_origin_(security_origin) { 37 security_origin_(security_origin) {
50 DCHECK(client_callback_); 38 DCHECK(client_callback_);
51 null_audio_sink_->Initialize(params_, this);
52 null_audio_sink_->Start();
53 } 39 }
54 40
55 RendererWebAudioDeviceImpl::~RendererWebAudioDeviceImpl() { 41 RendererWebAudioDeviceImpl::~RendererWebAudioDeviceImpl() {
56 DCHECK(!sink_); 42 DCHECK(!sink_);
57 } 43 }
58 44
59 void RendererWebAudioDeviceImpl::start() { 45 void RendererWebAudioDeviceImpl::start() {
60 DCHECK(thread_checker_.CalledOnValidThread()); 46 DCHECK(thread_checker_.CalledOnValidThread());
61 47
62 if (sink_) 48 if (sink_)
(...skipping 12 matching lines...) Expand all
75 sink_ = AudioDeviceFactory::NewAudioRendererSink( 61 sink_ = AudioDeviceFactory::NewAudioRendererSink(
76 AudioDeviceFactory::kSourceWebAudioInteractive, 62 AudioDeviceFactory::kSourceWebAudioInteractive,
77 render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE, 63 render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE,
78 session_id_, std::string(), security_origin_); 64 session_id_, std::string(), security_origin_);
79 65
80 // Specify the latency info to be passed to the browser side. 66 // Specify the latency info to be passed to the browser side.
81 media::AudioParameters sink_params(params_); 67 media::AudioParameters sink_params(params_);
82 sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType( 68 sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType(
83 AudioDeviceFactory::kSourceWebAudioInteractive)); 69 AudioDeviceFactory::kSourceWebAudioInteractive));
84 70
71 #if defined(OS_ANDROID)
72 // Use the media thread instead of the render thread for fake Render() calls
73 // since it has special connotations for Blink and garbage collection.
74 webaudio_suspender_.reset(new WebAudioSuspender(
75 this, sink_params, sink_,
76 RenderThreadImpl::current()->GetMediaThreadTaskRunner()));
77 sink_->Initialize(sink_params, webaudio_suspender_.get());
78 #else
85 sink_->Initialize(sink_params, this); 79 sink_->Initialize(sink_params, this);
86 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 80 #endif
87 // use-after-free issue. http://crbug.com/619463 81
88 {
89 CHECK(base::AtomicRefCountIsZero(&sink_is_running_))
90 << "Illegal state: sink_is_running_ should be 0.";
91 base::AtomicRefCountInc(&sink_is_running_);
92 }
93 sink_->Start(); 82 sink_->Start();
94 sink_->Play(); 83 sink_->Play();
95 start_null_audio_sink_callback_.Reset(
96 base::Bind(&media::NullAudioSink::Play, null_audio_sink_));
97 // Note: Default behavior is to auto-play on start.
98 } 84 }
99 85
100 void RendererWebAudioDeviceImpl::stop() { 86 void RendererWebAudioDeviceImpl::stop() {
101 DCHECK(thread_checker_.CalledOnValidThread()); 87 DCHECK(thread_checker_.CalledOnValidThread());
102
103 if (sink_) { 88 if (sink_) {
104 sink_->Stop(); 89 sink_->Stop();
105 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 90 sink_ = nullptr;
106 // use-after-free issue. http://crbug.com/619463
107 CHECK(!base::AtomicRefCountDec(&sink_is_running_))
108 << "Illegal state: sink_is_running_ should have been 1.";
109 sink_ = NULL;
110 } 91 }
111 null_audio_sink_->Stop(); 92
112 is_using_null_audio_sink_ = false; 93 #if defined(OS_ANDROID)
113 is_first_buffer_after_silence_ = false; 94 webaudio_suspender_.reset();
114 start_null_audio_sink_callback_.Cancel(); 95 #endif
115 } 96 }
116 97
117 double RendererWebAudioDeviceImpl::sampleRate() { 98 double RendererWebAudioDeviceImpl::sampleRate() {
118 return params_.sample_rate(); 99 return params_.sample_rate();
119 } 100 }
120 101
121 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest, 102 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest,
122 uint32_t frames_delayed, 103 uint32_t frames_delayed,
123 uint32_t frames_skipped) { 104 uint32_t frames_skipped) {
124 // TODO(miu): Remove this temporary instrumentation to root-cause a memory
125 // use-after-free issue. http://crbug.com/619463
126 CHECK(base::AtomicRefCountIsOne(&sink_is_running_))
127 << "Contract violation: Render() being called after stop().";
128
129 #if defined(OS_ANDROID)
130 if (is_first_buffer_after_silence_) {
131 DCHECK(!is_using_null_audio_sink_);
132 first_buffer_after_silence_->CopyTo(dest);
133 is_first_buffer_after_silence_ = false;
134 return dest->frames();
135 }
136 #endif
137 // Wrap the output pointers using WebVector. 105 // Wrap the output pointers using WebVector.
138 WebVector<float*> web_audio_dest_data( 106 WebVector<float*> web_audio_dest_data(static_cast<size_t>(dest->channels()));
139 static_cast<size_t>(dest->channels()));
140 for (int i = 0; i < dest->channels(); ++i) 107 for (int i = 0; i < dest->channels(); ++i)
141 web_audio_dest_data[i] = dest->channel(i); 108 web_audio_dest_data[i] = dest->channel(i);
142 109
143 // TODO(xians): Remove the following |web_audio_source_data| after 110 // TODO(xians): Remove the following |web_audio_source_data| after
144 // changing the blink interface. 111 // changing the blink interface.
145 WebVector<float*> web_audio_source_data(static_cast<size_t>(0)); 112 WebVector<float*> web_audio_source_data(static_cast<size_t>(0));
146 client_callback_->render(web_audio_source_data, 113 client_callback_->render(web_audio_source_data, web_audio_dest_data,
147 web_audio_dest_data,
148 dest->frames()); 114 dest->frames());
149
150 #if defined(OS_ANDROID)
151 const bool is_zero = dest->AreFramesZero();
152 if (!is_zero) {
153 first_silence_time_ = base::TimeTicks();
154 if (is_using_null_audio_sink_) {
155 // This is called on the main render thread when audio is detected.
156 DCHECK(thread_checker_.CalledOnValidThread());
157 is_using_null_audio_sink_ = false;
158 is_first_buffer_after_silence_ = true;
159 dest->CopyTo(first_buffer_after_silence_.get());
160 task_runner_->PostTask(
161 FROM_HERE,
162 base::Bind(&media::NullAudioSink::Pause, null_audio_sink_));
163 // Calling sink_->Play() may trigger reentrancy into this
164 // function, so this should be called at the end.
165 sink_->Play();
166 return dest->frames();
167 }
168 } else if (!is_using_null_audio_sink_) {
169 // Called on the audio device thread.
170 const base::TimeTicks now = base::TimeTicks::Now();
171 if (first_silence_time_.is_null())
172 first_silence_time_ = now;
173 if (now - first_silence_time_
174 > base::TimeDelta::FromSeconds(kSilenceInSecondsToEnterIdleMode)) {
175 sink_->Pause();
176 is_using_null_audio_sink_ = true;
177 // If Stop() is called right after the task is posted, need to cancel
178 // this task.
179 task_runner_->PostDelayedTask(
180 FROM_HERE,
181 start_null_audio_sink_callback_.callback(),
182 params_.GetBufferDuration());
183 }
184 }
185 #endif
186
187 // TODO(miu): Remove this temporary instrumentation to root-cause a memory
188 // use-after-free issue. http://crbug.com/619463
189 CHECK(base::AtomicRefCountIsOne(&sink_is_running_))
190 << "Race condition: stop() was called during Render() operation.";
191
192 return dest->frames(); 115 return dest->frames();
193 } 116 }
194 117
195 void RendererWebAudioDeviceImpl::OnRenderError() { 118 void RendererWebAudioDeviceImpl::OnRenderError() {
196 // TODO(crogers): implement error handling. 119 // TODO(crogers): implement error handling.
197 } 120 }
198 121
199 } // namespace content 122 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698