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

Side by Side Diff: content/renderer/media/renderer_webaudiodevice_impl.cc

Issue 2382473002: Merge M54: "Break out WebAudio suspension code into new class. Add tests." (Closed)
Patch Set: Fix conflicts. 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_)
63 return; // Already started. 49 return; // Already started.
64 50
65 // Assumption: This method is being invoked within a V8 call stack. CHECKs 51 // Assumption: This method is being invoked within a V8 call stack. CHECKs
66 // will fail in the call to frameForCurrentContext() otherwise. 52 // will fail in the call to frameForCurrentContext() otherwise.
67 // 53 //
68 // Therefore, we can perform look-ups to determine which RenderView is 54 // Therefore, we can perform look-ups to determine which RenderView is
69 // starting the audio device. The reason for all this is because the creator 55 // starting the audio device. The reason for all this is because the creator
70 // of the WebAudio objects might not be the actual source of the audio (e.g., 56 // of the WebAudio objects might not be the actual source of the audio (e.g.,
71 // an extension creates a object that is passed and used within a page). 57 // an extension creates a object that is passed and used within a page).
72 WebLocalFrame* const web_frame = WebLocalFrame::frameForCurrentContext(); 58 WebLocalFrame* const web_frame = WebLocalFrame::frameForCurrentContext();
73 RenderFrame* const render_frame = 59 RenderFrame* const render_frame =
74 web_frame ? RenderFrame::FromWebFrame(web_frame) : NULL; 60 web_frame ? RenderFrame::FromWebFrame(web_frame) : NULL;
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_);
65
66 #if defined(OS_ANDROID)
67 // Use the media thread instead of the render thread for fake Render() calls
68 // since it has special connotations for Blink and garbage collection. Timeout
69 // value chosen to be highly unlikely in the normal case.
70 webaudio_suspender_.reset(new media::SilentSinkSuspender(
71 this, base::TimeDelta::FromSeconds(30), params_, sink_,
72 RenderThreadImpl::current()->GetMediaThreadTaskRunner()));
73 sink_->Initialize(params_, webaudio_suspender_.get());
74 #else
79 sink_->Initialize(params_, this); 75 sink_->Initialize(params_, this);
80 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 76 #endif
81 // use-after-free issue. http://crbug.com/619463 77
82 {
83 CHECK(base::AtomicRefCountIsZero(&sink_is_running_))
84 << "Illegal state: sink_is_running_ should be 0.";
85 base::AtomicRefCountInc(&sink_is_running_);
86 }
87 sink_->Start(); 78 sink_->Start();
88 sink_->Play(); 79 sink_->Play();
89 start_null_audio_sink_callback_.Reset(
90 base::Bind(&media::NullAudioSink::Play, null_audio_sink_));
91 // Note: Default behavior is to auto-play on start.
92 } 80 }
93 81
94 void RendererWebAudioDeviceImpl::stop() { 82 void RendererWebAudioDeviceImpl::stop() {
95 DCHECK(thread_checker_.CalledOnValidThread()); 83 DCHECK(thread_checker_.CalledOnValidThread());
96
97 if (sink_) { 84 if (sink_) {
98 sink_->Stop(); 85 sink_->Stop();
99 // TODO(miu): Remove this temporary instrumentation to root-cause a memory 86 sink_ = nullptr;
100 // use-after-free issue. http://crbug.com/619463
101 CHECK(!base::AtomicRefCountDec(&sink_is_running_))
102 << "Illegal state: sink_is_running_ should have been 1.";
103 sink_ = NULL;
104 } 87 }
105 null_audio_sink_->Stop(); 88
106 is_using_null_audio_sink_ = false; 89 #if defined(OS_ANDROID)
107 is_first_buffer_after_silence_ = false; 90 webaudio_suspender_.reset();
108 start_null_audio_sink_callback_.Cancel(); 91 #endif
109 } 92 }
110 93
111 double RendererWebAudioDeviceImpl::sampleRate() { 94 double RendererWebAudioDeviceImpl::sampleRate() {
112 return params_.sample_rate(); 95 return params_.sample_rate();
113 } 96 }
114 97
115 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest, 98 int RendererWebAudioDeviceImpl::Render(media::AudioBus* dest,
116 uint32_t frames_delayed, 99 uint32_t frames_delayed,
117 uint32_t frames_skipped) { 100 uint32_t frames_skipped) {
118 // TODO(miu): Remove this temporary instrumentation to root-cause a memory
119 // use-after-free issue. http://crbug.com/619463
120 CHECK(base::AtomicRefCountIsOne(&sink_is_running_))
121 << "Contract violation: Render() being called after stop().";
122
123 #if defined(OS_ANDROID)
124 if (is_first_buffer_after_silence_) {
125 DCHECK(!is_using_null_audio_sink_);
126 first_buffer_after_silence_->CopyTo(dest);
127 is_first_buffer_after_silence_ = false;
128 return dest->frames();
129 }
130 #endif
131 // Wrap the output pointers using WebVector. 101 // Wrap the output pointers using WebVector.
132 WebVector<float*> web_audio_dest_data( 102 WebVector<float*> web_audio_dest_data(static_cast<size_t>(dest->channels()));
133 static_cast<size_t>(dest->channels()));
134 for (int i = 0; i < dest->channels(); ++i) 103 for (int i = 0; i < dest->channels(); ++i)
135 web_audio_dest_data[i] = dest->channel(i); 104 web_audio_dest_data[i] = dest->channel(i);
136 105
137 // TODO(xians): Remove the following |web_audio_source_data| after 106 // TODO(xians): Remove the following |web_audio_source_data| after
138 // changing the blink interface. 107 // changing the blink interface.
139 WebVector<float*> web_audio_source_data(static_cast<size_t>(0)); 108 WebVector<float*> web_audio_source_data(static_cast<size_t>(0));
140 client_callback_->render(web_audio_source_data, 109 client_callback_->render(web_audio_source_data, web_audio_dest_data,
141 web_audio_dest_data,
142 dest->frames()); 110 dest->frames());
143
144 #if defined(OS_ANDROID)
145 const bool is_zero = dest->AreFramesZero();
146 if (!is_zero) {
147 first_silence_time_ = base::TimeTicks();
148 if (is_using_null_audio_sink_) {
149 // This is called on the main render thread when audio is detected.
150 DCHECK(thread_checker_.CalledOnValidThread());
151 is_using_null_audio_sink_ = false;
152 is_first_buffer_after_silence_ = true;
153 dest->CopyTo(first_buffer_after_silence_.get());
154 task_runner_->PostTask(
155 FROM_HERE,
156 base::Bind(&media::NullAudioSink::Pause, null_audio_sink_));
157 // Calling sink_->Play() may trigger reentrancy into this
158 // function, so this should be called at the end.
159 sink_->Play();
160 return dest->frames();
161 }
162 } else if (!is_using_null_audio_sink_) {
163 // Called on the audio device thread.
164 const base::TimeTicks now = base::TimeTicks::Now();
165 if (first_silence_time_.is_null())
166 first_silence_time_ = now;
167 if (now - first_silence_time_
168 > base::TimeDelta::FromSeconds(kSilenceInSecondsToEnterIdleMode)) {
169 sink_->Pause();
170 is_using_null_audio_sink_ = true;
171 // If Stop() is called right after the task is posted, need to cancel
172 // this task.
173 task_runner_->PostDelayedTask(
174 FROM_HERE,
175 start_null_audio_sink_callback_.callback(),
176 params_.GetBufferDuration());
177 }
178 }
179 #endif
180
181 // TODO(miu): Remove this temporary instrumentation to root-cause a memory
182 // use-after-free issue. http://crbug.com/619463
183 CHECK(base::AtomicRefCountIsOne(&sink_is_running_))
184 << "Race condition: stop() was called during Render() operation.";
185
186 return dest->frames(); 111 return dest->frames();
187 } 112 }
188 113
189 void RendererWebAudioDeviceImpl::OnRenderError() { 114 void RendererWebAudioDeviceImpl::OnRenderError() {
190 // TODO(crogers): implement error handling. 115 // TODO(crogers): implement error handling.
191 } 116 }
192 117
193 } // namespace content 118 } // 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