| OLD | NEW |
| 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.h" | 5 #include "content/browser/media/session/media_session.h" |
| 6 | 6 |
| 7 #include "content/browser/media/session/media_session_delegate.h" | 7 #include "content/browser/media/session/media_session_delegate.h" |
| 8 #include "content/browser/media/session/media_session_observer.h" | 8 #include "content/browser/media/session/media_session_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" |
| 11 #include "content/public/browser/web_contents_delegate.h" | 11 #include "content/public/browser/web_contents_delegate.h" |
| 12 #include "media/base/media_content_type.h" |
| 12 | 13 |
| 13 namespace content { | 14 namespace content { |
| 14 | 15 |
| 15 namespace { | 16 namespace { |
| 16 | 17 |
| 17 const double kDefaultVolumeMultiplier = 1.0; | 18 const double kDefaultVolumeMultiplier = 1.0; |
| 18 | 19 |
| 19 } // anonymous namespace | 20 } // anonymous namespace |
| 20 | 21 |
| 21 using MediaSessionSuspendedSource = | 22 using MediaSessionSuspendedSource = |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 void MediaSession::SetMetadata(const MediaMetadata& metadata) { | 62 void MediaSession::SetMetadata(const MediaMetadata& metadata) { |
| 62 metadata_ = metadata; | 63 metadata_ = metadata; |
| 63 // TODO(zqzhang): On Android, the metadata is sent though JNI everytime the | 64 // TODO(zqzhang): On Android, the metadata is sent though JNI everytime the |
| 64 // media session play/pause state changes. Need to find a way to seprate the | 65 // media session play/pause state changes. Need to find a way to seprate the |
| 65 // state change and Metadata update. See https://crbug.com/621855. | 66 // state change and Metadata update. See https://crbug.com/621855. |
| 66 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged(); | 67 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged(); |
| 67 } | 68 } |
| 68 | 69 |
| 69 bool MediaSession::AddPlayer(MediaSessionObserver* observer, | 70 bool MediaSession::AddPlayer(MediaSessionObserver* observer, |
| 70 int player_id, | 71 int player_id, |
| 71 Type type) { | 72 media::MediaContentType media_content_type) { |
| 72 observer->OnSetVolumeMultiplier(player_id, volume_multiplier_); | 73 observer->OnSetVolumeMultiplier(player_id, volume_multiplier_); |
| 73 | 74 |
| 75 // Determine the audio focus type required for playing the new player. |
| 76 // TODO(zqzhang): handle duckable and uncontrollable. |
| 77 // See https://crbug.com/639277. |
| 78 AudioFocusManager::AudioFocusType required_audio_focus_type; |
| 79 if (media_content_type == media::MediaContentType::Persistent) { |
| 80 required_audio_focus_type = AudioFocusManager::AudioFocusType::Gain; |
| 81 } else { |
| 82 required_audio_focus_type = |
| 83 AudioFocusManager::AudioFocusType::GainTransientMayDuck; |
| 84 } |
| 85 |
| 74 // If the audio focus is already granted and is of type Content, there is | 86 // If the audio focus is already granted and is of type Content, there is |
| 75 // nothing to do. If it is granted of type Transient the requested type is | 87 // nothing to do. If it is granted of type Transient the requested type is |
| 76 // also transient, there is also nothing to do. Otherwise, the session needs | 88 // also transient, there is also nothing to do. Otherwise, the session needs |
| 77 // to request audio focus again. | 89 // to request audio focus again. |
| 78 if (audio_focus_state_ == State::ACTIVE && | 90 if (audio_focus_state_ == State::ACTIVE && |
| 79 (audio_focus_type_ == Type::Content || audio_focus_type_ == type)) { | 91 (audio_focus_type_ == AudioFocusManager::AudioFocusType::Gain || |
| 92 audio_focus_type_ == required_audio_focus_type)) { |
| 80 players_.insert(PlayerIdentifier(observer, player_id)); | 93 players_.insert(PlayerIdentifier(observer, player_id)); |
| 81 return true; | 94 return true; |
| 82 } | 95 } |
| 83 | 96 |
| 84 State old_audio_focus_state = audio_focus_state_; | 97 State old_audio_focus_state = audio_focus_state_; |
| 85 State audio_focus_state = RequestSystemAudioFocus(type) ? State::ACTIVE | 98 State audio_focus_state = RequestSystemAudioFocus(required_audio_focus_type) |
| 86 : State::INACTIVE; | 99 ? State::ACTIVE |
| 100 : State::INACTIVE; |
| 87 SetAudioFocusState(audio_focus_state); | 101 SetAudioFocusState(audio_focus_state); |
| 88 audio_focus_type_ = type; | 102 audio_focus_type_ = required_audio_focus_type; |
| 89 | 103 |
| 90 if (audio_focus_state_ != State::ACTIVE) | 104 if (audio_focus_state_ != State::ACTIVE) |
| 91 return false; | 105 return false; |
| 92 | 106 |
| 93 // The session should be reset if a player is starting while all players are | 107 // The session should be reset if a player is starting while all players are |
| 94 // suspended. | 108 // suspended. |
| 95 if (old_audio_focus_state != State::ACTIVE) | 109 if (old_audio_focus_state != State::ACTIVE) |
| 96 players_.clear(); | 110 players_.clear(); |
| 97 | 111 |
| 98 players_.insert(PlayerIdentifier(observer, player_id)); | 112 players_.insert(PlayerIdentifier(observer, player_id)); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 140 if (players_.size() != 1) { | 154 if (players_.size() != 1) { |
| 141 RemovePlayer(observer, player_id); | 155 RemovePlayer(observer, player_id); |
| 142 return; | 156 return; |
| 143 } | 157 } |
| 144 | 158 |
| 145 // Otherwise, suspend the session. | 159 // Otherwise, suspend the session. |
| 146 DCHECK(!IsSuspended()); | 160 DCHECK(!IsSuspended()); |
| 147 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED); | 161 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED); |
| 148 } | 162 } |
| 149 | 163 |
| 150 void MediaSession::Resume(SuspendType type) { | 164 void MediaSession::Resume(SuspendType suspend_type) { |
| 151 DCHECK(IsReallySuspended()); | 165 DCHECK(IsReallySuspended()); |
| 152 | 166 |
| 153 // When the resume requests comes from another source than system, audio focus | 167 // When the resume requests comes from another source than system, audio focus |
| 154 // must be requested. | 168 // must be requested. |
| 155 if (type != SuspendType::SYSTEM) { | 169 if (suspend_type != SuspendType::SYSTEM) { |
| 156 // Request audio focus again in case we lost it because another app started | 170 // Request audio focus again in case we lost it because another app started |
| 157 // playing while the playback was paused. | 171 // playing while the playback was paused. |
| 158 State audio_focus_state = RequestSystemAudioFocus(audio_focus_type_) | 172 State audio_focus_state = RequestSystemAudioFocus(audio_focus_type_) |
| 159 ? State::ACTIVE | 173 ? State::ACTIVE |
| 160 : State::INACTIVE; | 174 : State::INACTIVE; |
| 161 SetAudioFocusState(audio_focus_state); | 175 SetAudioFocusState(audio_focus_state); |
| 162 | 176 |
| 163 if (audio_focus_state_ != State::ACTIVE) | 177 if (audio_focus_state_ != State::ACTIVE) |
| 164 return; | 178 return; |
| 165 } | 179 } |
| 166 | 180 |
| 167 OnResumeInternal(type); | 181 OnResumeInternal(suspend_type); |
| 168 } | 182 } |
| 169 | 183 |
| 170 void MediaSession::Suspend(SuspendType type) { | 184 void MediaSession::Suspend(SuspendType suspend_type) { |
| 171 DCHECK(!IsSuspended()); | 185 DCHECK(!IsSuspended()); |
| 172 | 186 |
| 173 OnSuspendInternal(type, State::SUSPENDED); | 187 OnSuspendInternal(suspend_type, State::SUSPENDED); |
| 174 } | 188 } |
| 175 | 189 |
| 176 void MediaSession::Stop(SuspendType type) { | 190 void MediaSession::Stop(SuspendType suspend_type) { |
| 177 DCHECK(audio_focus_state_ != State::INACTIVE); | 191 DCHECK(audio_focus_state_ != State::INACTIVE); |
| 178 | 192 |
| 179 DCHECK(type != SuspendType::CONTENT); | 193 DCHECK(suspend_type != SuspendType::CONTENT); |
| 180 | 194 |
| 181 // TODO(mlamouri): merge the logic between UI and SYSTEM. | 195 // TODO(mlamouri): merge the logic between UI and SYSTEM. |
| 182 if (type == SuspendType::SYSTEM) { | 196 if (suspend_type == SuspendType::SYSTEM) { |
| 183 OnSuspendInternal(type, State::INACTIVE); | 197 OnSuspendInternal(suspend_type, State::INACTIVE); |
| 184 return; | 198 return; |
| 185 } | 199 } |
| 186 | 200 |
| 187 if (audio_focus_state_ != State::SUSPENDED) | 201 if (audio_focus_state_ != State::SUSPENDED) |
| 188 OnSuspendInternal(type, State::SUSPENDED); | 202 OnSuspendInternal(suspend_type, State::SUSPENDED); |
| 189 | 203 |
| 190 DCHECK(audio_focus_state_ == State::SUSPENDED); | 204 DCHECK(audio_focus_state_ == State::SUSPENDED); |
| 191 players_.clear(); | 205 players_.clear(); |
| 192 AbandonSystemAudioFocusIfNeeded(); | 206 AbandonSystemAudioFocusIfNeeded(); |
| 193 } | 207 } |
| 194 | 208 |
| 195 void MediaSession::SetVolumeMultiplier(double volume_multiplier) { | 209 void MediaSession::SetVolumeMultiplier(double volume_multiplier) { |
| 196 volume_multiplier_ = volume_multiplier; | 210 volume_multiplier_ = volume_multiplier; |
| 197 for (const auto& it : players_) | 211 for (const auto& it : players_) |
| 198 it.observer->OnSetVolumeMultiplier(it.player_id, volume_multiplier_); | 212 it.observer->OnSetVolumeMultiplier(it.player_id, volume_multiplier_); |
| 199 } | 213 } |
| 200 | 214 |
| 201 bool MediaSession::IsActive() const { | 215 bool MediaSession::IsActive() const { |
| 202 return audio_focus_state_ == State::ACTIVE; | 216 return audio_focus_state_ == State::ACTIVE; |
| 203 } | 217 } |
| 204 | 218 |
| 205 bool MediaSession::IsReallySuspended() const { | 219 bool MediaSession::IsReallySuspended() const { |
| 206 return audio_focus_state_ == State::SUSPENDED; | 220 return audio_focus_state_ == State::SUSPENDED; |
| 207 } | 221 } |
| 208 | 222 |
| 209 bool MediaSession::IsSuspended() const { | 223 bool MediaSession::IsSuspended() const { |
| 210 // TODO(mlamouri): should be == State::SUSPENDED. | 224 // TODO(mlamouri): should be == State::SUSPENDED. |
| 211 return audio_focus_state_ != State::ACTIVE; | 225 return audio_focus_state_ != State::ACTIVE; |
| 212 } | 226 } |
| 213 | 227 |
| 214 bool MediaSession::IsControllable() const { | 228 bool MediaSession::IsControllable() const { |
| 215 // Only content type media session can be controllable unless it is inactive. | 229 // Only media session having focus Gain can be controllable unless it is |
| 230 // inactive. |
| 216 return audio_focus_state_ != State::INACTIVE && | 231 return audio_focus_state_ != State::INACTIVE && |
| 217 audio_focus_type_ == Type::Content; | 232 audio_focus_type_ == AudioFocusManager::AudioFocusType::Gain; |
| 218 } | 233 } |
| 219 | 234 |
| 220 std::unique_ptr<base::CallbackList<void(MediaSession::State)>::Subscription> | 235 std::unique_ptr<base::CallbackList<void(MediaSession::State)>::Subscription> |
| 221 MediaSession::RegisterMediaSessionStateChangedCallbackForTest( | 236 MediaSession::RegisterMediaSessionStateChangedCallbackForTest( |
| 222 const StateChangedCallback& cb) { | 237 const StateChangedCallback& cb) { |
| 223 return media_session_state_listeners_.Add(cb); | 238 return media_session_state_listeners_.Add(cb); |
| 224 } | 239 } |
| 225 | 240 |
| 226 void MediaSession::SetDelegateForTests( | 241 void MediaSession::SetDelegateForTests( |
| 227 std::unique_ptr<MediaSessionDelegate> delegate) { | 242 std::unique_ptr<MediaSessionDelegate> delegate) { |
| 228 delegate_ = std::move(delegate); | 243 delegate_ = std::move(delegate); |
| 229 } | 244 } |
| 230 | 245 |
| 231 bool MediaSession::IsActiveForTest() const { | 246 bool MediaSession::IsActiveForTest() const { |
| 232 return audio_focus_state_ == State::ACTIVE; | 247 return audio_focus_state_ == State::ACTIVE; |
| 233 } | 248 } |
| 234 | 249 |
| 235 MediaSession::Type MediaSession::audio_focus_type_for_test() const { | 250 AudioFocusManager::AudioFocusType MediaSession::audio_focus_type_for_test() |
| 251 const { |
| 236 return audio_focus_type_; | 252 return audio_focus_type_; |
| 237 } | 253 } |
| 238 | 254 |
| 239 MediaSessionUmaHelper* MediaSession::uma_helper_for_test() { | 255 MediaSessionUmaHelper* MediaSession::uma_helper_for_test() { |
| 240 return &uma_helper_; | 256 return &uma_helper_; |
| 241 } | 257 } |
| 242 | 258 |
| 243 void MediaSession::RemoveAllPlayersForTest() { | 259 void MediaSession::RemoveAllPlayersForTest() { |
| 244 players_.clear(); | 260 players_.clear(); |
| 245 AbandonSystemAudioFocusIfNeeded(); | 261 AbandonSystemAudioFocusIfNeeded(); |
| 246 } | 262 } |
| 247 | 263 |
| 248 void MediaSession::OnSuspendInternal(SuspendType type, State new_state) { | 264 void MediaSession::OnSuspendInternal(SuspendType suspend_type, |
| 265 State new_state) { |
| 249 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE); | 266 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE); |
| 250 // UI suspend cannot use State::INACTIVE. | 267 // UI suspend cannot use State::INACTIVE. |
| 251 DCHECK(type == SuspendType::SYSTEM || new_state == State::SUSPENDED); | 268 DCHECK(suspend_type == SuspendType::SYSTEM || new_state == State::SUSPENDED); |
| 252 | 269 |
| 253 if (audio_focus_state_ != State::ACTIVE) | 270 if (audio_focus_state_ != State::ACTIVE) |
| 254 return; | 271 return; |
| 255 | 272 |
| 256 switch (type) { | 273 switch (suspend_type) { |
| 257 case SuspendType::UI: | 274 case SuspendType::UI: |
| 258 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI); | 275 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI); |
| 259 break; | 276 break; |
| 260 case SuspendType::SYSTEM: | 277 case SuspendType::SYSTEM: |
| 261 switch (new_state) { | 278 switch (new_state) { |
| 262 case State::SUSPENDED: | 279 case State::SUSPENDED: |
| 263 uma_helper_.RecordSessionSuspended( | 280 uma_helper_.RecordSessionSuspended( |
| 264 MediaSessionSuspendedSource::SystemTransient); | 281 MediaSessionSuspendedSource::SystemTransient); |
| 265 break; | 282 break; |
| 266 case State::INACTIVE: | 283 case State::INACTIVE: |
| 267 uma_helper_.RecordSessionSuspended( | 284 uma_helper_.RecordSessionSuspended( |
| 268 MediaSessionSuspendedSource::SystemPermanent); | 285 MediaSessionSuspendedSource::SystemPermanent); |
| 269 break; | 286 break; |
| 270 case State::ACTIVE: | 287 case State::ACTIVE: |
| 271 NOTREACHED(); | 288 NOTREACHED(); |
| 272 break; | 289 break; |
| 273 } | 290 } |
| 274 break; | 291 break; |
| 275 case SuspendType::CONTENT: | 292 case SuspendType::CONTENT: |
| 276 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::CONTENT); | 293 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::CONTENT); |
| 277 break; | 294 break; |
| 278 } | 295 } |
| 279 | 296 |
| 280 SetAudioFocusState(new_state); | 297 SetAudioFocusState(new_state); |
| 281 suspend_type_ = type; | 298 suspend_type_ = suspend_type; |
| 282 | 299 |
| 283 if (type != SuspendType::CONTENT) { | 300 if (suspend_type != SuspendType::CONTENT) { |
| 284 // SuspendType::CONTENT happens when the suspend action came from | 301 // SuspendType::CONTENT happens when the suspend action came from |
| 285 // the page in which case the player is already paused. | 302 // the page in which case the player is already paused. |
| 286 // Otherwise, the players need to be paused. | 303 // Otherwise, the players need to be paused. |
| 287 for (const auto& it : players_) | 304 for (const auto& it : players_) |
| 288 it.observer->OnSuspend(it.player_id); | 305 it.observer->OnSuspend(it.player_id); |
| 289 } | 306 } |
| 290 | 307 |
| 291 UpdateWebContents(); | 308 UpdateWebContents(); |
| 292 } | 309 } |
| 293 | 310 |
| 294 void MediaSession::OnResumeInternal(SuspendType type) { | 311 void MediaSession::OnResumeInternal(SuspendType suspend_type) { |
| 295 if (type == SuspendType::SYSTEM && suspend_type_ != type) | 312 if (suspend_type == SuspendType::SYSTEM && suspend_type_ != suspend_type) |
| 296 return; | 313 return; |
| 297 | 314 |
| 298 SetAudioFocusState(State::ACTIVE); | 315 SetAudioFocusState(State::ACTIVE); |
| 299 | 316 |
| 300 for (const auto& it : players_) | 317 for (const auto& it : players_) |
| 301 it.observer->OnResume(it.player_id); | 318 it.observer->OnResume(it.player_id); |
| 302 | 319 |
| 303 UpdateWebContents(); | 320 UpdateWebContents(); |
| 304 } | 321 } |
| 305 | 322 |
| 306 MediaSession::MediaSession(WebContents* web_contents) | 323 MediaSession::MediaSession(WebContents* web_contents) |
| 307 : WebContentsObserver(web_contents), | 324 : WebContentsObserver(web_contents), |
| 308 audio_focus_state_(State::INACTIVE), | 325 audio_focus_state_(State::INACTIVE), |
| 309 audio_focus_type_(Type::Transient), | 326 audio_focus_type_( |
| 310 volume_multiplier_(kDefaultVolumeMultiplier) { | 327 AudioFocusManager::AudioFocusType::GainTransientMayDuck), |
| 311 } | 328 volume_multiplier_(kDefaultVolumeMultiplier) {} |
| 312 | 329 |
| 313 void MediaSession::Initialize() { | 330 void MediaSession::Initialize() { |
| 314 delegate_ = MediaSessionDelegate::Create(this); | 331 delegate_ = MediaSessionDelegate::Create(this); |
| 315 } | 332 } |
| 316 | 333 |
| 317 bool MediaSession::RequestSystemAudioFocus(Type type) { | 334 bool MediaSession::RequestSystemAudioFocus( |
| 318 bool result = delegate_->RequestAudioFocus(type); | 335 AudioFocusManager::AudioFocusType audio_focus_type) { |
| 336 bool result = delegate_->RequestAudioFocus(audio_focus_type); |
| 319 uma_helper_.RecordRequestAudioFocusResult(result); | 337 uma_helper_.RecordRequestAudioFocusResult(result); |
| 320 return result; | 338 return result; |
| 321 } | 339 } |
| 322 | 340 |
| 323 void MediaSession::AbandonSystemAudioFocusIfNeeded() { | 341 void MediaSession::AbandonSystemAudioFocusIfNeeded() { |
| 324 if (audio_focus_state_ == State::INACTIVE || !players_.empty()) | 342 if (audio_focus_state_ == State::INACTIVE || !players_.empty()) |
| 325 return; | 343 return; |
| 326 | 344 |
| 327 delegate_->AbandonAudioFocus(); | 345 delegate_->AbandonAudioFocus(); |
| 328 | 346 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 347 case State::SUSPENDED: | 365 case State::SUSPENDED: |
| 348 uma_helper_.OnSessionSuspended(); | 366 uma_helper_.OnSessionSuspended(); |
| 349 break; | 367 break; |
| 350 case State::INACTIVE: | 368 case State::INACTIVE: |
| 351 uma_helper_.OnSessionInactive(); | 369 uma_helper_.OnSessionInactive(); |
| 352 break; | 370 break; |
| 353 } | 371 } |
| 354 } | 372 } |
| 355 | 373 |
| 356 } // namespace content | 374 } // namespace content |
| OLD | NEW |