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

Unified Diff: webkit/media/android/webmediaplayer_android.cc

Issue 10073016: Upstream WebMediaPlayerAndroid as WebKit::WebMediaPlayer implementation on android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: small changes to address comments Created 8 years, 8 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webkit/media/android/webmediaplayer_android.h ('k') | webkit/media/android/webmediaplayer_proxy_android.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webkit/media/android/webmediaplayer_android.cc
diff --git a/webkit/media/android/webmediaplayer_android.cc b/webkit/media/android/webmediaplayer_android.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4966d0a4da6778d33f66ad4b604a5ec24efe6547
--- /dev/null
+++ b/webkit/media/android/webmediaplayer_android.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 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/webmediaplayer_android.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "media/base/android/media_player_bridge.h"
+#include "net/base/mime_util.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCookieJar.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h"
+#include "webkit/media/android/webmediaplayer_proxy_android.h"
+#include "webkit/media/webmediaplayer_util.h"
+#include "webkit/media/webvideoframe_impl.h"
+
+using WebKit::WebCanvas;
+using WebKit::WebMediaPlayerClient;
+using WebKit::WebMediaPlayer;
+using WebKit::WebRect;
+using WebKit::WebSize;
+using WebKit::WebTimeRanges;
+using WebKit::WebURL;
+using WebKit::WebVideoFrame;
+using media::MediaPlayerBridge;
+using media::VideoFrame;
+using webkit_media::WebVideoFrameImpl;
+
+namespace webkit_media {
+
+// Because we create the media player lazily on android, the duration of the
+// media is initially unknown to us. This makes the user unable to perform
+// seek. To solve this problem, we use a temporary duration of 100 seconds when
+// the duration is unknown. And we scale the seek position later when duration
+// is available.
+// TODO(qinmin): create a thread and use android MediaMetadataRetriever
+// class to extract the duration.
+static const float kTemporaryDuration = 100.0f;
+
+bool WebMediaPlayerAndroid::incognito_mode_ = false;
+
+WebMediaPlayerAndroid::WebMediaPlayerAndroid(
+ WebMediaPlayerClient* client,
+ WebKit::WebCookieJar* cookie_jar)
+ : client_(client),
+ buffered_(1u),
+ video_frame_(new WebVideoFrameImpl(VideoFrame::CreateEmptyFrame())),
+ proxy_(new WebMediaPlayerProxyAndroid(base::MessageLoopProxy::current(),
+ AsWeakPtr())),
+ prepared_(false),
+ duration_(0),
+ pending_seek_(0),
+ seeking_(false),
+ playback_completed_(false),
+ buffered_bytes_(0),
+ cookie_jar_(cookie_jar),
+ pending_play_event_(false),
+ network_state_(WebMediaPlayer::Empty),
+ ready_state_(WebMediaPlayer::HaveNothing) {
+ video_frame_.reset(new WebVideoFrameImpl(VideoFrame::CreateEmptyFrame()));
+}
+
+WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
+ if (media_player_.get()) {
+ media_player_->Stop();
+ }
+}
+
+void WebMediaPlayerAndroid::InitIncognito(bool incognito_mode) {
+ incognito_mode_ = incognito_mode;
+}
+
+void WebMediaPlayerAndroid::load(const WebURL& url) {
+ url_ = url;
+
+ UpdateNetworkState(WebMediaPlayer::Loading);
+ UpdateReadyState(WebMediaPlayer::HaveNothing);
+
+ // Calling InitializeMediaPlayer() will cause android mediaplayer to start
+ // buffering and decoding the data. On mobile devices, this costs a lot of
+ // data usage and could even introduce performance issues. So we don't
+ // initialize the player unless it is a local file. We will start loading
+ // the media only when play/seek/fullsceen button is clicked.
+ if (url_.SchemeIs("file")) {
+ InitializeMediaPlayer();
+ return;
+ }
+
+ // TODO(qinmin): we need a method to calculate the duration of the media.
+ // Android does not provide any function to do that.
+ // Set the initial duration value to kTemporaryDuration so that user can
+ // touch the seek bar to perform seek. We will scale the seek position later
+ // when we got the actual duration.
+ duration_ = kTemporaryDuration;
+
+ // Pretend everything has been loaded so that webkit can
+ // still call play() and seek().
+ UpdateReadyState(WebMediaPlayer::HaveMetadata);
+ UpdateReadyState(WebMediaPlayer::HaveEnoughData);
+}
+
+void WebMediaPlayerAndroid::cancelLoad() {
+ NOTIMPLEMENTED();
+}
+
+void WebMediaPlayerAndroid::play() {
+ if (media_player_.get()) {
+ if (!prepared_)
+ pending_play_event_ = true;
+ else
+ PlayInternal();
+ } else {
+ pending_play_event_ = true;
+ InitializeMediaPlayer();
+ }
+}
+
+void WebMediaPlayerAndroid::pause() {
+ if (media_player_.get()) {
+ if (!prepared_)
+ pending_play_event_ = false;
+ else
+ PauseInternal();
+ } else {
+ // We don't need to load media if pause() is called.
+ pending_play_event_ = false;
+ }
+}
+
+void WebMediaPlayerAndroid::seek(float seconds) {
+ // Record the time to seek when OnMediaPrepared() is called.
+ pending_seek_ = seconds;
+
+ // Reset |playback_completed_| so that we return the correct current time.
+ playback_completed_ = false;
+
+ if (media_player_.get()) {
+ if (prepared_)
+ SeekInternal(seconds);
+ } else {
+ InitializeMediaPlayer();
+ }
+}
+
+bool WebMediaPlayerAndroid::supportsFullscreen() const {
+ return true;
+}
+
+bool WebMediaPlayerAndroid::supportsSave() const {
+ return false;
+}
+
+void WebMediaPlayerAndroid::setEndTime(float seconds) {
+ // Deprecated.
+ // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
+}
+
+void WebMediaPlayerAndroid::setRate(float rate) {
+ NOTIMPLEMENTED();
+}
+
+void WebMediaPlayerAndroid::setVolume(float volume) {
+ if (media_player_.get())
+ media_player_->SetVolume(volume, volume);
+}
+
+void WebMediaPlayerAndroid::setVisible(bool visible) {
+ // Deprecated.
+ // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
+}
+
+bool WebMediaPlayerAndroid::totalBytesKnown() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool WebMediaPlayerAndroid::hasVideo() const {
+ // TODO(qinmin): need a better method to determine whether the current media
+ // content contains video. Android does not provide any function to do
+ // this.
+ // We don't know whether the current media content has video unless
+ // the player is prepared. If the player is not prepared, we fall back
+ // to the mime-type. There may be no mime-type on a redirect URL.
+ // In that case, we conservatively assume it contains video so that
+ // enterfullscreen call will not fail.
+ if (!prepared_) {
+ if (!url_.has_path())
+ return false;
+ std::string mime;
+ if(!net::GetMimeTypeFromFile(FilePath(url_.path()), &mime))
+ return true;
+ return mime.find("audio/") == std::string::npos;
+ }
+
+ return !natural_size_.isEmpty();
+}
+
+bool WebMediaPlayerAndroid::hasAudio() const {
+ // TODO(hclam): Query status of audio and return the actual value.
+ return true;
+}
+
+bool WebMediaPlayerAndroid::paused() const {
+ if (!prepared_)
+ return !pending_play_event_;
+ return !media_player_->IsPlaying();
+}
+
+bool WebMediaPlayerAndroid::seeking() const {
+ return seeking_;
+}
+
+float WebMediaPlayerAndroid::duration() const {
+ return duration_;
+}
+
+float WebMediaPlayerAndroid::currentTime() const {
+ // If the player is pending for a seek, return the seek time.
+ if (!prepared_ || seeking())
+ return pending_seek_;
+
+ // When playback is about to finish, android media player often stops
+ // at a time which is smaller than the duration. This makes webkit never
+ // know that the playback has finished. To solve this, we set the
+ // current time to media duration when OnPlaybackComplete() get called.
+ // And return the greater of the two values so that the current
+ // time is most updated.
+ if (playback_completed_)
+ return duration();
+ return static_cast<float>(media_player_->GetCurrentTime().InSecondsF());
+}
+
+int WebMediaPlayerAndroid::dataRate() const {
+ // Deprecated.
+ // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
+ return 0;
+}
+
+WebSize WebMediaPlayerAndroid::naturalSize() const {
+ return natural_size_;
+}
+
+WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
+ return network_state_;
+}
+
+WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
+ return ready_state_;
+}
+
+const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
+ return buffered_;
+}
+
+float WebMediaPlayerAndroid::maxTimeSeekable() const {
+ // TODO(hclam): If this stream is not seekable this should return 0.
+ return duration();
+}
+
+unsigned long long WebMediaPlayerAndroid::bytesLoaded() const {
+ return buffered_bytes_;
+}
+
+unsigned long long WebMediaPlayerAndroid::totalBytes() const {
+ // Deprecated.
+ // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
+ return 0;
+}
+
+void WebMediaPlayerAndroid::setSize(const WebSize& size) {
+ texture_size_ = size;
+}
+
+void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas,
+ const WebKit::WebRect& rect,
+ uint8_t alpha) {
+ NOTIMPLEMENTED();
+}
+
+bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
+ return false;
+}
+
+WebMediaPlayer::MovieLoadType
+ WebMediaPlayerAndroid::movieLoadType() const {
+ // Deprecated.
+ // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
+ return WebMediaPlayer::Unknown;
+}
+
+float WebMediaPlayerAndroid::mediaTimeForTimeValue(float timeValue) const {
+ return ConvertSecondsToTimestamp(timeValue).InSecondsF();
+}
+
+unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+void WebMediaPlayerAndroid::OnMediaPrepared() {
+ if (!media_player_.get())
+ return;
+
+ prepared_ = true;
+
+ // Update the media duration first so that webkit will get the correct
+ // duration when UpdateReadyState is called.
+ float dur = duration_;
+ duration_ = media_player_->GetDuration().InSecondsF();
+
+ if (url_.SchemeIs("file"))
+ UpdateNetworkState(WebMediaPlayer::Loaded);
+
+ if (ready_state_ != WebMediaPlayer::HaveEnoughData) {
+ UpdateReadyState(WebMediaPlayer::HaveMetadata);
+ UpdateReadyState(WebMediaPlayer::HaveEnoughData);
+ } else {
+ // If the status is already set to HaveEnoughData, set it again to make sure
+ // that Videolayerchromium will get created.
+ UpdateReadyState(WebMediaPlayer::HaveEnoughData);
+ }
+
+ if (!url_.SchemeIs("file")) {
+ // In we have skipped loading, the duration was preset to
+ // kTemporaryDuration. We have to update webkit about the new duration.
+ if (duration_ != dur) {
+ // Scale the |pending_seek_| according to the new duration.
+ pending_seek_ = pending_seek_ * duration_ / kTemporaryDuration;
+ client_->durationChanged();
+ }
+ }
+
+ // If media player was recovered from a saved state, consume all the pending
+ // events.
+ seek(pending_seek_);
+
+ if (pending_play_event_)
+ PlayInternal();
+
+ pending_play_event_ = false;
+}
+
+void WebMediaPlayerAndroid::OnPlaybackComplete() {
+ // Set the current time equal to duration to let webkit know that play back
+ // is completed.
+ playback_completed_ = true;
+ client_->timeChanged();
+}
+
+void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
+ buffered_[0].end = duration() * percentage / 100;
+ // Implement a trick here to fake progress event, as WebKit checks
+ // consecutive bytesLoaded() to see if any progress made.
+ // See HTMLMediaElement::progressEventTimerFired.
+ // TODO(qinmin): need a method to calculate the buffered bytes.
+ buffered_bytes_++;
+}
+
+void WebMediaPlayerAndroid::OnSeekComplete() {
+ seeking_ = false;
+
+ UpdateReadyState(WebMediaPlayer::HaveEnoughData);
+
+ client_->timeChanged();
+}
+
+void WebMediaPlayerAndroid::OnMediaError(int error_type) {
+ switch (error_type) {
+ case MediaPlayerBridge::MEDIA_ERROR_UNKNOWN:
+ // When playing an bogus URL or bad file we fire a MEDIA_ERROR_UNKNOWN.
+ // As WebKit uses FormatError to indicate an error for bogus URL or bad
+ // file we default a MEDIA_ERROR_UNKNOWN to FormatError.
+ UpdateNetworkState(WebMediaPlayer::FormatError);
+ break;
+ case MediaPlayerBridge::MEDIA_ERROR_SERVER_DIED:
+ // TODO(zhenghao): Media server died. In this case, the application must
+ // release the MediaPlayer object and instantiate a new one.
+ UpdateNetworkState(WebMediaPlayer::DecodeError);
+ break;
+ case MediaPlayerBridge::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
+ UpdateNetworkState(WebMediaPlayer::FormatError);
+ break;
+ case MediaPlayerBridge::MEDIA_ERROR_INVALID_CODE:
+ break;
+ }
+ client_->repaint();
+}
+
+void WebMediaPlayerAndroid::OnMediaInfo(int info_type) {
+ NOTIMPLEMENTED();
+}
+
+void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
+ natural_size_.width = width;
+ natural_size_.height = height;
+}
+
+void WebMediaPlayerAndroid::UpdateNetworkState(
+ WebMediaPlayer::NetworkState state) {
+ network_state_ = state;
+ client_->networkStateChanged();
+}
+
+void WebMediaPlayerAndroid::UpdateReadyState(
+ WebMediaPlayer::ReadyState state) {
+ ready_state_ = state;
+ client_->readyStateChanged();
+}
+
+void WebMediaPlayerAndroid::SetVideoSurface(jobject j_surface) {
+ if (media_player_.get())
+ media_player_->SetVideoSurface(j_surface);
+}
+
+void WebMediaPlayerAndroid::InitializeMediaPlayer() {
+ CHECK(!media_player_.get());
+ prepared_ = false;
+ media_player_.reset(new MediaPlayerBridge());
+ media_player_->SetStayAwakeWhilePlaying();
+
+ std::string cookies;
+ if (cookie_jar_ != NULL) {
+ WebURL url(url_);
+ cookies = UTF16ToUTF8(cookie_jar_->cookies(url, url));
+ }
+ media_player_->SetDataSource(url_.spec(), cookies, incognito_mode_);
+
+ media_player_->Prepare(
+ base::Bind(&WebMediaPlayerProxyAndroid::MediaInfoCallback, proxy_),
+ base::Bind(&WebMediaPlayerProxyAndroid::MediaErrorCallback, proxy_),
+ base::Bind(&WebMediaPlayerProxyAndroid::VideoSizeChangedCallback, proxy_),
+ base::Bind(&WebMediaPlayerProxyAndroid::BufferingUpdateCallback, proxy_),
+ base::Bind(&WebMediaPlayerProxyAndroid::MediaPreparedCallback, proxy_));
+}
+
+void WebMediaPlayerAndroid::PlayInternal() {
+ CHECK(prepared_);
+
+ if (paused())
+ media_player_->Start(base::Bind(
+ &WebMediaPlayerProxyAndroid::PlaybackCompleteCallback, proxy_));
+}
+
+void WebMediaPlayerAndroid::PauseInternal() {
+ CHECK(prepared_);
+ media_player_->Pause();
+}
+
+void WebMediaPlayerAndroid::SeekInternal(float seconds) {
+ CHECK(prepared_);
+ seeking_ = true;
+ media_player_->SeekTo(ConvertSecondsToTimestamp(seconds), base::Bind(
+ &WebMediaPlayerProxyAndroid::SeekCompleteCallback, proxy_));
+}
+
+WebVideoFrame* WebMediaPlayerAndroid::getCurrentFrame() {
+ return video_frame_.get();
+}
+
+void WebMediaPlayerAndroid::putCurrentFrame(
+ WebVideoFrame* web_video_frame) {
+}
+
+} // namespace webkit_media
« no previous file with comments | « webkit/media/android/webmediaplayer_android.h ('k') | webkit/media/android/webmediaplayer_proxy_android.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698