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

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

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

Powered by Google App Engine
This is Rietveld 408576698