Index: content/browser/media/android/media_web_contents_observer_android.cc |
diff --git a/content/browser/media/android/media_web_contents_observer_android.cc b/content/browser/media/android/media_web_contents_observer_android.cc |
index 1b48959e9bad9ae15030a60b37bcce87c4d49bd8..aa66741fd92fe0de407e3498f4281d59a11fecfc 100644 |
--- a/content/browser/media/android/media_web_contents_observer_android.cc |
+++ b/content/browser/media/android/media_web_contents_observer_android.cc |
@@ -6,9 +6,11 @@ |
#include "content/browser/media/android/browser_media_player_manager.h" |
#include "content/browser/media/android/browser_media_session_manager.h" |
+#include "content/browser/media/android/media_session.h" |
#include "content/browser/media/android/media_session_observer.h" |
#include "content/browser/media/cdm/browser_cdm_manager.h" |
#include "content/browser/web_contents/web_contents_impl.h" |
+#include "content/common/frame_messages.h" |
#include "content/common/media/media_player_messages_android.h" |
#include "content/common/media/media_session_messages_android.h" |
#include "content/public/browser/render_frame_host.h" |
@@ -18,6 +20,80 @@ |
namespace content { |
+class MediaSessionController : public MediaSessionObserver { |
+ public: |
+ MediaSessionController(const WebContentsObserver::MediaPlayerId& id, |
+ MediaWebContentsObserver* media_web_contents_observer) |
+ : id_(id), media_web_contents_observer_(media_web_contents_observer) {} |
+ |
+ // Clients must call this after construction and destroy the controller if it |
+ // returns false. |
+ bool Initialize(bool has_audio, bool is_remote, base::TimeDelta duration) { |
+ // These objects are only created on the UI thread, so this is safe. |
+ static uint32_t player_id = 0; |
+ player_id_ = static_cast<int>(player_id++); |
+ |
+ // Don't bother with a MediaSession for remote players or without audio. |
+ if (!has_audio || is_remote) |
+ return true; |
+ |
+ // Minimal duration of a player in order to be considered as Content type. |
+ const base::TimeDelta kMinimumDurationForContent = |
+ base::TimeDelta::FromSeconds(5); |
+ |
+ const MediaSession::Type media_session_type = |
+ duration == base::TimeDelta() || duration > kMinimumDurationForContent |
+ ? MediaSession::Type::Content |
+ : MediaSession::Type::Transient; |
+ |
+ // If a session can't be created, force a pause immediately. |
+ if (!MediaSession::Get(media_web_contents_observer_->web_contents()) |
+ ->AddPlayer(this, player_id_, media_session_type)) { |
+ OnSuspend(player_id_); |
+ return false; |
+ } |
+ |
+ initialized_ = true; |
+ return true; |
+ } |
+ |
+ ~MediaSessionController() { |
+ if (initialized_) { |
+ MediaSession::Get(media_web_contents_observer_->web_contents()) |
+ ->RemovePlayer(this, player_id_); |
+ } |
+ } |
+ |
+ void OnSuspend(int player_id) { |
+ DCHECK_EQ(player_id_, player_id); |
+ media_web_contents_observer_->Send( |
+ new FrameMsg_MediaDelegatePause(id_.first->GetRoutingID(), id_.second)); |
+ } |
+ |
+ void OnResume(int player_id) { |
+ DCHECK_EQ(player_id_, player_id); |
+ media_web_contents_observer_->Send( |
+ new FrameMsg_MediaDelegatePlay(id_.first->GetRoutingID(), id_.second)); |
+ } |
+ |
+ void PausePlayback() { |
+ MediaSession* session = |
+ MediaSession::Get(media_web_contents_observer_->web_contents()); |
+ // We check for suspension here since the renderer may issue its own pause |
+ // in response to or while a pause from the browser is in flight. |
+ if (!session->IsSuspended()) |
+ session->OnPlayerPaused(this, player_id_); |
+ } |
+ |
+ private: |
+ const WebContentsObserver::MediaPlayerId id_; |
+ MediaWebContentsObserver* const media_web_contents_observer_; |
+ int player_id_ = 0; |
+ bool initialized_ = false; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MediaSessionController); |
+}; |
+ |
MediaWebContentsObserverAndroid::MediaWebContentsObserverAndroid( |
WebContents* web_contents) |
: MediaWebContentsObserver(web_contents) {} |
@@ -58,6 +134,19 @@ MediaWebContentsObserverAndroid::GetMediaSessionManager( |
return manager; |
} |
+bool MediaWebContentsObserverAndroid::RequestPlay( |
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie, |
+ bool has_audio, |
+ bool is_remote, |
+ base::TimeDelta duration) { |
+ // |has_video| forced to true since the value doesn't matter at present. |
+ OnMediaPlayingNotification(render_frame_host, player_cookie, true, |
+ has_audio, is_remote, duration); |
+ return media_session_map_.find(MediaPlayerId( |
+ render_frame_host, player_cookie)) != media_session_map_.end(); |
+} |
+ |
#if defined(VIDEO_HOLE) |
void MediaWebContentsObserverAndroid::OnFrameInfoUpdated() { |
for (auto it = media_player_managers_.begin(); |
@@ -71,6 +160,13 @@ void MediaWebContentsObserverAndroid::RenderFrameDeleted( |
RenderFrameHost* render_frame_host) { |
MediaWebContentsObserver::RenderFrameDeleted(render_frame_host); |
+ for (auto it = media_session_map_.begin(); it != media_session_map_.end();) { |
+ if (it->first.first == render_frame_host) |
+ it = media_session_map_.erase(it); |
+ else |
+ ++it; |
+ } |
+ |
// Always destroy the media players before CDMs because we do not support |
// detaching CDMs from media players yet. See http://crbug.com/330324 |
media_player_managers_.erase(render_frame_host); |
@@ -90,6 +186,10 @@ void MediaWebContentsObserverAndroid::RenderFrameDeleted( |
bool MediaWebContentsObserverAndroid::OnMessageReceived( |
const IPC::Message& msg, |
RenderFrameHost* render_frame_host) { |
+ // Receive play/pause/destroyed messages, but don't mark as processed so they |
+ // are also handled by MediaWebContentsObserver. |
+ OnMediaPlayerDelegateMessageReceived(msg, render_frame_host); |
+ |
if (MediaWebContentsObserver::OnMessageReceived(msg, render_frame_host)) |
return true; |
@@ -99,6 +199,21 @@ bool MediaWebContentsObserverAndroid::OnMessageReceived( |
return OnMediaPlayerSetCdmMessageReceived(msg, render_frame_host); |
} |
+void MediaWebContentsObserverAndroid::OnMediaPlayerDelegateMessageReceived( |
+ const IPC::Message& msg, |
+ RenderFrameHost* render_frame_host) { |
+ // TODO(dalecurtis): These should no longer be FrameHostMsg. |
+ IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MediaWebContentsObserverAndroid, msg, |
+ render_frame_host) |
+ IPC_MESSAGE_HANDLER(FrameHostMsg_MediaDestroyedNotification, |
+ OnMediaDestroyedNotification) |
+ IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPlayingNotification, |
+ OnMediaPlayingNotification) |
+ IPC_MESSAGE_HANDLER(FrameHostMsg_MediaPausedNotification, |
+ OnMediaPausedNotification) |
+ IPC_END_MESSAGE_MAP() |
+} |
+ |
bool MediaWebContentsObserverAndroid::OnMediaPlayerMessageReceived( |
const IPC::Message& msg, |
RenderFrameHost* render_frame_host) { |
@@ -197,4 +312,51 @@ void MediaWebContentsObserverAndroid::OnSetCdm( |
media_player->SetCdm(cdm); |
} |
+void MediaWebContentsObserverAndroid::OnMediaDestroyedNotification( |
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie) { |
+ media_session_map_.erase(MediaPlayerId(render_frame_host, player_cookie)); |
+} |
+ |
+void MediaWebContentsObserverAndroid::OnMediaPlayingNotification( |
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie, |
+ bool has_video, |
+ bool has_audio, |
+ bool is_remote, |
+ base::TimeDelta duration) { |
+ // TODO(dalecurtis): Remove this allowance once RequestPlay() is deleted. |
+ const MediaPlayerId id(render_frame_host, player_cookie); |
+ if (media_session_map_.find(id) != media_session_map_.end()) |
+ return; |
+ |
+ scoped_ptr<MediaSessionController> controller( |
+ new MediaSessionController(id, this)); |
+ |
+ // If initialize fails, the controller should be destroyed and a new one |
+ // attempted later after another playback attempt occurs. |
+ if (!controller->Initialize(has_audio, is_remote, duration)) |
+ return; |
+ |
+ media_session_map_[id] = std::move(controller); |
+} |
+ |
+void MediaWebContentsObserverAndroid::OnMediaPausedNotification( |
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie, |
+ bool reached_end_of_stream) { |
+ // Drop the session if playback completes normally. |
+ if (reached_end_of_stream) { |
+ OnMediaDestroyedNotification(render_frame_host, player_cookie); |
+ return; |
+ } |
+ |
+ auto it = |
+ media_session_map_.find(MediaPlayerId(render_frame_host, player_cookie)); |
+ if (it == media_session_map_.end()) |
+ return; |
+ |
+ it->second->PausePlayback(); |
+} |
+ |
} // namespace content |