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 2bd332681c614786dc1d33fa59fd8669995b2221..5e1ea718b447cdc393d2c93c8e0825120d607ddd 100644 |
--- a/content/browser/media/android/media_web_contents_observer_android.cc |
+++ b/content/browser/media/android/media_web_contents_observer_android.cc |
@@ -8,6 +8,7 @@ |
#include "base/stl_util.h" |
#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" |
@@ -21,11 +22,81 @@ |
namespace content { |
+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
|
+ 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_video, bool has_audio, 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++); |
+ |
+ // 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; |
+ } |
+ |
+ return true; |
+ } |
+ |
+ ~MediaSessionController() { |
+ 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()); |
+ // TODO(dalecurtis, mlamouri): This seems odd, if we don't check suspend of |
+ // the entire session, notifying a pause will DCHECK in mediasession.cc:146. |
+ 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
|
+ session->OnPlayerPaused(this, player_id_); |
+ } |
+ |
+ private: |
+ const WebContentsObserver::MediaPlayerId id_; |
+ MediaWebContentsObserver* const media_web_contents_observer_; |
+ int player_id_ = 0; |
+ bool initialize_failed_ = false; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MediaSessionController); |
+}; |
+ |
MediaWebContentsObserverAndroid::MediaWebContentsObserverAndroid( |
WebContents* web_contents) |
: MediaWebContentsObserver(web_contents) {} |
-MediaWebContentsObserverAndroid::~MediaWebContentsObserverAndroid() {} |
+MediaWebContentsObserverAndroid::~MediaWebContentsObserverAndroid() { |
+ // Clear active sessions before destructing the observer. |
+ media_session_map_.clear(); |
+} |
// static |
MediaWebContentsObserverAndroid* |
@@ -93,6 +164,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; |
@@ -105,6 +180,21 @@ bool MediaWebContentsObserverAndroid::OnMessageReceived( |
return false; |
} |
+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) { |
@@ -203,4 +293,51 @@ void MediaWebContentsObserverAndroid::OnSetCdm( |
media_player->SetCdm(cdm); |
} |
+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
|
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie) { |
+ media_session_map_.erase(MediaPlayerId(render_frame_host, player_cookie)); |
+} |
+ |
+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
|
+ RenderFrameHost* render_frame_host, |
+ int64_t player_cookie, |
+ bool has_video, |
+ bool has_audio, |
+ bool is_remote, |
+ base::TimeDelta duration) { |
+ // Don't setup a media session for remote playback instances. |
+ if (is_remote) |
+ return; |
+ |
+ const MediaPlayerId id(render_frame_host, player_cookie); |
+ 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_video, has_audio, 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 |