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