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

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

Issue 1698933004: Make MediaSession a runtime-enabled feature on Desktop. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review comments and windows build fix Created 4 years, 9 months 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/android/media_session.h" 5 #include "content/browser/media/session/media_session.h"
6 6
7 #include "base/android/context_utils.h" 7 #include "content/browser/media/session/media_session_delegate.h"
8 #include "base/android/jni_android.h" 8 #include "content/browser/media/session/media_session_observer.h"
9 #include "content/browser/media/android/media_session_observer.h"
10 #include "content/browser/web_contents/web_contents_impl.h" 9 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/public/browser/web_contents.h" 10 #include "content/public/browser/web_contents.h"
12 #include "content/public/browser/web_contents_delegate.h" 11 #include "content/public/browser/web_contents_delegate.h"
13 #include "jni/MediaSession_jni.h"
14 #include "media/base/android/media_player_android.h"
15 12
16 namespace content { 13 namespace content {
17 14
15 namespace {
16
17 const double kDefaultVolumeMultiplier = 1.0;
18
19 } // anonymous namespace
20
18 using MediaSessionSuspendedSource = 21 using MediaSessionSuspendedSource =
19 MediaSessionUmaHelper::MediaSessionSuspendedSource; 22 MediaSessionUmaHelper::MediaSessionSuspendedSource;
20 23
21 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession); 24 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession);
22 25
23 MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver* observer, 26 MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver* observer,
24 int player_id) 27 int player_id)
25 : observer(observer), 28 : observer(observer),
26 player_id(player_id) { 29 player_id(player_id) {
27 } 30 }
28 31
29 bool MediaSession::PlayerIdentifier::operator==( 32 bool MediaSession::PlayerIdentifier::operator==(
30 const PlayerIdentifier& other) const { 33 const PlayerIdentifier& other) const {
31 return this->observer == other.observer && this->player_id == other.player_id; 34 return this->observer == other.observer && this->player_id == other.player_id;
32 } 35 }
33 36
34 size_t MediaSession::PlayerIdentifier::Hash::operator()( 37 size_t MediaSession::PlayerIdentifier::Hash::operator()(
35 const PlayerIdentifier& player_identifier) const { 38 const PlayerIdentifier& player_identifier) const {
36 size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionObserver*>()( 39 size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionObserver*>()(
37 player_identifier.observer); 40 player_identifier.observer);
38 hash += BASE_HASH_NAMESPACE::hash<int>()(player_identifier.player_id); 41 hash += BASE_HASH_NAMESPACE::hash<int>()(player_identifier.player_id);
39 return hash; 42 return hash;
40 } 43 }
41 44
42 // static 45 // static
43 bool content::MediaSession::RegisterMediaSession(JNIEnv* env) {
44 return RegisterNativesImpl(env);
45 }
46
47 // static
48 MediaSession* MediaSession::Get(WebContents* web_contents) { 46 MediaSession* MediaSession::Get(WebContents* web_contents) {
49 MediaSession* session = FromWebContents(web_contents); 47 MediaSession* session = FromWebContents(web_contents);
50 if (!session) { 48 if (!session) {
51 CreateForWebContents(web_contents); 49 CreateForWebContents(web_contents);
52 session = FromWebContents(web_contents); 50 session = FromWebContents(web_contents);
53 session->Initialize(); 51 session->Initialize();
54 } 52 }
55 return session; 53 return session;
56 } 54 }
57 55
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 for (auto it = players_.begin(); it != players_.end();) { 106 for (auto it = players_.begin(); it != players_.end();) {
109 if (it->observer == observer) 107 if (it->observer == observer)
110 players_.erase(it++); 108 players_.erase(it++);
111 else 109 else
112 ++it; 110 ++it;
113 } 111 }
114 112
115 AbandonSystemAudioFocusIfNeeded(); 113 AbandonSystemAudioFocusIfNeeded();
116 } 114 }
117 115
118 void MediaSession::OnSuspend(JNIEnv* env, 116 void MediaSession::RecordSessionDuck() {
119 const JavaParamRef<jobject>& obj,
120 jboolean temporary) {
121 // TODO(mlamouri): this check makes it so that if a MediaSession is paused and
122 // then loses audio focus, it will still stay in the Suspended state.
123 // See https://crbug.com/539998
124 if (audio_focus_state_ != State::ACTIVE)
125 return;
126
127 OnSuspendInternal(SuspendType::SYSTEM,
128 temporary ? State::SUSPENDED : State::INACTIVE);
129
130 }
131
132 void MediaSession::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
133 if (audio_focus_state_ != State::SUSPENDED)
134 return;
135
136 OnResumeInternal(SuspendType::SYSTEM);
137 }
138
139 void MediaSession::RecordSessionDuck(JNIEnv* env,
140 const JavaParamRef<jobject>& obj) {
141 uma_helper_.RecordSessionSuspended( 117 uma_helper_.RecordSessionSuspended(
142 MediaSessionSuspendedSource::SystemTransientDuck); 118 MediaSessionSuspendedSource::SystemTransientDuck);
143 } 119 }
144 120
145 void MediaSession::OnPlayerPaused(MediaSessionObserver* observer, 121 void MediaSession::OnPlayerPaused(MediaSessionObserver* observer,
146 int player_id) { 122 int player_id) {
147 // If a playback is completed, BrowserMediaPlayerManager will call 123 // If a playback is completed, BrowserMediaPlayerManager will call
148 // OnPlayerPaused() after RemovePlayer(). This is a workaround. 124 // OnPlayerPaused() after RemovePlayer(). This is a workaround.
149 // Also, this method may be called when a player that is not added 125 // Also, this method may be called when a player that is not added
150 // to this session (e.g. a silent video) is paused. MediaSession 126 // to this session (e.g. a silent video) is paused. MediaSession
151 // should ignore the paused player for this case. 127 // should ignore the paused player for this case.
152 if (!players_.count(PlayerIdentifier(observer, player_id))) 128 if (!players_.count(PlayerIdentifier(observer, player_id)))
153 return; 129 return;
154 130
155 // If there is more than one observer, remove the paused one from the session. 131 // If there is more than one observer, remove the paused one from the session.
156 if (players_.size() != 1) { 132 if (players_.size() != 1) {
157 RemovePlayer(observer, player_id); 133 RemovePlayer(observer, player_id);
158 return; 134 return;
159 } 135 }
160 136
161 // Otherwise, suspend the session. 137 // Otherwise, suspend the session.
162 DCHECK(!IsSuspended()); 138 DCHECK(!IsSuspended());
163 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED); 139 OnSuspendInternal(SuspendType::CONTENT, State::SUSPENDED);
164 } 140 }
165 void MediaSession::OnSetVolumeMultiplier(JNIEnv *env, jobject obj, 141
166 jdouble volume_multiplier) { 142 void MediaSession::Resume(SuspendType type) {
167 OnSetVolumeMultiplierInternal(volume_multiplier); 143 DCHECK(IsReallySuspended());
144
145 // When the resume requests comes from another source than system, audio focus
146 // must be requested.
147 if (type != SuspendType::SYSTEM) {
148 // Request audio focus again in case we lost it because another app started
149 // playing while the playback was paused.
150 State audio_focus_state = RequestSystemAudioFocus(audio_focus_type_)
151 ? State::ACTIVE
152 : State::INACTIVE;
153 SetAudioFocusState(audio_focus_state);
154
155 if (audio_focus_state_ != State::ACTIVE)
156 return;
157 }
158
159 OnResumeInternal(type);
168 } 160 }
169 161
170 void MediaSession::Resume() { 162 void MediaSession::Suspend(SuspendType type) {
171 DCHECK(IsSuspended()); 163 DCHECK(!IsSuspended());
172 164
173 // Request audio focus again in case we lost it because another app started 165 OnSuspendInternal(type, State::SUSPENDED);
174 // playing while the playback was paused.
175 State audio_focus_state = RequestSystemAudioFocus(audio_focus_type_)
176 ? State::ACTIVE
177 : State::INACTIVE;
178 SetAudioFocusState(audio_focus_state);
179
180 if (audio_focus_state_ != State::ACTIVE)
181 return;
182
183 OnResumeInternal(SuspendType::UI);
184 } 166 }
185 167
186 void MediaSession::Suspend() { 168 void MediaSession::Stop(SuspendType type) {
187 DCHECK(!IsSuspended());
188
189 OnSuspendInternal(SuspendType::UI, State::SUSPENDED);
190 }
191
192 void MediaSession::Stop() {
193 DCHECK(audio_focus_state_ != State::INACTIVE); 169 DCHECK(audio_focus_state_ != State::INACTIVE);
194 170
171 DCHECK(type != SuspendType::CONTENT);
172
173 // TODO(mlamouri): merge the logic between UI and SYSTEM.
174 if (type == SuspendType::SYSTEM) {
175 OnSuspendInternal(type, State::INACTIVE);
176 return;
177 }
178
195 if (audio_focus_state_ != State::SUSPENDED) 179 if (audio_focus_state_ != State::SUSPENDED)
196 OnSuspendInternal(SuspendType::UI, State::SUSPENDED); 180 OnSuspendInternal(type, State::SUSPENDED);
197 181
198 DCHECK(audio_focus_state_ == State::SUSPENDED); 182 DCHECK(audio_focus_state_ == State::SUSPENDED);
199 players_.clear(); 183 players_.clear();
200 AbandonSystemAudioFocusIfNeeded(); 184 AbandonSystemAudioFocusIfNeeded();
201 } 185 }
202 186
187 void MediaSession::SetVolumeMultiplier(double volume_multiplier) {
188 volume_multiplier_ = volume_multiplier;
189 for (const auto& it : players_)
190 it.observer->OnSetVolumeMultiplier(it.player_id, volume_multiplier_);
191 }
192
193 bool MediaSession::IsActive() const {
194 return audio_focus_state_ == State::ACTIVE;
195 }
196
197 bool MediaSession::IsReallySuspended() const {
198 return audio_focus_state_ == State::SUSPENDED;
whywhat 2016/03/01 03:12:10 Given there's three states, your comment in the he
mlamouri (slow - plz ping) 2016/03/07 19:28:09 My comment says that ::IsSuspend() (not this metho
199 }
200
203 bool MediaSession::IsSuspended() const { 201 bool MediaSession::IsSuspended() const {
204 // TODO(mlamouri): should be == State::SUSPENDED. 202 // TODO(mlamouri): should be == State::SUSPENDED.
205 return audio_focus_state_ != State::ACTIVE; 203 return audio_focus_state_ != State::ACTIVE;
206 } 204 }
207 205
208 bool MediaSession::IsControllable() const { 206 bool MediaSession::IsControllable() const {
209 // Only content type media session can be controllable unless it is inactive. 207 // Only content type media session can be controllable unless it is inactive.
210 return audio_focus_state_ != State::INACTIVE && 208 return audio_focus_state_ != State::INACTIVE &&
211 audio_focus_type_ == Type::Content; 209 audio_focus_type_ == Type::Content;
212 } 210 }
213 211
214 void MediaSession::ResetJavaRefForTest() { 212 void MediaSession::SetDelegateForTests(
215 j_media_session_.Reset(); 213 scoped_ptr<MediaSessionDelegate> delegate) {
214 delegate_ = std::move(delegate);
216 } 215 }
217 216
218 bool MediaSession::IsActiveForTest() const { 217 bool MediaSession::IsActiveForTest() const {
219 return audio_focus_state_ == State::ACTIVE; 218 return audio_focus_state_ == State::ACTIVE;
220 } 219 }
221 220
222 MediaSession::Type MediaSession::audio_focus_type_for_test() const { 221 MediaSession::Type MediaSession::audio_focus_type_for_test() const {
223 return audio_focus_type_; 222 return audio_focus_type_;
224 } 223 }
225 224
226 MediaSessionUmaHelper* MediaSession::uma_helper_for_test() { 225 MediaSessionUmaHelper* MediaSession::uma_helper_for_test() {
227 return &uma_helper_; 226 return &uma_helper_;
228 } 227 }
229 228
230 void MediaSession::RemoveAllPlayersForTest() { 229 void MediaSession::RemoveAllPlayersForTest() {
231 players_.clear(); 230 players_.clear();
232 AbandonSystemAudioFocusIfNeeded(); 231 AbandonSystemAudioFocusIfNeeded();
233 } 232 }
234 233
235 void MediaSession::OnSuspendInternal(SuspendType type, State new_state) { 234 void MediaSession::OnSuspendInternal(SuspendType type, State new_state) {
236 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE); 235 DCHECK(new_state == State::SUSPENDED || new_state == State::INACTIVE);
237 // UI suspend cannot use State::INACTIVE. 236 // UI suspend cannot use State::INACTIVE.
238 DCHECK(type == SuspendType::SYSTEM || new_state == State::SUSPENDED); 237 DCHECK(type == SuspendType::SYSTEM || new_state == State::SUSPENDED);
239 238
239 if (audio_focus_state_ != State::ACTIVE)
240 return;
241
240 switch (type) { 242 switch (type) {
241 case SuspendType::UI: 243 case SuspendType::UI:
242 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI); 244 uma_helper_.RecordSessionSuspended(MediaSessionSuspendedSource::UI);
243 break; 245 break;
244 case SuspendType::SYSTEM: 246 case SuspendType::SYSTEM:
245 switch (new_state) { 247 switch (new_state) {
246 case State::SUSPENDED: 248 case State::SUSPENDED:
247 uma_helper_.RecordSessionSuspended( 249 uma_helper_.RecordSessionSuspended(
248 MediaSessionSuspendedSource::SystemTransient); 250 MediaSessionSuspendedSource::SystemTransient);
249 break; 251 break;
(...skipping 30 matching lines...) Expand all
280 return; 282 return;
281 283
282 SetAudioFocusState(State::ACTIVE); 284 SetAudioFocusState(State::ACTIVE);
283 285
284 for (const auto& it : players_) 286 for (const auto& it : players_)
285 it.observer->OnResume(it.player_id); 287 it.observer->OnResume(it.player_id);
286 288
287 UpdateWebContents(); 289 UpdateWebContents();
288 } 290 }
289 291
290 void MediaSession::OnSetVolumeMultiplierInternal(double volume_multiplier) {
291 volume_multiplier_ = volume_multiplier;
292 for (const auto& it : players_)
293 it.observer->OnSetVolumeMultiplier(it.player_id, volume_multiplier_);
294 }
295
296 MediaSession::MediaSession(WebContents* web_contents) 292 MediaSession::MediaSession(WebContents* web_contents)
297 : WebContentsObserver(web_contents), 293 : WebContentsObserver(web_contents),
298 audio_focus_state_(State::INACTIVE), 294 audio_focus_state_(State::INACTIVE),
299 audio_focus_type_(Type::Transient), 295 audio_focus_type_(Type::Transient),
300 volume_multiplier_(media::MediaPlayerAndroid::kDefaultVolumeMultiplier) { 296 volume_multiplier_(kDefaultVolumeMultiplier) {
301 } 297 }
302 298
303 void MediaSession::Initialize() { 299 void MediaSession::Initialize() {
304 JNIEnv* env = base::android::AttachCurrentThread(); 300 delegate_ = MediaSessionDelegate::Create(this);
305 DCHECK(env);
306 j_media_session_.Reset(Java_MediaSession_createMediaSession(
307 env,
308 base::android::GetApplicationContext(),
309 reinterpret_cast<intptr_t>(this)));
310 } 301 }
311 302
312 bool MediaSession::RequestSystemAudioFocus(Type type) { 303 bool MediaSession::RequestSystemAudioFocus(Type type) {
313 // During tests, j_media_session_ might be null. 304 bool result = delegate_->RequestAudioFocus(type);
314 if (j_media_session_.is_null())
315 return true;
316
317 JNIEnv* env = base::android::AttachCurrentThread();
318 DCHECK(env);
319 bool result = Java_MediaSession_requestAudioFocus(env, j_media_session_.obj(),
320 type == Type::Transient);
321 uma_helper_.RecordRequestAudioFocusResult(result); 305 uma_helper_.RecordRequestAudioFocusResult(result);
322 return result; 306 return result;
323 } 307 }
324 308
325 void MediaSession::AbandonSystemAudioFocusIfNeeded() { 309 void MediaSession::AbandonSystemAudioFocusIfNeeded() {
326 if (audio_focus_state_ == State::INACTIVE || !players_.empty()) 310 if (audio_focus_state_ == State::INACTIVE || !players_.empty())
327 return; 311 return;
328 312
329 // During tests, j_media_session_ might be null. 313 delegate_->AbandonAudioFocus();
330 if (!j_media_session_.is_null()) {
331 JNIEnv* env = base::android::AttachCurrentThread();
332 DCHECK(env);
333 Java_MediaSession_abandonAudioFocus(env, j_media_session_.obj());
334 }
335 314
336 SetAudioFocusState(State::INACTIVE); 315 SetAudioFocusState(State::INACTIVE);
337 UpdateWebContents(); 316 UpdateWebContents();
338 } 317 }
339 318
340 void MediaSession::UpdateWebContents() { 319 void MediaSession::UpdateWebContents() {
341 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged(); 320 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged();
342 } 321 }
343 322
344 void MediaSession::SetAudioFocusState(State audio_focus_state) { 323 void MediaSession::SetAudioFocusState(State audio_focus_state) {
345 if (audio_focus_state == audio_focus_state_) 324 if (audio_focus_state == audio_focus_state_)
346 return; 325 return;
347 326
348 audio_focus_state_ = audio_focus_state; 327 audio_focus_state_ = audio_focus_state;
349 switch (audio_focus_state_) { 328 switch (audio_focus_state_) {
350 case State::ACTIVE: 329 case State::ACTIVE:
351 uma_helper_.OnSessionActive(); 330 uma_helper_.OnSessionActive();
352 break; 331 break;
353 case State::SUSPENDED: 332 case State::SUSPENDED:
354 uma_helper_.OnSessionSuspended(); 333 uma_helper_.OnSessionSuspended();
355 break; 334 break;
356 case State::INACTIVE: 335 case State::INACTIVE:
357 uma_helper_.OnSessionInactive(); 336 uma_helper_.OnSessionInactive();
358 break; 337 break;
359 } 338 }
360 } 339 }
361 340
362 } // namespace content 341 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698