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/renderer/media/webrtc_audio_renderer.h" | 5 #include "content/renderer/media/webrtc_audio_renderer.h" |
6 | 6 |
7 #include <algorithm> | |
7 #include <utility> | 8 #include <utility> |
8 | 9 |
9 #include "base/bind.h" | 10 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
11 #include "base/location.h" | 12 #include "base/location.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
14 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
15 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
16 #include "build/build_config.h" | 17 #include "build/build_config.h" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
48 // This is a simple wrapper class that's handed out to users of a shared | 49 // This is a simple wrapper class that's handed out to users of a shared |
49 // WebRtcAudioRenderer instance. This class maintains the per-user 'playing' | 50 // WebRtcAudioRenderer instance. This class maintains the per-user 'playing' |
50 // and 'started' states to avoid problems related to incorrect usage which | 51 // and 'started' states to avoid problems related to incorrect usage which |
51 // might violate the implementation assumptions inside WebRtcAudioRenderer | 52 // might violate the implementation assumptions inside WebRtcAudioRenderer |
52 // (see the play reference count). | 53 // (see the play reference count). |
53 class SharedAudioRenderer : public MediaStreamAudioRenderer { | 54 class SharedAudioRenderer : public MediaStreamAudioRenderer { |
54 public: | 55 public: |
55 // Callback definition for a callback that is called when when Play(), Pause() | 56 // Callback definition for a callback that is called when when Play(), Pause() |
56 // or SetVolume are called (whenever the internal |playing_state_| changes). | 57 // or SetVolume are called (whenever the internal |playing_state_| changes). |
57 typedef base::Callback<void(const blink::WebMediaStream&, | 58 typedef base::Callback<void(const blink::WebMediaStream&, |
58 WebRtcAudioRenderer::PlayingState*)> | 59 WebRtcAudioRenderer::PlayingState*, |
60 bool playing_state_deleted)> | |
59 OnPlayStateChanged; | 61 OnPlayStateChanged; |
60 | 62 |
61 SharedAudioRenderer(const scoped_refptr<MediaStreamAudioRenderer>& delegate, | 63 SharedAudioRenderer(const scoped_refptr<MediaStreamAudioRenderer>& delegate, |
62 const blink::WebMediaStream& media_stream, | 64 const blink::WebMediaStream& media_stream, |
63 const OnPlayStateChanged& on_play_state_changed) | 65 const OnPlayStateChanged& on_play_state_changed) |
64 : delegate_(delegate), | 66 : delegate_(delegate), |
65 media_stream_(media_stream), | 67 media_stream_(media_stream), |
66 started_(false), | 68 started_(false), |
67 on_play_state_changed_(on_play_state_changed) { | 69 on_play_state_changed_(on_play_state_changed) { |
68 DCHECK(!on_play_state_changed_.is_null()); | 70 DCHECK(!on_play_state_changed_.is_null()); |
69 DCHECK(!media_stream_.isNull()); | 71 DCHECK(!media_stream_.isNull()); |
70 } | 72 } |
71 | 73 |
72 protected: | 74 protected: |
73 ~SharedAudioRenderer() override { | 75 ~SharedAudioRenderer() override { |
74 DCHECK(thread_checker_.CalledOnValidThread()); | 76 DCHECK(thread_checker_.CalledOnValidThread()); |
75 DVLOG(1) << __func__; | 77 DVLOG(1) << __func__; |
76 Stop(); | 78 Stop(); |
79 // Make extra sure no reference to the playing state is left. | |
80 on_play_state_changed_.Run(media_stream_, &playing_state_, true); | |
tommi (sloooow) - chröme
2017/03/17 12:44:08
did you consider having a separate callback for wh
Max Morin
2017/03/17 14:08:04
I went ahead and kept delegate_ as a pointer to in
| |
77 } | 81 } |
78 | 82 |
79 void Start() override { | 83 void Start() override { |
80 DCHECK(thread_checker_.CalledOnValidThread()); | 84 DCHECK(thread_checker_.CalledOnValidThread()); |
81 if (started_) | 85 if (started_) |
82 return; | 86 return; |
83 started_ = true; | 87 started_ = true; |
84 delegate_->Start(); | 88 delegate_->Start(); |
85 } | 89 } |
86 | 90 |
87 void Play() override { | 91 void Play() override { |
88 DCHECK(thread_checker_.CalledOnValidThread()); | 92 DCHECK(thread_checker_.CalledOnValidThread()); |
89 DCHECK(started_); | 93 DCHECK(started_); |
90 if (playing_state_.playing()) | 94 if (playing_state_.playing()) |
91 return; | 95 return; |
92 playing_state_.set_playing(true); | 96 playing_state_.set_playing(true); |
93 on_play_state_changed_.Run(media_stream_, &playing_state_); | 97 on_play_state_changed_.Run(media_stream_, &playing_state_, false); |
94 } | 98 } |
95 | 99 |
96 void Pause() override { | 100 void Pause() override { |
97 DCHECK(thread_checker_.CalledOnValidThread()); | 101 DCHECK(thread_checker_.CalledOnValidThread()); |
98 DCHECK(started_); | 102 DCHECK(started_); |
99 if (!playing_state_.playing()) | 103 if (!playing_state_.playing()) |
100 return; | 104 return; |
101 playing_state_.set_playing(false); | 105 playing_state_.set_playing(false); |
102 on_play_state_changed_.Run(media_stream_, &playing_state_); | 106 on_play_state_changed_.Run(media_stream_, &playing_state_, false); |
103 } | 107 } |
104 | 108 |
105 void Stop() override { | 109 void Stop() override { |
106 DCHECK(thread_checker_.CalledOnValidThread()); | 110 DCHECK(thread_checker_.CalledOnValidThread()); |
107 if (!started_) | 111 if (!started_) |
108 return; | 112 return; |
109 Pause(); | 113 Pause(); |
110 started_ = false; | 114 started_ = false; |
111 delegate_->Stop(); | 115 delegate_->Stop(); |
112 } | 116 } |
113 | 117 |
114 void SetVolume(float volume) override { | 118 void SetVolume(float volume) override { |
115 DCHECK(thread_checker_.CalledOnValidThread()); | 119 DCHECK(thread_checker_.CalledOnValidThread()); |
116 DCHECK(volume >= 0.0f && volume <= 1.0f); | 120 DCHECK(volume >= 0.0f && volume <= 1.0f); |
117 playing_state_.set_volume(volume); | 121 playing_state_.set_volume(volume); |
118 on_play_state_changed_.Run(media_stream_, &playing_state_); | 122 on_play_state_changed_.Run(media_stream_, &playing_state_, false); |
119 } | 123 } |
120 | 124 |
121 media::OutputDeviceInfo GetOutputDeviceInfo() override { | 125 media::OutputDeviceInfo GetOutputDeviceInfo() override { |
122 DCHECK(thread_checker_.CalledOnValidThread()); | 126 DCHECK(thread_checker_.CalledOnValidThread()); |
123 return delegate_->GetOutputDeviceInfo(); | 127 return delegate_->GetOutputDeviceInfo(); |
124 } | 128 } |
125 | 129 |
126 void SwitchOutputDevice( | 130 void SwitchOutputDevice( |
127 const std::string& device_id, | 131 const std::string& device_id, |
128 const url::Origin& security_origin, | 132 const url::Origin& security_origin, |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
242 | 246 |
243 void WebRtcAudioRenderer::Play() { | 247 void WebRtcAudioRenderer::Play() { |
244 DVLOG(1) << "WebRtcAudioRenderer::Play()"; | 248 DVLOG(1) << "WebRtcAudioRenderer::Play()"; |
245 DCHECK(thread_checker_.CalledOnValidThread()); | 249 DCHECK(thread_checker_.CalledOnValidThread()); |
246 | 250 |
247 if (playing_state_.playing()) | 251 if (playing_state_.playing()) |
248 return; | 252 return; |
249 | 253 |
250 playing_state_.set_playing(true); | 254 playing_state_.set_playing(true); |
251 | 255 |
252 OnPlayStateChanged(media_stream_, &playing_state_); | 256 OnPlayStateChanged(media_stream_, &playing_state_, false); |
253 } | 257 } |
254 | 258 |
255 void WebRtcAudioRenderer::EnterPlayState() { | 259 void WebRtcAudioRenderer::EnterPlayState() { |
256 DVLOG(1) << "WebRtcAudioRenderer::EnterPlayState()"; | 260 DVLOG(1) << "WebRtcAudioRenderer::EnterPlayState()"; |
257 DCHECK(thread_checker_.CalledOnValidThread()); | 261 DCHECK(thread_checker_.CalledOnValidThread()); |
258 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; | 262 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; |
259 base::AutoLock auto_lock(lock_); | 263 base::AutoLock auto_lock(lock_); |
260 if (state_ == UNINITIALIZED) | 264 if (state_ == UNINITIALIZED) |
261 return; | 265 return; |
262 | 266 |
(...skipping 11 matching lines...) Expand all Loading... | |
274 } | 278 } |
275 | 279 |
276 void WebRtcAudioRenderer::Pause() { | 280 void WebRtcAudioRenderer::Pause() { |
277 DVLOG(1) << "WebRtcAudioRenderer::Pause()"; | 281 DVLOG(1) << "WebRtcAudioRenderer::Pause()"; |
278 DCHECK(thread_checker_.CalledOnValidThread()); | 282 DCHECK(thread_checker_.CalledOnValidThread()); |
279 if (!playing_state_.playing()) | 283 if (!playing_state_.playing()) |
280 return; | 284 return; |
281 | 285 |
282 playing_state_.set_playing(false); | 286 playing_state_.set_playing(false); |
283 | 287 |
284 OnPlayStateChanged(media_stream_, &playing_state_); | 288 OnPlayStateChanged(media_stream_, &playing_state_, false); |
285 } | 289 } |
286 | 290 |
287 void WebRtcAudioRenderer::EnterPauseState() { | 291 void WebRtcAudioRenderer::EnterPauseState() { |
288 DVLOG(1) << "WebRtcAudioRenderer::EnterPauseState()"; | 292 DVLOG(1) << "WebRtcAudioRenderer::EnterPauseState()"; |
289 DCHECK(thread_checker_.CalledOnValidThread()); | 293 DCHECK(thread_checker_.CalledOnValidThread()); |
290 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; | 294 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; |
291 base::AutoLock auto_lock(lock_); | 295 base::AutoLock auto_lock(lock_); |
292 if (state_ == UNINITIALIZED) | 296 if (state_ == UNINITIALIZED) |
293 return; | 297 return; |
294 | 298 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 // callback may currently be executing and trying to grab the lock while we're | 337 // callback may currently be executing and trying to grab the lock while we're |
334 // stopping the thread on which it runs. | 338 // stopping the thread on which it runs. |
335 sink_->Stop(); | 339 sink_->Stop(); |
336 } | 340 } |
337 | 341 |
338 void WebRtcAudioRenderer::SetVolume(float volume) { | 342 void WebRtcAudioRenderer::SetVolume(float volume) { |
339 DCHECK(thread_checker_.CalledOnValidThread()); | 343 DCHECK(thread_checker_.CalledOnValidThread()); |
340 DCHECK(volume >= 0.0f && volume <= 1.0f); | 344 DCHECK(volume >= 0.0f && volume <= 1.0f); |
341 | 345 |
342 playing_state_.set_volume(volume); | 346 playing_state_.set_volume(volume); |
343 OnPlayStateChanged(media_stream_, &playing_state_); | 347 OnPlayStateChanged(media_stream_, &playing_state_, false); |
344 } | 348 } |
345 | 349 |
346 media::OutputDeviceInfo WebRtcAudioRenderer::GetOutputDeviceInfo() { | 350 media::OutputDeviceInfo WebRtcAudioRenderer::GetOutputDeviceInfo() { |
347 DCHECK(thread_checker_.CalledOnValidThread()); | 351 DCHECK(thread_checker_.CalledOnValidThread()); |
348 return sink_ ? sink_->GetOutputDeviceInfo() : media::OutputDeviceInfo(); | 352 return sink_ ? sink_->GetOutputDeviceInfo() : media::OutputDeviceInfo(); |
349 } | 353 } |
350 | 354 |
351 base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const { | 355 base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const { |
352 DCHECK(thread_checker_.CalledOnValidThread()); | 356 DCHECK(thread_checker_.CalledOnValidThread()); |
353 base::AutoLock auto_lock(lock_); | 357 base::AutoLock auto_lock(lock_); |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
559 array.erase(state_it); | 563 array.erase(state_it); |
560 | 564 |
561 if (array.empty()) | 565 if (array.empty()) |
562 source_playing_states_.erase(found); | 566 source_playing_states_.erase(found); |
563 | 567 |
564 return true; | 568 return true; |
565 } | 569 } |
566 | 570 |
567 void WebRtcAudioRenderer::OnPlayStateChanged( | 571 void WebRtcAudioRenderer::OnPlayStateChanged( |
568 const blink::WebMediaStream& media_stream, | 572 const blink::WebMediaStream& media_stream, |
569 PlayingState* state) { | 573 PlayingState* state, |
574 bool playing_state_deleted) { | |
570 DCHECK(thread_checker_.CalledOnValidThread()); | 575 DCHECK(thread_checker_.CalledOnValidThread()); |
576 if (playing_state_deleted) { | |
577 // It is possible we associated |state| to a source for a track that is no | |
578 // longer in |media_stream|. We iterate over |source_playing_states_| to | |
579 // ensure there are no dangling pointers to |state| there. See | |
580 // crbug.com/697256. | |
581 for (auto it = source_playing_states_.begin(); | |
582 it != source_playing_states_.end();) { | |
583 PlayingStates& states = it->second; | |
584 // We cannot use RemovePlayingState as it might invalidate |it|. | |
585 states.erase(std::remove(states.begin(), states.end(), state), | |
586 states.end()); | |
587 if (states.empty()) | |
588 it = source_playing_states_.erase(it); | |
589 else | |
590 ++it; | |
591 } | |
592 return; | |
593 } | |
571 blink::WebVector<blink::WebMediaStreamTrack> web_tracks; | 594 blink::WebVector<blink::WebMediaStreamTrack> web_tracks; |
572 media_stream.audioTracks(web_tracks); | 595 media_stream.audioTracks(web_tracks); |
573 | 596 |
574 for (const blink::WebMediaStreamTrack& web_track : web_tracks) { | 597 for (const blink::WebMediaStreamTrack& web_track : web_tracks) { |
575 // WebRtcAudioRenderer can only render audio tracks received from a remote | 598 // WebRtcAudioRenderer can only render audio tracks received from a remote |
576 // peer. Since the actual MediaStream is mutable from JavaScript, we need | 599 // peer. Since the actual MediaStream is mutable from JavaScript, we need |
577 // to make sure |web_track| is actually a remote track. | 600 // to make sure |web_track| is actually a remote track. |
578 PeerConnectionRemoteAudioTrack* const remote_track = | 601 PeerConnectionRemoteAudioTrack* const remote_track = |
579 PeerConnectionRemoteAudioTrack::From( | 602 PeerConnectionRemoteAudioTrack::From( |
580 MediaStreamAudioTrack::From(web_track)); | 603 MediaStreamAudioTrack::From(web_track)); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
658 sink_params_ = new_sink_params; | 681 sink_params_ = new_sink_params; |
659 } | 682 } |
660 | 683 |
661 // Specify the latency info to be passed to the browser side. | 684 // Specify the latency info to be passed to the browser side. |
662 new_sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType( | 685 new_sink_params.set_latency_tag(AudioDeviceFactory::GetSourceLatencyType( |
663 AudioDeviceFactory::AudioDeviceFactory::kSourceWebRtc)); | 686 AudioDeviceFactory::AudioDeviceFactory::kSourceWebRtc)); |
664 sink_->Initialize(new_sink_params, this); | 687 sink_->Initialize(new_sink_params, this); |
665 } | 688 } |
666 | 689 |
667 } // namespace content | 690 } // namespace content |
OLD | NEW |