diff options
author | qinmin@chromium.org <qinmin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-27 20:45:42 +0000 |
---|---|---|
committer | qinmin@chromium.org <qinmin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-27 20:45:42 +0000 |
commit | 1e04260f2a0311b43b0d6dc060ff2eabe3e6cf42 (patch) | |
tree | 1001e11d141bbca9b4ac0cf9c9763392e26b3521 | |
parent | ad0e54c0b0cf51ccd29a105b4117244faf361081 (diff) | |
download | chromium_src-1e04260f2a0311b43b0d6dc060ff2eabe3e6cf42.zip chromium_src-1e04260f2a0311b43b0d6dc060ff2eabe3e6cf42.tar.gz chromium_src-1e04260f2a0311b43b0d6dc060ff2eabe3e6cf42.tar.bz2 |
Enable MSE for TV
BUG=215275
R=acolwell@chromium.org, jamesr@chromium.org, palmer@chromium.org, qinmin@chromium.org, yfriedman@chromium.org
Review URL: https://codereview.chromium.org/14341010
Patch from Yuncheol Heo <ycheo@chromium.org>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196968 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 1153 insertions, 45 deletions
diff --git a/content/browser/android/media_player_manager_android.cc b/content/browser/android/media_player_manager_android.cc index e024ba1..c046e59 100644 --- a/content/browser/android/media_player_manager_android.cc +++ b/content/browser/android/media_player_manager_android.cc @@ -50,6 +50,10 @@ bool MediaPlayerManagerAndroid::OnMessageReceived(const IPC::Message& msg) { OnRequestExternalSurface) IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_NotifyGeometryChange, OnNotifyGeometryChange) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady, + OnDemuxerReady) + IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ReadFromDemuxerAck, + OnReadFromDemuxerAck) #endif IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -103,7 +107,9 @@ void MediaPlayerManagerAndroid::SetVideoSurface(jobject surface) { } void MediaPlayerManagerAndroid::OnInitialize( - int player_id, const GURL& url, const GURL& first_party_for_cookies) { + int player_id, const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies) { for (ScopedVector<MediaPlayerBridge>::iterator it = players_.begin(); it != players_.end(); ++it) { if ((*it)->player_id() == player_id) { @@ -117,11 +123,15 @@ void MediaPlayerManagerAndroid::OnInitialize( StoragePartition* partition = host->GetStoragePartition(); fileapi::FileSystemContext* file_system_context = partition ? partition->GetFileSystemContext() : NULL; - players_.push_back(new MediaPlayerBridge( - player_id, url, first_party_for_cookies, + players_.push_back(media::MediaPlayerBridge::Create( + player_id, url, is_media_source, first_party_for_cookies, new MediaResourceGetterImpl(context, file_system_context, host->GetID(), routing_id()), context->IsOffTheRecord(), this, +#if defined(GOOGLE_TV) + base::Bind(&MediaPlayerManagerAndroid::OnReadFromDemuxer, + base::Unretained(this)), +#endif base::Bind(&MediaPlayerManagerAndroid::OnError, base::Unretained(this)), base::Bind(&MediaPlayerManagerAndroid::OnVideoSizeChanged, base::Unretained(this)), @@ -237,6 +247,22 @@ void MediaPlayerManagerAndroid::OnNotifyGeometryChange(int player_id, if (view) view->NotifyGeometryChange(player_id, rect); } + +void MediaPlayerManagerAndroid::OnDemuxerReady( + int player_id, + const media::MediaPlayerHostMsg_DemuxerReady_Params& params) { + MediaPlayerBridge* player = GetPlayer(player_id); + if (player) + player->DemuxerReady(params); +} + +void MediaPlayerManagerAndroid::OnReadFromDemuxerAck( + int player_id, + const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { + MediaPlayerBridge* player = GetPlayer(player_id); + if (player) + player->ReadFromDemuxerAck(params); +} #endif MediaPlayerBridge* MediaPlayerManagerAndroid::GetPlayer(int player_id) { @@ -307,6 +333,14 @@ void MediaPlayerManagerAndroid::OnTimeUpdate(int player_id, routing_id(), player_id, current_time)); } +#if defined(GOOGLE_TV) +void MediaPlayerManagerAndroid::OnReadFromDemuxer( + int player_id, media::DemuxerStream::Type type, bool seek_done) { + Send(new MediaPlayerMsg_ReadFromDemuxer( + routing_id(), player_id, type, seek_done)); +} +#endif + void MediaPlayerManagerAndroid::RequestMediaResources( MediaPlayerBridge* player) { if (player == NULL) diff --git a/content/browser/android/media_player_manager_android.h b/content/browser/android/media_player_manager_android.h index c21cb6d..7021b69 100644 --- a/content/browser/android/media_player_manager_android.h +++ b/content/browser/android/media_player_manager_android.h @@ -14,6 +14,9 @@ #include "content/browser/android/content_video_view.h" #include "content/public/browser/render_view_host_observer.h" #include "googleurl/src/gurl.h" +#if defined(GOOGLE_TV) +#include "media/base/android/demuxer_stream_player_params.h" +#endif #include "media/base/android/media_player_bridge.h" #include "media/base/android/media_player_bridge_manager.h" #include "ui/gfx/rect_f.h" @@ -59,6 +62,12 @@ class MediaPlayerManagerAndroid void OnError(int player_id, int error); void OnVideoSizeChanged(int player_id, int width, int height); +#if defined(GOOGLE_TV) + // Callbacks needed by media::DemuxerStreamPlayer. + void OnReadFromDemuxer( + int player_id, media::DemuxerStream::Type type, bool seek_done); +#endif + // media::MediaPlayerBridgeManager overrides. virtual void RequestMediaResources(media::MediaPlayerBridge* player) OVERRIDE; virtual void ReleaseMediaResources(media::MediaPlayerBridge* player) OVERRIDE; @@ -79,6 +88,7 @@ class MediaPlayerManagerAndroid void OnEnterFullscreen(int player_id); void OnExitFullscreen(int player_id); void OnInitialize(int player_id, const GURL& url, + bool is_media_source, const GURL& first_party_for_cookies); void OnStart(int player_id); void OnSeek(int player_id, base::TimeDelta time); @@ -88,6 +98,12 @@ class MediaPlayerManagerAndroid #if defined(GOOGLE_TV) void OnRequestExternalSurface(int player_id); void OnNotifyGeometryChange(int player_id, const gfx::RectF& rect); + void OnDemuxerReady( + int player_id, + const media::MediaPlayerHostMsg_DemuxerReady_Params& params); + void OnReadFromDemuxerAck( + int player_id, + const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params); #endif // An array of managed players. diff --git a/content/common/media/media_player_messages.h b/content/common/media/media_player_messages.h index e2871bf..3df3c0c 100644 --- a/content/common/media/media_player_messages.h +++ b/content/common/media/media_player_messages.h @@ -17,6 +17,50 @@ #define IPC_MESSAGE_EXPORT CONTENT_EXPORT #define IPC_MESSAGE_START MediaPlayerMsgStart +#if defined(GOOGLE_TV) +#include "media/base/android/demuxer_stream_player_params.h" + +IPC_ENUM_TRAITS(media::AudioCodec) +IPC_ENUM_TRAITS(media::DemuxerStream::Status) +IPC_ENUM_TRAITS(media::DemuxerStream::Type) +IPC_ENUM_TRAITS(media::VideoCodec) + +IPC_STRUCT_TRAITS_BEGIN(media::MediaPlayerHostMsg_DemuxerReady_Params) + IPC_STRUCT_TRAITS_MEMBER(audio_codec) + IPC_STRUCT_TRAITS_MEMBER(audio_channels) + IPC_STRUCT_TRAITS_MEMBER(audio_sampling_rate) + IPC_STRUCT_TRAITS_MEMBER(is_audio_encrypted) + + IPC_STRUCT_TRAITS_MEMBER(video_codec) + IPC_STRUCT_TRAITS_MEMBER(video_size) + IPC_STRUCT_TRAITS_MEMBER(is_video_encrypted) + + IPC_STRUCT_TRAITS_MEMBER(duration_ms) + IPC_STRUCT_TRAITS_MEMBER(key_system) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params) + IPC_STRUCT_TRAITS_MEMBER(type) + IPC_STRUCT_TRAITS_MEMBER(access_units) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN( + media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit) + IPC_STRUCT_TRAITS_MEMBER(status) + IPC_STRUCT_TRAITS_MEMBER(end_of_stream) + IPC_STRUCT_TRAITS_MEMBER(data) + IPC_STRUCT_TRAITS_MEMBER(timestamp) + IPC_STRUCT_TRAITS_MEMBER(key_id) + IPC_STRUCT_TRAITS_MEMBER(iv) + IPC_STRUCT_TRAITS_MEMBER(subsamples) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(media::SubsampleEntry) + IPC_STRUCT_TRAITS_MEMBER(clear_bytes) + IPC_STRUCT_TRAITS_MEMBER(cypher_bytes) +IPC_STRUCT_TRAITS_END() +#endif + // Messages for notifying the render process of media playback status ------- // Media buffering has updated. @@ -77,6 +121,14 @@ IPC_MESSAGE_ROUTED1(MediaPlayerMsg_DidMediaPlayerPlay, IPC_MESSAGE_ROUTED1(MediaPlayerMsg_DidMediaPlayerPause, int /* player_id */) +#if defined(GOOGLE_TV) +// The media source player reads data from demuxer +IPC_MESSAGE_ROUTED3(MediaPlayerMsg_ReadFromDemuxer, + int /* player_id */, + media::DemuxerStream::Type /* type */, + bool /* seek_done */) +#endif + // Messages for controllering the media playback in browser process ---------- // Destroy the media player object. @@ -87,9 +139,10 @@ IPC_MESSAGE_ROUTED1(MediaPlayerHostMsg_DestroyMediaPlayer, IPC_MESSAGE_ROUTED0(MediaPlayerHostMsg_DestroyAllMediaPlayers) // Initialize a media player object with the given player_id. -IPC_MESSAGE_ROUTED3(MediaPlayerHostMsg_MediaPlayerInitialize, +IPC_MESSAGE_ROUTED4(MediaPlayerHostMsg_MediaPlayerInitialize, int /* player_id */, GURL /* url */, + bool /* is_media_source */, GURL /* first_party_for_cookies */) // Pause the player. @@ -126,4 +179,14 @@ IPC_MESSAGE_ROUTED1(MediaPlayerHostMsg_RequestExternalSurface, IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_NotifyGeometryChange, int /* player_id */, gfx::RectF /* rect */) + +// Inform the media source player that the demuxer is ready. +IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_DemuxerReady, + int /* player_id */, + media::MediaPlayerHostMsg_DemuxerReady_Params) + +// Sent when the data was read from the ChunkDemuxer. +IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_ReadFromDemuxerAck, + int /* player_id */, + media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params) #endif diff --git a/content/renderer/media/webmediaplayer_proxy_impl_android.cc b/content/renderer/media/webmediaplayer_proxy_impl_android.cc index 0a164ae..652964a 100644 --- a/content/renderer/media/webmediaplayer_proxy_impl_android.cc +++ b/content/renderer/media/webmediaplayer_proxy_impl_android.cc @@ -44,15 +44,20 @@ bool WebMediaPlayerProxyImplAndroid::OnMessageReceived( IPC_MESSAGE_HANDLER(MediaPlayerMsg_DidExitFullscreen, OnDidExitFullscreen) IPC_MESSAGE_HANDLER(MediaPlayerMsg_DidMediaPlayerPlay, OnPlayerPlay) IPC_MESSAGE_HANDLER(MediaPlayerMsg_DidMediaPlayerPause, OnPlayerPause) +#if defined(GOOGLE_TV) + IPC_MESSAGE_HANDLER(MediaPlayerMsg_ReadFromDemuxer, OnReadFromDemuxer) +#endif IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void WebMediaPlayerProxyImplAndroid::Initialize( - int player_id, const GURL& url, const GURL& first_party_for_cookies) { + int player_id, const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies) { Send(new MediaPlayerHostMsg_MediaPlayerInitialize( - routing_id(), player_id, url, first_party_for_cookies)); + routing_id(), player_id, url, is_media_source, first_party_for_cookies)); } void WebMediaPlayerProxyImplAndroid::Start(int player_id) { @@ -185,6 +190,26 @@ void WebMediaPlayerProxyImplAndroid::DidCommitCompositorFrame() { it->second)); } } + +void WebMediaPlayerProxyImplAndroid::DemuxerReady( + int player_id, + const media::MediaPlayerHostMsg_DemuxerReady_Params& params) { + Send(new MediaPlayerHostMsg_DemuxerReady(routing_id(), player_id, params)); +} + +void WebMediaPlayerProxyImplAndroid::ReadFromDemuxerAck( + int player_id, + const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { + Send(new MediaPlayerHostMsg_ReadFromDemuxerAck( + routing_id(), player_id, params)); + +} +void WebMediaPlayerProxyImplAndroid::OnReadFromDemuxer( + int player_id, media::DemuxerStream::Type type, bool seek_done) { + webkit_media::WebMediaPlayerAndroid* player = GetWebMediaPlayer(player_id); + if (player) + player->OnReadFromDemuxer(type, seek_done); +} #endif webkit_media::WebMediaPlayerAndroid* diff --git a/content/renderer/media/webmediaplayer_proxy_impl_android.h b/content/renderer/media/webmediaplayer_proxy_impl_android.h index 74830cd..a990459 100644 --- a/content/renderer/media/webmediaplayer_proxy_impl_android.h +++ b/content/renderer/media/webmediaplayer_proxy_impl_android.h @@ -36,6 +36,7 @@ class WebMediaPlayerProxyImplAndroid // Methods inherited from WebMediaPlayerProxyAndroid. virtual void Initialize(int player_id, const GURL& url, + bool is_media_source, const GURL& first_party_for_cookies) OVERRIDE; virtual void Start(int player_id) OVERRIDE; virtual void Pause(int player_id) OVERRIDE; @@ -46,6 +47,12 @@ class WebMediaPlayerProxyImplAndroid virtual void ExitFullscreen(int player_id) OVERRIDE; #if defined(GOOGLE_TV) virtual void RequestExternalSurface(int player_id) OVERRIDE; + virtual void DemuxerReady( + int player_id, + const media::MediaPlayerHostMsg_DemuxerReady_Params& params) OVERRIDE; + virtual void ReadFromDemuxerAck( + int player_id, + const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params&) OVERRIDE; // Methods inherited from RenderViewObserver. virtual void DidCommitCompositorFrame() OVERRIDE; @@ -68,6 +75,10 @@ class WebMediaPlayerProxyImplAndroid void OnDidEnterFullscreen(int player_id); void OnPlayerPlay(int player_id); void OnPlayerPause(int player_id); +#if defined(GOOGLE_TV) + void OnReadFromDemuxer( + int player_id, media::DemuxerStream::Type type, bool seek_done); +#endif webkit_media::WebMediaPlayerManagerAndroid* manager_; diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 2383846..17a2150 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -2711,7 +2711,8 @@ WebMediaPlayer* RenderViewImpl::createMediaPlayer( media_player_manager_.get(), media_player_proxy_, new StreamTextureFactoryImpl( - context_provider->Context3d(), gpu_channel_host, routing_id_)); + context_provider->Context3d(), gpu_channel_host, routing_id_), + new RenderMediaLog()); #endif scoped_refptr<media::AudioRendererSink> sink; diff --git a/media/base/android/demuxer_stream_player_params.cc b/media/base/android/demuxer_stream_player_params.cc new file mode 100644 index 0000000..ab9fcaf --- /dev/null +++ b/media/base/android/demuxer_stream_player_params.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/android/demuxer_stream_player_params.h" + +namespace media { + +MediaPlayerHostMsg_DemuxerReady_Params:: + MediaPlayerHostMsg_DemuxerReady_Params() + : audio_channels(0), + audio_sampling_rate(0), + is_audio_encrypted(false), + is_video_encrypted(false), + duration_ms(0) {} + +MediaPlayerHostMsg_DemuxerReady_Params:: + ~MediaPlayerHostMsg_DemuxerReady_Params() {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: + MediaPlayerHostMsg_ReadFromDemuxerAck_Params() + : type(DemuxerStream::UNKNOWN) {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params:: + ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params() {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::AccessUnit() + : end_of_stream(false) {} + +MediaPlayerHostMsg_ReadFromDemuxerAck_Params::AccessUnit::~AccessUnit() {} + +} // namespace media diff --git a/media/base/android/demuxer_stream_player_params.h b/media/base/android/demuxer_stream_player_params.h new file mode 100644 index 0000000..face665 --- /dev/null +++ b/media/base/android/demuxer_stream_player_params.h @@ -0,0 +1,61 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ +#define MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ + +#include <string> +#include <vector> + +#include "media/base/audio_decoder_config.h" +#include "media/base/decrypt_config.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_export.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/size.h" + +namespace media { + +struct MEDIA_EXPORT MediaPlayerHostMsg_DemuxerReady_Params { + MediaPlayerHostMsg_DemuxerReady_Params(); + ~MediaPlayerHostMsg_DemuxerReady_Params(); + + AudioCodec audio_codec; + int audio_channels; + int audio_sampling_rate; + bool is_audio_encrypted; + + VideoCodec video_codec; + gfx::Size video_size; + bool is_video_encrypted; + + int duration_ms; + std::string key_system; +}; + +struct MEDIA_EXPORT MediaPlayerHostMsg_ReadFromDemuxerAck_Params { + struct MEDIA_EXPORT AccessUnit { + AccessUnit(); + ~AccessUnit(); + + DemuxerStream::Status status; + bool end_of_stream; + // TODO(ycheo): Use the shared memory to transfer the block data. + std::vector<unsigned char> data; + base::TimeDelta timestamp; + std::vector<char> key_id; + std::vector<char> iv; + std::vector<media::SubsampleEntry> subsamples; + }; + + MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); + ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); + + DemuxerStream::Type type; + std::vector<AccessUnit> access_units; +}; + +}; // namespace media + +#endif // MEDIA_BASE_ANDROID_DEMUXER_STREAM_PLAYER_PARAMS_H_ diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index cc3d5f9..267cfe7 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc @@ -29,6 +29,43 @@ static const int kTemporaryDuration = 100; namespace media { +#if !defined(GOOGLE_TV) +// static +MediaPlayerBridge* MediaPlayerBridge::Create( + int player_id, + const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies, + MediaResourceGetter* resource_getter, + bool hide_url_log, + MediaPlayerBridgeManager* manager, + const MediaErrorCB& media_error_cb, + const VideoSizeChangedCB& video_size_changed_cb, + const BufferingUpdateCB& buffering_update_cb, + const MediaMetadataChangedCB& media_prepared_cb, + const PlaybackCompleteCB& playback_complete_cb, + const SeekCompleteCB& seek_complete_cb, + const TimeUpdateCB& time_update_cb, + const MediaInterruptedCB& media_interrupted_cb) { + LOG_IF(WARNING, is_media_source) << "MSE is not supported"; + return new MediaPlayerBridge( + player_id, + url, + first_party_for_cookies, + resource_getter, + hide_url_log, + manager, + media_error_cb, + video_size_changed_cb, + buffering_update_cb, + media_prepared_cb, + playback_complete_cb, + seek_complete_cb, + time_update_cb, + media_interrupted_cb); +} +#endif + MediaPlayerBridge::MediaPlayerBridge( int player_id, const GURL& url, @@ -100,6 +137,17 @@ void MediaPlayerBridge::CreateMediaPlayer() { j_media_player_.Reset(JNI_MediaPlayer::Java_MediaPlayer_Constructor(env)); + SetMediaPlayerListener(); +} + +void MediaPlayerBridge::SetMediaPlayer(jobject j_media_player) { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + + j_media_player_.Reset(env, j_media_player); +} + +void MediaPlayerBridge::SetMediaPlayerListener() { jobject j_context = base::android::GetApplicationContext(); DCHECK(j_context); @@ -149,8 +197,7 @@ void MediaPlayerBridge::SetDataSource(const std::string& url) { if (Java_MediaPlayerBridge_setDataSource( env, j_media_player_.obj(), j_context, j_url_string.obj(), j_cookies.obj(), hide_url_log_)) { - if (manager_) - manager_->RequestMediaResources(this); + RequestMediaResourcesFromManager(); JNI_MediaPlayer::Java_MediaPlayer_prepareAsync( env, j_media_player_.obj()); } else { @@ -180,6 +227,11 @@ void MediaPlayerBridge::OnMediaMetadataExtracted( success); } +void MediaPlayerBridge::RequestMediaResourcesFromManager() { + if (manager_) + manager_->RequestMediaResources(this); +} + void MediaPlayerBridge::Start() { if (j_media_player_.is_null()) { pending_play_ = true; @@ -339,7 +391,7 @@ void MediaPlayerBridge::OnMediaPrepared() { // If media player was recovered from a saved state, consume all the pending // events. - SeekInternal(pending_seek_); + PendingSeekInternal(pending_seek_); if (pending_play_) { StartInternal(); @@ -380,6 +432,10 @@ void MediaPlayerBridge::PauseInternal() { time_update_timer_.Stop(); } +void MediaPlayerBridge::PendingSeekInternal(base::TimeDelta time) { + SeekInternal(time); +} + void MediaPlayerBridge::SeekInternal(base::TimeDelta time) { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); @@ -397,4 +453,16 @@ bool MediaPlayerBridge::RegisterMediaPlayerBridge(JNIEnv* env) { return ret; } +#if defined(GOOGLE_TV) +void MediaPlayerBridge::DemuxerReady( + const MediaPlayerHostMsg_DemuxerReady_Params& params) { + NOTREACHED() << "Unexpected ipc received"; +} + +void MediaPlayerBridge::ReadFromDemuxerAck( + const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { + NOTREACHED() << "Unexpected ipc received"; +} +#endif + } // namespace media diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index 1287de4..c6db77b 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h @@ -17,6 +17,9 @@ #include "base/timer.h" #include "googleurl/src/gurl.h" #include "media/base/media_export.h" +#if defined(GOOGLE_TV) +#include "media/base/android/demuxer_stream_player_params.h" +#endif #include "media/base/android/media_player_listener.h" namespace media { @@ -68,8 +71,34 @@ class MEDIA_EXPORT MediaPlayerBridge { // current time. typedef base::Callback<void(int, base::TimeDelta)> TimeUpdateCB; +#if defined(GOOGLE_TV) + // Callback when DemuxerStreamPlayer wants to read data from the demuxer. + typedef base::Callback<void(int, DemuxerStream::Type, bool)> + ReadFromDemuxerCB; +#endif + static bool RegisterMediaPlayerBridge(JNIEnv* env); + static MediaPlayerBridge* Create( + int player_id, + const GURL& url, + bool is_media_source, + const GURL& first_party_for_cookies, + MediaResourceGetter* resource_getter, + bool hide_url_log, + MediaPlayerBridgeManager* manager, +#if defined(GOOGLE_TV) + const ReadFromDemuxerCB read_from_demuxer_cb, +#endif + const MediaErrorCB& media_error_cb, + const VideoSizeChangedCB& video_size_changed_cb, + const BufferingUpdateCB& buffering_update_cb, + const MediaMetadataChangedCB& media_prepared_cb, + const PlaybackCompleteCB& playback_complete_cb, + const SeekCompleteCB& seek_complete_cb, + const TimeUpdateCB& time_update_cb, + const MediaInterruptedCB& media_interrupted_cb); + // Construct a MediaPlayerBridge object with all the needed media player // callbacks. This object needs to call |manager|'s RequestMediaResources() // before decoding the media stream. This allows |manager| to track @@ -89,7 +118,7 @@ class MEDIA_EXPORT MediaPlayerBridge { const SeekCompleteCB& seek_complete_cb, const TimeUpdateCB& time_update_cb, const MediaInterruptedCB& media_interrupted_cb); - ~MediaPlayerBridge(); + virtual ~MediaPlayerBridge(); typedef std::map<std::string, std::string> HeadersMap; @@ -106,7 +135,7 @@ class MEDIA_EXPORT MediaPlayerBridge { void SeekTo(base::TimeDelta time); // Release the player resources. - void Release(); + virtual void Release(); // Set the player volume. void SetVolume(float leftVolume, float rightVolume); @@ -121,6 +150,16 @@ class MEDIA_EXPORT MediaPlayerBridge { // Get allowed operations from the player. void GetAllowedOperations(); +#if defined(GOOGLE_TV) + // Methods for DeumxerStreamPlayer. + // Informs DemuxerStreamPlayer that the demuxer is ready. + virtual void DemuxerReady( + const MediaPlayerHostMsg_DemuxerReady_Params& params); + // Called when the requested data is received from the demuxer. + virtual void ReadFromDemuxerAck( + const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params); +#endif + // Called by the timer to check for current time routinely and generates // time update events. void DoTimeUpdate(); @@ -138,7 +177,7 @@ class MEDIA_EXPORT MediaPlayerBridge { // Prepare the player for playback, asynchronously. When succeeds, // OnMediaPrepared() will be called. Otherwise, OnMediaError() will // be called with an error type. - void Prepare(); + virtual void Prepare(); // Callback function passed to |resource_getter_|. Called when the cookies // are retrieved. @@ -150,6 +189,13 @@ class MEDIA_EXPORT MediaPlayerBridge { bool can_seek_backward() { return can_seek_backward_; } bool prepared() { return prepared_; } + protected: + void SetMediaPlayer(jobject j_media_player); + void SetMediaPlayerListener(); + void RequestMediaResourcesFromManager(); + + virtual void PendingSeekInternal(base::TimeDelta time); + private: // Initialize this object and extract the metadata from the media. void Initialize(); diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc index 43b6729..93b0e8b 100644 --- a/media/filters/stream_parser_factory.cc +++ b/media/filters/stream_parser_factory.cc @@ -62,11 +62,7 @@ static const CodecInfo* kAudioWebMCodecs[] = { static media::StreamParser* BuildWebMParser( const std::vector<std::string>& codecs, const media::LogCB& log_cb) { -#if defined(OS_ANDROID) - return NULL; -#else return new media::WebMStreamParser(); -#endif } #if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS) @@ -127,9 +123,6 @@ static const CodecInfo* kAudioMP4Codecs[] = { static media::StreamParser* BuildMP4Parser( const std::vector<std::string>& codecs, const media::LogCB& log_cb) { -#if defined(OS_ANDROID) - return NULL; -#else std::set<int> audio_object_types; bool has_sbr = false; for (size_t i = 0; i < codecs.size(); ++i) { @@ -150,7 +143,6 @@ static media::StreamParser* BuildMP4Parser( } return new media::mp4::MP4StreamParser(audio_object_types, has_sbr); -#endif } #endif diff --git a/media/media.gyp b/media/media.gyp index a49fc64..692e7bc 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -1546,6 +1546,14 @@ 'base/android/webaudio_media_codec_bridge.cc', 'base/android/webaudio_media_codec_bridge.h', ], + 'conditions': [ + ['google_tv == 1', { + 'sources': [ + 'base/android/demuxer_stream_player_params.cc', + 'base/android/demuxer_stream_player_params.h', + ], + }], + ], 'dependencies': [ '../base/base.gyp:base', 'media_android_jni_headers', diff --git a/webkit/media/android/media_source_delegate.cc b/webkit/media/android/media_source_delegate.cc new file mode 100644 index 0000000..908a4fa --- /dev/null +++ b/webkit/media/android/media_source_delegate.cc @@ -0,0 +1,444 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/media/android/media_source_delegate.h" + +#include "base/message_loop_proxy.h" +#include "base/strings/string_number_conversions.h" +#include "media/base/android/demuxer_stream_player_params.h" +#include "media/base/bind_to_loop.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_log.h" +#include "media/filters/chunk_demuxer.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaSource.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebRuntimeFeatures.h" +#include "webkit/media/android/webmediaplayer_proxy_android.h" +#include "webkit/media/crypto/key_systems.h" +#include "webkit/media/crypto/proxy_decryptor.h" +#include "webkit/media/webmediaplayer_util.h" +#include "webkit/media/webmediasourceclient_impl.h" + +using media::DemuxerStream; +using media::MediaPlayerHostMsg_DemuxerReady_Params; +using media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params; +using WebKit::WebMediaPlayer; +using WebKit::WebString; + +namespace { + +// The size of the access unit to transfer in an IPC. +// 16: approximately 250ms of content in 60 fps movies. +const size_t kAccessUnitSize = 16; + +} // namespace + +namespace webkit_media { + +#define BIND_TO_RENDER_LOOP(function) \ + media::BindToLoop(base::MessageLoopProxy::current(), \ + base::Bind(function, weak_this_.GetWeakPtr())) + +#define BIND_TO_RENDER_LOOP_1(function, arg1) \ + media::BindToLoop(base::MessageLoopProxy::current(), \ + base::Bind(function, weak_this_.GetWeakPtr(), arg1)) + +#define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \ + media::BindToLoop(base::MessageLoopProxy::current(), \ + base::Bind(function, weak_this_.GetWeakPtr(), arg1, arg2)) + +#define BIND_TO_RENDER_LOOP_3(function, arg1, arg2, arg3) \ + media::BindToLoop(base::MessageLoopProxy::current(), \ + base::Bind(function, \ + weak_this_.GetWeakPtr(), arg1, arg2, arg3)) + +static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log, + const std::string& error) { + media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); +} + +MediaSourceDelegate::MediaSourceDelegate( + WebKit::WebFrame* frame, + WebKit::WebMediaPlayerClient* client, + WebMediaPlayerProxyAndroid* proxy, + int player_id, + media::MediaLog* media_log) + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), + client_(client), + proxy_(proxy), + player_id_(player_id), + media_log_(media_log), + audio_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), + video_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), + seeking_(false) { + if (WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled()) { + decryptor_.reset(new ProxyDecryptor( + client, + frame, + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnKeyAdded), + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnKeyError), + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnKeyMessage), + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnNeedKey))); + decryptor_->SetDecryptorReadyCB( + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDecryptorReady)); + } +} + +MediaSourceDelegate::~MediaSourceDelegate() {} + +void MediaSourceDelegate::Initialize( + scoped_ptr<WebKit::WebMediaSource> media_source, + const UpdateNetworkStateCB& update_network_state_cb) { + DCHECK(media_source); + media_source_ = media_source.Pass(); + update_network_state_cb_ = update_network_state_cb; + + chunk_demuxer_.reset(new media::ChunkDemuxer( + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerOpened), + BIND_TO_RENDER_LOOP_2(&MediaSourceDelegate::OnNeedKey, "", ""), + base::Bind(&LogMediaSourceError, media_log_))); + chunk_demuxer_->Initialize(this, + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerInitDone)); +} + +const WebKit::WebTimeRanges& MediaSourceDelegate::Buffered() { + buffered_web_time_ranges_ = + ConvertToWebTimeRanges(buffered_time_ranges_); + return buffered_web_time_ranges_; +} + +size_t MediaSourceDelegate::DecodedFrameCount() const { + return statistics_.video_frames_decoded; +} + +size_t MediaSourceDelegate::DroppedFrameCount() const { + return statistics_.video_frames_dropped; +} + +size_t MediaSourceDelegate::AudioDecodedByteCount() const { + return statistics_.audio_bytes_decoded; +} + +size_t MediaSourceDelegate::VideoDecodedByteCount() const { + return statistics_.video_bytes_decoded; +} + +WebMediaPlayer::MediaKeyException MediaSourceDelegate::GenerateKeyRequest( + const WebString& key_system, + const unsigned char* init_data, + size_t init_data_length) { + if (!IsSupportedKeySystem(key_system)) + return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; + + // We do not support run-time switching between key systems for now. + if (current_key_system_.isEmpty()) + current_key_system_ = key_system; + else if (key_system != current_key_system_) + return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; + + DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": " + << std::string(reinterpret_cast<const char*>(init_data), + init_data_length); + + // TODO(xhwang): We assume all streams are from the same container (thus have + // the same "type") for now. In the future, the "type" should be passed down + // from the application. + if (!decryptor_->GenerateKeyRequest(key_system.utf8(), + init_data_type_, + init_data, init_data_length)) { + current_key_system_.reset(); + return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; + } + + return WebMediaPlayer::MediaKeyExceptionNoError; +} + +WebMediaPlayer::MediaKeyException MediaSourceDelegate::AddKey( + const WebString& key_system, + const unsigned char* key, + size_t key_length, + const unsigned char* init_data, + size_t init_data_length, + const WebString& session_id) { + DCHECK(key); + DCHECK_EQ(key_length, 16u); + + if (!IsSupportedKeySystem(key_system)) + return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; + + if (current_key_system_.isEmpty() || key_system != current_key_system_) + return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; + + DVLOG(1) << "addKey: " << key_system.utf8().data() << ": " + << base::HexEncode(key, key_length) << ", " + << base::HexEncode(init_data, std::max(init_data_length, 256u)) + << " [" << session_id.utf8().data() << "]"; + + decryptor_->AddKey(key_system.utf8(), key, key_length, + init_data, init_data_length, session_id.utf8()); + return WebMediaPlayer::MediaKeyExceptionNoError; +} + +WebMediaPlayer::MediaKeyException MediaSourceDelegate::CancelKeyRequest( + const WebString& key_system, + const WebString& session_id) { + if (!IsSupportedKeySystem(key_system)) + return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; + + if (current_key_system_.isEmpty() || key_system != current_key_system_) + return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; + + decryptor_->CancelKeyRequest(key_system.utf8(), session_id.utf8()); + return WebMediaPlayer::MediaKeyExceptionNoError; +} + +void MediaSourceDelegate::Seek(base::TimeDelta time) { + seeking_ = true; + DCHECK(chunk_demuxer_); + if (!chunk_demuxer_) + return; + chunk_demuxer_->StartWaitingForSeek(); + chunk_demuxer_->Seek(time, + BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerError)); +} + +void MediaSourceDelegate::SetTotalBytes(int64 total_bytes) { + NOTIMPLEMENTED(); +} + +void MediaSourceDelegate::AddBufferedByteRange(int64 start, int64 end) { + NOTIMPLEMENTED(); +} + +void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start, + base::TimeDelta end) { + buffered_time_ranges_.Add(start, end); +} + +void MediaSourceDelegate::SetDuration(base::TimeDelta duration) { + // Do nothing +} + +void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type, + bool seek_done) { + if (seeking_ && !seek_done) + return; // Drop the request during seeking. + seeking_ = false; + + DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO); + MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params = + type == DemuxerStream::AUDIO ? audio_params_.get() : video_params_.get(); + params->type = type; + params->access_units.resize(kAccessUnitSize); + DemuxerStream* stream = chunk_demuxer_->GetStream(type); + DCHECK(stream != NULL); + ReadFromDemuxerStream(stream, params, 0); +} + +void MediaSourceDelegate::ReadFromDemuxerStream( + DemuxerStream* stream, + MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, + size_t index) { + stream->Read(BIND_TO_RENDER_LOOP_3(&MediaSourceDelegate::OnBufferReady, + stream, params, index)); +} + +void MediaSourceDelegate::OnBufferReady( + DemuxerStream* stream, + MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, + size_t index, + DemuxerStream::Status status, + const scoped_refptr<media::DecoderBuffer>& buffer) { + DCHECK(status == DemuxerStream::kAborted || + index < params->access_units.size()); + bool is_audio = stream->type() == DemuxerStream::AUDIO; + if (status != DemuxerStream::kAborted && + index >= params->access_units.size()) { + LOG(ERROR) << "The internal state inconsistency onBufferReady: " + << (is_audio ? "Audio" : "Video") << ", index " << index + <<", size " << params->access_units.size() + << ", status " << static_cast<int>(status); + return; + } + switch (status) { + case DemuxerStream::kAborted: + // Because the abort was caused by the seek, don't respond ack. + return; + + case DemuxerStream::kConfigChanged: + // In case of kConfigChanged, need to read decoder_config once + // for the next reads. + if (is_audio) { + stream->audio_decoder_config(); + } else { + gfx::Size size = stream->video_decoder_config().coded_size(); + DVLOG(1) << "Video config is changed: " << + size.width() << "x" << size.height(); + } + params->access_units[index].status = status; + params->access_units.resize(index + 1); + break; + + case DemuxerStream::kOk: + params->access_units[index].status = status; + if (buffer->IsEndOfStream()) { + params->access_units[index].end_of_stream = true; + params->access_units.resize(index + 1); + break; + } + // TODO(ycheo): We assume that the inputed stream will be decoded + // right away. + // Need to implement this properly using MediaPlayer.OnInfoListener. + if (is_audio) { + statistics_.audio_bytes_decoded += buffer->GetDataSize(); + } else { + statistics_.video_bytes_decoded += buffer->GetDataSize(); + statistics_.video_frames_decoded++; + } + params->access_units[index].timestamp = buffer->GetTimestamp(); + params->access_units[index].data = std::vector<unsigned char>( + buffer->GetData(), + buffer->GetData() + buffer->GetDataSize()); + if (buffer->GetDecryptConfig()) { + params->access_units[index].key_id = std::vector<char>( + buffer->GetDecryptConfig()->key_id().begin(), + buffer->GetDecryptConfig()->key_id().end()); + params->access_units[index].iv = std::vector<char>( + buffer->GetDecryptConfig()->iv().begin(), + buffer->GetDecryptConfig()->iv().end()); + params->access_units[index].subsamples = + buffer->GetDecryptConfig()->subsamples(); + } + if (++index < params->access_units.size()) { + ReadFromDemuxerStream(stream, params, index); + return; + } + break; + + default: + NOTREACHED(); + } + + if (proxy_) + proxy_->ReadFromDemuxerAck(player_id_, *params); + params->access_units.resize(0); +} + +void MediaSourceDelegate::OnDemuxerError( + media::PipelineStatus status) { + if (status != media::PIPELINE_OK) { + DCHECK(status == media::DEMUXER_ERROR_COULD_NOT_OPEN || + status == media::DEMUXER_ERROR_COULD_NOT_PARSE || + status == media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS) + << "Unexpected error from demuxer: " << static_cast<int>(status); + if (!update_network_state_cb_.is_null()) + update_network_state_cb_.Run(WebMediaPlayer::NetworkStateFormatError); + } +} + +void MediaSourceDelegate::OnDemuxerInitDone( + media::PipelineStatus status) { + if (status != media::PIPELINE_OK) { + OnDemuxerError(status); + return; + } + NotifyDemuxerReady(""); +} + +void MediaSourceDelegate::NotifyDemuxerReady( + const std::string& key_system) { + MediaPlayerHostMsg_DemuxerReady_Params params; + DemuxerStream* audio_stream = chunk_demuxer_->GetStream(DemuxerStream::AUDIO); + if (audio_stream) { + const media::AudioDecoderConfig& config = + audio_stream->audio_decoder_config(); + params.audio_codec = config.codec(); + params.audio_channels = + media::ChannelLayoutToChannelCount(config.channel_layout()); + params.audio_sampling_rate = config.samples_per_second(); + params.is_audio_encrypted = config.is_encrypted(); + } + DemuxerStream* video_stream = chunk_demuxer_->GetStream(DemuxerStream::VIDEO); + if (video_stream) { + const media::VideoDecoderConfig& config = + video_stream->video_decoder_config(); + params.video_codec = config.codec(); + params.video_size = config.natural_size(); + params.is_video_encrypted = config.is_encrypted(); + } + double duration_ms = chunk_demuxer_->GetDuration() * 1000; + DCHECK(duration_ms >= 0); + if (duration_ms > std::numeric_limits<int>::max()) + duration_ms = std::numeric_limits<int>::max(); + params.duration_ms = duration_ms; + params.key_system = key_system; + + bool ready_to_send = (!params.is_audio_encrypted && + !params.is_video_encrypted) || !key_system.empty(); + if (proxy_ && ready_to_send) + proxy_->DemuxerReady(player_id_, params); +} + +void MediaSourceDelegate::OnDemuxerOpened() { + media_source_->open(new WebMediaSourceClientImpl( + chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_))); +} + +void MediaSourceDelegate::OnKeyError(const std::string& key_system, + const std::string& session_id, + media::Decryptor::KeyError error_code, + int system_code) { + client_->keyError( + WebString::fromUTF8(key_system), + WebString::fromUTF8(session_id), + static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), + system_code); +} + +void MediaSourceDelegate::OnKeyMessage(const std::string& key_system, + const std::string& session_id, + const std::string& message, + const std::string& default_url) { + const GURL default_url_gurl(default_url); + DLOG_IF(WARNING, !default_url.empty() && !default_url_gurl.is_valid()) + << "Invalid URL in default_url: " << default_url; + + client_->keyMessage(WebString::fromUTF8(key_system), + WebString::fromUTF8(session_id), + reinterpret_cast<const uint8*>(message.data()), + message.size(), + default_url_gurl); +} + +void MediaSourceDelegate::OnKeyAdded(const std::string& key_system, + const std::string& session_id) { + NotifyDemuxerReady(key_system); + client_->keyAdded(WebString::fromUTF8(key_system), + WebString::fromUTF8(session_id)); +} + +void MediaSourceDelegate::OnNeedKey(const std::string& key_system, + const std::string& session_id, + const std::string& type, + scoped_ptr<uint8[]> init_data, + int init_data_size) { + // Do not fire NeedKey event if encrypted media is not enabled. + if (!decryptor_) + return; + + CHECK(init_data_size >= 0); + DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); + if (init_data_type_.empty()) + init_data_type_ = type; + + client_->keyNeeded(WebString::fromUTF8(key_system), + WebString::fromUTF8(session_id), + init_data.get(), + init_data_size); +} + +void MediaSourceDelegate::OnDecryptorReady(media::Decryptor* decryptor) {} + +} // namespace webkit_media diff --git a/webkit/media/android/media_source_delegate.h b/webkit/media/android/media_source_delegate.h new file mode 100644 index 0000000..482f110 --- /dev/null +++ b/webkit/media/android/media_source_delegate.h @@ -0,0 +1,160 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_MEDIA_ANDROID_MEDIA_SOURCE_DELEGATE_H_ +#define WEBKIT_MEDIA_ANDROID_MEDIA_SOURCE_DELEGATE_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/time.h" +#include "media/base/decryptor.h" +#include "media/base/demuxer.h" +#include "media/base/pipeline_status.h" +#include "media/base/ranges.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayer.h" + +namespace media { +class ChunkDemuxer; +class DecoderBuffer; +class DemuxerStream; +class MediaLog; +struct MediaPlayerHostMsg_ReadFromDemuxerAck_Params; +} + +namespace WebKit { +class WebFrame; +class WebMediaSource; +} + +namespace webkit_media { + +class ProxyDecryptor; +class WebMediaPlayerProxyAndroid; + +class MediaSourceDelegate : public media::DemuxerHost { + public: + typedef base::Callback<void(WebKit::WebMediaPlayer::NetworkState)> + UpdateNetworkStateCB; + + MediaSourceDelegate(WebKit::WebFrame* frame, + WebKit::WebMediaPlayerClient* client, + WebMediaPlayerProxyAndroid* proxy, + int player_id, + media::MediaLog* media_log); + virtual ~MediaSourceDelegate(); + + void Initialize(scoped_ptr<WebKit::WebMediaSource> media_source, + const UpdateNetworkStateCB& update_network_state_cb); + + const WebKit::WebTimeRanges& Buffered(); + size_t DecodedFrameCount() const; + size_t DroppedFrameCount() const; + size_t AudioDecodedByteCount() const; + size_t VideoDecodedByteCount() const; + + WebKit::WebMediaPlayer::MediaKeyException GenerateKeyRequest( + const WebKit::WebString& key_system, + const unsigned char* init_data, + size_t init_data_length); + WebKit::WebMediaPlayer::MediaKeyException AddKey( + const WebKit::WebString& key_system, + const unsigned char* key, + size_t key_length, + const unsigned char* init_data, + size_t init_data_length, + const WebKit::WebString& session_id); + WebKit::WebMediaPlayer::MediaKeyException CancelKeyRequest( + const WebKit::WebString& key_system, + const WebKit::WebString& session_id); + + void Seek(base::TimeDelta time); + + // Called when DemuxerStreamPlayer needs to read data from ChunkDemuxer. + // If it's the first request after the seek, |seek_done| will be true. + void OnReadFromDemuxer(media::DemuxerStream::Type type, bool seek_done); + + private: + // Methods inherited from DemuxerHost. + virtual void SetTotalBytes(int64 total_bytes) OVERRIDE; + virtual void AddBufferedByteRange(int64 start, int64 end) OVERRIDE; + virtual void AddBufferedTimeRange(base::TimeDelta start, + base::TimeDelta end) OVERRIDE; + virtual void SetDuration(base::TimeDelta duration) OVERRIDE; + virtual void OnDemuxerError(media::PipelineStatus status) OVERRIDE; + + // Callbacks for ChunkDemuxer & Decryptor. + void OnDemuxerInitDone(media::PipelineStatus status); + void OnDemuxerOpened(); + void OnKeyAdded(const std::string& key_system, const std::string& session_id); + void OnKeyError(const std::string& key_system, + const std::string& session_id, + media::Decryptor::KeyError error_code, + int system_code); + void OnKeyMessage(const std::string& key_system, + const std::string& session_id, + const std::string& message, + const std::string& default_url); + void OnNeedKey(const std::string& key_system, + const std::string& type, + const std::string& session_id, + scoped_ptr<uint8[]> init_data, + int init_data_size); + void OnDecryptorReady(media::Decryptor*); + + // Reads an access unit from the demuxer stream |stream| and stores it in + // the |index|th access unit in |params|. + void ReadFromDemuxerStream( + media::DemuxerStream* stream, + media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, + size_t index); + void OnBufferReady( + media::DemuxerStream* stream, + media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, + size_t index, + media::DemuxerStream::Status status, + const scoped_refptr<media::DecoderBuffer>& buffer); + + void NotifyDemuxerReady(const std::string& key_system); + + base::WeakPtrFactory<MediaSourceDelegate> weak_this_; + + WebKit::WebMediaPlayerClient* const client_; + WebMediaPlayerProxyAndroid* proxy_; + int player_id_; + + scoped_refptr<media::MediaLog> media_log_; + UpdateNetworkStateCB update_network_state_cb_; + + scoped_ptr<media::ChunkDemuxer> chunk_demuxer_; + scoped_ptr<WebKit::WebMediaSource> media_source_; + + media::PipelineStatistics statistics_; + media::Ranges<base::TimeDelta> buffered_time_ranges_; + // Keep a list of buffered time ranges. + WebKit::WebTimeRanges buffered_web_time_ranges_; + + // The decryptor that manages decryption keys and decrypts encrypted frames. + scoped_ptr<ProxyDecryptor> decryptor_; + + // The currently selected key system. Empty string means that no key system + // has been selected. + WebKit::WebString current_key_system_; + + // Temporary for EME v0.1. In the future the init data type should be passed + // through GenerateKeyRequest() directly from WebKit. + std::string init_data_type_; + + scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> audio_params_; + scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> video_params_; + + bool seeking_; + + DISALLOW_COPY_AND_ASSIGN(MediaSourceDelegate); +}; + +} // namespace webkit_media +#endif // WEBKIT_MEDIA_ANDROID_MEDIA_SOURCE_DELEGATE_H_ + diff --git a/webkit/media/android/webmediaplayer_android.cc b/webkit/media/android/webmediaplayer_android.cc index 317c256..7907512 100644 --- a/webkit/media/android/webmediaplayer_android.cc +++ b/webkit/media/android/webmediaplayer_android.cc @@ -13,9 +13,11 @@ #include "media/base/android/media_player_bridge.h" #include "media/base/video_frame.h" #include "net/base/mime_util.h" +#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "webkit/compositor_bindings/web_layer_impl.h" #include "webkit/media/android/webmediaplayer_manager_android.h" @@ -23,11 +25,16 @@ #include "webkit/media/media_switches.h" #include "webkit/media/webmediaplayer_util.h" +#if defined(GOOGLE_TV) +#include "webkit/media/android/media_source_delegate.h" +#endif + static const uint32 kGLTextureExternalOES = 0x8D65; using WebKit::WebMediaPlayer; using WebKit::WebMediaSource; using WebKit::WebSize; +using WebKit::WebString; using WebKit::WebTimeRanges; using WebKit::WebURL; using media::MediaPlayerBridge; @@ -40,7 +47,8 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid( WebKit::WebMediaPlayerClient* client, WebMediaPlayerManagerAndroid* manager, WebMediaPlayerProxyAndroid* proxy, - StreamTextureFactory* factory) + StreamTextureFactory* factory, + media::MediaLog* media_log) : frame_(frame), client_(client), buffered_(1u), @@ -58,7 +66,8 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid( needs_external_surface_(false), video_frame_provider_client_(NULL), proxy_(proxy), - current_time_(0) { + current_time_(0), + media_log_(media_log) { main_loop_->AddDestructionObserver(this); if (manager_) player_id_ = manager_->RegisterMediaPlayer(this); @@ -88,13 +97,33 @@ WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { } void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { + load(url, NULL, cors_mode); +} + +void WebMediaPlayerAndroid::load(const WebURL& url, + WebMediaSource* media_source, + CORSMode cors_mode) { if (cors_mode != CORSModeUnspecified) NOTIMPLEMENTED() << "No CORS support"; + scoped_ptr<WebKit::WebMediaSource> scoped_media_source(media_source); +#if defined(GOOGLE_TV) + if (media_source) { + media_source_delegate_.reset( + new MediaSourceDelegate( + frame_, client_, proxy_, player_id_, media_log_)); + // |media_source_delegate_| is owned, so Unretained() is safe here. + media_source_delegate_->Initialize( + scoped_media_source.Pass(), + base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, + base::Unretained(this))); + } +#endif + url_ = url; GURL first_party_url = frame_->document().firstPartyForCookies(); if (proxy_) { - proxy_->Initialize(player_id_, url_, first_party_url); + proxy_->Initialize(player_id_, url_, media_source != NULL, first_party_url); if (manager_->IsInFullscreen(frame_)) proxy_->EnterFullscreen(player_id_); } @@ -103,12 +132,6 @@ void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); } -void WebMediaPlayerAndroid::load(const WebURL& url, - WebMediaSource* media_source, - CORSMode cors_mode) { - NOTIMPLEMENTED(); -} - void WebMediaPlayerAndroid::cancelLoad() { NOTIMPLEMENTED(); } @@ -139,8 +162,13 @@ void WebMediaPlayerAndroid::seek(double seconds) { pending_seek_ = seconds; seeking_ = true; + base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); +#if defined(GOOGLE_TV) + if (media_source_delegate_) + media_source_delegate_->Seek(seek_time); +#endif if (proxy_) - proxy_->Seek(player_id_, ConvertSecondsToTimestamp(seconds)); + proxy_->Seek(player_id_, seek_time); } bool WebMediaPlayerAndroid::supportsFullscreen() const { @@ -234,6 +262,10 @@ WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { } const WebTimeRanges& WebMediaPlayerAndroid::buffered() { +#if defined(GOOGLE_TV) + if (media_source_delegate_) + return media_source_delegate_->Buffered(); +#endif return buffered_; } @@ -314,21 +346,37 @@ double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { } unsigned WebMediaPlayerAndroid::decodedFrameCount() const { +#if defined(GOOGLE_TV) + if (media_source_delegate_) + return media_source_delegate_->DecodedFrameCount(); +#endif NOTIMPLEMENTED(); return 0; } unsigned WebMediaPlayerAndroid::droppedFrameCount() const { +#if defined(GOOGLE_TV) + if (media_source_delegate_) + return media_source_delegate_->DroppedFrameCount(); +#endif NOTIMPLEMENTED(); return 0; } unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { +#if defined(GOOGLE_TV) + if (media_source_delegate_) + return media_source_delegate_->AudioDecodedByteCount(); +#endif NOTIMPLEMENTED(); return 0; } unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { +#if defined(GOOGLE_TV) + if (media_source_delegate_) + return media_source_delegate_->VideoDecodedByteCount(); +#endif NOTIMPLEMENTED(); return 0; } @@ -417,11 +465,13 @@ void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { switches::kUseExternalVideoSurfaceThresholdInPixels), &threshold); - if (parsed_arg && threshold <= width * height) { + if ((parsed_arg && threshold <= width * height) || + // Use H/W surface for MSE as the content is protected. + media_source_delegate_) { needs_external_surface_ = true; SetNeedsEstablishPeer(false); - if (!paused()) - RequestExternalSurface(); + if (!paused() && proxy_) + proxy_->RequestExternalSurface(player_id_); } #endif @@ -603,6 +653,47 @@ bool WebMediaPlayerAndroid::RetrieveGeometryChange(gfx::RectF* rect) { last_computed_rect_ = *rect; return true; } + +WebMediaPlayer::MediaKeyException +WebMediaPlayerAndroid::generateKeyRequest(const WebString& key_system, + const unsigned char* init_data, + unsigned init_data_length) { + if (media_source_delegate_) { + return media_source_delegate_->GenerateKeyRequest( + key_system, init_data, init_data_length); + } + return MediaKeyExceptionKeySystemNotSupported; +} + +WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey( + const WebString& key_system, + const unsigned char* key, + unsigned key_length, + const unsigned char* init_data, + unsigned init_data_length, + const WebString& session_id) { + if (media_source_delegate_) { + return media_source_delegate_->AddKey( + key_system, key, key_length, init_data, init_data_length, session_id); + } + return MediaKeyExceptionKeySystemNotSupported; +} + +WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest( + const WebString& key_system, + const WebString& session_id) { + if (media_source_delegate_) + return media_source_delegate_->CancelKeyRequest(key_system, session_id); + return MediaKeyExceptionKeySystemNotSupported; +} + +void WebMediaPlayerAndroid::OnReadFromDemuxer( + media::DemuxerStream::Type type, bool seek_done) { + if (media_source_delegate_) + media_source_delegate_->OnReadFromDemuxer(type, seek_done); + else + NOTIMPLEMENTED(); +} #endif void WebMediaPlayerAndroid::enterFullscreen() { diff --git a/webkit/media/android/webmediaplayer_android.h b/webkit/media/android/webmediaplayer_android.h index d488786..e07d48f 100644 --- a/webkit/media/android/webmediaplayer_android.h +++ b/webkit/media/android/webmediaplayer_android.h @@ -12,6 +12,9 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/time.h" +#if defined(GOOGLE_TV) +#include "media/base/demuxer_stream.h" +#endif #include "cc/layers/video_frame_provider.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h" #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" @@ -20,6 +23,10 @@ #include "ui/gfx/rect_f.h" #include "webkit/media/android/stream_texture_factory_android.h" +namespace media { +class MediaLog; +} + namespace WebKit { class WebFrame; } @@ -30,6 +37,9 @@ class WebLayerImpl; namespace webkit_media { +#if defined(GOOGLE_TV) +class MediaSourceDelegate; +#endif class WebMediaPlayerManagerAndroid; class WebMediaPlayerProxyAndroid; @@ -53,7 +63,8 @@ class WebMediaPlayerAndroid WebKit::WebMediaPlayerClient* client, WebMediaPlayerManagerAndroid* manager, WebMediaPlayerProxyAndroid* proxy, - StreamTextureFactory* factory); + StreamTextureFactory* factory, + media::MediaLog* media_log); virtual ~WebMediaPlayerAndroid(); // WebKit::WebMediaPlayer implementation. @@ -175,6 +186,24 @@ class WebMediaPlayerAndroid // frame) if changed. Returns true only if the geometry has been changed since // the last call. bool RetrieveGeometryChange(gfx::RectF* rect); + + virtual MediaKeyException generateKeyRequest( + const WebKit::WebString& key_system, + const unsigned char* init_data, + unsigned init_data_length) OVERRIDE; + virtual MediaKeyException addKey( + const WebKit::WebString& key_system, + const unsigned char* key, + unsigned key_length, + const unsigned char* init_data, + unsigned init_data_length, + const WebKit::WebString& session_id) OVERRIDE; + virtual MediaKeyException cancelKeyRequest( + const WebKit::WebString& key_system, + const WebKit::WebString& session_id) OVERRIDE; + + // Called when DemuxerStreamPlayer needs to read data from ChunkDemuxer. + void OnReadFromDemuxer(media::DemuxerStream::Type type, bool seek_done); #endif protected: @@ -276,6 +305,8 @@ class WebMediaPlayerAndroid // A rectangle represents the geometry of video frame, when computed last // time. gfx::RectF last_computed_rect_; + + scoped_ptr<MediaSourceDelegate> media_source_delegate_; #endif // Proxy object that delegates method calls on Render Thread. @@ -288,6 +319,8 @@ class WebMediaPlayerAndroid // OnTimeUpdate(). float current_time_; + media::MediaLog* media_log_; + DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerAndroid); }; diff --git a/webkit/media/android/webmediaplayer_proxy_android.h b/webkit/media/android/webmediaplayer_proxy_android.h index 2961c2f..bc29479 100644 --- a/webkit/media/android/webmediaplayer_proxy_android.h +++ b/webkit/media/android/webmediaplayer_proxy_android.h @@ -9,6 +9,9 @@ #include "base/time.h" #include "googleurl/src/gurl.h" +#if defined(GOOGLE_TV) +#include "media/base/android/demuxer_stream_player_params.h" +#endif namespace webkit_media { @@ -22,6 +25,7 @@ class WebMediaPlayerProxyAndroid { // Initialize a MediaPlayerBridge object in browser process virtual void Initialize(int player_id, const GURL& url, + bool is_media_source, const GURL& first_party_for_cookies) = 0; // Start the player. @@ -48,6 +52,16 @@ class WebMediaPlayerProxyAndroid { #if defined(GOOGLE_TV) // Request an external surface for out-of-band compositing. virtual void RequestExternalSurface(int player_id) = 0; + + // Inform the media source player that the demuxer is ready. + virtual void DemuxerReady( + int player_id, + const media::MediaPlayerHostMsg_DemuxerReady_Params&) = 0; + + // Return the data to the media source player when data is ready. + virtual void ReadFromDemuxerAck( + int player_id, + const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) = 0; #endif }; diff --git a/webkit/media/webkit_media.gypi b/webkit/media/webkit_media.gypi index 60cfd69..6cbd9cd 100644 --- a/webkit/media/webkit_media.gypi +++ b/webkit/media/webkit_media.gypi @@ -124,6 +124,10 @@ ], }], ['google_tv == 1', { + 'sources': [ + 'android/media_source_delegate.cc', + 'android/media_source_delegate.h', + ], 'sources!': [ 'crypto/key_systems_info.cc', ], diff --git a/webkit/media/webmediaplayer_impl.cc b/webkit/media/webmediaplayer_impl.cc index ba2834b..a589f16 100644 --- a/webkit/media/webmediaplayer_impl.cc +++ b/webkit/media/webmediaplayer_impl.cc @@ -114,16 +114,6 @@ COMPILE_ASSERT_MATCHING_ENUM(UseCredentials); #define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \ media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr(), arg1, arg2)) -static WebKit::WebTimeRanges ConvertToWebTimeRanges( - const media::Ranges<base::TimeDelta>& ranges) { - WebKit::WebTimeRanges result(ranges.size()); - for (size_t i = 0; i < ranges.size(); i++) { - result[i].start = ranges.start(i).InSecondsF(); - result[i].end = ranges.end(i).InSecondsF(); - } - return result; -} - static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log, const std::string& error) { media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); diff --git a/webkit/media/webmediaplayer_util.cc b/webkit/media/webmediaplayer_util.cc index 17e603e..ebac0f8 100644 --- a/webkit/media/webmediaplayer_util.cc +++ b/webkit/media/webmediaplayer_util.cc @@ -14,4 +14,14 @@ base::TimeDelta ConvertSecondsToTimestamp(double seconds) { microseconds > 0 ? microseconds + 0.5 : ceil(microseconds - 0.5)); } +WebKit::WebTimeRanges ConvertToWebTimeRanges( + const media::Ranges<base::TimeDelta>& ranges) { + WebKit::WebTimeRanges result(ranges.size()); + for (size_t i = 0; i < ranges.size(); i++) { + result[i].start = ranges.start(i).InSecondsF(); + result[i].end = ranges.end(i).InSecondsF(); + } + return result; +} + } // namespace webkit_media diff --git a/webkit/media/webmediaplayer_util.h b/webkit/media/webmediaplayer_util.h index 27888db..b18a961 100644 --- a/webkit/media/webmediaplayer_util.h +++ b/webkit/media/webmediaplayer_util.h @@ -6,6 +6,8 @@ #define WEBKIT_MEDIA_WEBMEDIAPLAYER_UTIL_H_ #include "base/time.h" +#include "media/base/ranges.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebTimeRange.h" namespace webkit_media { @@ -15,6 +17,9 @@ namespace webkit_media { // Refer to https://bugs.webkit.org/show_bug.cgi?id=52697 for details. base::TimeDelta ConvertSecondsToTimestamp(double seconds); +WebKit::WebTimeRanges ConvertToWebTimeRanges( + const media::Ranges<base::TimeDelta>& ranges); + } // namespace webkit_media #endif // WEBKIT_MEDIA_WEBMEDIAPLAYER_UTIL_H_ |