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

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: Address comments. 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
« no previous file with comments | « content/renderer/media/renderer_webaudiodevice_impl.h ('k') | media/base/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"
18 #include "content/renderer/render_frame_impl.h" 16 #include "content/renderer/render_frame_impl.h"
19 #include "media/audio/null_audio_sink.h" 17 #include "content/renderer/render_thread_impl.h"
20 #include "media/base/media_switches.h" 18 #include "media/base/silent_sink_suspender.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. Timeout
74 // value chosen to be highly unlikely in the normal case.
75 webaudio_suspender_.reset(new media::SilentSinkSuspender(
76 this, base::TimeDelta::FromSeconds(30), sink_params, sink_,
77 RenderThreadImpl::current()->GetMediaThreadTaskRunner()));
78 sink_->Initialize(sink_params, webaudio_suspender_.get());
79 #else
85 sink_->Initialize(sink_params, this); 80 sink_->Initialize(sink_params, this);
86 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 81 #endif
87 // use-after-free issue. http://crbug.com/619463 82
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(); 83 sink_->Start();
94 sink_->Play(); 84 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 } 85 }
99 86
100 void RendererWebAudioDeviceImpl::stop() { 87 void RendererWebAudioDeviceImpl::stop() {
101 DCHECK(thread_checker_.CalledOnValidThread()); 88 DCHECK(thread_checker_.CalledOnValidThread());
102
103 if (sink_) { 89 if (sink_) {
104 sink_->Stop(); 90 sink_->Stop();
105 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 91 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 } 92 }
111 null_audio_sink_->Stop(); 93
112 is_using_null_audio_sink_ = false; 94 #if defined(OS_ANDROID)
113 is_first_buffer_after_silence_ = false; 95 webaudio_suspender_.reset();
114 start_null_audio_sink_callback_.Cancel(); 96 #endif
115 } 97 }
116 98
117 double RendererWebAudioDeviceImpl::sampleRate() { 99 double RendererWebAudioDeviceImpl::sampleRate() {
118 return params_.sample_rate(); 100 return params_.sample_rate();
119 } 101 }
120 102
121 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest, 103 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest,
122 uint32_t frames_delayed, 104 uint32_t frames_delayed,
123 uint32_t frames_skipped) { 105 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. 106 // Wrap the output pointers using WebVector.
138 WebVector<float*> web_audio_dest_data( 107 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) 108 for (int i = 0; i < dest->channels(); ++i)
141 web_audio_dest_data[i] = dest->channel(i); 109 web_audio_dest_data[i] = dest->channel(i);
142 110
143 // TODO(xians): Remove the following |web_audio_source_data| after 111 // TODO(xians): Remove the following |web_audio_source_data| after
144 // changing the blink interface. 112 // changing the blink interface.
145 WebVector<float*> web_audio_source_data(static_cast<size_t>(0)); 113 WebVector<float*> web_audio_source_data(static_cast<size_t>(0));
146 client_callback_->render(web_audio_source_data, 114 client_callback_->render(web_audio_source_data, web_audio_dest_data,
147 web_audio_dest_data,
148 dest->frames()); 115 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(); 116 return dest->frames();
193 } 117 }
194 118
195 void RendererWebAudioDeviceImpl::OnRenderError() { 119 void RendererWebAudioDeviceImpl::OnRenderError() {
196 // TODO(crogers): implement error handling. 120 // TODO(crogers): implement error handling.
197 } 121 }
198 122
199 } // namespace content 123 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/renderer_webaudiodevice_impl.h ('k') | media/base/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698