Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright 2009, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "MediaPlayerPrivateAndroid.h"
     28 
     29 #if ENABLE(VIDEO)
     30 
     31 #include "BaseLayerAndroid.h"
     32 #include "GraphicsContext.h"
     33 #include "HTMLMediaElement.h"
     34 #include "SkiaUtils.h"
     35 #include "TilesManager.h"
     36 #include "VideoLayerAndroid.h"
     37 #include "WebCoreJni.h"
     38 #include "WebViewCore.h"
     39 #include <GraphicsJNI.h>
     40 #include <JNIHelp.h>
     41 #include <JNIUtility.h>
     42 #include <SkBitmap.h>
     43 #include <gui/GLConsumer.h>
     44 
     45 using namespace android;
     46 // Forward decl
     47 namespace android {
     48 sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
     49 };
     50 
     51 namespace WebCore {
     52 
     53 static const char* g_ProxyJavaClass = "android/webkit/HTML5VideoViewProxy";
     54 static const char* g_ProxyJavaClassAudio = "android/webkit/HTML5Audio";
     55 
     56 struct MediaPlayerPrivate::JavaGlue {
     57     jobject   m_javaProxy;
     58     jmethodID m_play;
     59     jmethodID m_enterFullscreenForVideoLayer;
     60     jmethodID m_teardown;
     61     jmethodID m_seek;
     62     jmethodID m_pause;
     63     // Audio
     64     jmethodID m_newInstance;
     65     jmethodID m_setDataSource;
     66     jmethodID m_getMaxTimeSeekable;
     67     // Video
     68     jmethodID m_getInstance;
     69     jmethodID m_loadPoster;
     70 };
     71 
     72 MediaPlayerPrivate::~MediaPlayerPrivate()
     73 {
     74     TilesManager::instance()->videoLayerManager()->removeLayer(m_videoLayer->uniqueId());
     75     // m_videoLayer is reference counted, unref is enough here.
     76     m_videoLayer->unref();
     77     if (m_glue->m_javaProxy) {
     78         JNIEnv* env = JSC::Bindings::getJNIEnv();
     79         if (env) {
     80             env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_teardown);
     81             env->DeleteGlobalRef(m_glue->m_javaProxy);
     82         }
     83     }
     84     delete m_glue;
     85 }
     86 
     87 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
     88 {
     89     registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
     90 }
     91 
     92 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
     93 {
     94     if (WebViewCore::isSupportedMediaMimeType(type))
     95         return MediaPlayer::MayBeSupported;
     96     return MediaPlayer::IsNotSupported;
     97 }
     98 
     99 void MediaPlayerPrivate::pause()
    100 {
    101     JNIEnv* env = JSC::Bindings::getJNIEnv();
    102     if (!env || !m_glue->m_javaProxy || !m_url.length())
    103         return;
    104 
    105     m_paused = true;
    106     m_player->playbackStateChanged();
    107     env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_pause);
    108     checkException(env);
    109 }
    110 
    111 void MediaPlayerPrivate::setVisible(bool visible)
    112 {
    113     m_isVisible = visible;
    114     if (m_isVisible)
    115         createJavaPlayerIfNeeded();
    116 }
    117 
    118 void MediaPlayerPrivate::seek(float time)
    119 {
    120     JNIEnv* env = JSC::Bindings::getJNIEnv();
    121     if (!env || !m_url.length())
    122         return;
    123 
    124     if (m_glue->m_javaProxy) {
    125         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_seek, static_cast<jint>(time * 1000.0f));
    126         m_currentTime = time;
    127     }
    128     checkException(env);
    129 }
    130 
    131 void MediaPlayerPrivate::prepareToPlay()
    132 {
    133     // We are about to start playing. Since our Java VideoView cannot
    134     // buffer any data, we just simply transition to the HaveEnoughData
    135     // state in here. This will allow the MediaPlayer to transition to
    136     // the "play" state, at which point our VideoView will start downloading
    137     // the content and start the playback.
    138     m_networkState = MediaPlayer::Loaded;
    139     m_player->networkStateChanged();
    140     m_readyState = MediaPlayer::HaveEnoughData;
    141     m_player->readyStateChanged();
    142 }
    143 
    144 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
    145     : m_player(player),
    146     m_glue(0),
    147     m_duration(1), // keep this minimal to avoid initial seek problem
    148     m_currentTime(0),
    149     m_paused(true),
    150     m_readyState(MediaPlayer::HaveNothing),
    151     m_networkState(MediaPlayer::Empty),
    152     m_poster(0),
    153     m_naturalSize(100, 100),
    154     m_naturalSizeUnknown(true),
    155     m_isVisible(false),
    156     m_videoLayer(new VideoLayerAndroid())
    157 {
    158 }
    159 
    160 void MediaPlayerPrivate::onEnded()
    161 {
    162     m_currentTime = duration();
    163     m_player->timeChanged();
    164     m_paused = true;
    165     m_player->playbackStateChanged();
    166     m_networkState = MediaPlayer::Idle;
    167 }
    168 
    169 void MediaPlayerPrivate::onRequestPlay()
    170 {
    171     play();
    172 }
    173 
    174 void MediaPlayerPrivate::onRestoreState()
    175 {
    176     if (!m_paused) {
    177         //Kick off a JNI call to start the video.
    178         play();
    179     }
    180 }
    181 
    182 void MediaPlayerPrivate::onPaused()
    183 {
    184     m_paused = true;
    185     m_player->playbackStateChanged();
    186     m_networkState = MediaPlayer::Idle;
    187     m_player->playbackStateChanged();
    188 }
    189 
    190 void MediaPlayerPrivate::onTimeupdate(int position)
    191 {
    192     m_currentTime = position / 1000.0f;
    193     m_player->timeChanged();
    194 }
    195 
    196 void MediaPlayerPrivate::onStopFullscreen(bool stillPlaying)
    197 {
    198     if (m_player && m_player->mediaPlayerClient()) {
    199         Document* doc = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
    200         if (doc) {
    201             HTMLMediaElement* element =
    202                 static_cast<HTMLMediaElement*>(doc->webkitCurrentFullScreenElement());
    203             element->exitFullscreen();
    204             doc->webkitDidExitFullScreenForElement(element);
    205 
    206             if (stillPlaying)
    207                 element->play(true);
    208         }
    209     }
    210 }
    211 
    212 class MediaPlayerVideoPrivate : public MediaPlayerPrivate {
    213 public:
    214     void load(const String& url)
    215     {
    216         m_url = url;
    217         // Cheat a bit here to make sure Window.onLoad event can be triggered
    218         // at the right time instead of real video play time, since only full
    219         // screen video play is supported in Java's VideoView.
    220         // See also comments in prepareToPlay function.
    221         m_networkState = MediaPlayer::Loading;
    222         m_player->networkStateChanged();
    223         m_readyState = MediaPlayer::HaveCurrentData;
    224         m_player->readyStateChanged();
    225     }
    226 
    227     void play()
    228     {
    229         JNIEnv* env = JSC::Bindings::getJNIEnv();
    230         if (!env || !m_url.length() || !m_glue->m_javaProxy)
    231             return;
    232 
    233         m_paused = false;
    234         m_player->playbackStateChanged();
    235 
    236         if (m_currentTime == duration())
    237             m_currentTime = 0;
    238 
    239         jstring jUrl = wtfStringToJstring(env, m_url);
    240         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play, jUrl,
    241                             static_cast<jint>(m_currentTime * 1000.0f),
    242                             m_videoLayer->uniqueId());
    243         env->DeleteLocalRef(jUrl);
    244 
    245         checkException(env);
    246     }
    247 
    248     void enterFullscreenMode()
    249     {
    250         JNIEnv* env = JSC::Bindings::getJNIEnv();
    251         if (!env || !m_url.length() || !m_glue->m_javaProxy)
    252             return;
    253 
    254         jstring jUrl = wtfStringToJstring(env, m_url);
    255         env->CallVoidMethod(m_glue->m_javaProxy,
    256                             m_glue->m_enterFullscreenForVideoLayer, jUrl,
    257                             m_videoLayer->uniqueId());
    258         env->DeleteLocalRef(jUrl);
    259 
    260         checkException(env);
    261     }
    262 
    263     bool canLoadPoster() const { return true; }
    264     void setPoster(const String& url)
    265     {
    266         if (m_posterUrl == url)
    267             return;
    268 
    269         m_posterUrl = url;
    270         JNIEnv* env = JSC::Bindings::getJNIEnv();
    271         if (!env || !m_glue->m_javaProxy || !m_posterUrl.length())
    272             return;
    273         // Send the poster
    274         jstring jUrl = wtfStringToJstring(env, m_posterUrl);
    275         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
    276         env->DeleteLocalRef(jUrl);
    277     }
    278     void paint(GraphicsContext* ctxt, const IntRect& r)
    279     {
    280         if (ctxt->paintingDisabled())
    281             return;
    282 
    283         if (!m_isVisible)
    284             return;
    285 
    286         if (!m_poster || (!m_poster->getPixels() && !m_poster->pixelRef()))
    287             return;
    288 
    289         // We paint with the following rules in mind:
    290         // - only downscale the poster, never upscale
    291         // - maintain the natural aspect ratio of the poster
    292         // - the poster should be centered in the target rect
    293         float originalRatio = static_cast<float>(m_poster->width()) / static_cast<float>(m_poster->height());
    294         int posterWidth = r.width() > m_poster->width() ? m_poster->width() : r.width();
    295         int posterHeight = posterWidth / originalRatio;
    296         int posterX = ((r.width() - posterWidth) / 2) + r.x();
    297         int posterY = ((r.height() - posterHeight) / 2) + r.y();
    298         IntRect targetRect(posterX, posterY, posterWidth, posterHeight);
    299         ctxt->platformContext()->drawBitmapRect(*m_poster, 0, targetRect);
    300     }
    301 
    302     void onPosterFetched(SkBitmap* poster)
    303     {
    304         m_poster = poster;
    305         if (m_naturalSizeUnknown) {
    306             // We had to fake the size at startup, or else our paint
    307             // method would not be called. If we haven't yet received
    308             // the onPrepared event, update the intrinsic size to the size
    309             // of the poster. That will be overriden when onPrepare comes.
    310             // In case of an error, we should report the poster size, rather
    311             // than our initial fake value.
    312             m_naturalSize = IntSize(poster->width(), poster->height());
    313             m_player->sizeChanged();
    314         }
    315         // At this time, we know that the proxy has been setup. And it is the
    316         // right time to trigger autoplay through the HTMLMediaElement state
    317         // change. Since we are using the java MediaPlayer, so we have to
    318         // pretend that the MediaPlayer has enough data.
    319         m_readyState = MediaPlayer::HaveEnoughData;
    320         m_player->readyStateChanged();
    321 
    322     }
    323 
    324     void onPrepared(int duration, int width, int height)
    325     {
    326         m_duration = duration / 1000.0f;
    327         m_naturalSize = IntSize(width, height);
    328         m_naturalSizeUnknown = false;
    329         m_player->durationChanged();
    330         m_player->sizeChanged();
    331         TilesManager::instance()->videoLayerManager()->updateVideoLayerSize(
    332             m_player->platformLayer()->uniqueId(), width * height,
    333             width / (float)height);
    334     }
    335 
    336     virtual bool hasAudio() const { return false; } // do not display the audio UI
    337     virtual bool hasVideo() const { return true; }
    338     virtual bool supportsFullscreen() const { return true; }
    339 
    340     MediaPlayerVideoPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
    341     {
    342         JNIEnv* env = JSC::Bindings::getJNIEnv();
    343         if (!env)
    344             return;
    345 
    346         jclass clazz = env->FindClass(g_ProxyJavaClass);
    347 
    348         if (!clazz)
    349             return;
    350 
    351         m_glue = new JavaGlue;
    352         m_glue->m_getInstance =
    353             env->GetStaticMethodID(clazz, "getInstance",
    354                                    "(Landroid/webkit/WebViewCore;I)Landroid/webkit/HTML5VideoViewProxy;");
    355         m_glue->m_loadPoster = env->GetMethodID(clazz, "loadPoster", "(Ljava/lang/String;)V");
    356         m_glue->m_play = env->GetMethodID(clazz, "play", "(Ljava/lang/String;II)V");
    357         m_glue->m_enterFullscreenForVideoLayer =
    358             env->GetMethodID(clazz, "enterFullscreenForVideoLayer", "(Ljava/lang/String;I)V");
    359 
    360         m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
    361         m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
    362         m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
    363         m_glue->m_javaProxy = 0;
    364         env->DeleteLocalRef(clazz);
    365         // An exception is raised if any of the above fails.
    366         checkException(env);
    367     }
    368 
    369     void createJavaPlayerIfNeeded()
    370     {
    371         // Check if we have been already created.
    372         if (m_glue->m_javaProxy)
    373             return;
    374 
    375         JNIEnv* env = JSC::Bindings::getJNIEnv();
    376         if (!env)
    377             return;
    378 
    379         jclass clazz = env->FindClass(g_ProxyJavaClass);
    380 
    381         if (!clazz)
    382             return;
    383 
    384         jobject obj = 0;
    385 
    386         FrameView* frameView = m_player->frameView();
    387         if (!frameView)
    388             return;
    389         AutoJObject javaObject = WebViewCore::getWebViewCore(frameView)->getJavaObject();
    390         if (!javaObject.get())
    391             return;
    392 
    393         // Get the HTML5VideoViewProxy instance
    394         obj = env->CallStaticObjectMethod(clazz, m_glue->m_getInstance, javaObject.get(), this);
    395         m_glue->m_javaProxy = env->NewGlobalRef(obj);
    396         // Send the poster
    397         jstring jUrl = 0;
    398         if (m_posterUrl.length())
    399             jUrl = wtfStringToJstring(env, m_posterUrl);
    400         // Sending a NULL jUrl allows the Java side to try to load the default poster.
    401         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_loadPoster, jUrl);
    402         if (jUrl)
    403             env->DeleteLocalRef(jUrl);
    404 
    405         // Clean up.
    406         env->DeleteLocalRef(obj);
    407         env->DeleteLocalRef(clazz);
    408         checkException(env);
    409     }
    410 
    411     float maxTimeSeekable() const
    412     {
    413         return m_duration;
    414     }
    415 };
    416 
    417 class MediaPlayerAudioPrivate : public MediaPlayerPrivate {
    418 public:
    419     void load(const String& url)
    420     {
    421         m_url = url;
    422         JNIEnv* env = JSC::Bindings::getJNIEnv();
    423         if (!env || !m_url.length())
    424             return;
    425 
    426         createJavaPlayerIfNeeded();
    427 
    428         if (!m_glue->m_javaProxy)
    429             return;
    430 
    431         jstring jUrl = wtfStringToJstring(env, m_url);
    432         // start loading the data asynchronously
    433         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_setDataSource, jUrl);
    434         env->DeleteLocalRef(jUrl);
    435         checkException(env);
    436     }
    437 
    438     void play()
    439     {
    440         JNIEnv* env = JSC::Bindings::getJNIEnv();
    441         if (!env || !m_url.length())
    442             return;
    443 
    444         createJavaPlayerIfNeeded();
    445 
    446         if (!m_glue->m_javaProxy)
    447             return;
    448 
    449         m_paused = false;
    450         m_player->playbackStateChanged();
    451         env->CallVoidMethod(m_glue->m_javaProxy, m_glue->m_play);
    452         checkException(env);
    453     }
    454 
    455     virtual bool hasAudio() const { return true; }
    456     virtual bool hasVideo() const { return false; }
    457     virtual bool supportsFullscreen() const { return false; }
    458 
    459     float maxTimeSeekable() const
    460     {
    461         if (m_glue->m_javaProxy) {
    462             JNIEnv* env = JSC::Bindings::getJNIEnv();
    463             if (env) {
    464                 float maxTime = env->CallFloatMethod(m_glue->m_javaProxy,
    465                                                      m_glue->m_getMaxTimeSeekable);
    466                 checkException(env);
    467                 return maxTime;
    468             }
    469         }
    470         return 0;
    471     }
    472 
    473     MediaPlayerAudioPrivate(MediaPlayer* player) : MediaPlayerPrivate(player)
    474     {
    475         JNIEnv* env = JSC::Bindings::getJNIEnv();
    476         if (!env)
    477             return;
    478 
    479         jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
    480 
    481         if (!clazz)
    482             return;
    483 
    484         m_glue = new JavaGlue;
    485         m_glue->m_newInstance = env->GetMethodID(clazz, "<init>", "(Landroid/webkit/WebViewCore;I)V");
    486         m_glue->m_setDataSource = env->GetMethodID(clazz, "setDataSource", "(Ljava/lang/String;)V");
    487         m_glue->m_play = env->GetMethodID(clazz, "play", "()V");
    488         m_glue->m_getMaxTimeSeekable = env->GetMethodID(clazz, "getMaxTimeSeekable", "()F");
    489         m_glue->m_teardown = env->GetMethodID(clazz, "teardown", "()V");
    490         m_glue->m_seek = env->GetMethodID(clazz, "seek", "(I)V");
    491         m_glue->m_pause = env->GetMethodID(clazz, "pause", "()V");
    492         m_glue->m_javaProxy = 0;
    493         env->DeleteLocalRef(clazz);
    494         // An exception is raised if any of the above fails.
    495         checkException(env);
    496     }
    497 
    498     void createJavaPlayerIfNeeded()
    499     {
    500         // Check if we have been already created.
    501         if (m_glue->m_javaProxy)
    502             return;
    503 
    504         JNIEnv* env = JSC::Bindings::getJNIEnv();
    505         if (!env)
    506             return;
    507 
    508         jclass clazz = env->FindClass(g_ProxyJavaClassAudio);
    509 
    510         if (!clazz)
    511             return;
    512 
    513         FrameView* frameView = m_player->mediaPlayerClient()->mediaPlayerOwningDocument()->view();
    514         if (!frameView)
    515             return;
    516         AutoJObject javaObject = WebViewCore::getWebViewCore(frameView)->getJavaObject();
    517         if (!javaObject.get())
    518             return;
    519 
    520         jobject obj = 0;
    521 
    522         // Get the HTML5Audio instance
    523         obj = env->NewObject(clazz, m_glue->m_newInstance, javaObject.get(), this);
    524         m_glue->m_javaProxy = env->NewGlobalRef(obj);
    525 
    526         // Clean up.
    527         if (obj)
    528             env->DeleteLocalRef(obj);
    529         env->DeleteLocalRef(clazz);
    530         checkException(env);
    531     }
    532 
    533     void onPrepared(int duration, int width, int height)
    534     {
    535         // Android media player gives us a duration of 0 for a live
    536         // stream, so in that case set the real duration to infinity.
    537         // We'll still be able to handle the case that we genuinely
    538         // get an audio clip with a duration of 0s as we'll get the
    539         // ended event when it stops playing.
    540         if (duration > 0) {
    541             m_duration = duration / 1000.0f;
    542         } else {
    543             m_duration = std::numeric_limits<float>::infinity();
    544         }
    545         m_player->durationChanged();
    546         m_player->sizeChanged();
    547         m_player->prepareToPlay();
    548     }
    549 };
    550 
    551 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
    552 {
    553     if (player->mediaElementType() == MediaPlayer::Video)
    554        return new MediaPlayerVideoPrivate(player);
    555     return new MediaPlayerAudioPrivate(player);
    556 }
    557 
    558 }
    559 
    560 namespace android {
    561 
    562 static void OnPrepared(JNIEnv* env, jobject obj, int duration, int width, int height, int pointer)
    563 {
    564     if (pointer) {
    565         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    566         player->onPrepared(duration, width, height);
    567     }
    568 }
    569 
    570 static void OnEnded(JNIEnv* env, jobject obj, int pointer)
    571 {
    572     if (pointer) {
    573         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    574         player->onEnded();
    575     }
    576 }
    577 
    578 static void OnRequestPlay(JNIEnv* env, jobject obj, int pointer)
    579 {
    580     if (pointer) {
    581         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    582         player->onRequestPlay();
    583     }
    584 }
    585 
    586 static void OnPaused(JNIEnv* env, jobject obj, int pointer)
    587 {
    588     if (pointer) {
    589         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    590         player->onPaused();
    591     }
    592 }
    593 
    594 static void OnPosterFetched(JNIEnv* env, jobject obj, jobject poster, int pointer)
    595 {
    596     if (!pointer || !poster)
    597         return;
    598 
    599     WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    600     SkBitmap* posterNative = GraphicsJNI::getNativeBitmap(env, poster);
    601     if (!posterNative)
    602         return;
    603     player->onPosterFetched(posterNative);
    604 }
    605 
    606 static void OnBuffering(JNIEnv* env, jobject obj, int percent, int pointer)
    607 {
    608     if (pointer) {
    609         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    610         // TODO: player->onBuffering(percent);
    611     }
    612 }
    613 
    614 static void OnTimeupdate(JNIEnv* env, jobject obj, int position, int pointer)
    615 {
    616     if (pointer) {
    617         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    618         player->onTimeupdate(position);
    619     }
    620 }
    621 
    622 static void OnRestoreState(JNIEnv* env, jobject obj, int pointer)
    623 {
    624     if (pointer) {
    625         WebCore::MediaPlayerPrivate* player = reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    626         player->onRestoreState();
    627     }
    628 }
    629 
    630 
    631 // This is called on the UI thread only.
    632 // The video layers are composited on the webkit thread and then copied over
    633 // to the UI thread with the same ID. For rendering, we are only using the
    634 // video layers on the UI thread. Therefore, on the UI thread, we have to use
    635 // the videoLayerId from Java side to find the exact video layer in the tree
    636 // to set the surface texture.
    637 // Every time a play call into Java side, the videoLayerId will be sent and
    638 // saved in Java side. Then every time setBaseLayer call, the saved
    639 // videoLayerId will be passed to this function to find the Video Layer.
    640 // Return value: true when the video layer is found.
    641 static bool SendSurfaceTexture(JNIEnv* env, jobject obj, jobject surfTex,
    642                                int baseLayer, int videoLayerId,
    643                                int textureName, int playerState) {
    644     if (!surfTex)
    645         return false;
    646 
    647     sp<GLConsumer> texture = android::SurfaceTexture_getSurfaceTexture(env, surfTex);
    648     if (!texture.get())
    649         return false;
    650 
    651     BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(baseLayer);
    652     if (!layerImpl)
    653         return false;
    654 
    655     VideoLayerAndroid* videoLayer =
    656         static_cast<VideoLayerAndroid*>(layerImpl->findById(videoLayerId));
    657     if (!videoLayer)
    658         return false;
    659 
    660     // Set the GLConsumer to the layer we found
    661     videoLayer->setSurfaceTexture(texture, textureName, static_cast<PlayerState>(playerState));
    662     return true;
    663 }
    664 
    665 static void OnStopFullscreen(JNIEnv* env, jobject obj, int stillPlaying, int pointer)
    666 {
    667     if (pointer) {
    668         WebCore::MediaPlayerPrivate* player =
    669             reinterpret_cast<WebCore::MediaPlayerPrivate*>(pointer);
    670         player->onStopFullscreen(stillPlaying);
    671     }
    672 }
    673 
    674 /*
    675  * JNI registration
    676  */
    677 static JNINativeMethod g_MediaPlayerMethods[] = {
    678     { "nativeOnPrepared", "(IIII)V",
    679         (void*) OnPrepared },
    680     { "nativeOnEnded", "(I)V",
    681         (void*) OnEnded },
    682     { "nativeOnStopFullscreen", "(II)V",
    683         (void*) OnStopFullscreen },
    684     { "nativeOnPaused", "(I)V",
    685         (void*) OnPaused },
    686     { "nativeOnPosterFetched", "(Landroid/graphics/Bitmap;I)V",
    687         (void*) OnPosterFetched },
    688     { "nativeOnRestoreState", "(I)V",
    689         (void*) OnRestoreState },
    690     { "nativeSendSurfaceTexture", "(Landroid/graphics/SurfaceTexture;IIII)Z",
    691         (void*) SendSurfaceTexture },
    692     { "nativeOnTimeupdate", "(II)V",
    693         (void*) OnTimeupdate },
    694 };
    695 
    696 static JNINativeMethod g_MediaAudioPlayerMethods[] = {
    697     { "nativeOnBuffering", "(II)V",
    698         (void*) OnBuffering },
    699     { "nativeOnEnded", "(I)V",
    700         (void*) OnEnded },
    701     { "nativeOnPrepared", "(IIII)V",
    702         (void*) OnPrepared },
    703     { "nativeOnRequestPlay", "(I)V",
    704         (void*) OnRequestPlay },
    705     { "nativeOnTimeupdate", "(II)V",
    706         (void*) OnTimeupdate },
    707 };
    708 
    709 int registerMediaPlayerVideo(JNIEnv* env)
    710 {
    711     return jniRegisterNativeMethods(env, g_ProxyJavaClass,
    712             g_MediaPlayerMethods, NELEM(g_MediaPlayerMethods));
    713 }
    714 
    715 int registerMediaPlayerAudio(JNIEnv* env)
    716 {
    717     return jniRegisterNativeMethods(env, g_ProxyJavaClassAudio,
    718             g_MediaAudioPlayerMethods, NELEM(g_MediaAudioPlayerMethods));
    719 }
    720 
    721 }
    722 #endif // VIDEO
    723