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

Side by Side Diff: content/browser/media/session/media_session_impl.cc

Issue 2475473002: Implement one-shot audio focus inside MediaSession (Closed)
Patch Set: . Created 4 years, 1 month 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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/media/session/media_session_impl.h" 5 #include "content/browser/media/session/media_session_impl.h"
6 6
7 #include "content/browser/media/session/audio_focus_delegate.h" 7 #include "content/browser/media/session/audio_focus_delegate.h"
8 #include "content/browser/media/session/media_session_player_observer.h" 8 #include "content/browser/media/session/media_session_player_observer.h"
9 #include "content/browser/web_contents/web_contents_impl.h" 9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/public/browser/web_contents.h" 10 #include "content/public/browser/web_contents.h"
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 if (!session) { 58 if (!session) {
59 CreateForWebContents(web_contents); 59 CreateForWebContents(web_contents);
60 session = FromWebContents(web_contents); 60 session = FromWebContents(web_contents);
61 session->Initialize(); 61 session->Initialize();
62 } 62 }
63 return session; 63 return session;
64 } 64 }
65 65
66 MediaSessionImpl::~MediaSessionImpl() { 66 MediaSessionImpl::~MediaSessionImpl() {
67 DCHECK(players_.empty()); 67 DCHECK(players_.empty());
68 DCHECK(pepper_players_.empty());
69 DCHECK(one_shot_players_.empty());
68 DCHECK(audio_focus_state_ == State::INACTIVE); 70 DCHECK(audio_focus_state_ == State::INACTIVE);
69 for (auto& observer : observers_) 71 for (auto& observer : observers_)
70 observer.MediaSessionDestroyed(); 72 observer.MediaSessionDestroyed();
71 } 73 }
72 74
73 void MediaSessionImpl::WebContentsDestroyed() { 75 void MediaSessionImpl::WebContentsDestroyed() {
74 // This should only work for tests. In production, all the players should have 76 // This should only work for tests. In production, all the players should have
75 // already been removed before WebContents is destroyed. 77 // already been removed before WebContents is destroyed.
76 78
77 // TODO(zqzhang): refactor MediaSessionImpl, maybe move the interface used to 79 // TODO(zqzhang): refactor MediaSessionImpl, maybe move the interface used to
(...skipping 16 matching lines...) Expand all
94 void MediaSessionImpl::SetMetadata( 96 void MediaSessionImpl::SetMetadata(
95 const base::Optional<MediaMetadata>& metadata) { 97 const base::Optional<MediaMetadata>& metadata) {
96 metadata_ = metadata; 98 metadata_ = metadata;
97 for (auto& observer : observers_) 99 for (auto& observer : observers_)
98 observer.MediaSessionMetadataChanged(metadata); 100 observer.MediaSessionMetadataChanged(metadata);
99 } 101 }
100 102
101 bool MediaSessionImpl::AddPlayer(MediaSessionPlayerObserver* observer, 103 bool MediaSessionImpl::AddPlayer(MediaSessionPlayerObserver* observer,
102 int player_id, 104 int player_id,
103 media::MediaContentType media_content_type) { 105 media::MediaContentType media_content_type) {
104 if (media_content_type == media::MediaContentType::Uncontrollable) 106 if (media_content_type == media::MediaContentType::OneShot)
105 return true; 107 return AddOneShotPlayer(observer, player_id);
106 if (media_content_type == media::MediaContentType::Pepper) 108 if (media_content_type == media::MediaContentType::Pepper)
107 return AddPepperPlayer(observer, player_id); 109 return AddPepperPlayer(observer, player_id);
108 110
109 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier()); 111 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier());
110 112
111 // Determine the audio focus type required for playing the new player.
112 // TODO(zqzhang): handle duckable and uncontrollable.
113 // See https://crbug.com/639277.
114 AudioFocusManager::AudioFocusType required_audio_focus_type; 113 AudioFocusManager::AudioFocusType required_audio_focus_type;
115 if (media_content_type == media::MediaContentType::Persistent) { 114 if (media_content_type == media::MediaContentType::Persistent) {
116 required_audio_focus_type = AudioFocusManager::AudioFocusType::Gain; 115 required_audio_focus_type = AudioFocusManager::AudioFocusType::Gain;
117 } else { 116 } else {
118 required_audio_focus_type = 117 required_audio_focus_type =
119 AudioFocusManager::AudioFocusType::GainTransientMayDuck; 118 AudioFocusManager::AudioFocusType::GainTransientMayDuck;
120 } 119 }
121 120
122 // If the audio focus is already granted and is of type Content, there is 121 // If the audio focus is already granted and is of type Content, there is
123 // nothing to do. If it is granted of type Transient the requested type is 122 // nothing to do. If it is granted of type Transient the requested type is
(...skipping 11 matching lines...) Expand all
135 134
136 if (audio_focus_state_ != State::ACTIVE) 135 if (audio_focus_state_ != State::ACTIVE)
137 return false; 136 return false;
138 137
139 // The session should be reset if a player is starting while all players are 138 // The session should be reset if a player is starting while all players are
140 // suspended. 139 // suspended.
141 if (old_audio_focus_state != State::ACTIVE) 140 if (old_audio_focus_state != State::ACTIVE)
142 players_.clear(); 141 players_.clear();
143 142
144 players_.insert(PlayerIdentifier(observer, player_id)); 143 players_.insert(PlayerIdentifier(observer, player_id));
145 UpdateWebContents(); 144 DispatchStateChange();
146 145
147 return true; 146 return true;
148 } 147 }
149 148
150 void MediaSessionImpl::RemovePlayer(MediaSessionPlayerObserver* observer, 149 void MediaSessionImpl::RemovePlayer(MediaSessionPlayerObserver* observer,
151 int player_id) { 150 int player_id) {
152 auto it = players_.find(PlayerIdentifier(observer, player_id)); 151 auto it = players_.find(PlayerIdentifier(observer, player_id));
153 if (it != players_.end()) 152 if (it != players_.end())
154 players_.erase(it); 153 players_.erase(it);
155 154
156 it = pepper_players_.find(PlayerIdentifier(observer, player_id)); 155 it = pepper_players_.find(PlayerIdentifier(observer, player_id));
157 if (it != pepper_players_.end()) 156 if (it != pepper_players_.end())
158 pepper_players_.erase(it); 157 pepper_players_.erase(it);
159 158
159 it = one_shot_players_.find(PlayerIdentifier(observer, player_id));
160 if (it != one_shot_players_.end()) {
161 one_shot_players_.erase(it);
162
163 // The session may become controllable after all one-shot players are
164 // removed.
165 if (one_shot_players_.empty())
166 DispatchStateChange();
167 }
168
160 AbandonSystemAudioFocusIfNeeded(); 169 AbandonSystemAudioFocusIfNeeded();
161 } 170 }
162 171
163 void MediaSessionImpl::RemovePlayers(MediaSessionPlayerObserver* observer) { 172 void MediaSessionImpl::RemovePlayers(MediaSessionPlayerObserver* observer) {
164 for (auto it = players_.begin(); it != players_.end();) { 173 for (auto it = players_.begin(); it != players_.end();) {
165 if (it->observer == observer) 174 if (it->observer == observer)
166 players_.erase(it++); 175 players_.erase(it++);
167 else 176 else
168 ++it; 177 ++it;
169 } 178 }
170 179
171 for (auto it = pepper_players_.begin(); it != pepper_players_.end();) { 180 for (auto it = pepper_players_.begin(); it != pepper_players_.end();) {
172 if (it->observer == observer) 181 if (it->observer == observer)
173 pepper_players_.erase(it++); 182 pepper_players_.erase(it++);
174 else 183 else
175 ++it; 184 ++it;
176 } 185 }
177 186
187 for (auto it = one_shot_players_.begin(); it != one_shot_players_.end();) {
188 if (it->observer == observer)
189 one_shot_players_.erase(it++);
190 else
191 ++it;
192
193 // The session may become controllable after all one-shot players are
194 // removed.
195 if (one_shot_players_.empty())
196 DispatchStateChange();
197 }
198
178 AbandonSystemAudioFocusIfNeeded(); 199 AbandonSystemAudioFocusIfNeeded();
179 } 200 }
180 201
181 void MediaSessionImpl::RecordSessionDuck() { 202 void MediaSessionImpl::RecordSessionDuck() {
182 uma_helper_.RecordSessionSuspended( 203 uma_helper_.RecordSessionSuspended(
183 MediaSessionSuspendedSource::SystemTransientDuck); 204 MediaSessionSuspendedSource::SystemTransientDuck);
184 } 205 }
185 206
186 void MediaSessionImpl::OnPlayerPaused(MediaSessionPlayerObserver* observer, 207 void MediaSessionImpl::OnPlayerPaused(MediaSessionPlayerObserver* observer,
187 int player_id) { 208 int player_id) {
188 // If a playback is completed, BrowserMediaPlayerManager will call 209 // If a playback is completed, BrowserMediaPlayerManager will call
189 // OnPlayerPaused() after RemovePlayer(). This is a workaround. 210 // OnPlayerPaused() after RemovePlayer(). This is a workaround.
190 // Also, this method may be called when a player that is not added 211 // Also, this method may be called when a player that is not added
191 // to this session (e.g. a silent video) is paused. MediaSessionImpl 212 // to this session (e.g. a silent video) is paused. MediaSessionImpl
192 // should ignore the paused player for this case. 213 // should ignore the paused player for this case.
193 if (!players_.count(PlayerIdentifier(observer, player_id)) && 214 if (!players_.count(PlayerIdentifier(observer, player_id)) &&
194 !pepper_players_.count(PlayerIdentifier(observer, player_id))) { 215 !pepper_players_.count(PlayerIdentifier(observer, player_id)) &&
216 !one_shot_players_.count(PlayerIdentifier(observer, player_id))) {
195 return; 217 return;
196 } 218 }
197 219
198 // If the player to be removed is a pepper player, or there is more than one 220 // If the player to be removed is a pepper player, or there is more than one
199 // observer, remove the paused one from the session. 221 // observer, remove the paused one from the session.
200 if (pepper_players_.count(PlayerIdentifier(observer, player_id)) || 222 if (pepper_players_.count(PlayerIdentifier(observer, player_id)) ||
201 players_.size() != 1) { 223 players_.size() != 1) {
202 RemovePlayer(observer, player_id); 224 RemovePlayer(observer, player_id);
203 return; 225 return;
204 } 226 }
205 227
228 // If the player is a one-shot player, just remove it since it is not expected
229 // to resume a one-shot player via resuming MediaSession.
230 if (one_shot_players_.count(PlayerIdentifier(observer, player_id))) {
231 RemovePlayer(observer, player_id);
232 return;
233 }
234
206 // Otherwise, suspend the session. 235 // Otherwise, suspend the session.
207 DCHECK(!IsSuspended()); 236 DCHECK(!IsSuspended());
208 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED); 237 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED);
209 } 238 }
210 239
211 void MediaSessionImpl::Resume(SuspendType suspend_type) { 240 void MediaSessionImpl::Resume(SuspendType suspend_type) {
212 DCHECK(IsReallySuspended()); 241 DCHECK(IsReallySuspended());
213 242
214 // When the resume requests comes from another source than system, audio focus 243 // When the resume requests comes from another source than system, audio focus
215 // must be requested. 244 // must be requested.
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 return audio_focus_state_ == State::SUSPENDED; 316 return audio_focus_state_ == State::SUSPENDED;
288 } 317 }
289 318
290 bool MediaSessionImpl::IsSuspended() const { 319 bool MediaSessionImpl::IsSuspended() const {
291 // TODO(mlamouri): should be == State::SUSPENDED. 320 // TODO(mlamouri): should be == State::SUSPENDED.
292 return audio_focus_state_ != State::ACTIVE; 321 return audio_focus_state_ != State::ACTIVE;
293 } 322 }
294 323
295 bool MediaSessionImpl::IsControllable() const { 324 bool MediaSessionImpl::IsControllable() const {
296 // Only media session having focus Gain can be controllable unless it is 325 // Only media session having focus Gain can be controllable unless it is
297 // inactive. 326 // inactive. Also, the session will be uncontrollable if it contains one-shot
327 // players.
298 return audio_focus_state_ != State::INACTIVE && 328 return audio_focus_state_ != State::INACTIVE &&
299 audio_focus_type_ == AudioFocusManager::AudioFocusType::Gain; 329 audio_focus_type_ == AudioFocusManager::AudioFocusType::Gain &&
330 (one_shot_players_.empty());
300 } 331 }
301 332
302 bool MediaSessionImpl::HasPepper() const { 333 bool MediaSessionImpl::HasPepper() const {
303 return !pepper_players_.empty(); 334 return !pepper_players_.empty();
304 } 335 }
305 336
306 std::unique_ptr<base::CallbackList<void(MediaSessionImpl::State)>::Subscription> 337 std::unique_ptr<base::CallbackList<void(MediaSessionImpl::State)>::Subscription>
307 MediaSessionImpl::RegisterMediaSessionStateChangedCallbackForTest( 338 MediaSessionImpl::RegisterMediaSessionStateChangedCallbackForTest(
308 const StateChangedCallback& cb) { 339 const StateChangedCallback& cb) {
309 return media_session_state_listeners_.Add(cb); 340 return media_session_state_listeners_.Add(cb);
310 } 341 }
311 342
312 void MediaSessionImpl::SetDelegateForTests( 343 void MediaSessionImpl::SetDelegateForTests(
313 std::unique_ptr<AudioFocusDelegate> delegate) { 344 std::unique_ptr<AudioFocusDelegate> delegate) {
314 delegate_ = std::move(delegate); 345 delegate_ = std::move(delegate);
315 } 346 }
316 347
317 bool MediaSessionImpl::IsActiveForTest() const { 348 bool MediaSessionImpl::IsActiveForTest() const {
318 return audio_focus_state_ == State::ACTIVE; 349 return audio_focus_state_ == State::ACTIVE;
319 } 350 }
320 351
321 MediaSessionUmaHelper* MediaSessionImpl::uma_helper_for_test() { 352 MediaSessionUmaHelper* MediaSessionImpl::uma_helper_for_test() {
322 return &uma_helper_; 353 return &uma_helper_;
323 } 354 }
324 355
325 void MediaSessionImpl::RemoveAllPlayersForTest() { 356 void MediaSessionImpl::RemoveAllPlayersForTest() {
326 players_.clear(); 357 players_.clear();
358 pepper_players_.clear();
359 one_shot_players_.clear();
whywhat 2016/11/03 14:32:17 nit: I feel there's more code duplication here w.r
Zhiqiang Zhang (Slow) 2016/11/04 14:10:54 Yeah, I feel this too. But as pepper_players_ is l
327 AbandonSystemAudioFocusIfNeeded(); 360 AbandonSystemAudioFocusIfNeeded();
328 } 361 }
329 362
330 void MediaSessionImpl::OnSuspendInternal(SuspendType suspend_type, 363 void MediaSessionImpl::OnSuspendInternal(SuspendType suspend_type,
331 State new_state) { 364 State new_state) {
332 DCHECK(!HasPepper()); 365 DCHECK(!HasPepper());
333 366
334 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE); 367 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE);
335 // UI suspend cannot use State::INACTIVE. 368 // UI suspend cannot use State::INACTIVE.
336 DCHECK(suspend_type == SuspendType::SYSTEM || new_state == State::SUSPENDED); 369 DCHECK(suspend_type == SuspendType::SYSTEM || new_state == State::SUSPENDED);
337 370
371 if (!one_shot_players_.empty())
372 return;
373
338 if (audio_focus_state_ != State::ACTIVE) 374 if (audio_focus_state_ != State::ACTIVE)
339 return; 375 return;
340 376
341 switch (suspend_type) { 377 switch (suspend_type) {
342 case SuspendType::UI: 378 case SuspendType::UI:
343 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI); 379 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI);
344 break; 380 break;
345 case SuspendType::SYSTEM: 381 case SuspendType::SYSTEM:
346 switch (new_state) { 382 switch (new_state) {
347 case State::SUSPENDED: 383 case State::SUSPENDED:
(...skipping 21 matching lines...) Expand all
369 // SuspendType::CONTENT happens when the suspend action came from 405 // SuspendType::CONTENT happens when the suspend action came from
370 // the page in which case the player is already paused. 406 // the page in which case the player is already paused.
371 // Otherwise, the players need to be paused. 407 // Otherwise, the players need to be paused.
372 for (const auto& it : players_) 408 for (const auto& it : players_)
373 it.observer->OnSuspend(it.player_id); 409 it.observer->OnSuspend(it.player_id);
374 } 410 }
375 411
376 for (const auto& it : pepper_players_) 412 for (const auto& it : pepper_players_)
377 it.observer->OnSetVolumeMultiplier(it.player_id, kDuckingVolumeMultiplier); 413 it.observer->OnSetVolumeMultiplier(it.player_id, kDuckingVolumeMultiplier);
378 414
379 UpdateWebContents(); 415 DispatchStateChange();
380 } 416 }
381 417
382 void MediaSessionImpl::OnResumeInternal(SuspendType suspend_type) { 418 void MediaSessionImpl::OnResumeInternal(SuspendType suspend_type) {
383 if (suspend_type == SuspendType::SYSTEM && suspend_type_ != suspend_type) 419 if (suspend_type == SuspendType::SYSTEM && suspend_type_ != suspend_type)
384 return; 420 return;
385 421
386 SetAudioFocusState(State::ACTIVE); 422 SetAudioFocusState(State::ACTIVE);
387 423
388 for (const auto& it : players_) 424 for (const auto& it : players_)
389 it.observer->OnResume(it.player_id); 425 it.observer->OnResume(it.player_id);
390 426
391 for (const auto& it : pepper_players_) 427 for (const auto& it : pepper_players_)
392 it.observer->OnSetVolumeMultiplier(it.player_id, GetVolumeMultiplier()); 428 it.observer->OnSetVolumeMultiplier(it.player_id, GetVolumeMultiplier());
393 429
394 UpdateWebContents(); 430 DispatchStateChange();
395 } 431 }
396 432
397 MediaSessionImpl::MediaSessionImpl(WebContents* web_contents) 433 MediaSessionImpl::MediaSessionImpl(WebContents* web_contents)
398 : WebContentsObserver(web_contents), 434 : WebContentsObserver(web_contents),
399 audio_focus_state_(State::INACTIVE), 435 audio_focus_state_(State::INACTIVE),
400 audio_focus_type_( 436 audio_focus_type_(
401 AudioFocusManager::AudioFocusType::GainTransientMayDuck), 437 AudioFocusManager::AudioFocusType::GainTransientMayDuck),
402 is_ducking_(false) { 438 is_ducking_(false) {
403 #if defined(OS_ANDROID) 439 #if defined(OS_ANDROID)
404 session_android_.reset(new MediaSessionAndroid(this)); 440 session_android_.reset(new MediaSessionAndroid(this));
(...skipping 11 matching lines...) Expand all
416 452
417 // MediaSessionImpl must change its state & audio focus type AFTER requesting 453 // MediaSessionImpl must change its state & audio focus type AFTER requesting
418 // audio focus. 454 // audio focus.
419 SetAudioFocusState(result ? State::ACTIVE : State::INACTIVE); 455 SetAudioFocusState(result ? State::ACTIVE : State::INACTIVE);
420 audio_focus_type_ = audio_focus_type; 456 audio_focus_type_ = audio_focus_type;
421 return result; 457 return result;
422 } 458 }
423 459
424 void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() { 460 void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
425 if (audio_focus_state_ == State::INACTIVE || !players_.empty() || 461 if (audio_focus_state_ == State::INACTIVE || !players_.empty() ||
426 !pepper_players_.empty()) { 462 !pepper_players_.empty() || !one_shot_players_.empty()) {
427 return; 463 return;
428 } 464 }
429 delegate_->AbandonAudioFocus(); 465 delegate_->AbandonAudioFocus();
430 466
431 SetAudioFocusState(State::INACTIVE); 467 SetAudioFocusState(State::INACTIVE);
432 UpdateWebContents(); 468 DispatchStateChange();
433 } 469 }
434 470
435 void MediaSessionImpl::UpdateWebContents() { 471 void MediaSessionImpl::DispatchStateChange() {
436 media_session_state_listeners_.Notify(audio_focus_state_); 472 media_session_state_listeners_.Notify(audio_focus_state_);
437 for (auto& observer : observers_) 473 for (auto& observer : observers_)
438 observer.MediaSessionStateChanged(IsControllable(), IsSuspended()); 474 observer.MediaSessionStateChanged(IsControllable(), IsSuspended());
439 } 475 }
440 476
441 void MediaSessionImpl::SetAudioFocusState(State audio_focus_state) { 477 void MediaSessionImpl::SetAudioFocusState(State audio_focus_state) {
442 if (audio_focus_state == audio_focus_state_) 478 if (audio_focus_state == audio_focus_state_)
443 return; 479 return;
444 480
445 audio_focus_state_ = audio_focus_state; 481 audio_focus_state_ = audio_focus_state;
(...skipping 13 matching lines...) Expand all
459 bool MediaSessionImpl::AddPepperPlayer(MediaSessionPlayerObserver* observer, 495 bool MediaSessionImpl::AddPepperPlayer(MediaSessionPlayerObserver* observer,
460 int player_id) { 496 int player_id) {
461 bool success = 497 bool success =
462 RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain); 498 RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain);
463 DCHECK(success); 499 DCHECK(success);
464 500
465 pepper_players_.insert(PlayerIdentifier(observer, player_id)); 501 pepper_players_.insert(PlayerIdentifier(observer, player_id));
466 502
467 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier()); 503 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier());
468 504
505 DispatchStateChange();
469 return true; 506 return true;
470 } 507 }
471 508
509 bool MediaSessionImpl::AddOneShotPlayer(MediaSessionPlayerObserver* observer,
510 int player_id) {
511 // Don't check whether the request is successful or not. One-shot players
512 // should play uninterrupted.
whywhat 2016/11/03 14:32:16 that means we'd play them even if the user is in t
Zhiqiang Zhang (Slow) 2016/11/04 14:10:54 OK, let's give a chance for the players to respond
513 RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain);
514
515 one_shot_players_.insert(PlayerIdentifier(observer, player_id));
516
517 DispatchStateChange();
518 return true;
519 }
520
472 } // namespace content 521 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698