OLD | NEW |
---|---|
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/browser/renderer_host/media/audio_renderer_host.h" | 5 #include "content/browser/renderer_host/media/audio_renderer_host.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
13 #include "base/memory/ptr_util.h" | |
14 #include "base/memory/shared_memory.h" | |
15 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
16 #include "content/browser/bad_message.h" | 14 #include "content/browser/bad_message.h" |
17 #include "content/browser/browser_main_loop.h" | 15 #include "content/browser/browser_main_loop.h" |
18 #include "content/browser/media/audio_stream_monitor.h" | 16 #include "content/browser/media/audio_stream_monitor.h" |
19 #include "content/browser/media/capture/audio_mirroring_manager.h" | 17 #include "content/browser/media/capture/audio_mirroring_manager.h" |
20 #include "content/browser/media/media_internals.h" | 18 #include "content/browser/media/media_internals.h" |
21 #include "content/browser/renderer_host/media/audio_input_device_manager.h" | 19 #include "content/browser/renderer_host/media/audio_input_device_manager.h" |
22 #include "content/browser/renderer_host/media/audio_sync_reader.h" | 20 #include "content/browser/renderer_host/media/audio_sync_reader.h" |
23 #include "content/browser/renderer_host/media/media_stream_manager.h" | 21 #include "content/browser/renderer_host/media/media_stream_manager.h" |
24 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" | 22 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" |
(...skipping 13 matching lines...) Expand all Loading... | |
38 namespace content { | 36 namespace content { |
39 | 37 |
40 namespace { | 38 namespace { |
41 | 39 |
42 // Tracks the maximum number of simultaneous output streams browser-wide. | 40 // Tracks the maximum number of simultaneous output streams browser-wide. |
43 // Accessed on IO thread. | 41 // Accessed on IO thread. |
44 base::LazyInstance<media::AudioStreamsTracker> g_audio_streams_tracker = | 42 base::LazyInstance<media::AudioStreamsTracker> g_audio_streams_tracker = |
45 LAZY_INSTANCE_INITIALIZER; | 43 LAZY_INSTANCE_INITIALIZER; |
46 | 44 |
47 void NotifyRenderProcessHostThatAudioStateChanged(int render_process_id) { | 45 void NotifyRenderProcessHostThatAudioStateChanged(int render_process_id) { |
48 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 46 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
miu
2016/11/29 21:36:28
ditto here: Burden the caller, not the callee with
| |
47 BrowserThread::PostTask( | |
48 BrowserThread::UI, FROM_HERE, | |
49 base::Bind(&NotifyRenderProcessHostThatAudioStateChanged, | |
50 render_process_id)); | |
51 return; | |
52 } | |
49 | 53 |
50 RenderProcessHost* render_process_host = | 54 RenderProcessHost* render_process_host = |
51 RenderProcessHost::FromID(render_process_id); | 55 RenderProcessHost::FromID(render_process_id); |
52 | 56 |
53 if (render_process_host) | 57 if (render_process_host) |
54 render_process_host->AudioStateChanged(); | 58 render_process_host->AudioStateChanged(); |
55 } | 59 } |
56 | 60 |
57 void UMALogDeviceAuthorizationTime(base::TimeTicks auth_start_time) { | 61 void UMALogDeviceAuthorizationTime(base::TimeTicks auth_start_time) { |
58 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.OutputDeviceAuthorizationTime", | 62 UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.OutputDeviceAuthorizationTime", |
59 base::TimeTicks::Now() - auth_start_time, | 63 base::TimeTicks::Now() - auth_start_time, |
60 base::TimeDelta::FromMilliseconds(1), | 64 base::TimeDelta::FromMilliseconds(1), |
61 base::TimeDelta::FromMilliseconds(5000), 50); | 65 base::TimeDelta::FromMilliseconds(5000), 50); |
62 } | 66 } |
63 | 67 |
64 // Check that the routing ID references a valid RenderFrameHost, and run | 68 // Check that the routing ID references a valid RenderFrameHost, and run |
65 // |callback| on the IO thread with true if the ID is valid. | 69 // |callback| on the IO thread with true if the ID is valid. |
66 void ValidateRenderFrameId(int render_process_id, | 70 void ValidateRenderFrameId(int render_process_id, |
67 int render_frame_id, | 71 int render_frame_id, |
68 const base::Callback<void(bool)>& callback) { | 72 const base::Callback<void(bool)>& callback) { |
69 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 73 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
70 const bool frame_exists = | 74 const bool frame_exists = |
71 !!RenderFrameHost::FromID(render_process_id, render_frame_id); | 75 !!RenderFrameHost::FromID(render_process_id, render_frame_id); |
72 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 76 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
73 base::Bind(callback, frame_exists)); | 77 base::Bind(callback, frame_exists)); |
74 } | 78 } |
75 | 79 |
76 } // namespace | 80 } // namespace |
77 | 81 |
78 class AudioRendererHost::AudioEntry | |
79 : public media::AudioOutputController::EventHandler { | |
80 public: | |
81 ~AudioEntry() override; | |
82 | |
83 // Returns nullptr on failure. | |
84 static std::unique_ptr<AudioRendererHost::AudioEntry> Create( | |
85 AudioRendererHost* host, | |
86 int stream_id, | |
87 int render_frame_id, | |
88 const media::AudioParameters& params, | |
89 const std::string& output_device_id); | |
90 | |
91 int stream_id() const { | |
92 return stream_id_; | |
93 } | |
94 | |
95 int render_frame_id() const { return render_frame_id_; } | |
96 | |
97 media::AudioOutputController* controller() const { return controller_.get(); } | |
98 | |
99 AudioSyncReader* reader() const { return reader_.get(); } | |
100 | |
101 // Used by ARH to track the number of active streams for UMA stats. | |
102 bool playing() const { return playing_; } | |
103 void set_playing(bool playing) { playing_ = playing; } | |
104 | |
105 private: | |
106 AudioEntry(AudioRendererHost* host, | |
107 int stream_id, | |
108 int render_frame_id, | |
109 const media::AudioParameters& params, | |
110 const std::string& output_device_id, | |
111 std::unique_ptr<AudioSyncReader> reader); | |
112 | |
113 // media::AudioOutputController::EventHandler implementation. | |
114 void OnCreated() override; | |
115 void OnPlaying() override; | |
116 void OnPaused() override; | |
117 void OnError() override; | |
118 | |
119 AudioRendererHost* const host_; | |
120 const int stream_id_; | |
121 | |
122 // The routing ID of the source RenderFrame. | |
123 const int render_frame_id_; | |
124 | |
125 // The synchronous reader to be used by |controller_|. | |
126 const std::unique_ptr<AudioSyncReader> reader_; | |
127 | |
128 // The AudioOutputController that manages the audio stream. | |
129 const scoped_refptr<media::AudioOutputController> controller_; | |
130 | |
131 bool playing_; | |
132 | |
133 DISALLOW_COPY_AND_ASSIGN(AudioEntry); | |
134 }; | |
135 | |
136 AudioRendererHost::AudioEntry::AudioEntry( | |
137 AudioRendererHost* host, | |
138 int stream_id, | |
139 int render_frame_id, | |
140 const media::AudioParameters& params, | |
141 const std::string& output_device_id, | |
142 std::unique_ptr<AudioSyncReader> reader) | |
143 : host_(host), | |
144 stream_id_(stream_id), | |
145 render_frame_id_(render_frame_id), | |
146 reader_(std::move(reader)), | |
147 controller_(media::AudioOutputController::Create(host->audio_manager_, | |
148 this, | |
149 params, | |
150 output_device_id, | |
151 reader_.get())), | |
152 playing_(false) { | |
153 DCHECK(controller_); | |
154 } | |
155 | |
156 AudioRendererHost::AudioEntry::~AudioEntry() {} | |
157 | |
158 // static | |
159 std::unique_ptr<AudioRendererHost::AudioEntry> | |
160 AudioRendererHost::AudioEntry::Create(AudioRendererHost* host, | |
161 int stream_id, | |
162 int render_frame_id, | |
163 const media::AudioParameters& params, | |
164 const std::string& output_device_id) { | |
165 std::unique_ptr<AudioSyncReader> reader(AudioSyncReader::Create(params)); | |
166 if (!reader) { | |
167 return nullptr; | |
168 } | |
169 return base::WrapUnique(new AudioEntry(host, stream_id, render_frame_id, | |
170 params, output_device_id, | |
171 std::move(reader))); | |
172 } | |
173 | |
174 /////////////////////////////////////////////////////////////////////////////// | 82 /////////////////////////////////////////////////////////////////////////////// |
175 // AudioRendererHost implementations. | 83 // AudioRendererHost implementations. |
176 | 84 |
177 AudioRendererHost::AudioRendererHost(int render_process_id, | 85 AudioRendererHost::AudioRendererHost(int render_process_id, |
178 media::AudioManager* audio_manager, | 86 media::AudioManager* audio_manager, |
179 AudioMirroringManager* mirroring_manager, | 87 AudioMirroringManager* mirroring_manager, |
180 MediaInternals* media_internals, | |
181 MediaStreamManager* media_stream_manager, | 88 MediaStreamManager* media_stream_manager, |
182 const std::string& salt) | 89 const std::string& salt) |
183 : BrowserMessageFilter(AudioMsgStart), | 90 : BrowserMessageFilter(AudioMsgStart), |
184 render_process_id_(render_process_id), | 91 render_process_id_(render_process_id), |
185 audio_manager_(audio_manager), | 92 audio_manager_(audio_manager), |
186 mirroring_manager_(mirroring_manager), | 93 mirroring_manager_(mirroring_manager), |
187 audio_log_(media_internals->CreateAudioLog( | 94 media_stream_manager_(media_stream_manager), |
188 media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER)), | |
189 num_playing_streams_(0), | 95 num_playing_streams_(0), |
190 salt_(salt), | 96 salt_(salt), |
191 validate_render_frame_id_function_(&ValidateRenderFrameId), | 97 validate_render_frame_id_function_(&ValidateRenderFrameId), |
192 max_simultaneous_streams_(0), | 98 max_simultaneous_streams_(0), |
193 authorization_handler_(audio_manager_, | 99 authorization_handler_(audio_manager_, |
194 media_stream_manager, | 100 media_stream_manager, |
195 render_process_id_, | 101 render_process_id_, |
196 salt) { | 102 salt) { |
197 DCHECK(audio_manager_); | 103 DCHECK(audio_manager_); |
198 } | 104 } |
199 | 105 |
200 AudioRendererHost::~AudioRendererHost() { | 106 AudioRendererHost::~AudioRendererHost() { |
201 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 107 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
202 CHECK(audio_entries_.empty()); | 108 CHECK(delegates_.empty()); |
DaleCurtis
2016/11/29 20:31:24
Probably can go back to a DCHECK() now, was for de
| |
203 | 109 |
204 // If we had any streams, report UMA stats for the maximum number of | 110 // If we had any streams, report UMA stats for the maximum number of |
205 // simultaneous streams for this render process and for the whole browser | 111 // simultaneous streams for this render process and for the whole browser |
206 // process since last reported. | 112 // process since last reported. |
207 if (max_simultaneous_streams_ > 0) { | 113 if (max_simultaneous_streams_ > 0) { |
208 UMA_HISTOGRAM_CUSTOM_COUNTS("Media.AudioRendererIpcStreams", | 114 UMA_HISTOGRAM_CUSTOM_COUNTS("Media.AudioRendererIpcStreams", |
209 max_simultaneous_streams_, 1, 50, 51); | 115 max_simultaneous_streams_, 1, 50, 51); |
210 UMA_HISTOGRAM_CUSTOM_COUNTS( | 116 UMA_HISTOGRAM_CUSTOM_COUNTS( |
211 "Media.AudioRendererIpcStreamsTotal", | 117 "Media.AudioRendererIpcStreamsTotal", |
212 g_audio_streams_tracker.Get().max_stream_count(), | 118 g_audio_streams_tracker.Get().max_stream_count(), |
213 1, 100, 101); | 119 1, 100, 101); |
214 g_audio_streams_tracker.Get().ResetMaxStreamCount(); | 120 g_audio_streams_tracker.Get().ResetMaxStreamCount(); |
215 } | 121 } |
216 } | 122 } |
217 | 123 |
218 void AudioRendererHost::GetOutputControllers( | 124 void AudioRendererHost::GetOutputControllers( |
219 const RenderProcessHost::GetAudioOutputControllersCallback& | 125 const RenderProcessHost::GetAudioOutputControllersCallback& |
220 callback) const { | 126 callback) const { |
221 BrowserThread::PostTaskAndReplyWithResult( | 127 BrowserThread::PostTaskAndReplyWithResult( |
222 BrowserThread::IO, FROM_HERE, | 128 BrowserThread::IO, FROM_HERE, |
223 base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback); | 129 base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback); |
224 } | 130 } |
225 | 131 |
226 void AudioRendererHost::OnChannelClosing() { | 132 void AudioRendererHost::OnChannelClosing() { |
227 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 133 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
228 // Since the IPC sender is gone, close all requested audio streams. | 134 // Since the IPC sender is gone, close all requested audio streams. |
229 while (!audio_entries_.empty()) { | 135 // The audio streams tracker isn't automatically decremented since the |
230 // Note: OnCloseStream() removes the entries from audio_entries_. | 136 // removal isn't done through OnCloseStream. |
231 OnCloseStream(audio_entries_.begin()->first); | 137 g_audio_streams_tracker.Get().DecreaseStreamCount(delegates_.size()); |
232 } | 138 delegates_.clear(); |
miu
2016/11/29 21:36:27
Get ready for some pain here. In the past, wheneve
Max Morin
2016/12/01 16:08:37
Yes, this is tricky (and I and Olga spent a lot of
| |
233 | 139 |
234 // Remove any authorizations for streams that were not yet created | 140 // Remove any authorizations for streams that were not yet created |
235 authorizations_.clear(); | 141 authorizations_.clear(); |
236 } | 142 } |
237 | 143 |
238 void AudioRendererHost::OnDestruct() const { | 144 void AudioRendererHost::OnDestruct() const { |
239 BrowserThread::DeleteOnIOThread::Destruct(this); | 145 BrowserThread::DeleteOnIOThread::Destruct(this); |
240 } | 146 } |
241 | 147 |
242 void AudioRendererHost::AudioEntry::OnCreated() { | 148 void AudioRendererHost::OnStreamCreated( |
243 BrowserThread::PostTask( | 149 int stream_id, |
244 BrowserThread::IO, | 150 base::SharedMemory* shared_memory, |
245 FROM_HERE, | 151 base::CancelableSyncSocket* foreign_socket) { |
246 base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_)); | |
247 } | |
248 | |
249 void AudioRendererHost::AudioEntry::OnPlaying() { | |
250 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
251 base::Bind(&AudioRendererHost::StreamStateChanged, | |
252 host_, stream_id_, true)); | |
253 } | |
254 | |
255 void AudioRendererHost::AudioEntry::OnPaused() { | |
256 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
257 base::Bind(&AudioRendererHost::StreamStateChanged, | |
258 host_, stream_id_, false)); | |
259 } | |
260 | |
261 void AudioRendererHost::AudioEntry::OnError() { | |
262 BrowserThread::PostTask( | |
263 BrowserThread::IO, | |
264 FROM_HERE, | |
265 base::Bind(&AudioRendererHost::ReportErrorAndClose, host_, stream_id_)); | |
266 } | |
267 | |
268 void AudioRendererHost::DoCompleteCreation(int stream_id) { | |
269 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 152 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
270 | 153 |
271 if (!PeerHandle()) { | 154 if (!PeerHandle()) { |
272 DLOG(WARNING) << "Renderer process handle is invalid."; | 155 DLOG(WARNING) << "Renderer process handle is invalid."; |
273 ReportErrorAndClose(stream_id); | 156 OnStreamError(stream_id); |
274 return; | 157 return; |
275 } | 158 } |
276 | 159 |
277 AudioEntry* const entry = LookupById(stream_id); | 160 if (!LookupById(stream_id)) { |
278 if (!entry) { | 161 SendErrorMessage(stream_id); |
DaleCurtis
2016/11/29 20:31:24
Why a different error path than above?
| |
279 ReportErrorAndClose(stream_id); | |
280 return; | 162 return; |
281 } | 163 } |
282 | 164 |
283 // Now construction is done and we are ready to send the shared memory and the | |
284 // sync socket to the renderer. | |
285 base::SharedMemory* shared_memory = entry->reader()->shared_memory(); | |
286 base::CancelableSyncSocket* foreign_socket = | |
287 entry->reader()->foreign_socket(); | |
288 | |
289 base::SharedMemoryHandle foreign_memory_handle; | 165 base::SharedMemoryHandle foreign_memory_handle; |
290 base::SyncSocket::TransitDescriptor socket_descriptor; | 166 base::SyncSocket::TransitDescriptor socket_descriptor; |
291 size_t shared_memory_size = shared_memory->requested_size(); | 167 size_t shared_memory_size = shared_memory->requested_size(); |
292 | 168 |
169 // TODO(maxmorin): Figure out how/if this is safe, since shared_memory is also | |
DaleCurtis
2016/11/29 20:31:24
Can you elaborate? IIRC this completes before any
Max Morin
2016/12/01 16:08:37
This function gets the shared memory from the audi
| |
170 // accessed on the audio thread. | |
293 if (!(shared_memory->ShareToProcess(PeerHandle(), &foreign_memory_handle) && | 171 if (!(shared_memory->ShareToProcess(PeerHandle(), &foreign_memory_handle) && |
294 foreign_socket->PrepareTransitDescriptor(PeerHandle(), | 172 foreign_socket->PrepareTransitDescriptor(PeerHandle(), |
295 &socket_descriptor))) { | 173 &socket_descriptor))) { |
296 // Something went wrong in preparing the IPC handles. | 174 // Something went wrong in preparing the IPC handles. |
297 ReportErrorAndClose(entry->stream_id()); | 175 OnStreamError(stream_id); |
298 return; | 176 return; |
299 } | 177 } |
300 | 178 |
301 Send(new AudioMsg_NotifyStreamCreated( | 179 Send(new AudioMsg_NotifyStreamCreated( |
302 stream_id, foreign_memory_handle, socket_descriptor, | 180 stream_id, foreign_memory_handle, socket_descriptor, |
303 base::checked_cast<uint32_t>(shared_memory_size))); | 181 base::checked_cast<uint32_t>(shared_memory_size))); |
304 } | 182 } |
305 | 183 |
184 void AudioRendererHost::OnStreamStateChanged(bool is_playing) { | |
185 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
186 if (is_playing) { | |
187 base::AtomicRefCountInc(&num_playing_streams_); | |
DaleCurtis
2016/11/29 20:31:24
We might be able to drop all this code now. altimi
Max Morin
2016/12/01 16:08:37
Looks good, but it should probably be another CL?
DaleCurtis
2016/12/01 19:33:06
Sure, I was just pointing it out. Filed http://crb
| |
188 | |
189 // Inform the RenderProcessHost when audio starts playing for the first | |
190 // time. The nonatomic increment-and-read is ok since this is the only | |
191 // thread that |num_plaing_streams| may be updated on. | |
192 if (base::AtomicRefCountIsOne(&num_playing_streams_)) | |
193 NotifyRenderProcessHostThatAudioStateChanged(render_process_id_); | |
194 } else { | |
195 // Inform the RenderProcessHost when there is no more audio playing. | |
196 if (!base::AtomicRefCountDec(&num_playing_streams_)) | |
197 NotifyRenderProcessHostThatAudioStateChanged(render_process_id_); | |
198 } | |
199 } | |
200 | |
201 void AudioRendererHost::OnStreamError(int stream_id) { | |
202 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
203 | |
204 SendErrorMessage(stream_id); | |
205 | |
206 OnCloseStream(stream_id); | |
207 } | |
208 | |
306 void AudioRendererHost::DidValidateRenderFrame(int stream_id, bool is_valid) { | 209 void AudioRendererHost::DidValidateRenderFrame(int stream_id, bool is_valid) { |
307 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 210 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
308 | 211 |
309 if (!is_valid) { | 212 if (!is_valid) { |
310 DLOG(WARNING) << "Render frame for stream (id=" << stream_id | 213 DLOG(WARNING) << "Render frame for stream (id=" << stream_id |
311 << ") no longer exists."; | 214 << ") no longer exists."; |
312 ReportErrorAndClose(stream_id); | 215 OnStreamError(stream_id); |
313 } | 216 } |
314 } | 217 } |
315 | 218 |
316 void AudioRendererHost::StreamStateChanged(int stream_id, bool is_playing) { | |
317 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
318 | |
319 AudioEntry* const entry = LookupById(stream_id); | |
320 if (!entry) | |
321 return; | |
322 | |
323 if (is_playing) { | |
324 AudioStreamMonitor::StartMonitoringStream( | |
325 render_process_id_, | |
326 entry->render_frame_id(), | |
327 entry->stream_id(), | |
328 base::Bind(&media::AudioOutputController::ReadCurrentPowerAndClip, | |
329 entry->controller())); | |
330 } else { | |
331 AudioStreamMonitor::StopMonitoringStream( | |
332 render_process_id_, entry->render_frame_id(), entry->stream_id()); | |
333 } | |
334 UpdateNumPlayingStreams(entry, is_playing); | |
335 } | |
336 | |
337 RenderProcessHost::AudioOutputControllerList | 219 RenderProcessHost::AudioOutputControllerList |
338 AudioRendererHost::DoGetOutputControllers() const { | 220 AudioRendererHost::DoGetOutputControllers() const { |
339 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 221 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
340 | 222 |
341 RenderProcessHost::AudioOutputControllerList controllers; | 223 RenderProcessHost::AudioOutputControllerList controllers; |
342 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); | 224 for (const auto& delegate : delegates_) |
343 it != audio_entries_.end(); | 225 controllers.push_back(delegate->controller()); |
344 ++it) { | |
345 controllers.push_back(it->second->controller()); | |
346 } | |
347 | 226 |
348 return controllers; | 227 return controllers; |
349 } | 228 } |
350 | 229 |
351 /////////////////////////////////////////////////////////////////////////////// | 230 /////////////////////////////////////////////////////////////////////////////// |
352 // IPC Messages handler | 231 // IPC Messages handler |
353 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { | 232 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { |
354 bool handled = true; | 233 bool handled = true; |
355 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) | 234 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) |
356 IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization, | 235 IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization, |
(...skipping 15 matching lines...) Expand all Loading... | |
372 int session_id, | 251 int session_id, |
373 const std::string& device_id, | 252 const std::string& device_id, |
374 const url::Origin& security_origin) { | 253 const url::Origin& security_origin) { |
375 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 254 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
376 const base::TimeTicks auth_start_time = base::TimeTicks::Now(); | 255 const base::TimeTicks auth_start_time = base::TimeTicks::Now(); |
377 DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization" | 256 DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization" |
378 << "(stream_id=" << stream_id | 257 << "(stream_id=" << stream_id |
379 << ", render_frame_id=" << render_frame_id | 258 << ", render_frame_id=" << render_frame_id |
380 << ", session_id=" << session_id << ", device_id=" << device_id | 259 << ", session_id=" << session_id << ", device_id=" << device_id |
381 << ", security_origin=" << security_origin << ")"; | 260 << ", security_origin=" << security_origin << ")"; |
261 | |
382 if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) | 262 if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) |
383 return; | 263 return; |
384 | 264 |
385 authorizations_.insert( | 265 authorizations_.insert( |
386 std::make_pair(stream_id, std::make_pair(false, std::string()))); | 266 std::make_pair(stream_id, std::make_pair(false, std::string()))); |
387 | 267 |
388 // Unretained is ok here since |this| owns |authorization_handler_| and | 268 // Unretained is ok here since |this| owns |authorization_handler_| and |
389 // |authorization_handler_| owns the callback. | 269 // |authorization_handler_| owns the callback. |
390 authorization_handler_.RequestDeviceAuthorization( | 270 authorization_handler_.RequestDeviceAuthorization( |
391 render_frame_id, session_id, device_id, security_origin, | 271 render_frame_id, session_id, device_id, security_origin, |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
446 // to create it now. | 326 // to create it now. |
447 content::bad_message::ReceivedBadMessage( | 327 content::bad_message::ReceivedBadMessage( |
448 this, bad_message::ARH_CREATED_STREAM_WITHOUT_AUTHORIZATION); | 328 this, bad_message::ARH_CREATED_STREAM_WITHOUT_AUTHORIZATION); |
449 return; | 329 return; |
450 } | 330 } |
451 device_unique_id.swap(auth_data->second.second); | 331 device_unique_id.swap(auth_data->second.second); |
452 authorizations_.erase(auth_data); | 332 authorizations_.erase(auth_data); |
453 } | 333 } |
454 | 334 |
455 // Fail early if either of two sanity-checks fail: | 335 // Fail early if either of two sanity-checks fail: |
456 // 1. There should not yet exist an AudioEntry for the given |stream_id| | 336 // 1. There should not yet exist an AudioOutputDelegate for the given |
457 // since the renderer may not create two streams with the same ID. | 337 // |stream_id| since the renderer may not create two streams with the |
338 // same ID. | |
458 // 2. An out-of-range render frame ID was provided. Renderers must *always* | 339 // 2. An out-of-range render frame ID was provided. Renderers must *always* |
459 // specify a valid render frame ID for each audio output they create, as | 340 // specify a valid render frame ID for each audio output they create, as |
460 // several browser-level features depend on this (e.g., OOM manager, UI | 341 // several browser-level features depend on this (e.g., OOM manager, UI |
461 // audio indicator, muting, audio capture). | 342 // audio indicator, muting, audio capture). |
462 // Note: media::AudioParameters is validated in the deserializer, so there is | 343 // Note: media::AudioParameters is validated in the deserializer, so there is |
463 // no need to check that here. | 344 // no need to check that here. |
464 if (LookupById(stream_id)) { | 345 if (LookupById(stream_id)) { |
465 SendErrorMessage(stream_id); | 346 SendErrorMessage(stream_id); |
466 return; | 347 return; |
467 } | 348 } |
468 if (render_frame_id <= 0) { | 349 if (render_frame_id <= 0) { |
469 SendErrorMessage(stream_id); | 350 SendErrorMessage(stream_id); |
470 return; | 351 return; |
471 } | 352 } |
472 | 353 |
473 // Post a task to the UI thread to check that the |render_frame_id| references | 354 // Post a task to the UI thread to check that the |render_frame_id| references |
474 // a valid render frame. This validation is important for all the reasons | 355 // a valid render frame. This validation is important for all the reasons |
475 // stated in the comments above. This does not block stream creation, but will | 356 // stated in the comments above. This does not block stream creation, but will |
476 // force-close the stream later if validation fails. | 357 // force-close the stream later if validation fails. |
477 BrowserThread::PostTask( | 358 BrowserThread::PostTask( |
478 BrowserThread::UI, FROM_HERE, | 359 BrowserThread::UI, FROM_HERE, |
479 base::Bind(validate_render_frame_id_function_, render_process_id_, | 360 base::Bind(validate_render_frame_id_function_, render_process_id_, |
480 render_frame_id, | 361 render_frame_id, |
481 base::Bind(&AudioRendererHost::DidValidateRenderFrame, this, | 362 base::Bind(&AudioRendererHost::DidValidateRenderFrame, this, |
482 stream_id))); | 363 stream_id))); |
483 | 364 |
484 std::unique_ptr<AudioEntry> entry = AudioEntry::Create( | |
485 this, stream_id, render_frame_id, params, device_unique_id); | |
486 if (!entry) { | |
487 SendErrorMessage(stream_id); | |
488 return; | |
489 } | |
490 | |
491 MediaObserver* const media_observer = | 365 MediaObserver* const media_observer = |
492 GetContentClient()->browser()->GetMediaObserver(); | 366 GetContentClient()->browser()->GetMediaObserver(); |
493 if (media_observer) | |
494 media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); | |
495 | 367 |
496 if (mirroring_manager_) { | 368 MediaInternals* const media_internals = MediaInternals::GetInstance(); |
497 mirroring_manager_->AddDiverter( | 369 std::unique_ptr<media::AudioLog> audio_log = media_internals->CreateAudioLog( |
498 render_process_id_, entry->render_frame_id(), entry->controller()); | 370 media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER); |
499 } | 371 media_internals->SetWebContentsTitleForAudioLogEntry( |
500 audio_entries_.insert(std::make_pair(stream_id, entry.release())); | 372 stream_id, render_process_id_, render_frame_id, audio_log.get()); |
373 delegates_.push_back(AudioOutputDelegate::Create( | |
374 this, audio_manager_, std::move(audio_log), mirroring_manager_, | |
375 media_observer, stream_id, render_frame_id, render_process_id_, params, | |
376 device_unique_id)); | |
377 | |
501 g_audio_streams_tracker.Get().IncreaseStreamCount(); | 378 g_audio_streams_tracker.Get().IncreaseStreamCount(); |
DaleCurtis
2016/11/29 20:31:24
Seems like code that should be moved to AudioStrea
Max Morin
2016/12/01 16:08:37
For another CL?
DaleCurtis
2016/12/01 19:33:06
Covered by http://crbug.com/670383
| |
502 | 379 |
503 audio_log_->OnCreated(stream_id, params, device_unique_id); | 380 if (delegates_.size() > max_simultaneous_streams_) |
504 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( | 381 max_simultaneous_streams_ = delegates_.size(); |
505 stream_id, render_process_id_, render_frame_id, audio_log_.get()); | |
506 | |
507 if (audio_entries_.size() > max_simultaneous_streams_) | |
508 max_simultaneous_streams_ = audio_entries_.size(); | |
509 } | 382 } |
510 | 383 |
511 void AudioRendererHost::OnPlayStream(int stream_id) { | 384 void AudioRendererHost::OnPlayStream(int stream_id) { |
512 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 385 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
513 | 386 |
514 AudioEntry* entry = LookupById(stream_id); | 387 AudioOutputDelegate* delegate = LookupById(stream_id); |
515 if (!entry) { | 388 if (!delegate) { |
516 SendErrorMessage(stream_id); | 389 SendErrorMessage(stream_id); |
517 return; | 390 return; |
518 } | 391 } |
519 | 392 |
520 entry->controller()->Play(); | 393 delegate->OnPlayStream(); |
521 audio_log_->OnStarted(stream_id); | |
522 } | 394 } |
523 | 395 |
524 void AudioRendererHost::OnPauseStream(int stream_id) { | 396 void AudioRendererHost::OnPauseStream(int stream_id) { |
525 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 397 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
526 | 398 |
527 AudioEntry* entry = LookupById(stream_id); | 399 AudioOutputDelegate* delegate = LookupById(stream_id); |
528 if (!entry) { | 400 if (!delegate) { |
529 SendErrorMessage(stream_id); | 401 SendErrorMessage(stream_id); |
530 return; | 402 return; |
531 } | 403 } |
532 | 404 |
533 entry->controller()->Pause(); | 405 delegate->OnPauseStream(); |
534 audio_log_->OnStopped(stream_id); | |
535 } | 406 } |
536 | 407 |
537 void AudioRendererHost::OnSetVolume(int stream_id, double volume) { | 408 void AudioRendererHost::OnSetVolume(int stream_id, double volume) { |
538 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 409 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
539 | 410 |
540 AudioEntry* entry = LookupById(stream_id); | 411 AudioOutputDelegate* delegate = LookupById(stream_id); |
541 if (!entry) { | 412 if (!delegate) { |
542 SendErrorMessage(stream_id); | 413 SendErrorMessage(stream_id); |
543 return; | 414 return; |
544 } | 415 } |
545 | 416 |
546 // Make sure the volume is valid. | 417 // Make sure the volume is valid. |
547 if (volume < 0 || volume > 1.0) | 418 if (volume < 0 || volume > 1.0) |
miu
2016/11/29 21:36:28
Should this volume check be in the delegate instea
Max Morin
2016/12/01 16:08:37
Right now I'm leaning towards "0 is minimum and 1
| |
548 return; | 419 return; |
549 entry->controller()->SetVolume(volume); | 420 delegate->OnSetVolume(volume); |
550 audio_log_->OnSetVolume(stream_id, volume); | |
551 } | 421 } |
552 | 422 |
553 void AudioRendererHost::SendErrorMessage(int stream_id) { | 423 void AudioRendererHost::SendErrorMessage(int stream_id) { |
554 Send(new AudioMsg_NotifyStreamError(stream_id)); | 424 Send(new AudioMsg_NotifyStreamError(stream_id)); |
555 } | 425 } |
556 | 426 |
557 void AudioRendererHost::OnCloseStream(int stream_id) { | 427 void AudioRendererHost::OnCloseStream(int stream_id) { |
558 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 428 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
559 authorizations_.erase(stream_id); | 429 authorizations_.erase(stream_id); |
560 | 430 |
431 auto i = LookupIteratorById(stream_id); | |
DaleCurtis
2016/11/29 20:31:24
Could just use the delegates_.erase(std::remove_if
Max Morin
2016/12/01 16:08:37
This would require checking if the returned iterat
DaleCurtis
2016/12/01 19:33:06
Not true, remove_if() returns a range, so if nothi
Max Morin
2016/12/02 12:07:37
Right, but the code must still check if it should
DaleCurtis
2016/12/02 18:20:13
Whoops, sorry misread the question.
| |
432 | |
561 // Prevent oustanding callbacks from attempting to close/delete the same | 433 // Prevent oustanding callbacks from attempting to close/delete the same |
562 // AudioEntry twice. | 434 // AudioOutputDelegate twice. |
563 AudioEntryMap::iterator i = audio_entries_.find(stream_id); | 435 if (i == delegates_.end()) |
564 if (i == audio_entries_.end()) | |
565 return; | 436 return; |
566 std::unique_ptr<AudioEntry> entry(i->second); | 437 |
567 audio_entries_.erase(i); | 438 std::swap(*i, delegates_.back()); |
miu
2016/11/29 21:36:28
I like this: Since ordering of this list doesn't m
| |
439 delegates_.pop_back(); | |
440 | |
568 g_audio_streams_tracker.Get().DecreaseStreamCount(); | 441 g_audio_streams_tracker.Get().DecreaseStreamCount(); |
569 | |
570 media::AudioOutputController* const controller = entry->controller(); | |
571 controller->Close( | |
572 base::Bind(&AudioRendererHost::DeleteEntry, this, base::Passed(&entry))); | |
573 audio_log_->OnClosed(stream_id); | |
574 } | 442 } |
575 | 443 |
576 void AudioRendererHost::DeleteEntry(std::unique_ptr<AudioEntry> entry) { | 444 AudioRendererHost::AudioOutputDelegateList::iterator |
445 AudioRendererHost::LookupIteratorById(int stream_id) { | |
577 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 446 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
578 | 447 |
579 // De-register the controller from the AudioMirroringManager now that the | 448 return std::find_if(delegates_.begin(), delegates_.end(), |
580 // controller has closed the AudioOutputStream and shut itself down. This | 449 [stream_id](const AudioOutputDelegate::UniquePtr& d) { |
581 // ensures that calling RemoveDiverter() here won't trigger the controller to | 450 return d->stream_id() == stream_id; |
582 // re-start the default AudioOutputStream and cause a brief audio blip to come | 451 }); |
583 // out the user's speakers. http://crbug.com/474432 | |
584 if (mirroring_manager_) | |
585 mirroring_manager_->RemoveDiverter(entry->controller()); | |
586 | |
587 AudioStreamMonitor::StopMonitoringStream( | |
588 render_process_id_, entry->render_frame_id(), entry->stream_id()); | |
589 UpdateNumPlayingStreams(entry.get(), false); | |
590 } | 452 } |
591 | 453 |
592 void AudioRendererHost::ReportErrorAndClose(int stream_id) { | 454 AudioOutputDelegate* AudioRendererHost::LookupById(int stream_id) { |
593 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 455 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
594 | 456 |
595 // Make sure this isn't a stray callback executing after the stream has been | 457 auto i = LookupIteratorById(stream_id); |
596 // closed, so error notifications aren't sent after clients believe the stream | 458 return i != delegates_.end() ? i->get() : nullptr; |
597 // is closed. | |
598 if (!LookupById(stream_id)) | |
599 return; | |
600 | |
601 SendErrorMessage(stream_id); | |
602 | |
603 audio_log_->OnError(stream_id); | |
604 OnCloseStream(stream_id); | |
605 } | |
606 | |
607 AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(int stream_id) { | |
608 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
609 | |
610 AudioEntryMap::const_iterator i = audio_entries_.find(stream_id); | |
611 return i != audio_entries_.end() ? i->second : NULL; | |
612 } | |
613 | |
614 void AudioRendererHost::UpdateNumPlayingStreams(AudioEntry* entry, | |
615 bool is_playing) { | |
616 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
617 if (entry->playing() == is_playing) | |
618 return; | |
619 | |
620 if (is_playing) { | |
621 entry->set_playing(true); | |
622 base::AtomicRefCountInc(&num_playing_streams_); | |
623 | |
624 // Inform the RenderProcessHost when audio starts playing for the first | |
625 // time. | |
626 if (base::AtomicRefCountIsOne(&num_playing_streams_)) { | |
627 BrowserThread::PostTask( | |
628 BrowserThread::UI, FROM_HERE, | |
629 base::Bind(&NotifyRenderProcessHostThatAudioStateChanged, | |
630 render_process_id_)); | |
631 } | |
632 } else { | |
633 entry->set_playing(false); | |
634 // Inform the RenderProcessHost when there is no more audio playing. | |
635 if (!base::AtomicRefCountDec(&num_playing_streams_)) { | |
636 BrowserThread::PostTask( | |
637 BrowserThread::UI, FROM_HERE, | |
638 base::Bind(&NotifyRenderProcessHostThatAudioStateChanged, | |
639 render_process_id_)); | |
640 } | |
641 } | |
642 } | 459 } |
643 | 460 |
644 bool AudioRendererHost::IsAuthorizationStarted(int stream_id) { | 461 bool AudioRendererHost::IsAuthorizationStarted(int stream_id) { |
645 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 462 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
646 return authorizations_.find(stream_id) != authorizations_.end(); | 463 return authorizations_.find(stream_id) != authorizations_.end(); |
647 } | 464 } |
648 | 465 |
649 bool AudioRendererHost::HasActiveAudio() { | 466 bool AudioRendererHost::HasActiveAudio() { |
650 return !base::AtomicRefCountIsZero(&num_playing_streams_); | 467 return !base::AtomicRefCountIsZero(&num_playing_streams_); |
651 } | 468 } |
652 | 469 |
653 void AudioRendererHost::OverrideDevicePermissionsForTesting(bool has_access) { | 470 void AudioRendererHost::OverrideDevicePermissionsForTesting(bool has_access) { |
654 authorization_handler_.OverridePermissionsForTesting(has_access); | 471 authorization_handler_.OverridePermissionsForTesting(has_access); |
655 } | 472 } |
656 } // namespace content | 473 } // namespace content |
OLD | NEW |