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

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

Issue 2526533002: Allow MediaSession in iframes to be routed (Closed)
Patch Set: nits Created 4 years 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 <algorithm> 7 #include <algorithm>
8 #include "content/browser/media/session/audio_focus_delegate.h" 8 #include "content/browser/media/session/audio_focus_delegate.h"
9 #include "content/browser/media/session/media_session_controller.h"
9 #include "content/browser/media/session/media_session_player_observer.h" 10 #include "content/browser/media/session/media_session_player_observer.h"
10 #include "content/browser/media/session/media_session_service_impl.h" 11 #include "content/browser/media/session/media_session_service_impl.h"
11 #include "content/browser/web_contents/web_contents_impl.h" 12 #include "content/browser/web_contents/web_contents_impl.h"
12 #include "content/public/browser/media_session.h" 13 #include "content/public/browser/media_session.h"
13 #include "content/public/browser/media_session_observer.h" 14 #include "content/public/browser/media_session_observer.h"
14 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
15 #include "media/base/media_content_type.h" 16 #include "media/base/media_content_type.h"
16 17
17 #if defined(OS_ANDROID) 18 #if defined(OS_ANDROID)
18 #include "content/browser/media/session/media_session_android.h" 19 #include "content/browser/media/session/media_session_android.h"
19 #endif // defined(OS_ANDROID) 20 #endif // defined(OS_ANDROID)
20 21
21 namespace content { 22 namespace content {
22 23
23 namespace { 24 namespace {
24 25
25 const double kDefaultVolumeMultiplier = 1.0; 26 const double kDefaultVolumeMultiplier = 1.0;
26 const double kDuckingVolumeMultiplier = 0.2; 27 const double kDuckingVolumeMultiplier = 0.2;
27 28
29 using MapRenderFrameHostToDepth = std::map<RenderFrameHost*, size_t>;
30
31 size_t ComputeFrameDepth(RenderFrameHost* rfh,
32 MapRenderFrameHostToDepth* map_rfh_to_depth) {
33 size_t depth = 0;
whywhat 2016/12/02 16:09:17 nit: IIUC, you could DCHECK(rfh && map->find(rfh)
Zhiqiang Zhang (Slow) 2016/12/05 11:37:32 Added DCHECK(rfh) here. I prefer not to DCHECK(ma
34 RenderFrameHost* current_frame = rfh;
35 while (current_frame) {
36 if (map_rfh_to_depth->count(current_frame)) {
whywhat 2016/12/02 16:09:18 you could use an iterator to avoid double lookup i
Zhiqiang Zhang (Slow) 2016/12/05 11:37:32 Done.
37 depth += (*map_rfh_to_depth)[current_frame];
38 break;
39 }
40 ++depth;
41 current_frame = current_frame->GetParent();
42 }
43 (*map_rfh_to_depth)[rfh] = depth;
44 return depth;
45 }
46
28 } // anonymous namespace 47 } // anonymous namespace
29 48
30 using MediaSessionSuspendedSource = 49 using MediaSessionSuspendedSource =
31 MediaSessionUmaHelper::MediaSessionSuspendedSource; 50 MediaSessionUmaHelper::MediaSessionSuspendedSource;
32 51
33 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSessionImpl); 52 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSessionImpl);
34 53
35 MediaSessionImpl::PlayerIdentifier::PlayerIdentifier( 54 MediaSessionImpl::PlayerIdentifier::PlayerIdentifier(
36 MediaSessionPlayerObserver* observer, 55 MediaSessionPlayerObserver* observer,
37 int player_id) 56 int player_id)
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 // TODO(zqzhang): refactor MediaSessionImpl, maybe move the interface used to 101 // TODO(zqzhang): refactor MediaSessionImpl, maybe move the interface used to
83 // talk with AudioFocusManager out to a seperate class. The AudioFocusManager 102 // talk with AudioFocusManager out to a seperate class. The AudioFocusManager
84 // unit tests then could mock the interface and abandon audio focus when 103 // unit tests then could mock the interface and abandon audio focus when
85 // WebContents is destroyed. See https://crbug.com/651069 104 // WebContents is destroyed. See https://crbug.com/651069
86 normal_players_.clear(); 105 normal_players_.clear();
87 pepper_players_.clear(); 106 pepper_players_.clear();
88 one_shot_players_.clear(); 107 one_shot_players_.clear();
89 AbandonSystemAudioFocusIfNeeded(); 108 AbandonSystemAudioFocusIfNeeded();
90 } 109 }
91 110
92 void MediaSessionImpl::SetMediaSessionService(
93 MediaSessionServiceImpl* service) {
94 service_ = service;
95 }
96
97 void MediaSessionImpl::AddObserver(MediaSessionObserver* observer) { 111 void MediaSessionImpl::AddObserver(MediaSessionObserver* observer) {
98 observers_.AddObserver(observer); 112 observers_.AddObserver(observer);
99 } 113 }
100 114
101 void MediaSessionImpl::RemoveObserver(MediaSessionObserver* observer) { 115 void MediaSessionImpl::RemoveObserver(MediaSessionObserver* observer) {
102 observers_.RemoveObserver(observer); 116 observers_.RemoveObserver(observer);
103 } 117 }
104 118
105 void MediaSessionImpl::SetMetadata( 119 void MediaSessionImpl::NotifyMediaSessionMetadataChange(
106 const base::Optional<MediaMetadata>& metadata) { 120 const base::Optional<MediaMetadata>& metadata) {
107 metadata_ = metadata;
108 for (auto& observer : observers_) 121 for (auto& observer : observers_)
109 observer.MediaSessionMetadataChanged(metadata); 122 observer.MediaSessionMetadataChanged(metadata);
110 } 123 }
111 124
125 void MediaSessionImpl::NotifyMediaSessionActionsChange(
126 const std::set<blink::mojom::MediaSessionAction>& actions) {
127 for (auto& observer : observers_)
128 observer.MediaSessionActionsChanged(actions);
129 }
130
112 bool MediaSessionImpl::AddPlayer(MediaSessionPlayerObserver* observer, 131 bool MediaSessionImpl::AddPlayer(MediaSessionPlayerObserver* observer,
113 int player_id, 132 int player_id,
114 media::MediaContentType media_content_type) { 133 media::MediaContentType media_content_type) {
115 if (media_content_type == media::MediaContentType::OneShot) 134 if (media_content_type == media::MediaContentType::OneShot)
116 return AddOneShotPlayer(observer, player_id); 135 return AddOneShotPlayer(observer, player_id);
117 if (media_content_type == media::MediaContentType::Pepper) 136 if (media_content_type == media::MediaContentType::Pepper)
118 return AddPepperPlayer(observer, player_id); 137 return AddPepperPlayer(observer, player_id);
119 138
120 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier()); 139 observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier());
121 140
(...skipping 21 matching lines...) Expand all
143 162
144 if (audio_focus_state_ != State::ACTIVE) 163 if (audio_focus_state_ != State::ACTIVE)
145 return false; 164 return false;
146 165
147 // The session should be reset if a player is starting while all players are 166 // The session should be reset if a player is starting while all players are
148 // suspended. 167 // suspended.
149 if (old_audio_focus_state != State::ACTIVE) 168 if (old_audio_focus_state != State::ACTIVE)
150 normal_players_.clear(); 169 normal_players_.clear();
151 170
152 normal_players_.insert(PlayerIdentifier(observer, player_id)); 171 normal_players_.insert(PlayerIdentifier(observer, player_id));
172
173 UpdateRoutedService();
153 NotifyAboutStateChange(); 174 NotifyAboutStateChange();
154
155 return true; 175 return true;
156 } 176 }
157 177
158 void MediaSessionImpl::RemovePlayer(MediaSessionPlayerObserver* observer, 178 void MediaSessionImpl::RemovePlayer(MediaSessionPlayerObserver* observer,
159 int player_id) { 179 int player_id) {
160 bool was_controllable = IsControllable(); 180 bool was_controllable = IsControllable();
161 181
162 PlayerIdentifier identifier(observer, player_id); 182 PlayerIdentifier identifier(observer, player_id);
163 183
164 auto it = normal_players_.find(identifier); 184 auto it = normal_players_.find(identifier);
165 if (it != normal_players_.end()) 185 if (it != normal_players_.end())
166 normal_players_.erase(it); 186 normal_players_.erase(it);
167 187
168 it = pepper_players_.find(identifier); 188 it = pepper_players_.find(identifier);
169 if (it != pepper_players_.end()) 189 if (it != pepper_players_.end())
170 pepper_players_.erase(it); 190 pepper_players_.erase(it);
171 191
172 it = one_shot_players_.find(identifier); 192 it = one_shot_players_.find(identifier);
173 if (it != one_shot_players_.end()) 193 if (it != one_shot_players_.end())
174 one_shot_players_.erase(it); 194 one_shot_players_.erase(it);
175 195
176 AbandonSystemAudioFocusIfNeeded(); 196 AbandonSystemAudioFocusIfNeeded();
197 UpdateRoutedService();
177 198
178 // The session may become controllable after removing a one-shot player. 199 // The session may become controllable after removing a one-shot player.
179 // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify 200 // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify
180 // about the state change. 201 // about the state change.
181 if (!was_controllable && IsControllable()) 202 if (!was_controllable && IsControllable())
182 NotifyAboutStateChange(); 203 NotifyAboutStateChange();
183 } 204 }
184 205
185 void MediaSessionImpl::RemovePlayers(MediaSessionPlayerObserver* observer) { 206 void MediaSessionImpl::RemovePlayers(MediaSessionPlayerObserver* observer) {
186 bool was_controllable = IsControllable(); 207 bool was_controllable = IsControllable();
(...skipping 13 matching lines...) Expand all
200 } 221 }
201 222
202 for (auto it = one_shot_players_.begin(); it != one_shot_players_.end();) { 223 for (auto it = one_shot_players_.begin(); it != one_shot_players_.end();) {
203 if (it->observer == observer) 224 if (it->observer == observer)
204 one_shot_players_.erase(it++); 225 one_shot_players_.erase(it++);
205 else 226 else
206 ++it; 227 ++it;
207 } 228 }
208 229
209 AbandonSystemAudioFocusIfNeeded(); 230 AbandonSystemAudioFocusIfNeeded();
231 UpdateRoutedService();
210 232
211 // The session may become controllable after removing a one-shot player. 233 // The session may become controllable after removing a one-shot player.
212 // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify 234 // However AbandonSystemAudioFocusIfNeeded will short-return and won't notify
213 // about the state change. 235 // about the state change.
214 if (!was_controllable && IsControllable()) 236 if (!was_controllable && IsControllable())
215 NotifyAboutStateChange(); 237 NotifyAboutStateChange();
216 } 238 }
217 239
218 void MediaSessionImpl::RecordSessionDuck() { 240 void MediaSessionImpl::RecordSessionDuck() {
219 uma_helper_.RecordSessionSuspended( 241 uma_helper_.RecordSessionSuspended(
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 314
293 if (audio_focus_state_ != State::SUSPENDED) 315 if (audio_focus_state_ != State::SUSPENDED)
294 OnSuspendInternal(suspend_type, State::SUSPENDED); 316 OnSuspendInternal(suspend_type, State::SUSPENDED);
295 317
296 DCHECK(audio_focus_state_ == State::SUSPENDED); 318 DCHECK(audio_focus_state_ == State::SUSPENDED);
297 normal_players_.clear(); 319 normal_players_.clear();
298 320
299 AbandonSystemAudioFocusIfNeeded(); 321 AbandonSystemAudioFocusIfNeeded();
300 } 322 }
301 323
302 void MediaSessionImpl::DidReceiveAction(
303 blink::mojom::MediaSessionAction action) {
304 if (service_)
305 service_->GetClient()->DidReceiveAction(action);
306 }
307
308 void MediaSessionImpl::OnMediaSessionEnabledAction(
309 blink::mojom::MediaSessionAction action) {
310 for (auto& observer : observers_)
311 observer.MediaSessionEnabledAction(action);
312 }
313
314 void MediaSessionImpl::OnMediaSessionDisabledAction(
315 blink::mojom::MediaSessionAction action) {
316 for (auto& observer : observers_)
317 observer.MediaSessionDisabledAction(action);
318 }
319
320 void MediaSessionImpl::StartDucking() { 324 void MediaSessionImpl::StartDucking() {
321 if (is_ducking_) 325 if (is_ducking_)
322 return; 326 return;
323 is_ducking_ = true; 327 is_ducking_ = true;
324 UpdateVolumeMultiplier(); 328 UpdateVolumeMultiplier();
325 } 329 }
326 330
327 void MediaSessionImpl::StopDucking() { 331 void MediaSessionImpl::StopDucking() {
328 if (!is_ducking_) 332 if (!is_ducking_)
329 return; 333 return;
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 467
464 NotifyAboutStateChange(); 468 NotifyAboutStateChange();
465 } 469 }
466 470
467 MediaSessionImpl::MediaSessionImpl(WebContents* web_contents) 471 MediaSessionImpl::MediaSessionImpl(WebContents* web_contents)
468 : WebContentsObserver(web_contents), 472 : WebContentsObserver(web_contents),
469 audio_focus_state_(State::INACTIVE), 473 audio_focus_state_(State::INACTIVE),
470 audio_focus_type_( 474 audio_focus_type_(
471 AudioFocusManager::AudioFocusType::GainTransientMayDuck), 475 AudioFocusManager::AudioFocusType::GainTransientMayDuck),
472 is_ducking_(false), 476 is_ducking_(false),
473 service_(nullptr) { 477 routed_service_(nullptr) {
474 #if defined(OS_ANDROID) 478 #if defined(OS_ANDROID)
475 session_android_.reset(new MediaSessionAndroid(this)); 479 session_android_.reset(new MediaSessionAndroid(this));
476 #endif // defined(OS_ANDROID) 480 #endif // defined(OS_ANDROID)
477 } 481 }
478 482
479 void MediaSessionImpl::Initialize() { 483 void MediaSessionImpl::Initialize() {
480 delegate_ = AudioFocusDelegate::Create(this); 484 delegate_ = AudioFocusDelegate::Create(this);
481 } 485 }
482 486
483 bool MediaSessionImpl::RequestSystemAudioFocus( 487 bool MediaSessionImpl::RequestSystemAudioFocus(
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
545 int player_id) { 549 int player_id) {
546 if (!RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain)) 550 if (!RequestSystemAudioFocus(AudioFocusManager::AudioFocusType::Gain))
547 return false; 551 return false;
548 552
549 one_shot_players_.insert(PlayerIdentifier(observer, player_id)); 553 one_shot_players_.insert(PlayerIdentifier(observer, player_id));
550 NotifyAboutStateChange(); 554 NotifyAboutStateChange();
551 555
552 return true; 556 return true;
553 } 557 }
554 558
559 // MediaSessionService-related methods
560
561 void MediaSessionImpl::OnServiceCreated(MediaSessionServiceImpl* service) {
562 services_[service->GetRenderFrameHost()] = service;
563 }
564
565 void MediaSessionImpl::OnServiceDestroyed(MediaSessionServiceImpl* service) {
566 services_.erase(service->GetRenderFrameHost());
567 }
568
569 void MediaSessionImpl::OnMediaSessionMetadataChanged(
570 MediaSessionServiceImpl* service) {
571 if (service != routed_service_)
572 return;
573
574 NotifyMediaSessionMetadataChange(routed_service_->metadata());
575 }
576
577 void MediaSessionImpl::OnMediaSessionActionsChanged(
578 MediaSessionServiceImpl* service) {
579 if (service != routed_service_)
580 return;
581
582 NotifyMediaSessionActionsChange(routed_service_->actions());
583 }
584
585 void MediaSessionImpl::DidReceiveAction(
586 blink::mojom::MediaSessionAction action) {
587 if (!routed_service_)
588 return;
589
590 routed_service_->GetClient()->DidReceiveAction(action);
591 }
592
593 bool MediaSessionImpl::IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh) {
594 if (!services_.count(rfh))
595 return false;
596
597 return services_[rfh]->metadata().has_value() ||
598 !services_[rfh]->actions().empty();
599 }
600
601 void MediaSessionImpl::UpdateRoutedService() {
602 MediaSessionServiceImpl* new_service = ComputeServiceForRouting();
603 if (new_service == routed_service_)
604 return;
605
606 routed_service_ = new_service;
607 if (routed_service_) {
608 NotifyMediaSessionMetadataChange(routed_service_->metadata());
609 NotifyMediaSessionActionsChange(routed_service_->actions());
610 } else {
611 NotifyMediaSessionMetadataChange(base::nullopt);
612 NotifyMediaSessionActionsChange(
613 std::set<blink::mojom::MediaSessionAction>());
614 }
615 }
616
617 MediaSessionServiceImpl* MediaSessionImpl::ComputeServiceForRouting() {
618 // The service selection strategy is: select a frame that has a playing/paused
619 // player and has a corresponding MediaSessionService and return the
620 // corresponding MediaSessionService. If multiple frames satisfy the criteria,
621 // prefer the top-most frame.
622 std::set<RenderFrameHost*> frames;
623 for (const auto& player : normal_players_) {
624 RenderFrameHost* frame = player.observer->GetRenderFrameHost();
625 if (frame)
626 frames.insert(frame);
627 }
628
629 for (const auto& player : one_shot_players_) {
630 RenderFrameHost* frame = player.observer->GetRenderFrameHost();
631 if (frame)
632 frames.insert(frame);
633 }
634
635 for (const auto& player : pepper_players_) {
636 RenderFrameHost* frame = player.observer->GetRenderFrameHost();
637 if (frame)
638 frames.insert(frame);
639 }
640
641 RenderFrameHost* best_frame = nullptr;
642 size_t min_depth = std::numeric_limits<size_t>::max();
643 std::map<RenderFrameHost*, size_t> map_rfh_to_depth;
644
645 for (RenderFrameHost* frame : frames) {
646 size_t depth = ComputeFrameDepth(frame, &map_rfh_to_depth);
647 if (depth >= min_depth)
648 continue;
649 if (!IsServiceActiveForRenderFrameHost(frame))
650 continue;
651 best_frame = frame;
652 min_depth = depth;
653 }
654
655 return best_frame ? services_[best_frame] : nullptr;
656 }
657
555 } // namespace content 658 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698