OSDN Git Service

Fix plugin rendering bug when video surface is not ready.
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / MediaTexture.cpp
index d8dc960..1676186 100644 (file)
@@ -17,7 +17,7 @@
 #include "MediaTexture.h"
 #include "TilesManager.h"
 #include "GLUtils.h"
-#include "VideoListener.h"
+#include "MediaListener.h"
 
 #if USE(ACCELERATED_COMPOSITING)
 
 
 #endif // DEBUG
 
-namespace WebCore {
+// Limits the number of ANativeWindows that can be allocated for video playback.
+// The limit is currently set to 2 as that is the current max number of
+// simultaneous HW decodes that our OMX implementation allows.  This forces the
+// media producer to use their own SW decoders for subsequent video streams.
+#define MAX_WINDOW_COUNT 2
 
-MediaTexture::MediaTexture(EGLContext sharedContext) : DoubleBufferedTexture(sharedContext, EglImageMode)
-{
-    m_producerRefCount = 0;
-    m_consumerRefCount = 0;
-}
-
-/* Increment the number of objects in the producer's thread that hold a reference
- * to this object. In practice, there is often only one producer reference for
- * the lifetime of the object.
- */
-void MediaTexture::producerInc()
-{
-    android::Mutex::Autolock lock(m_mediaLock);
-    m_producerRefCount++;
-}
+namespace WebCore {
 
-/* Decrement the number of objects in the producer's thread that are holding a
- * reference to this object. When removing the last reference we must cleanup
- * all GL objects that are associated with the producer's thread. There may not
- * be a consumer reference as the object may not have synced to the UI thread,
- * in which case the producer needs to handle the deletion of the object.
- */
-void MediaTexture::producerDec()
+MediaTexture::MediaTexture(jobject webViewRef) : android::LightRefBase<MediaTexture>()
 {
-    bool needsDeleted = false;
-
-    m_mediaLock.lock();
-    m_producerRefCount--;
-    if (m_producerRefCount == 0) {
-        producerDeleteTextures();
-        if (m_consumerRefCount < 1) {
-            XLOG("INFO: This texture has not been synced to the UI thread");
-            needsDeleted = true;
-        }
-    }
-    m_mediaLock.unlock();
-
-    if (needsDeleted) {
-        XLOG("Deleting MediaTexture Object");
-        delete this;
+    if (webViewRef) {
+        JNIEnv* env = JSC::Bindings::getJNIEnv();
+        m_weakWebViewRef = env->NewWeakGlobalRef(webViewRef);
+    } else {
+        m_weakWebViewRef = 0;
     }
-}
 
-/* Increment the number of objects in the consumer's thread that hold a reference
- * to this object. In practice, there can be multiple producer references as the
- * consumer (i.e. UI) thread may have multiple copies of the layer tree.
- */
-void MediaTexture::consumerInc()
-{
-    android::Mutex::Autolock lock(m_mediaLock);
-    m_consumerRefCount++;
+    m_contentTexture = 0;
+    m_isContentInverted = false;
+    m_newWindowRequest = false;
 }
 
-/* Decrement the number of objects in the consumer's thread that are holding a
- * reference to this object. When removing the last reference we must delete
- * this object and by extension cleanup all GL objects that are associated with
- * the consumer's thread. At the time of deletion if there is a remaining
- * producer reference we must cleanup the consumer GL objects in the event that
- * this texture will not be re-synced with the UI thread.
- */
-void MediaTexture::consumerDec()
+MediaTexture::~MediaTexture()
 {
-    bool needsDeleted = false;
-
-    m_mediaLock.lock();
-    m_consumerRefCount--;
-    if (m_consumerRefCount == 0) {
-        consumerDeleteTextures();
-        if (m_producerRefCount < 1) {
-            XLOG("WARNING: This texture still exists within webkit.");
-            needsDeleted = true;
-        }
+    if (m_contentTexture)
+        deleteTexture(m_contentTexture, true);
+    for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
+        deleteTexture(m_videoTextures[i], true);
     }
-    m_mediaLock.unlock();
 
-    if (needsDeleted) {
-        XLOG("Deleting MediaTexture Object");
-        delete this;
+    if (m_weakWebViewRef) {
+        JNIEnv* env = JSC::Bindings::getJNIEnv();
+        env->DeleteWeakGlobalRef(m_weakWebViewRef);
     }
 }
 
-VideoTexture::VideoTexture(jobject weakWebViewRef) : android::LightRefBase<VideoTexture>()
+bool MediaTexture::isContentInverted()
 {
-    m_weakWebViewRef = weakWebViewRef;
-    m_textureId = 0;
-    m_dimensions.setEmpty();
-    m_newWindowRequest = false;
-    m_newWindowReady = false;
-    m_videoListener = new VideoListener(m_weakWebViewRef);
+    android::Mutex::Autolock lock(m_mediaLock);
+    return m_isContentInverted;
 }
-
-VideoTexture::~VideoTexture()
+void MediaTexture::invertContents(bool invertContent)
 {
-    releaseNativeWindow();
-    if (m_textureId)
-        glDeleteTextures(1, &m_textureId);
-    if (m_weakWebViewRef) {
-        JNIEnv* env = JSC::Bindings::getJNIEnv();
-        env->DeleteWeakGlobalRef(m_weakWebViewRef);
-    }
+    android::Mutex::Autolock lock(m_mediaLock);
+    m_isContentInverted = invertContent;
 }
 
-void VideoTexture::initNativeWindowIfNeeded()
+void MediaTexture::initNativeWindowIfNeeded()
 {
     {
-        android::Mutex::Autolock lock(m_videoLock);
-
-        if(!m_newWindowRequest)
-            return;
+        android::Mutex::Autolock lock(m_mediaLock);
+
+        // check to see if there are any unused textures to delete
+        if (m_unusedTextures.size() != 0) {
+            for (unsigned int i = 0; i < m_unusedTextures.size(); i++) {
+                glDeleteTextures(1, &m_unusedTextures[i]);
+            }
+            m_unusedTextures.clear();
+        }
 
-        // reuse an existing texture if possible
-        if (!m_textureId)
-            glGenTextures(1, &m_textureId);
+        // create a content texture if none exists
+        if (!m_contentTexture) {
+            m_contentTexture = createTexture();
+
+            // send a message to the WebKit thread to notify the plugin that it can draw
+            if (m_weakWebViewRef) {
+                JNIEnv* env = JSC::Bindings::getJNIEnv();
+                jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
+                if (localWebViewRef) {
+                    jclass wvClass = env->GetObjectClass(localWebViewRef);
+                    jmethodID sendPluginDrawMsg =
+                            env->GetMethodID(wvClass, "sendPluginDrawMsg", "()V");
+                    env->CallVoidMethod(localWebViewRef, sendPluginDrawMsg);
+                    env->DeleteLocalRef(wvClass);
+                    env->DeleteLocalRef(localWebViewRef);
+                }
+                checkException(env);
+            }
+        }
 
-        m_surfaceTexture = new android::SurfaceTexture(m_textureId);
-        m_surfaceTextureClient = new android::SurfaceTextureClient(m_surfaceTexture);
+        // finally create a video texture if needed
+        if (!m_newWindowRequest)
+            return;
 
-        //setup callback
-        m_videoListener->resetFrameAvailable();
-        m_surfaceTexture->setFrameAvailableListener(m_videoListener);
+        // add the texture and add it to the list
+        TextureWrapper* videoTexture = createTexture();
+        m_videoTextures.append(videoTexture);
 
+        // setup the state variables to signal the other thread
         m_newWindowRequest = false;
-        m_newWindowReady = true;
+        m_newWindow = videoTexture->nativeWindow;
     }
-    m_newVideoRequestCond.signal();
+
+    // signal the WebKit thread in case it is waiting
+    m_newMediaRequestCond.signal();
 }
 
-void VideoTexture::drawVideo(const TransformationMatrix& matrix, const SkRect& parentBounds)
+void MediaTexture::draw(const TransformationMatrix& contentMatrix,
+          const TransformationMatrix& videoMatrix,
+          const SkRect& mediaBounds)
 {
-    android::Mutex::Autolock lock(m_videoLock);
+    android::Mutex::Autolock lock(m_mediaLock);
 
-    if(!m_surfaceTexture.get() || m_dimensions.isEmpty()
-            || !m_videoListener->isFrameAvailable())
+    if (mediaBounds.isEmpty())
         return;
 
-    m_surfaceTexture->updateTexImage();
+    // draw all the video textures first
+    for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
+
+        TextureWrapper* video = m_videoTextures[i];
 
-    float surfaceMatrix[16];
-    m_surfaceTexture->getTransformMatrix(surfaceMatrix);
+        if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
+                || !video->mediaListener->isFrameAvailable())
+            continue;
 
-    SkRect dimensions = m_dimensions;
-    dimensions.offset(parentBounds.fLeft, parentBounds.fTop);
+        video->surfaceTexture->updateTexImage();
+
+        float surfaceMatrix[16];
+        video->surfaceTexture->getTransformMatrix(surfaceMatrix);
+
+        SkRect dimensions = video->dimensions;
+        dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
 
 #ifdef DEBUG
-    if (!parentBounds.contains(dimensions)) {
-        XLOG("The video exceeds is parent's bounds.");
-    }
+        if (!mediaBounds.contains(dimensions)) {
+            XLOG("The video exceeds is parent's bounds.");
+        }
 #endif // DEBUG
 
-    TilesManager::instance()->shader()->drawVideoLayerQuad(matrix, surfaceMatrix,
-            dimensions, m_textureId);
+        TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
+                surfaceMatrix, dimensions, video->textureId);
+    }
+
+    if (!m_contentTexture->mediaListener->isFrameAvailable())
+        return;
+
+    m_contentTexture->surfaceTexture->updateTexImage();
+
+    sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
+
+    PixelFormat f = buf->getPixelFormat();
+    // only attempt to use alpha blending if alpha channel exists
+    bool forceAlphaBlending = !(
+        PIXEL_FORMAT_RGBX_8888 == f ||
+        PIXEL_FORMAT_RGB_888 == f ||
+        PIXEL_FORMAT_RGB_565 == f ||
+        PIXEL_FORMAT_RGB_332 == f);
+
+    TilesManager::instance()->shader()->drawLayerQuad(contentMatrix,
+                                                      mediaBounds,
+                                                      m_contentTexture->textureId,
+                                                      1.0f, forceAlphaBlending,
+                                                      GL_TEXTURE_EXTERNAL_OES);
 }
 
-ANativeWindow* VideoTexture::requestNewWindow()
+ANativeWindow* MediaTexture::requestNativeWindowForVideo()
 {
-    android::Mutex::Autolock lock(m_videoLock);
+    android::Mutex::Autolock lock(m_mediaLock);
 
     // the window was not ready before the timeout so return it this time
-    if (m_newWindowReady) {
-        m_newWindowReady = false;
-        return m_surfaceTextureClient.get();
+    if (ANativeWindow* window = m_newWindow.get()) {
+        m_newWindow.clear();
+        return window;
     }
-    // we only allow for one texture, so if one already exists return null
-    else if (m_surfaceTextureClient.get()) {
+
+    // we only allow for so many textures, so return NULL if we exceed that limit
+    else if (m_videoTextures.size() >= MAX_WINDOW_COUNT) {
         return 0;
     }
 
@@ -234,39 +235,99 @@ ANativeWindow* VideoTexture::requestNewWindow()
     //block until the request can be fulfilled or we time out
     bool timedOut = false;
     while (m_newWindowRequest && !timedOut) {
-        int ret = m_newVideoRequestCond.waitRelative(m_videoLock, 500000000); // .5 sec
+        int ret = m_newMediaRequestCond.waitRelative(m_mediaLock, 500000000); // .5 sec
         timedOut = ret == TIMED_OUT;
     }
 
-    if (m_surfaceTextureClient.get())
-        m_newWindowReady = false;
+    // if the window is ready then return it otherwise return NULL
+    if (ANativeWindow* window = m_newWindow.get()) {
+        m_newWindow.clear();
+        return window;
+    }
+    return 0;
+}
 
-    return m_surfaceTextureClient.get();
+ANativeWindow* MediaTexture::getNativeWindowForContent()
+{
+    android::Mutex::Autolock lock(m_mediaLock);
+    if (m_contentTexture)
+        return m_contentTexture->nativeWindow.get();
+    else
+        return 0;
 }
 
-ANativeWindow* VideoTexture::getNativeWindow()
+void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
 {
-    android::Mutex::Autolock lock(m_videoLock);
-    return m_surfaceTextureClient.get();
+    android::Mutex::Autolock lock(m_mediaLock);
+    for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
+        if (m_videoTextures[i]->nativeWindow.get() == window) {
+            deleteTexture(m_videoTextures[i]);
+            m_videoTextures.remove(i);
+            break;
+        }
+    }
 }
 
-void VideoTexture::releaseNativeWindow()
+void MediaTexture::setDimensions(const ANativeWindow* window,
+                                 const SkRect& dimensions)
 {
-    android::Mutex::Autolock lock(m_videoLock);
-    m_dimensions.setEmpty();
+    android::Mutex::Autolock lock(m_mediaLock);
+    for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
+        if (m_videoTextures[i]->nativeWindow.get() == window) {
+            m_videoTextures[i]->dimensions = dimensions;
+            break;
+        }
+    }
+}
 
-    if (m_surfaceTexture.get())
-        m_surfaceTexture->setFrameAvailableListener(0);
+void MediaTexture::setFramerateCallback(const ANativeWindow* window,
+                                        FramerateCallbackProc callback)
+{
+    XLOG("Release ANW %p (%p):(%p)", this, m_surfaceTexture.get(), m_surfaceTextureClient.get());
+    android::Mutex::Autolock lock(m_mediaLock);
+    for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
+        if (m_videoTextures[i]->nativeWindow.get() == window) {
+            m_videoTextures[i]->mediaListener->setFramerateCallback(callback);
+            break;
+        }
+    }
+}
 
-    // clear the strong pointer references
-    m_surfaceTextureClient.clear();
-    m_surfaceTexture.clear();
+MediaTexture::TextureWrapper* MediaTexture::createTexture()
+{
+    TextureWrapper* wrapper = new TextureWrapper();
+
+    // populate the wrapper
+    glGenTextures(1, &wrapper->textureId);
+    wrapper->surfaceTexture = new android::SurfaceTexture(wrapper->textureId);
+    wrapper->nativeWindow = new android::SurfaceTextureClient(wrapper->surfaceTexture);
+    wrapper->dimensions.setEmpty();
+
+    // setup callback
+    wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
+                                               wrapper->surfaceTexture,
+                                               wrapper->nativeWindow);
+    wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
+
+    return wrapper;
 }
 
-void VideoTexture::setDimensions(const SkRect& dimensions)
+void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
 {
-    android::Mutex::Autolock lock(m_videoLock);
-    m_dimensions = dimensions;
+    if (texture->surfaceTexture.get())
+        texture->surfaceTexture->setFrameAvailableListener(0);
+
+    if (force)
+        glDeleteTextures(1, &texture->textureId);
+    else
+        m_unusedTextures.append(texture->textureId);
+
+    // clear the strong pointer references
+    texture->mediaListener.clear();
+    texture->nativeWindow.clear();
+    texture->surfaceTexture.clear();
+
+    delete texture;
 }
 
 } // namespace WebCore