summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Popescu <andreip@google.com>2009-09-28 16:20:09 +0100
committerAndrei Popescu <andreip@google.com>2009-09-30 20:13:22 +0100
commit2b3d345465fdf225609499ffc5b1732157cafa22 (patch)
tree5b4d267c6550902107f26212337d00b8dd6aa920
parent14e3d9bdf2270d399bae78946e3efe62a6c6c373 (diff)
downloadexternal_webkit-2b3d345465fdf225609499ffc5b1732157cafa22.zip
external_webkit-2b3d345465fdf225609499ffc5b1732157cafa22.tar.gz
external_webkit-2b3d345465fdf225609499ffc5b1732157cafa22.tar.bz2
Update <video> implementation after new IRC discussion with Eric Carlsson.
- move poster drawing on the WebKit side - get rid of the child views - add prepareToPlay method to the MediaPlayer iface. Fixes http://b/issue?id=2156592
-rw-r--r--WebCore/html/HTMLMediaElement.cpp11
-rw-r--r--WebCore/html/HTMLMediaElement.h3
-rw-r--r--WebCore/platform/graphics/MediaPlayer.cpp6
-rw-r--r--WebCore/platform/graphics/MediaPlayer.h1
-rw-r--r--WebCore/platform/graphics/MediaPlayerPrivate.h1
-rw-r--r--WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h17
-rw-r--r--WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp155
7 files changed, 146 insertions, 48 deletions
diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp
index 17a3110..f98200d 100644
--- a/WebCore/html/HTMLMediaElement.cpp
+++ b/WebCore/html/HTMLMediaElement.cpp
@@ -1446,6 +1446,13 @@ bool HTMLMediaElement::potentiallyPlaying() const
return !paused() && m_readyState >= HAVE_FUTURE_DATA && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
}
+#if PLATFORM(ANDROID)
+bool HTMLMediaElement::couldPlayIfEnoughData() const
+{
+ return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
+}
+#endif
+
bool HTMLMediaElement::endedPlayback() const
{
if (!m_player || m_readyState < HAVE_METADATA)
@@ -1527,6 +1534,10 @@ void HTMLMediaElement::updatePlayState()
float time = currentTime();
if (m_lastSeekTime < time)
m_playedTimeRanges->add(m_lastSeekTime, time);
+#if PLATFORM(ANDROID)
+ } else if (couldPlayIfEnoughData() && playerPaused) {
+ m_player->prepareToPlay();
+#endif
}
if (renderer())
diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h
index 27b48ea..70b686e 100644
--- a/WebCore/html/HTMLMediaElement.h
+++ b/WebCore/html/HTMLMediaElement.h
@@ -227,6 +227,9 @@ private:
bool endedPlayback() const;
bool stoppedDueToErrors() const;
bool pausedForUserInteraction() const;
+#if PLATFORM(ANDROID)
+ bool couldPlayIfEnoughData() const;
+#endif
float minTimeSeekable() const;
float maxTimeSeekable() const;
diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp
index 15815dc..531c598 100644
--- a/WebCore/platform/graphics/MediaPlayer.cpp
+++ b/WebCore/platform/graphics/MediaPlayer.cpp
@@ -107,6 +107,7 @@ public:
#if PLATFORM(ANDROID)
virtual bool canLoadPoster() const { return false; }
virtual void setPoster(const String&) { }
+ virtual void prepareToPlay() { }
#endif
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
@@ -263,6 +264,11 @@ bool MediaPlayer::canLoadPoster() const
{
return m_private->canLoadPoster();
}
+
+void MediaPlayer::prepareToPlay()
+{
+ m_private->prepareToPlay();
+}
#endif
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || PLATFORM(ANDROID)
diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h
index 1cb7625..8eade50 100644
--- a/WebCore/platform/graphics/MediaPlayer.h
+++ b/WebCore/platform/graphics/MediaPlayer.h
@@ -187,6 +187,7 @@ public:
#if PLATFORM(ANDROID)
bool canLoadPoster() const;
void setPoster(const String&);
+ void prepareToPlay();
#endif
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h
index ba0f4b0..109ad10 100644
--- a/WebCore/platform/graphics/MediaPlayerPrivate.h
+++ b/WebCore/platform/graphics/MediaPlayerPrivate.h
@@ -95,6 +95,7 @@ public:
#if PLATFORM(ANDROID)
virtual bool canLoadPoster() const { return false; }
virtual void setPoster(const String&) { }
+ virtual void prepareToPlay() { }
#endif
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
diff --git a/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h b/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h
index 2d76ebb..812a337 100644
--- a/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h
+++ b/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h
@@ -28,6 +28,8 @@
#if ENABLE(VIDEO)
+class SkBitmap;
+
#include "MediaPlayerPrivate.h"
namespace WebCore {
@@ -79,11 +81,13 @@ public:
virtual bool canLoadPoster() const { return true; }
virtual void setPoster(const String&);
+ virtual void prepareToPlay();
virtual void paint(GraphicsContext*, const IntRect&);
void onPrepared(int duration, int width, int height);
void onEnded();
+ void onPosterFetched(SkBitmap*);
private:
// Android-specific methods and fields.
static MediaPlayerPrivateInterface* create(MediaPlayer* player);
@@ -97,10 +101,21 @@ private:
String m_url;
struct JavaGlue;
JavaGlue* m_glue;
+
float m_duration;
- IntSize m_size;
float m_currentTime;
+
bool m_paused;
+ MediaPlayer::ReadyState m_readyState;
+ MediaPlayer::NetworkState m_networkState;
+
+ SkBitmap* m_poster; // not owned
+ String m_posterUrl;
+
+ IntSize m_naturalSize;
+ bool m_naturalSizeUnknown;
+
+ bool m_isVisible;
};
} // namespace WebCore
diff --git a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
index 1913aef..5ca6e83 100644
--- a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
@@ -27,12 +27,17 @@
#if ENABLE(VIDEO)
+#include "GraphicsContext.h"
#include "MediaPlayerPrivateAndroid.h"
+#include "SkiaUtils.h"
#include "WebCoreJni.h"
#include "WebViewCore.h"
#include "jni_utility.h"
+#include <GraphicsJNI.h>
#include <JNIHelp.h>
+#include <SkBitmap.h>
+
using namespace android;
namespace WebCore {
@@ -44,10 +49,8 @@ struct MediaPlayerPrivate::JavaGlue
jobject m_javaProxy;
jmethodID m_getInstance;
jmethodID m_play;
- jmethodID m_createView;
- jmethodID m_attachView;
- jmethodID m_removeView;
- jmethodID m_setPoster;
+ jmethodID m_teardown;
+ jmethodID m_loadPoster;
jmethodID m_seek;
jmethodID m_pause;
};
@@ -57,7 +60,7 @@ MediaPlayerPrivate::~MediaPlayerPrivate()
if (m_glue->m_javaProxy) {
JNIEnv* env = JSC::Bindings::getJNIEnv();
if (env) {
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_removeView);
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown);
env->DeleteGlobalRef(m_glue->m_javaProxy);
}
}
@@ -72,19 +75,7 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
void MediaPlayerPrivate::load(const String& url)
{
- // To be able to create our java player, we need a Context object. To get
- // the Context object, we need a WebViewCore java object. To get a java
- // WebViewCore object, we need a WebCore::FrameView pointer. To get
- // the FrameView pointer, the MediaPlayer::setFrameView() must have been
- // called. However, that method is called only after the MediaPlayerClient
- // is called back and informed that enough data has been loaded.
- // We therefore need to fake a readyStateChanged callback before creating
- // the java player.
- m_player->readyStateChanged();
- // We now have a RenderVideo created and the MediaPlayer must have
- // been updated with a FrameView. Create our JavaPlayer.
- createJavaPlayerIfNeeded();
- // Save the URl.
+ // Just save the URl.
m_url = url;
}
@@ -118,7 +109,7 @@ void MediaPlayerPrivate::pause()
IntSize MediaPlayerPrivate::naturalSize() const
{
- return m_size;
+ return m_naturalSize;
}
bool MediaPlayerPrivate::hasVideo() const
@@ -126,8 +117,11 @@ bool MediaPlayerPrivate::hasVideo() const
return false;
}
-void MediaPlayerPrivate::setVisible(bool)
+void MediaPlayerPrivate::setVisible(bool visible)
{
+ m_isVisible = visible;
+ if (m_isVisible)
+ createJavaPlayerIfNeeded();
}
float MediaPlayerPrivate::duration() const
@@ -156,7 +150,7 @@ bool MediaPlayerPrivate::seeking() const
return false;
}
-void MediaPlayerPrivate::setEndTime(float time)
+void MediaPlayerPrivate::setEndTime(float)
{
}
@@ -175,12 +169,12 @@ void MediaPlayerPrivate::setVolume(float)
MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
{
- return MediaPlayer::Loaded;
+ return m_networkState;
}
MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
{
- return MediaPlayer::HaveEnoughData;
+ return m_readyState;
}
float MediaPlayerPrivate::maxTimeSeekable() const
@@ -214,25 +208,51 @@ void MediaPlayerPrivate::setSize(const IntSize&)
void MediaPlayerPrivate::setPoster(const String& url)
{
+ m_posterUrl = url;
JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
- return;
- jstring jUrl = env->NewString((unsigned short *)url.characters(), url.length());
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setPoster, jUrl);
- env->DeleteLocalRef(jUrl);
- checkException(env);
+ if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
+ return;
+ // Send the poster
+ jstring jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ env->DeleteLocalRef(jUrl);
+}
+
+void MediaPlayerPrivate::prepareToPlay() {
+ // We are about to start playing. Since our Java VideoView cannot
+ // buffer any data, we just simply transition to the HaveEnoughData
+ // state in here. This will allow the MediaPlayer to transition to
+ // the "play" state, at which point our VideoView will start downloading
+ // the content and start the playback.
+ m_networkState = MediaPlayer::Loaded;
+ m_player->networkStateChanged();
+ m_readyState = MediaPlayer::HaveEnoughData;
+ m_player->readyStateChanged();
}
-void MediaPlayerPrivate::paint(GraphicsContext*, const IntRect& r)
+void MediaPlayerPrivate::paint(GraphicsContext* ctxt, const IntRect& r)
{
- createJavaPlayerIfNeeded();
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
+ if (ctxt->paintingDisabled())
return;
- IntSize size = m_player->size();
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_attachView,
- r.x(), r.y(), size.width(), size.height());
+ if (!m_isVisible)
+ return;
+
+ if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
+ return;
+
+ SkCanvas* canvas = ctxt->platformContext()->mCanvas;
+ // We paint with the following rules in mind:
+ // - only downscale the poster, never upscale
+ // - maintain the natural aspect ratio of the poster
+ // - the poster should be centered in the target rect
+ float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
+ int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
+ int posterHeight = posterWidth / originalRatio;
+ int posterX = ((r.width() - posterWidth) / 2) + r.x();
+ int posterY = ((r.height() - posterHeight) / 2) + r.y();
+ IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
+ canvas->drawBitmapRect(*m_poster, 0, targetRect, 0);
}
MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
@@ -250,7 +270,17 @@ MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, c
}
MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
- : m_player(player), m_glue(NULL), m_duration(6000), m_size(100, 100), m_currentTime(0), m_paused(true)
+ : m_player(player),
+ m_glue(0),
+ m_duration(6000),
+ m_currentTime(0),
+ m_paused(true),
+ m_readyState(MediaPlayer::HaveNothing),
+ m_networkState(MediaPlayer::Empty),
+ m_poster(0),
+ m_naturalSize(100, 100),
+ m_naturalSizeUnknown(true),
+ m_isVisible(false)
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
if (!env)
@@ -263,10 +293,8 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
m_glue = new JavaGlue;
m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
- m_glue->m_createView = env->GetMethodID(clazz, "createView", "()V");
- m_glue->m_attachView = env->GetMethodID(clazz, "attachView", "(IIII)V");
- m_glue->m_removeView = env->GetMethodID(clazz, "removeView", "()V");
- m_glue->m_setPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
+ m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
m_glue->m_javaProxy = NULL;
@@ -299,8 +327,14 @@ void MediaPlayerPrivate::createJavaPlayerIfNeeded()
// Get the HTML5VideoViewProxy instance
jobject obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
m_glue->m_javaProxy = env->NewGlobalRef(obj);
- // Create our VideoView object.
- env->CallVoidMethod(obj, m_glue->m_createView);
+ // Send the poster
+ jstring jUrl = 0;
+ if (m_posterUrl.length())
+ jUrl = env->NewString((unsigned short *)m_posterUrl.characters(), m_posterUrl.length());
+ // Sending a NULL jUrl allows the Java side to try to load the default poster.
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
+ if (jUrl)
+ env->DeleteLocalRef(jUrl);
// Clean up.
env->DeleteLocalRef(obj);
env->DeleteLocalRef(clazz);
@@ -309,17 +343,31 @@ void MediaPlayerPrivate::createJavaPlayerIfNeeded()
void MediaPlayerPrivate::onPrepared(int duration, int width, int height) {
m_duration = duration / 1000.0f;
- m_size = IntSize(width, height);
+ m_naturalSize = IntSize(width, height);
+ m_naturalSizeUnknown = false;
m_player->durationChanged();
m_player->sizeChanged();
}
void MediaPlayerPrivate::onEnded() {
m_paused = true;
- m_currentTime = m_duration;
- m_player->timeChanged();
- // Reset to 0.
m_currentTime = 0;
+ m_networkState = MediaPlayer::Idle;
+ m_readyState = MediaPlayer::HaveNothing;
+}
+
+void MediaPlayerPrivate::onPosterFetched(SkBitmap* poster) {
+ m_poster = poster;
+ if (m_naturalSizeUnknown) {
+ // We had to fake the size at startup, or else our paint
+ // method would not be called. If we haven't yet received
+ // the onPrepared event, update the intrinsic size to the size
+ // of the poster. That will be overriden when onPrepare comes.
+ // In case of an error, we should report the poster size, rather
+ // than our initial fake value.
+ m_naturalSize = IntSize(poster->width(), poster->height());
+ m_player->sizeChanged();
+ }
}
}
@@ -340,6 +388,17 @@ static void OnEnded(JNIEnv* env, jobject obj, int pointer) {
}
}
+static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer) {
+ if (!pointer || !poster)
+ return;
+
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster);
+ if (!posterNative)
+ return;
+ player->onPosterFetched(posterNative);
+}
+
/*
* JNI registration
*/
@@ -348,6 +407,8 @@ static JNINativeMethod g_MediaPlayerMethods[] = {
(void*) OnPrepared },
{ "nativeOnEnded", "(I)V",
(void*) OnEnded },
+ { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
+ (void*) OnPosterFetched },
};
int register_mediaplayer(JNIEnv* env)