Chromium Code Reviews| 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/android/media_web_contents_observer_android.h" | 5 #include "content/browser/media/android/media_web_contents_observer_android.h" |
| 6 | 6 |
| 7 #include "base/memory/scoped_ptr.h" | 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
| 9 #include "content/browser/media/android/browser_media_player_manager.h" | 9 #include "content/browser/media/android/browser_media_player_manager.h" |
| 10 #include "content/browser/media/android/browser_media_session_manager.h" | 10 #include "content/browser/media/android/browser_media_session_manager.h" |
| 11 #include "content/browser/media/android/media_session.h" | |
| 11 #include "content/browser/media/android/media_session_observer.h" | 12 #include "content/browser/media/android/media_session_observer.h" |
| 12 #include "content/browser/media/cdm/browser_cdm_manager.h" | 13 #include "content/browser/media/cdm/browser_cdm_manager.h" |
| 13 #include "content/browser/web_contents/web_contents_impl.h" | 14 #include "content/browser/web_contents/web_contents_impl.h" |
| 14 #include "content/common/frame_messages.h" | 15 #include "content/common/frame_messages.h" |
| 15 #include "content/common/media/media_player_messages_android.h" | 16 #include "content/common/media/media_player_messages_android.h" |
| 16 #include "content/common/media/media_session_messages_android.h" | 17 #include "content/common/media/media_session_messages_android.h" |
| 17 #include "content/public/browser/render_frame_host.h" | 18 #include "content/public/browser/render_frame_host.h" |
| 18 #include "content/public/browser/web_contents.h" | 19 #include "content/public/browser/web_contents.h" |
| 19 #include "ipc/ipc_message_macros.h" | 20 #include "ipc/ipc_message_macros.h" |
| 20 #include "media/base/android/media_player_android.h" | 21 #include "media/base/android/media_player_android.h" |
| 21 | 22 |
| 22 namespace content { | 23 namespace content { |
| 23 | 24 |
| 25 class MediaSessionController : public MediaSessionObserver { | |
|
mlamouri (slow - plz ping)
2016/01/14 15:43:08
Maybe this could be in a separate file?
DaleCurtis
2016/01/14 18:21:18
Yeah, I was thinking the same since it ended up la
| |
| 26 public: | |
| 27 MediaSessionController(const WebContentsObserver::MediaPlayerId& id, | |
| 28 MediaWebContentsObserver* media_web_contents_observer) | |
| 29 : id_(id), media_web_contents_observer_(media_web_contents_observer) {} | |
| 30 | |
| 31 // Clients must call this after construction and destroy the controller if it | |
| 32 // returns false. | |
| 33 bool Initialize(bool has_video, bool has_audio, base::TimeDelta duration) { | |
| 34 // These objects are only created on the UI thread, so this is safe. | |
| 35 static uint32_t player_id = 0; | |
| 36 player_id_ = static_cast<int>(player_id++); | |
| 37 | |
| 38 // Minimal duration of a player in order to be considered as Content type. | |
| 39 const base::TimeDelta kMinimumDurationForContent = | |
| 40 base::TimeDelta::FromSeconds(5); | |
| 41 | |
| 42 const MediaSession::Type media_session_type = | |
| 43 duration == base::TimeDelta() || duration > kMinimumDurationForContent | |
| 44 ? MediaSession::Type::Content | |
| 45 : MediaSession::Type::Transient; | |
| 46 | |
| 47 // If a session can't be created, force a pause immediately. | |
| 48 if (!MediaSession::Get(media_web_contents_observer_->web_contents()) | |
| 49 ->AddPlayer(this, player_id_, media_session_type)) { | |
| 50 OnSuspend(player_id_); | |
| 51 return false; | |
| 52 } | |
| 53 | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 ~MediaSessionController() { | |
| 58 MediaSession::Get(media_web_contents_observer_->web_contents()) | |
| 59 ->RemovePlayer(this, player_id_); | |
| 60 } | |
| 61 | |
| 62 void OnSuspend(int player_id) { | |
| 63 DCHECK_EQ(player_id_, player_id); | |
| 64 media_web_contents_observer_->Send( | |
| 65 new FrameMsg_MediaDelegatePause(id_.first->GetRoutingID(), id_.second)); | |
| 66 } | |
| 67 | |
| 68 void OnResume(int player_id) { | |
| 69 DCHECK_EQ(player_id_, player_id); | |
| 70 media_web_contents_observer_->Send( | |
| 71 new FrameMsg_MediaDelegatePlay(id_.first->GetRoutingID(), id_.second)); | |
| 72 } | |
| 73 | |
| 74 void PausePlayback() { | |
| 75 MediaSession* session = | |
| 76 MediaSession::Get(media_web_contents_observer_->web_contents()); | |
| 77 // TODO(dalecurtis, mlamouri): This seems odd, if we don't check suspend of | |
| 78 // the entire session, notifying a pause will DCHECK in mediasession.cc:146. | |
| 79 if (!session->IsSuspended()) | |
|
mlamouri (slow - plz ping)
2016/01/14 15:43:08
How do you end up with a suspended session that pa
DaleCurtis
2016/01/14 18:21:18
This is due to the delegate sending a "pause" ack
mlamouri (slow - plz ping)
2016/01/19 16:59:11
zqzhang@ has been working on issues related to tha
| |
| 80 session->OnPlayerPaused(this, player_id_); | |
| 81 } | |
| 82 | |
| 83 private: | |
| 84 const WebContentsObserver::MediaPlayerId id_; | |
| 85 MediaWebContentsObserver* const media_web_contents_observer_; | |
| 86 int player_id_ = 0; | |
| 87 bool initialize_failed_ = false; | |
| 88 | |
| 89 DISALLOW_COPY_AND_ASSIGN(MediaSessionController); | |
| 90 }; | |
| 91 | |
| 24 MediaWebContentsObserverAndroid::MediaWebContentsObserverAndroid( | 92 MediaWebContentsObserverAndroid::MediaWebContentsObserverAndroid( |
| 25 WebContents* web_contents) | 93 WebContents* web_contents) |
| 26 : MediaWebContentsObserver(web_contents) {} | 94 : MediaWebContentsObserver(web_contents) {} |
| 27 | 95 |
| 28 MediaWebContentsObserverAndroid::~MediaWebContentsObserverAndroid() {} | 96 MediaWebContentsObserverAndroid::~MediaWebContentsObserverAndroid() { |
| 97 // Clear active sessions before destructing the observer. | |
| 98 media_session_map_.clear(); | |
| 99 } | |
| 29 | 100 |
| 30 // static | 101 // static |
| 31 MediaWebContentsObserverAndroid* | 102 MediaWebContentsObserverAndroid* |
| 32 MediaWebContentsObserverAndroid::FromWebContents(WebContents* web_contents) { | 103 MediaWebContentsObserverAndroid::FromWebContents(WebContents* web_contents) { |
| 33 return static_cast<MediaWebContentsObserverAndroid*>( | 104 return static_cast<MediaWebContentsObserverAndroid*>( |
| 34 static_cast<WebContentsImpl*>(web_contents) | 105 static_cast<WebContentsImpl*>(web_contents) |
| 35 ->media_web_contents_observer()); | 106 ->media_web_contents_observer()); |
| 36 } | 107 } |
| 37 | 108 |
| 38 BrowserMediaPlayerManager* | 109 BrowserMediaPlayerManager* |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 // BrowserCdmManager run on a different thread. | 157 // BrowserCdmManager run on a different thread. |
| 87 BrowserCdmManager* browser_cdm_manager = | 158 BrowserCdmManager* browser_cdm_manager = |
| 88 BrowserCdmManager::FromProcess(render_frame_host->GetProcess()->GetID()); | 159 BrowserCdmManager::FromProcess(render_frame_host->GetProcess()->GetID()); |
| 89 if (browser_cdm_manager) | 160 if (browser_cdm_manager) |
| 90 browser_cdm_manager->RenderFrameDeleted(render_frame_host->GetRoutingID()); | 161 browser_cdm_manager->RenderFrameDeleted(render_frame_host->GetRoutingID()); |
| 91 } | 162 } |
| 92 | 163 |
| 93 bool MediaWebContentsObserverAndroid::OnMessageReceived( | 164 bool MediaWebContentsObserverAndroid::OnMessageReceived( |
| 94 const IPC::Message& msg, | 165 const IPC::Message& msg, |
| 95 RenderFrameHost* render_frame_host) { | 166 RenderFrameHost* render_frame_host) { |
| 167 // Receive play/pause/destroyed messages, but don't mark as processed so they | |
| 168 // are also handled by MediaWebContentsObserver. | |
| 169 OnMediaPlayerDelegateMessageReceived(msg, render_frame_host); | |
| 170 | |
| 96 if (MediaWebContentsObserver::OnMessageReceived(msg, render_frame_host)) | 171 if (MediaWebContentsObserver::OnMessageReceived(msg, render_frame_host)) |
| 97 return true; | 172 return true; |
| 98 | 173 |
| 99 if (OnMediaPlayerMessageReceived(msg, render_frame_host)) | 174 if (OnMediaPlayerMessageReceived(msg, render_frame_host)) |
| 100 return true; | 175 return true; |
| 101 | 176 |
| 102 if (OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host)) | 177 if (OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host)) |
| 103 return true; | 178 return true; |
| 104 | 179 |
| 105 return false; | 180 return false; |
| 106 } | 181 } |
| 107 | 182 |
| 183 void MediaWebContentsObserverAndroid::OnMediaPlayerDelegateMessageReceived( | |
| 184 const IPC::Message& msg, | |
| 185 RenderFrameHost* render_frame_host) { | |
| 186 // TODO(dalecurtis): These should no longer be FrameHostMsg. | |
| 187 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MediaWebContentsObserverAndroid, msg, | |
| 188 render_frame_host) | |
| 189 IPC_MESSAGE_HANDLER(FrameHostMsg_MediaDestroyedNotification, | |
| 190 OnMediaDestroyedNotification) | |
| 191 IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPlayingNotification, | |
| 192 OnMediaPlayingNotification) | |
| 193 IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPausedNotification, | |
| 194 OnMediaPausedNotification) | |
| 195 IPC_END_MESSAGE_MAP() | |
| 196 } | |
| 197 | |
| 108 bool MediaWebContentsObserverAndroid::OnMediaPlayerMessageReceived( | 198 bool MediaWebContentsObserverAndroid::OnMediaPlayerMessageReceived( |
| 109 const IPC::Message& msg, | 199 const IPC::Message& msg, |
| 110 RenderFrameHost* render_frame_host) { | 200 RenderFrameHost* render_frame_host) { |
| 111 bool handled = true; | 201 bool handled = true; |
| 112 IPC_BEGIN_MESSAGE_MAP(MediaWebContentsObserverAndroid, msg) | 202 IPC_BEGIN_MESSAGE_MAP(MediaWebContentsObserverAndroid, msg) |
| 113 IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_EnterFullscreen, | 203 IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_EnterFullscreen, |
| 114 GetMediaPlayerManager(render_frame_host), | 204 GetMediaPlayerManager(render_frame_host), |
| 115 BrowserMediaPlayerManager::OnEnterFullscreen) | 205 BrowserMediaPlayerManager::OnEnterFullscreen) |
| 116 IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Initialize, | 206 IPC_MESSAGE_FORWARD(MediaPlayerHostMsg_Initialize, |
| 117 GetMediaPlayerManager(render_frame_host), | 207 GetMediaPlayerManager(render_frame_host), |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 if (!cdm) { | 286 if (!cdm) { |
| 197 NOTREACHED() << "OnSetCdm: CDM not found for " << cdm_id; | 287 NOTREACHED() << "OnSetCdm: CDM not found for " << cdm_id; |
| 198 return; | 288 return; |
| 199 } | 289 } |
| 200 | 290 |
| 201 // TODO(xhwang): This could possibly fail. In that case we should reject the | 291 // TODO(xhwang): This could possibly fail. In that case we should reject the |
| 202 // promise. | 292 // promise. |
| 203 media_player->SetCdm(cdm); | 293 media_player->SetCdm(cdm); |
| 204 } | 294 } |
| 205 | 295 |
| 296 void MediaWebContentsObserverAndroid::OnMediaDestroyedNotification( | |
|
mlamouri (slow - plz ping)
2016/01/14 15:43:08
Do we destroy the media as soon as it reached the
DaleCurtis
2016/01/14 18:21:18
No, this is only called when the WMP is being dest
| |
| 297 RenderFrameHost* render_frame_host, | |
| 298 int64_t player_cookie) { | |
| 299 media_session_map_.erase(MediaPlayerId(render_frame_host, player_cookie)); | |
| 300 } | |
| 301 | |
| 302 void MediaWebContentsObserverAndroid::OnMediaPlayingNotification( | |
|
mlamouri (slow - plz ping)
2016/01/14 15:43:08
Are we going to call this when the media _starts_
DaleCurtis
2016/01/14 18:21:18
This is only called when status changes to play.
mlamouri (slow - plz ping)
2016/01/19 16:59:11
Does that match the 'playing' event?
DaleCurtis
2016/01/20 00:43:58
Yes
| |
| 303 RenderFrameHost* render_frame_host, | |
| 304 int64_t player_cookie, | |
| 305 bool has_video, | |
| 306 bool has_audio, | |
| 307 bool is_remote, | |
| 308 base::TimeDelta duration) { | |
| 309 // Don't setup a media session for remote playback instances. | |
| 310 if (is_remote) | |
| 311 return; | |
| 312 | |
| 313 const MediaPlayerId id(render_frame_host, player_cookie); | |
| 314 scoped_ptr<MediaSessionController> controller( | |
| 315 new MediaSessionController(id, this)); | |
| 316 | |
| 317 // If initialize fails, the controller should be destroyed and a new one | |
| 318 // attempted later after another playback attempt occurs. | |
| 319 if (!controller->Initialize(has_video, has_audio, duration)) | |
| 320 return; | |
| 321 | |
| 322 media_session_map_[id] = std::move(controller); | |
| 323 } | |
| 324 | |
| 325 void MediaWebContentsObserverAndroid::OnMediaPausedNotification( | |
| 326 RenderFrameHost* render_frame_host, | |
| 327 int64_t player_cookie, | |
| 328 bool reached_end_of_stream) { | |
| 329 // Drop the session if playback completes normally. | |
| 330 if (reached_end_of_stream) { | |
| 331 OnMediaDestroyedNotification(render_frame_host, player_cookie); | |
| 332 return; | |
| 333 } | |
| 334 | |
| 335 auto it = | |
| 336 media_session_map_.find(MediaPlayerId(render_frame_host, player_cookie)); | |
| 337 if (it == media_session_map_.end()) | |
| 338 return; | |
| 339 | |
| 340 it->second->PausePlayback(); | |
| 341 } | |
| 342 | |
| 206 } // namespace content | 343 } // namespace content |
| OLD | NEW |