summaryrefslogtreecommitdiffstats
path: root/WebKit
diff options
context:
space:
mode:
authorNicolas Roard <nicolas@android.com>2010-02-02 13:11:32 +0000
committerBen Murdoch <benm@google.com>2010-10-18 13:07:05 +0100
commit3d57c253bfa22c17c5f38a70e162a6115a1809b6 (patch)
treeb17bd2355f8f57bd81cdac419a2c362194fd9ab1 /WebKit
parent5013a3fb99f9ca1c7be4319cdebc7cddcaa56d7c (diff)
downloadexternal_webkit-3d57c253bfa22c17c5f38a70e162a6115a1809b6.zip
external_webkit-3d57c253bfa22c17c5f38a70e162a6115a1809b6.tar.gz
external_webkit-3d57c253bfa22c17c5f38a70e162a6115a1809b6.tar.bz2
Implement the audio tag in webkit -- the corresponding java CL is https://android-git.corp.google.com/g/#change,41406
Cherry pick to Gingerbread, DO NOT MERGE! Bug:3101402 Change-Id: Idbfb0efcc777f9354fcf88df32105ca9e50a24cd
Diffstat (limited to 'WebKit')
-rw-r--r--WebKit/Android.mk1
-rw-r--r--WebKit/android/RenderSkinAndroid.cpp4
-rw-r--r--WebKit/android/RenderSkinMediaButton.cpp171
-rw-r--r--WebKit/android/RenderSkinMediaButton.h61
-rw-r--r--WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp538
-rw-r--r--WebKit/android/jni/WebCoreJniOnLoad.cpp6
6 files changed, 535 insertions, 246 deletions
diff --git a/WebKit/Android.mk b/WebKit/Android.mk
index 2c12ed3..47f37ca 100644
--- a/WebKit/Android.mk
+++ b/WebKit/Android.mk
@@ -31,6 +31,7 @@ LOCAL_SRC_FILES := \
android/RenderSkinAndroid.cpp \
android/RenderSkinButton.cpp \
android/RenderSkinCombo.cpp \
+ android/RenderSkinMediaButton.cpp \
android/RenderSkinRadio.cpp \
android/TimeCounter.cpp \
\
diff --git a/WebKit/android/RenderSkinAndroid.cpp b/WebKit/android/RenderSkinAndroid.cpp
index d148262..00f2b96 100644
--- a/WebKit/android/RenderSkinAndroid.cpp
+++ b/WebKit/android/RenderSkinAndroid.cpp
@@ -29,6 +29,7 @@
#include "RenderSkinAndroid.h"
#include "RenderSkinButton.h"
#include "RenderSkinCombo.h"
+#include "RenderSkinMediaButton.h"
#include "RenderSkinRadio.h"
#include "SkImageDecoder.h"
@@ -45,7 +46,8 @@ RenderSkinAndroid::RenderSkinAndroid()
void RenderSkinAndroid::Init(android::AssetManager* am, String drawableDirectory)
{
RenderSkinButton::Init(am, drawableDirectory);
- RenderSkinCombo::Init(am);
+ RenderSkinCombo::Init(am, drawableDirectory);
+ RenderSkinMediaButton::Init(am, drawableDirectory);
RenderSkinRadio::Init(am, drawableDirectory);
}
diff --git a/WebKit/android/RenderSkinMediaButton.cpp b/WebKit/android/RenderSkinMediaButton.cpp
new file mode 100644
index 0000000..149be4e
--- /dev/null
+++ b/WebKit/android/RenderSkinMediaButton.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "WebCore"
+
+#include "config.h"
+#include "CString.h"
+#include "android_graphics.h"
+#include "Document.h"
+#include "IntRect.h"
+#include "Node.h"
+#include "RenderSkinMediaButton.h"
+#include "SkCanvas.h"
+#include "SkNinePatch.h"
+#include "SkRect.h"
+#include <utils/Debug.h>
+#include <utils/Log.h>
+
+struct PatchData {
+ const char* name;
+ int8_t outset, margin;
+};
+
+static const PatchData gFiles[] =
+ {
+ { "btn_media_player.9.png", 0, 0 }, // DEFAULT BGD BUTTON
+ { "ic_media_pause.png", 0, 0}, // PAUSE
+ { "ic_media_play.png", 0, 0 }, // PLAY
+ { "ic_media_pause.png", 0, 0 }, // MUTE
+ { "ic_media_rew.png", 0, 0 }, // REWIND
+ { "ic_media_ff.png", 0, 0 }, // FORWARD
+ { "btn_media_player_disabled.9.png", 0, 0 }, // BACKGROUND_SLIDER
+ { "btn_media_player_pressed.9.png", 0, 0 }, // SLIDER_TRACK
+ { "btn_media_player.9.png", 0, 0 } // SLIDER_THUMB
+ };
+
+static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])];
+static bool gDecoded;
+static bool gHighRes;
+
+namespace WebCore {
+
+void RenderSkinMediaButton::Init(android::AssetManager* am, String drawableDirectory)
+{
+ static bool gInited;
+ if (gInited)
+ return;
+
+ gInited = true;
+ gDecoded = true;
+ gHighRes = drawableDirectory[drawableDirectory.length() - 5] == 'h';
+ for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) {
+ String path = drawableDirectory + gFiles[i].name;
+ if (!RenderSkinAndroid::DecodeBitmap(am, path.utf8().data(), &gButton[i])) {
+ gDecoded = false;
+ LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw");
+ break;
+ }
+ }
+}
+
+void RenderSkinMediaButton::Draw(SkCanvas* canvas, const IntRect& r, int buttonType)
+{
+ // If we failed to decode, do nothing. This way the browser still works,
+ // and webkit will still draw the label and layout space for us.
+ if (!gDecoded) {
+ return;
+ }
+
+ bool drawsNinePatch = true;
+ bool drawsImage = true;
+ bool drawsBackgroundColor = false;
+
+ int ninePatchIndex = 0;
+ int imageIndex = 0;
+
+ SkRect bounds(r);
+ SkScalar imageMargin = 8;
+ SkPaint paint;
+ SkColor backgroundColor = SkColorSetARGB(255, 200, 200, 200);
+ SkColor trackBackgroundColor = SkColorSetARGB(255, 100, 100, 100);
+
+ switch (buttonType) {
+ case PAUSE:
+ case PLAY:
+ case MUTE:
+ case REWIND:
+ case FORWARD:
+ {
+ imageIndex = buttonType + 1;
+ drawsBackgroundColor = true;
+ paint.setColor(backgroundColor);
+ break;
+ }
+ case BACKGROUND_SLIDER:
+ {
+ drawsImage = false;
+ drawsNinePatch = false;
+ drawsBackgroundColor = true;
+ paint.setColor(backgroundColor);
+ break;
+ }
+ case SLIDER_TRACK:
+ {
+ drawsImage = false;
+ drawsNinePatch = false;
+ drawsBackgroundColor = true;
+ paint.setColor(trackBackgroundColor);
+ bounds.fTop += 8;
+ bounds.fBottom -= 8;
+ break;
+ }
+ case SLIDER_THUMB:
+ {
+ drawsImage = false;
+ ninePatchIndex = buttonType + 1;
+ break;
+ }
+ default:
+ drawsImage = false;
+ drawsNinePatch = false;
+ }
+
+ if (drawsBackgroundColor) {
+ canvas->drawRect(r, paint);
+ }
+
+ if (drawsNinePatch) {
+ const PatchData& pd = gFiles[ninePatchIndex];
+ int marginValue = pd.margin + pd.outset;
+
+ SkIRect margin;
+ margin.set(marginValue, marginValue, marginValue, marginValue);
+ SkNinePatch::DrawNine(canvas, bounds, gButton[0], margin);
+ }
+
+ if (drawsImage) {
+ SkScalar SIZE = gButton[imageIndex].width();
+ SkScalar width = r.width();
+ SkScalar scale = SkScalarDiv(width - 2*imageMargin, SIZE);
+ int saveScaleCount = canvas->save();
+ canvas->translate(bounds.fLeft + imageMargin, bounds.fTop + imageMargin);
+ canvas->scale(scale, scale);
+ canvas->drawBitmap(gButton[imageIndex], 0, 0, &paint);
+ canvas->restoreToCount(saveScaleCount);
+ }
+}
+
+} //WebCore
diff --git a/WebKit/android/RenderSkinMediaButton.h b/WebKit/android/RenderSkinMediaButton.h
new file mode 100644
index 0000000..b4e99f4
--- /dev/null
+++ b/WebKit/android/RenderSkinMediaButton.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RenderSkinMediaButton_h
+#define RenderSkinMediaButton_h
+
+#include "RenderSkinAndroid.h"
+
+class SkCanvas;
+
+namespace WebCore {
+class IntRect;
+class RenderSkinMediaButton
+{
+public:
+ /**
+ * Initialize the class before use. Uses the AssetManager to initialize any
+ * bitmaps the class may use.
+ */
+ static void Init(android::AssetManager*, String drawableDirectory);
+ /**
+ * Draw the skin to the canvas, using the rectangle for its bounds and the
+ * State to determine which skin to use, i.e. focused or not focused.
+ */
+ static void Draw(SkCanvas* , const IntRect& , int buttonType);
+ /**
+ * Button types
+ */
+ enum { PAUSE, PLAY, MUTE, REWIND, FORWARD, BACKGROUND_SLIDER, SLIDER_TRACK, SLIDER_THUMB };
+ /**
+ * Slider dimensions
+ */
+ static int sliderThumbWidth() { return 10; }
+ static int sliderThumbHeight() { return 30; }
+
+};
+
+} // WebCore
+#endif // RenderSkinMediaButton_h
diff --git a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
index 3767516..b066511 100644
--- a/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
+++ b/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp
@@ -30,7 +30,6 @@
#include "GraphicsContext.h"
#include "SkiaUtils.h"
-#include "TimeRanges.h"
#include "WebCoreJni.h"
#include "WebViewCore.h"
@@ -44,16 +43,22 @@ using namespace android;
namespace WebCore {
static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
+static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio";
struct MediaPlayerPrivate::JavaGlue
{
jobject m_javaProxy;
- jmethodID m_getInstance;
jmethodID m_play;
jmethodID m_teardown;
- jmethodID m_loadPoster;
jmethodID m_seek;
jmethodID m_pause;
+ // Audio
+ jmethodID m_newInstance;
+ jmethodID m_setDataSource;
+ jmethodID m_getMaxTimeSeekable;
+ // Video
+ jmethodID m_getInstance;
+ jmethodID m_loadPoster;
};
MediaPlayerPrivate::~MediaPlayerPrivate()
@@ -65,7 +70,6 @@ MediaPlayerPrivate::~MediaPlayerPrivate()
env->DeleteGlobalRef(m_glue->m_javaProxy);
}
}
-
delete m_glue;
}
@@ -74,29 +78,6 @@ void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
registrar(create, getSupportedTypes, supportsType);
}
-void MediaPlayerPrivate::load(const String& url)
-{
- // Just save the URl.
- m_url = url;
-}
-
-void MediaPlayerPrivate::cancelLoad()
-{
-}
-
-void MediaPlayerPrivate::play()
-{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env || !m_glue->m_javaProxy || !m_url.length())
- return;
-
- m_paused = false;
- jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
- env->DeleteLocalRef(jUrl);
- checkException(env);
-}
-
void MediaPlayerPrivate::pause()
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
@@ -108,22 +89,6 @@ void MediaPlayerPrivate::pause()
checkException(env);
}
-IntSize MediaPlayerPrivate::naturalSize() const
-{
- return m_naturalSize;
-}
-
-bool MediaPlayerPrivate::hasAudio() const
-{
- // TODO
- return false;
-}
-
-bool MediaPlayerPrivate::hasVideo() const
-{
- return m_hasVideo;
-}
-
void MediaPlayerPrivate::setVisible(bool visible)
{
m_isVisible = visible;
@@ -131,99 +96,19 @@ void MediaPlayerPrivate::setVisible(bool visible)
createJavaPlayerIfNeeded();
}
-float MediaPlayerPrivate::duration() const
-{
- return m_duration;
-}
-
-float MediaPlayerPrivate::currentTime() const
-{
- return m_currentTime;
-}
-
void MediaPlayerPrivate::seek(float time)
{
JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env || !m_glue->m_javaProxy || !m_url.length())
+ if (!env || !m_url.length())
return;
- m_currentTime = time;
- env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
+ if (m_glue->m_javaProxy) {
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
+ m_currentTime = time;
+ }
checkException(env);
}
-bool MediaPlayerPrivate::seeking() const
-{
- return false;
-}
-
-void MediaPlayerPrivate::setEndTime(float)
-{
-}
-
-void MediaPlayerPrivate::setRate(float)
-{
-}
-
-bool MediaPlayerPrivate::paused() const
-{
- return m_paused;
-}
-
-void MediaPlayerPrivate::setVolume(float)
-{
-}
-
-MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const
-{
- return m_networkState;
-}
-
-MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const
-{
- return m_readyState;
-}
-
-float MediaPlayerPrivate::maxTimeSeekable() const
-{
- return 0;
-}
-
-PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
-{
- return TimeRanges::create();
-}
-
-int MediaPlayerPrivate::dataRate() const
-{
- return 0;
-}
-
-unsigned MediaPlayerPrivate::totalBytes() const
-{
- return 0;
-}
-
-unsigned MediaPlayerPrivate::bytesLoaded() const
-{
- return 0;
-}
-
-void MediaPlayerPrivate::setSize(const IntSize&)
-{
-}
-
-void MediaPlayerPrivate::setPoster(const String& url)
-{
- m_posterUrl = url;
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- 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
@@ -237,40 +122,6 @@ void MediaPlayerPrivate::prepareToPlay() {
m_player->readyStateChanged();
}
-void MediaPlayerPrivate::paint(GraphicsContext* ctxt, const IntRect& r)
-{
- if (ctxt->paintingDisabled())
- return;
-
- 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)
-{
- return new MediaPlayerPrivate(player);
-}
-
-void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&)
-{
-}
-
MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
{
if (WebViewCore::supportsMimeType(type))
@@ -292,72 +143,6 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
m_naturalSizeUnknown(true),
m_isVisible(false)
{
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
- return;
-
- jclass clazz = env->FindClass(g_ProxyJavaClass);
- if (!clazz)
- return;
-
- 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_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;
- env->DeleteLocalRef(clazz);
- // An exception is raised if any of the above fails.
- checkException(env);
-}
-
-void MediaPlayerPrivate::createJavaPlayerIfNeeded()
-{
- // Check if we have been already created.
- if (m_glue->m_javaProxy)
- return;
-
- FrameView* frameView = m_player->frameView();
- if (!frameView)
- return;
-
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env)
- return;
-
- jclass clazz = env->FindClass(g_ProxyJavaClass);
- if (!clazz)
- return;
-
- WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView);
- ASSERT(webViewCore);
-
- // Get the HTML5VideoViewProxy instance
- jobject obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
- m_glue->m_javaProxy = env->NewGlobalRef(obj);
- // 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);
- checkException(env);
-}
-
-void MediaPlayerPrivate::onPrepared(int duration, int width, int height) {
- m_duration = duration / 1000.0f;
- m_naturalSize = IntSize(width, height);
- m_naturalSizeUnknown = false;
- m_hasVideo = true;
- m_player->durationChanged();
- m_player->sizeChanged();
}
void MediaPlayerPrivate::onEnded() {
@@ -370,25 +155,268 @@ void MediaPlayerPrivate::onEnded() {
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();
- }
-}
-
void MediaPlayerPrivate::onTimeupdate(int position) {
m_currentTime = position / 1000.0f;
m_player->timeChanged();
}
+class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url) { m_url = url; }
+ void play() {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length() || !m_glue->m_javaProxy)
+ return;
+
+ m_paused = false;
+ jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl);
+ env->DeleteLocalRef(jUrl);
+
+ checkException(env);
+ }
+ bool canLoadPoster() const { return true; }
+ void setPoster(const String& url) {
+ m_posterUrl = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ 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 paint(GraphicsContext* ctxt, const IntRect& r) {
+ if (ctxt->paintingDisabled())
+ return;
+
+ 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);
+ }
+
+ void 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();
+ }
+ }
+
+ void onPrepared(int duration, int width, int height) {
+ m_duration = duration / 1000.0f;
+ m_naturalSize = IntSize(width, height);
+ m_naturalSizeUnknown = false;
+ m_hasVideo = true;
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ }
+
+ bool hasAudio() { return false; } // do not display the audio UI
+ bool hasVideo() { return m_hasVideo; }
+
+ MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_getInstance = env->GetStaticMethodID(clazz, "getInstance", "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
+ m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;)V");
+
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()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;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded() {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClass);
+
+ if (!clazz)
+ return;
+
+ jobject obj = NULL;
+
+ FrameView* frameView = m_player->frameView();
+ if (!frameView)
+ return;
+ WebViewCore* webViewCore = WebViewCore::getWebViewCore(frameView);
+ ASSERT(webViewCore);
+
+ // Get the HTML5VideoViewProxy instance
+ obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, webViewCore->getJavaObject().get(), this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+ // 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.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+};
+
+class MediaPlayerAudioPrivate : public MediaPlayerPrivate {
+public:
+ void load(const String& url) {
+ m_url = url;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ jstring jUrl = env->NewString((unsigned short *)m_url.characters(), m_url.length());
+ // start loading the data asynchronously
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl);
+ env->DeleteLocalRef(jUrl);
+ checkException(env);
+ }
+
+ void play() {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env || !m_url.length())
+ return;
+
+ createJavaPlayerIfNeeded();
+
+ if (!m_glue->m_javaProxy)
+ return;
+
+ env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play);
+ checkException(env);
+ }
+
+ bool hasAudio() { return true; }
+
+ float maxTimeSeekable() const {
+ if (m_glue->m_javaProxy) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (env) {
+ float maxTime = env->CallFloatMethod(m_glue->m_javaProxy,
+ m_glue->m_getMaxTimeSeekable);
+ checkException(env);
+ return maxTime;
+ }
+ }
+ return 0;
+ }
+
+ MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ m_glue = new JavaGlue;
+ m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(I)V");
+ m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V");
+ m_glue->m_play = env->GetMethodID(clazz, "play", "()V");
+ m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F");
+ m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()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;
+ env->DeleteLocalRef(clazz);
+ // An exception is raised if any of the above fails.
+ checkException(env);
+ }
+
+ void createJavaPlayerIfNeeded() {
+ // Check if we have been already created.
+ if (m_glue->m_javaProxy)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
+
+ if (!clazz)
+ return;
+
+ jobject obj = NULL;
+
+ // Get the HTML5Audio instance
+ obj = env->NewObject(clazz, m_glue->m_newInstance, this);
+ m_glue->m_javaProxy = env->NewGlobalRef(obj);
+
+ // Clean up.
+ if (obj)
+ env->DeleteLocalRef(obj);
+ env->DeleteLocalRef(clazz);
+ checkException(env);
+ }
+
+ void onPrepared(int duration, int width, int height) {
+ m_duration = duration / 1000.0f;
+ m_player->durationChanged();
+ m_player->sizeChanged();
+ m_player->prepareToPlay();
+ }
+};
+
+MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
+{
+ if (player->mediaElementType() == MediaPlayer::Video)
+ return new MediaPlayerVideoPrivate(player);
+ return new MediaPlayerAudioPrivate(player);
+}
+
}
namespace android {
@@ -418,6 +446,13 @@ static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointe
player->onPosterFetched(posterNative);
}
+static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer) {
+ if (pointer) {
+ WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
+ //TODO: player->onBuffering(percent);
+ }
+}
+
static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer) {
if (pointer) {
WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
@@ -439,11 +474,28 @@ static JNINativeMethod g_MediaPlayerMethods[] = {
(void*) OnTimeupdate },
};
-int register_mediaplayer(JNIEnv* env)
+static JNINativeMethod g_MediaAudioPlayerMethods[] = {
+ { "nativeOnBuffering", "(II)V",
+ (void*) OnBuffering },
+ { "nativeOnEnded", "(I)V",
+ (void*) OnEnded },
+ { "nativeOnPrepared", "(IIII)V",
+ (void*) OnPrepared },
+ { "nativeOnTimeupdate", "(II)V",
+ (void*) OnTimeupdate },
+};
+
+int register_mediaplayer_video(JNIEnv* env)
{
return jniRegisterNativeMethods(env, g_ProxyJavaClass,
g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
}
+int register_mediaplayer_audio(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio,
+ g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods));
+}
+
}
#endif // VIDEO
diff --git a/WebKit/android/jni/WebCoreJniOnLoad.cpp b/WebKit/android/jni/WebCoreJniOnLoad.cpp
index b5bf9dd..86248db 100644
--- a/WebKit/android/jni/WebCoreJniOnLoad.cpp
+++ b/WebKit/android/jni/WebCoreJniOnLoad.cpp
@@ -82,7 +82,8 @@ extern int register_webstorage(JNIEnv*);
extern int register_geolocation_permissions(JNIEnv*);
extern int register_mock_geolocation(JNIEnv*);
#if ENABLE(VIDEO)
-extern int register_mediaplayer(JNIEnv*);
+extern int register_mediaplayer_audio(JNIEnv*);
+extern int register_mediaplayer_video(JNIEnv*);
#endif
}
@@ -107,7 +108,8 @@ static RegistrationMethod gWebCoreRegMethods[] = {
{ "GeolocationPermissions", android::register_geolocation_permissions },
{ "MockGeolocation", android::register_mock_geolocation },
#if ENABLE(VIDEO)
- { "HTML5VideoViewProxy", android::register_mediaplayer },
+ { "HTML5Audio", android::register_mediaplayer_audio },
+ { "HTML5VideoViewProxy", android::register_mediaplayer_video },
#endif
};