From 7f493d591dfd3492d5916ca301a4dbcd61554a06 Mon Sep 17 00:00:00 2001 From: Nicolas Roard Date: Tue, 2 Feb 2010 13:11:32 +0000 Subject: Implement the audio tag in webkit -- the corresponding java CL is https://android-git.corp.google.com/g/#change,41406 Change-Id: Ief37a7d9879308f0749fcd569e82fddc3f989a8a --- CleanSpec.mk | 1 + WebCore/Android.derived.mk | 2 +- WebCore/css/mediaControlsAndroid.css | 134 +++++ WebCore/html/HTMLMediaElement.cpp | 6 + WebCore/platform/android/RenderThemeAndroid.cpp | 94 ++++ WebCore/platform/android/RenderThemeAndroid.h | 14 +- WebCore/platform/graphics/MediaPlayer.cpp | 3 + WebCore/platform/graphics/MediaPlayer.h | 8 + .../graphics/android/MediaPlayerPrivateAndroid.h | 68 +-- WebKit/Android.mk | 1 + WebKit/android/RenderSkinAndroid.cpp | 2 + WebKit/android/RenderSkinMediaButton.cpp | 171 +++++++ WebKit/android/RenderSkinMediaButton.h | 61 +++ .../WebCoreSupport/MediaPlayerPrivateAndroid.cpp | 543 +++++++++++---------- WebKit/android/jni/WebCoreJniOnLoad.cpp | 6 +- 15 files changed, 830 insertions(+), 284 deletions(-) create mode 100644 WebCore/css/mediaControlsAndroid.css create mode 100644 WebKit/android/RenderSkinMediaButton.cpp create mode 100644 WebKit/android/RenderSkinMediaButton.h diff --git a/CleanSpec.mk b/CleanSpec.mk index 02b4dea..ebb3f75 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -52,6 +52,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libwebcore_int $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libwebcore_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/WebCore/Android.derived.mk b/WebCore/Android.derived.mk index c4ae2f1..398c63f 100644 --- a/WebCore/Android.derived.mk +++ b/WebCore/Android.derived.mk @@ -111,7 +111,7 @@ $(GEN): $(LOCAL_BISON) LOCAL_GENERATED_SOURCES += $(GEN) # user agent style sheets -style_sheets := $(LOCAL_PATH)/css/html.css $(LOCAL_PATH)/css/quirks.css $(LOCAL_PATH)/css/view-source.css $(LOCAL_PATH)/css/mediaControls.css +style_sheets := $(LOCAL_PATH)/css/html.css $(LOCAL_PATH)/css/quirks.css $(LOCAL_PATH)/css/view-source.css $(LOCAL_PATH)/css/mediaControls.css $(LOCAL_PATH)/css/mediaControlsAndroid.css ifeq ($(ENABLE_SVG), true) style_sheets := $(style_sheets) $(LOCAL_PATH)/css/svg.css endif diff --git a/WebCore/css/mediaControlsAndroid.css b/WebCore/css/mediaControlsAndroid.css new file mode 100644 index 0000000..82f4bd3 --- /dev/null +++ b/WebCore/css/mediaControlsAndroid.css @@ -0,0 +1,134 @@ +/* + * 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. + */ + +/* media controls */ + +audio { + width: 200px; + height: 32px; +} + +audio::-webkit-media-controls-panel, video::-webkit-media-controls-panel { + display: -webkit-box; + -webkit-box-orient: horizontal; + -webkit-box-align: center; + -webkit-user-select: none; + position: absolute; + bottom: 0; + width: 100%; + z-index: 0; + overflow: hidden; + height: 32px; + text-align: right; +} + +video:-webkit-full-page-media::-webkit-media-controls-panel { + bottom: -32px; +} + +audio::-webkit-media-controls-mute-button, video::-webkit-media-controls-mute-button { + -webkit-appearance: media-mute-button; + display: -webkit-box; + width: 32px; + height: 32px; +} + +audio::-webkit-media-controls-play-button, video::-webkit-media-controls-play-button { + -webkit-appearance: media-play-button; + display: -webkit-box; + width: 32px; + height: 32px; +} + +audio::-webkit-media-controls-timeline-container, video::-webkit-media-controls-timeline-container { + -webkit-appearance: media-controls-background; + display: -webkit-box; + -webkit-box-orient: horizontal; + -webkit-box-align: center; + -webkit-box-pack: end; + -webkit-box-flex: 1; + -webkit-user-select: none; + height: 32px; +} + +audio::-webkit-media-controls-current-time-display, video::-webkit-media-controls-current-time-display { + display: none; +} + +audio::-webkit-media-controls-time-remaining-display, video::-webkit-media-controls-time-remaining-display { + display: none; +} + +audio::-webkit-media-controls-timeline, video::-webkit-media-controls-timeline { + -webkit-appearance: media-slider; + display: -webkit-box; + -webkit-box-flex: 1; + height: 32px; + padding: 0px 2px; +} + +audio::-webkit-media-controls-volume-slider-container, video::-webkit-media-controls-volume-slider-container { + display: none; +} + +audio::-webkit-media-controls-volume-slider, video::-webkit-media-controls-volume-slider { + display: none; +} + +audio::-webkit-media-controls-seek-back-button, video::-webkit-media-controls-seek-back-button { + -webkit-appearance: media-seek-back-button; + display: -webkit-box; + width: 32px; + height: 32px; +} + +audio::-webkit-media-controls-seek-forward-button, video::-webkit-media-controls-seek-forward-button { + -webkit-appearance: media-seek-forward-button; + display: -webkit-box; + width: 32px; + height: 32px; +} + +audio::-webkit-media-controls-fullscreen-button, video::-webkit-media-controls-fullscreen-button { + -webkit-appearance: media-fullscreen-button; + display: -webkit-box; + width: 32px; + height: 32px; +} + +audio::-webkit-media-controls-rewind-button, video::-webkit-media-controls-rewind-button { + display: none; +} + +audio::-webkit-media-controls-return-to-realtime-button, video::-webkit-media-controls-return-to-realtime-button { + display: none; +} + +audio::-webkit-media-controls-toggle-closed-captions-button, video::-webkit-media-controls-toggle-closed-captions-button { + -webkit-appearance: media-toggle-closed-captions-button; + display: -webkit-box; + width: 32px; + height: 32px +} diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp index 12b93ef..e5594ae 100644 --- a/WebCore/html/HTMLMediaElement.cpp +++ b/WebCore/html/HTMLMediaElement.cpp @@ -591,6 +591,12 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content updateVolume(); m_player->load(m_currentSrc, contentType); +#if PLATFORM(ANDROID) + if (isVideo()) + m_player->setMediaElementType(MediaPlayer::Video); + else + m_player->setMediaElementType(MediaPlayer::Audio); +#endif if (isVideo() && m_player->canLoadPoster()) { KURL posterUrl = static_cast(this)->poster(); diff --git a/WebCore/platform/android/RenderThemeAndroid.cpp b/WebCore/platform/android/RenderThemeAndroid.cpp index 7085bf4..ab93167 100644 --- a/WebCore/platform/android/RenderThemeAndroid.cpp +++ b/WebCore/platform/android/RenderThemeAndroid.cpp @@ -34,11 +34,16 @@ #include "HTMLSelectElement.h" #include "Node.h" #include "PlatformGraphicsContext.h" +#if ENABLE(VIDEO) +#include "RenderMediaControls.h" +#endif #include "RenderSkinAndroid.h" #include "RenderSkinButton.h" #include "RenderSkinCombo.h" +#include "RenderSkinMediaButton.h" #include "RenderSkinRadio.h" #include "SkCanvas.h" +#include "UserAgentStyleSheets.h" namespace WebCore { @@ -225,6 +230,95 @@ bool RenderThemeAndroid::paintButton(RenderObject* obj, const RenderObject::Pain return false; } +#if ENABLE(VIDEO) + +String RenderThemeAndroid::extraMediaControlsStyleSheet() +{ + return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet)); +} + +bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e) +{ + HTMLMediaElement* mediaElement = static_cast(e); + switch (part) { + case MediaMuteButtonPart: + return false; + case MediaSeekBackButtonPart: + case MediaSeekForwardButtonPart: + return true; + case MediaRewindButtonPart: + return mediaElement->movieLoadType() != MediaPlayer::LiveStream; + case MediaReturnToRealtimeButtonPart: + return mediaElement->movieLoadType() == MediaPlayer::LiveStream; + case MediaFullscreenButtonPart: + return mediaElement->supportsFullscreen(); + case MediaToggleClosedCaptionsButtonPart: + return mediaElement->hasClosedCaptions(); + default: + return true; + } +} + +bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::MUTE); + return false; +} + +bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + if (MediaControlPlayButtonElement* btn = static_cast(o->node())) { + if (btn->displayType() == MediaPlayButton) + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PLAY); + else + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PAUSE); + return false; + } + return true; +} + +bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::REWIND); + return false; +} + +bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FORWARD); + return false; +} + +bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::BACKGROUND_SLIDER); + return false; +} + +bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_TRACK); + return false; +} + +bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) +{ + RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_THUMB); + return false; +} + +void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const +{ + static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth(); + static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight(); + if (o->style()->appearance() == MediaSliderThumbPart) { + o->style()->setWidth(Length(sliderThumbWidth, Fixed)); + o->style()->setHeight(Length(sliderThumbHeight, Fixed)); + } +} + +#endif + bool RenderThemeAndroid::paintRadio(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, false); diff --git a/WebCore/platform/android/RenderThemeAndroid.h b/WebCore/platform/android/RenderThemeAndroid.h index ae1213c..3d7ac77 100644 --- a/WebCore/platform/android/RenderThemeAndroid.h +++ b/WebCore/platform/android/RenderThemeAndroid.h @@ -77,6 +77,19 @@ protected: virtual bool paintCheckbox(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void setCheckboxSize(RenderStyle*) const; +#if ENABLE(VIDEO) + virtual String extraMediaControlsStyleSheet(); + virtual void adjustSliderThumbSize(RenderObject* o) const; + virtual bool shouldRenderMediaControlPart(ControlPart part, Element* e); + bool paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + bool paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + bool paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + bool paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + bool paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + bool paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r); + virtual bool paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect); +#endif + virtual bool paintRadio(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void setRadioSize(RenderStyle*) const; @@ -113,4 +126,3 @@ private: } // namespace WebCore #endif // RenderThemeAndroid_h - diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 8a9a264..9a7f7b3 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -204,6 +204,9 @@ MediaPlayer::MediaPlayer(MediaPlayerClient* client) , m_muted(false) , m_preservesPitch(true) , m_autobuffer(false) +#if PLATFORM(ANDROID) + , m_mediaElementType(Video) +#endif #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) , m_playerProxy(0) #endif diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 1ca4576..f067cc9 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -143,6 +143,11 @@ public: IntSize naturalSize(); bool hasVideo() const; bool hasAudio() const; +#if PLATFORM(ANDROID) + enum MediaElementType { Video, Audio }; + void setMediaElementType(MediaElementType type) { m_mediaElementType = type; } + MediaElementType mediaElementType() { return m_mediaElementType; } +#endif void setFrameView(FrameView* frameView) { m_frameView = frameView; } FrameView* frameView() { return m_frameView; } @@ -254,6 +259,9 @@ private: bool m_muted; bool m_preservesPitch; bool m_autobuffer; +#if PLATFORM(ANDROID) + MediaElementType m_mediaElementType; +#endif #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private #endif diff --git a/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h b/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h index 19bfcd1..b97691f 100644 --- a/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h +++ b/WebCore/platform/graphics/android/MediaPlayerPrivateAndroid.h @@ -1,5 +1,5 @@ /* - * Copyright 2009, The Android Open Source Project + * Copyright 2009,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 @@ -31,73 +31,77 @@ class SkBitmap; #include "MediaPlayerPrivate.h" +#include "TimeRanges.h" namespace WebCore { class MediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - ~MediaPlayerPrivate(); + virtual ~MediaPlayerPrivate(); static void registerMediaEngine(MediaEngineRegistrar); - virtual void load(const String& url); - virtual void cancelLoad(); + virtual void load(const String& url) = 0; + virtual void cancelLoad() { } - virtual void play(); + virtual void play() = 0; virtual void pause(); - virtual IntSize naturalSize() const; + virtual IntSize naturalSize() const { return m_naturalSize; } - virtual bool hasAudio() const; - virtual bool hasVideo() const; + virtual bool hasAudio() const { return false; } + virtual bool hasVideo() const { return false; } virtual void setVisible(bool); - virtual float duration() const; + virtual float duration() const { return m_duration; } - virtual float currentTime() const; + virtual float currentTime() const { return m_currentTime; }; virtual void seek(float time); - virtual bool seeking() const; + virtual bool seeking() const { return false; } - virtual void setEndTime(float time); + virtual void setEndTime(float time) { } - virtual void setRate(float); - virtual bool paused() const; + virtual void setRate(float) { } + virtual bool paused() const { return m_paused; } - virtual void setVolume(float); + virtual void setVolume(float) { } - virtual MediaPlayer::NetworkState networkState() const; - virtual MediaPlayer::ReadyState readyState() const; + virtual MediaPlayer::NetworkState networkState() const { return m_networkState; } + virtual MediaPlayer::ReadyState readyState() const { return m_readyState; } - virtual float maxTimeSeekable() const; - virtual PassRefPtr buffered() const; + virtual float maxTimeSeekable() const { return 0; } + virtual PassRefPtr buffered() const { return TimeRanges::create(); } - virtual int dataRate() const; + virtual int dataRate() const { return 0; } virtual bool totalBytesKnown() const { return totalBytes() > 0; } - virtual unsigned totalBytes() const; - virtual unsigned bytesLoaded() const; + virtual unsigned totalBytes() const { return 0; } + virtual unsigned bytesLoaded() const { return 0; } - virtual void setSize(const IntSize&); + virtual void setSize(const IntSize&) { } - virtual bool canLoadPoster() const { return true; } - virtual void setPoster(const String&); + virtual bool canLoadPoster() const { return false; } + virtual void setPoster(const String&) { } virtual void prepareToPlay(); - virtual void paint(GraphicsContext*, const IntRect&); + virtual void paint(GraphicsContext*, const IntRect&) { } - void onPrepared(int duration, int width, int height); + virtual void onPrepared(int duration, int width, int height) { } void onEnded(); - void onPosterFetched(SkBitmap*); + virtual void onPosterFetched(SkBitmap*) { } + void onBuffering(int percent); void onTimeupdate(int position); -private: +protected: // Android-specific methods and fields. static MediaPlayerPrivateInterface* create(MediaPlayer* player); - static void getSupportedTypes(HashSet&); - static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static void getSupportedTypes(HashSet&) { } + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs) { + return MediaPlayer::IsNotSupported; + } MediaPlayerPrivate(MediaPlayer *); - void createJavaPlayerIfNeeded(); + virtual void createJavaPlayerIfNeeded() { } MediaPlayer* m_player; String m_url; 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 31c327a..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" @@ -46,6 +47,7 @@ void RenderSkinAndroid::Init(android::AssetManager* am, String drawableDirectory { RenderSkinButton::Init(am, drawableDirectory); 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 +#include + +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 bfb5305..7c0bb24 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(time * 1000.0f)); + if (m_glue->m_javaProxy) { + env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast(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 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,45 +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(m_poster->width()) / static_cast(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&) -{ -} - -MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) -{ - return MediaPlayer::IsNotSupported; -} - MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player), m_glue(0), @@ -290,72 +136,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() { @@ -368,25 +148,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(m_poster->width()) / static_cast(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, "", "(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 { @@ -416,6 +439,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(pointer); + //TODO: player->onBuffering(percent); + } +} + static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer) { if (pointer) { WebCore::MediaPlayerPrivate* player = reinterpret_cast(pointer); @@ -437,11 +467,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 }; -- cgit v1.1