Home | History | Annotate | Download | only in layers
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "MediaTexture"
     18 #define LOG_NDEBUG 1
     19 
     20 #include "config.h"
     21 #include "MediaTexture.h"
     22 
     23 #include "AndroidLog.h"
     24 #include "DrawQuadData.h"
     25 #include "TilesManager.h"
     26 #include "GLUtils.h"
     27 #include "MediaListener.h"
     28 
     29 #if USE(ACCELERATED_COMPOSITING)
     30 
     31 #include <android/native_window.h>
     32 #include <gui/SurfaceTexture.h>
     33 #include <gui/SurfaceTextureClient.h>
     34 #include <JNIUtility.h>
     35 #include "WebCoreJni.h"
     36 
     37 // Limits the number of ANativeWindows that can be allocated for video playback.
     38 // The limit is currently set to 2 as that is the current max number of
     39 // simultaneous HW decodes that our OMX implementation allows.  This forces the
     40 // media producer to use their own SW decoders for subsequent video streams.
     41 #define MAX_WINDOW_COUNT 2
     42 
     43 namespace WebCore {
     44 
     45 MediaTexture::MediaTexture(jobject webViewRef, jobject webViewCoreRef) : android::LightRefBase<MediaTexture>()
     46 {
     47     JNIEnv* env = JSC::Bindings::getJNIEnv();
     48     m_weakWebViewRef = env->NewWeakGlobalRef(webViewRef);
     49     m_weakWebViewCoreRef = env->NewWeakGlobalRef(webViewCoreRef);
     50 
     51     m_contentTexture = 0;
     52     m_isContentInverted = false;
     53     m_newWindowRequest = false;
     54 }
     55 
     56 MediaTexture::~MediaTexture()
     57 {
     58     if (m_contentTexture)
     59         deleteTexture(m_contentTexture, true);
     60     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
     61         deleteTexture(m_videoTextures[i], true);
     62     }
     63 
     64     JNIEnv* env = JSC::Bindings::getJNIEnv();
     65     env->DeleteWeakGlobalRef(m_weakWebViewRef);
     66     env->DeleteWeakGlobalRef(m_weakWebViewCoreRef);
     67 }
     68 
     69 bool MediaTexture::isContentInverted()
     70 {
     71     android::Mutex::Autolock lock(m_mediaLock);
     72     return m_isContentInverted;
     73 }
     74 void MediaTexture::invertContents(bool invertContent)
     75 {
     76     android::Mutex::Autolock lock(m_mediaLock);
     77     m_isContentInverted = invertContent;
     78 }
     79 
     80 void MediaTexture::initNativeWindowIfNeeded()
     81 {
     82     {
     83         android::Mutex::Autolock lock(m_mediaLock);
     84 
     85         // check to see if there are any unused textures to delete
     86         if (m_unusedTextures.size() != 0) {
     87             for (unsigned int i = 0; i < m_unusedTextures.size(); i++) {
     88                 glDeleteTextures(1, &m_unusedTextures[i]);
     89             }
     90             m_unusedTextures.clear();
     91         }
     92 
     93         // create a content texture if none exists
     94         if (!m_contentTexture) {
     95             m_contentTexture = createTexture();
     96 
     97             // send a message to the WebKit thread to notify the plugin that it can draw
     98             if (m_weakWebViewCoreRef) {
     99                 JNIEnv* env = JSC::Bindings::getJNIEnv();
    100                 jobject localWebViewCoreRef = env->NewLocalRef(m_weakWebViewCoreRef);
    101                 if (localWebViewCoreRef) {
    102                     jclass wvClass = env->GetObjectClass(localWebViewCoreRef);
    103                     jmethodID sendPluginDrawMsg =
    104                             env->GetMethodID(wvClass, "sendPluginDrawMsg", "()V");
    105                     env->CallVoidMethod(localWebViewCoreRef, sendPluginDrawMsg);
    106                     env->DeleteLocalRef(wvClass);
    107                     env->DeleteLocalRef(localWebViewCoreRef);
    108                 }
    109                 checkException(env);
    110             }
    111         }
    112 
    113         // finally create a video texture if needed
    114         if (!m_newWindowRequest)
    115             return;
    116 
    117         // add the texture and add it to the list
    118         TextureWrapper* videoTexture = createTexture();
    119         m_videoTextures.append(videoTexture);
    120 
    121         // setup the state variables to signal the other thread
    122         m_newWindowRequest = false;
    123         m_newWindow = videoTexture->nativeWindow;
    124     }
    125 
    126     // signal the WebKit thread in case it is waiting
    127     m_newMediaRequestCond.signal();
    128 }
    129 
    130 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
    131           const TransformationMatrix& videoMatrix,
    132           const SkRect& mediaBounds)
    133 {
    134     android::Mutex::Autolock lock(m_mediaLock);
    135 
    136     if (mediaBounds.isEmpty())
    137         return;
    138 
    139     // draw all the video textures first
    140     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
    141 
    142         TextureWrapper* video = m_videoTextures[i];
    143 
    144         if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
    145                 || !video->mediaListener->isFrameAvailable())
    146             continue;
    147 
    148         video->surfaceTexture->updateTexImage();
    149 
    150         float surfaceMatrix[16];
    151         video->surfaceTexture->getTransformMatrix(surfaceMatrix);
    152 
    153         SkRect dimensions = video->dimensions;
    154         dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
    155 
    156 #ifdef DEBUG
    157         if (!mediaBounds.contains(dimensions)) {
    158             ALOGV("The video exceeds is parent's bounds.");
    159         }
    160 #endif // DEBUG
    161 
    162         TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
    163                 surfaceMatrix, dimensions, video->textureId);
    164     }
    165 
    166     if (!m_contentTexture->mediaListener->isFrameAvailable())
    167         return;
    168 
    169     m_contentTexture->surfaceTexture->updateTexImage();
    170 
    171     sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
    172 
    173     PixelFormat f = buf->getPixelFormat();
    174     // only attempt to use alpha blending if alpha channel exists
    175     bool forceAlphaBlending = !(
    176         PIXEL_FORMAT_RGBX_8888 == f ||
    177         PIXEL_FORMAT_RGB_888 == f ||
    178         PIXEL_FORMAT_RGB_565 == f);
    179 
    180     TextureQuadData data(m_contentTexture->textureId, GL_TEXTURE_EXTERNAL_OES,
    181                          GL_LINEAR, LayerQuad, &contentMatrix, &mediaBounds,
    182                          1.0f, forceAlphaBlending);
    183     TilesManager::instance()->shader()->drawQuad(&data);
    184 }
    185 
    186 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
    187 {
    188     android::Mutex::Autolock lock(m_mediaLock);
    189 
    190     // the window was not ready before the timeout so return it this time
    191     if (ANativeWindow* window = m_newWindow.get()) {
    192         m_newWindow.clear();
    193         return window;
    194     }
    195 
    196     // we only allow for so many textures, so return NULL if we exceed that limit
    197     else if (m_videoTextures.size() >= MAX_WINDOW_COUNT) {
    198         return 0;
    199     }
    200 
    201     m_newWindowRequest = true;
    202 
    203     // post an inval message to the UI thread to fulfill the request
    204     if (m_weakWebViewRef) {
    205         JNIEnv* env = JSC::Bindings::getJNIEnv();
    206         jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
    207         if (localWebViewRef) {
    208             jclass wvClass = env->GetObjectClass(localWebViewRef);
    209             jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
    210             env->CallVoidMethod(localWebViewRef, postInvalMethod);
    211             env->DeleteLocalRef(wvClass);
    212             env->DeleteLocalRef(localWebViewRef);
    213         }
    214         checkException(env);
    215     }
    216 
    217     //block until the request can be fulfilled or we time out
    218     bool timedOut = false;
    219     while (m_newWindowRequest && !timedOut) {
    220         int ret = m_newMediaRequestCond.waitRelative(m_mediaLock, 500000000); // .5 sec
    221         timedOut = ret == TIMED_OUT;
    222     }
    223 
    224     // if the window is ready then return it otherwise return NULL
    225     if (ANativeWindow* window = m_newWindow.get()) {
    226         m_newWindow.clear();
    227         return window;
    228     }
    229     return 0;
    230 }
    231 
    232 ANativeWindow* MediaTexture::getNativeWindowForContent()
    233 {
    234     android::Mutex::Autolock lock(m_mediaLock);
    235     if (m_contentTexture)
    236         return m_contentTexture->nativeWindow.get();
    237     else
    238         return 0;
    239 }
    240 
    241 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
    242 {
    243     android::Mutex::Autolock lock(m_mediaLock);
    244     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
    245         if (m_videoTextures[i]->nativeWindow.get() == window) {
    246             deleteTexture(m_videoTextures[i]);
    247             m_videoTextures.remove(i);
    248             break;
    249         }
    250     }
    251 }
    252 
    253 void MediaTexture::setDimensions(const ANativeWindow* window,
    254                                  const SkRect& dimensions)
    255 {
    256     android::Mutex::Autolock lock(m_mediaLock);
    257     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
    258         if (m_videoTextures[i]->nativeWindow.get() == window) {
    259             m_videoTextures[i]->dimensions = dimensions;
    260             break;
    261         }
    262     }
    263 }
    264 
    265 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
    266                                         FramerateCallbackProc callback)
    267 {
    268     android::Mutex::Autolock lock(m_mediaLock);
    269     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
    270         if (m_videoTextures[i]->nativeWindow.get() == window) {
    271             m_videoTextures[i]->mediaListener->setFramerateCallback(callback);
    272             break;
    273         }
    274     }
    275 }
    276 
    277 MediaTexture::TextureWrapper* MediaTexture::createTexture()
    278 {
    279     TextureWrapper* wrapper = new TextureWrapper();
    280 
    281     // populate the wrapper
    282     glGenTextures(1, &wrapper->textureId);
    283     wrapper->surfaceTexture = new android::SurfaceTexture(wrapper->textureId);
    284     wrapper->nativeWindow = new android::SurfaceTextureClient(wrapper->surfaceTexture);
    285     wrapper->dimensions.setEmpty();
    286 
    287     // setup callback
    288     wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
    289                                                wrapper->surfaceTexture,
    290                                                wrapper->nativeWindow);
    291     wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
    292 
    293     return wrapper;
    294 }
    295 
    296 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
    297 {
    298     if (texture->surfaceTexture.get())
    299         texture->surfaceTexture->setFrameAvailableListener(0);
    300 
    301     if (force)
    302         glDeleteTextures(1, &texture->textureId);
    303     else
    304         m_unusedTextures.append(texture->textureId);
    305 
    306     // clear the strong pointer references
    307     texture->mediaListener.clear();
    308     texture->nativeWindow.clear();
    309     texture->surfaceTexture.clear();
    310 
    311     delete texture;
    312 }
    313 
    314 } // namespace WebCore
    315 
    316 #endif // USE(ACCELERATED_COMPOSITING)
    317