diff options
author | Teng-Hui Zhu <ztenghui@google.com> | 2011-05-31 15:24:42 -0700 |
---|---|---|
committer | Teng-Hui Zhu <ztenghui@google.com> | 2011-06-01 13:41:19 -0700 |
commit | 0bc8a6f1de48da432733e2bd21c5c793b7a0393d (patch) | |
tree | 418a64a17bcfcfd3934e0a15896b19f1a0605989 /Source | |
parent | a2f591386876bb23c1e69ad0bf7b0db72bf2865a (diff) | |
download | external_webkit-0bc8a6f1de48da432733e2bd21c5c793b7a0393d.zip external_webkit-0bc8a6f1de48da432733e2bd21c5c793b7a0393d.tar.gz external_webkit-0bc8a6f1de48da432733e2bd21c5c793b7a0393d.tar.bz2 |
Support the screen shot while the video is paused.
Basically, the GL textures generated for the surface texture will be used
for showing the screenshot.
Surface texture will be recycled every time a new video starts.
But GL textures will be recycled either when running out of memory bound,
or when the mediaplayer is deleted.
1. Add the VideoLayerManager as a static instance to manage the info of textures
allocated for the screenshot. The basic info will be added into the manager
while the video is played and prepared. During the draw time, the matirx info
will be updated, too.
2. When there are too many screenshots are created, then we will discard the
oldest one and just show the grey static rectangle to replace the screenshot.
The corresponding framework change is 112501.
Change-Id: Ifea60c96532500f9c93062cc97f7c4ef978046b0
Diffstat (limited to 'Source')
8 files changed, 375 insertions, 16 deletions
diff --git a/Source/WebCore/Android.mk b/Source/WebCore/Android.mk index 4529f83..c0e6d91 100644 --- a/Source/WebCore/Android.mk +++ b/Source/WebCore/Android.mk @@ -647,6 +647,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ platform/graphics/android/TilesManager.cpp \ platform/graphics/android/TiledPage.cpp \ platform/graphics/android/VideoLayerAndroid.cpp \ + platform/graphics/android/VideoLayerManager.cpp \ platform/graphics/android/android_graphics.cpp \ ifeq ($(ENABLE_SVG), true) diff --git a/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp index 8aa334f..cd19e55 100644 --- a/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/BaseLayerAndroid.cpp @@ -352,6 +352,9 @@ bool BaseLayerAndroid::drawGL(LayerAndroid* compositedRoot, // the unnecessary ones to make space... TilesManager::instance()->cleanupLayersTextures(compositedRoot); } + // Clean up GL textures for video layer. + TilesManager::instance()->videoLayerManager()->deleteUnusedTextures(); + // Finally do another pass to create new textures and schedule // repaints if needed compositedRoot->createGLTextures(); diff --git a/Source/WebCore/platform/graphics/android/TilesManager.h b/Source/WebCore/platform/graphics/android/TilesManager.h index 6d49cca..e43afed 100644 --- a/Source/WebCore/platform/graphics/android/TilesManager.h +++ b/Source/WebCore/platform/graphics/android/TilesManager.h @@ -35,6 +35,7 @@ #include "ShaderProgram.h" #include "TexturesGenerator.h" #include "TiledPage.h" +#include "VideoLayerManager.h" #include <utils/threads.h> namespace WebCore { @@ -76,6 +77,7 @@ public: } ShaderProgram* shader() { return &m_shader; } + VideoLayerManager* videoLayerManager() { return &m_videoLayerManager; } BackedDoubleBufferedTexture* getAvailableTexture(BaseTile* owner); @@ -153,6 +155,7 @@ private: static TilesManager* gInstance; ShaderProgram m_shader; + VideoLayerManager m_videoLayerManager; SkBitmap* m_tilesBitmap; }; diff --git a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp index 32e518d..aee5ae0 100644 --- a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.cpp @@ -75,9 +75,7 @@ void VideoLayerAndroid::init() { // m_surfaceTexture is only useful on UI thread, no need to copy. // And it will be set at setBaseLayer timeframe - m_playerState = INITIALIZED; - m_textureId = 0; } // We can use this function to set the Layer to point to surface texture. @@ -85,9 +83,8 @@ void VideoLayerAndroid::setSurfaceTexture(sp<SurfaceTexture> texture, int textureName, PlayerState playerState) { m_surfaceTexture = texture; - m_textureId = textureName; - m_playerState = playerState; + TilesManager::instance()->videoLayerManager()->registerTexture(uniqueId(), textureName); } GLuint VideoLayerAndroid::createSpinnerInnerTexture() @@ -199,18 +196,32 @@ bool VideoLayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix) // Show the real video. m_surfaceTexture->updateTexImage(); m_surfaceTexture->getTransformMatrix(surfaceMatrix); + GLuint textureId = + TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); TilesManager::instance()->shader()->drawVideoLayerQuad(drawTransform(), surfaceMatrix, - rect, m_textureId); + rect, textureId); + TilesManager::instance()->videoLayerManager()->updateMatrix(uniqueId(), + surfaceMatrix); } else { - // Show the poster - TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), rect, - m_backgroundTextureId, - 0.5, true); - - TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), innerRect, - m_posterTextureId, - 1, true); + GLuint textureId = + TilesManager::instance()->videoLayerManager()->getTextureId(uniqueId()); + GLfloat* matrix = + TilesManager::instance()->videoLayerManager()->getMatrix(uniqueId()); + if (textureId && matrix) { + // Show the screen shot for each video. + TilesManager::instance()->shader()->drawVideoLayerQuad(drawTransform(), + matrix, + rect, textureId); + } else { + // Show the static poster b/c there is no screen shot available. + TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), rect, + m_backgroundTextureId, + 0.5, true); + TilesManager::instance()->shader()->drawLayerQuad(drawTransform(), innerRect, + m_posterTextureId, + 1, true); + } } return drawChildrenGL(glWebViewState, matrix); diff --git a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h index eac565e..0f3f007 100644 --- a/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/VideoLayerAndroid.h @@ -68,14 +68,11 @@ private: void init(); // Surface texture for showing the video is actually allocated in Java side // and passed into this native code. - GLuint m_textureId; sp<android::SurfaceTexture> m_surfaceTexture; PlayerState m_playerState; // Texture for showing the static image will be created at native side. - // TODO: instead using a shared texture, we could make a texture pool to - // show different screen shots for different videos static bool m_createdTexture; static GLuint m_backgroundTextureId; static GLuint m_posterTextureId; diff --git a/Source/WebCore/platform/graphics/android/VideoLayerManager.cpp b/Source/WebCore/platform/graphics/android/VideoLayerManager.cpp new file mode 100644 index 0000000..cec4d67 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/VideoLayerManager.cpp @@ -0,0 +1,242 @@ +/* + * Copyright 2011 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. + */ + +#include "config.h" +#include "VideoLayerManager.h" + +#if USE(ACCELERATED_COMPOSITING) + +#ifdef DEBUG +#include <cutils/log.h> +#include <wtf/text/CString.h> + +#undef XLOG +#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "VideoLayerManager", __VA_ARGS__) + +#else + +#undef XLOG +#define XLOG(...) + +#endif // DEBUG + +// Define the max sum of all the video's sizes. +// Note that video_size = width * height. If there is no compression, then the +// maximum memory consumption could be 4 * video_size. +// Setting this to 2M, means that maximum memory consumption of all the +// screenshots would not be above 8M. +#define MAX_VIDEOSIZE_SUM 2097152 + +namespace WebCore { + +VideoLayerManager::VideoLayerManager() +{ + m_currentTimeStamp = 0; +} + +// Getting TextureId for GL draw call, in the UI thread. +GLuint VideoLayerManager::getTextureId(const int layerId) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + GLuint result = 0; + if (m_videoLayerInfoMap.contains(layerId)) + result = m_videoLayerInfoMap.get(layerId)->textureId; + return result; +} + +// Getting matrix for GL draw call, in the UI thread. +GLfloat* VideoLayerManager::getMatrix(const int layerId) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + GLfloat* result = 0; + if (m_videoLayerInfoMap.contains(layerId)) + result = m_videoLayerInfoMap.get(layerId)->surfaceMatrix; + return result; +} + +int VideoLayerManager::getTotalMemUsage() +{ + int sum = 0; + InfoIterator end = m_videoLayerInfoMap.end(); + for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) + sum += it->second->videoSize; + return sum; +} + +// When the video start, we know its texture info, so we register when we +// recieve the setSurfaceTexture call, this happens on UI thread. +void VideoLayerManager::registerTexture(const int layerId, const GLuint textureId) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + // If the texture has been registered, then early return. + if (m_videoLayerInfoMap.get(layerId)) { + GLuint oldTextureId = m_videoLayerInfoMap.get(layerId)->textureId; + if (oldTextureId != textureId) + removeLayerInternal(layerId); + else + return; + } + // The old info is deleted and now complete the new info and store it. + VideoLayerInfo* pInfo = new VideoLayerInfo(); + pInfo->textureId = textureId; + memset(pInfo->surfaceMatrix, 0, sizeof(pInfo->surfaceMatrix)); + pInfo->videoSize = 0; + m_currentTimeStamp++; + pInfo->timeStamp = m_currentTimeStamp; + + m_videoLayerInfoMap.add(layerId, pInfo); + XLOG("GL texture %d regisered for layerId %d", textureId, layerId); + + return; +} + +// Only when the video is prepared, we got the video size. So we should update +// the size for the video accordingly. +// This is called from webcore thread, from MediaPlayerPrivateAndroid. +void VideoLayerManager::updateVideoLayerSize(const int layerId, const int size ) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + if (m_videoLayerInfoMap.contains(layerId)) { + VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId); + if (pInfo) + pInfo->videoSize = size; + } + + // If the memory usage is out of bound, then just delete the oldest ones. + // Because we only recycle the texture before the current timestamp, the + // current video's texture will not be deleted. + while (getTotalMemUsage() > MAX_VIDEOSIZE_SUM) + if (!recycleTextureMem()) + break; + return; +} + +// This is called only from UI thread, at drawGL time. +void VideoLayerManager::updateMatrix(const int layerId, const GLfloat* matrix) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + if (m_videoLayerInfoMap.contains(layerId)) { + // If the existing video layer's matrix is matching the incoming one, + // then skip the update. + VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId); + ASSERT(matrix); + if (pInfo && !memcmp(matrix, pInfo->surfaceMatrix, sizeof(pInfo->surfaceMatrix))) + return; + memcpy(pInfo->surfaceMatrix, matrix, sizeof(pInfo->surfaceMatrix)); + } else { + XLOG("Error: should not reach here, the layerId %d should exist!", layerId); + ASSERT(false); + } + return; +} + +// This is called on the webcore thread, save the GL texture for recycle in +// the retired queue. They will be deleted in deleteUnusedTextures() in the UI +// thread. +// Return true when we found one texture to retire. +bool VideoLayerManager::recycleTextureMem() +{ + // Find the oldest texture int the m_videoLayerInfoMap, put it in m_retiredTextures + int oldestTimeStamp = m_currentTimeStamp; + int oldestLayerId = -1; + + InfoIterator end = m_videoLayerInfoMap.end(); +#ifdef DEBUG + XLOG("VideoLayerManager::recycleTextureMem m_videoLayerInfoMap contains"); + for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) + XLOG(" layerId %d, textureId %d, videoSize %d, timeStamp %d ", + it->first, it->second->textureId, it->second->videoSize, it->second->timeStamp); +#endif + for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) { + if (it->second->timeStamp < oldestTimeStamp) { + oldestTimeStamp = it->second->timeStamp; + oldestLayerId = it->first; + } + } + + bool foundTextureToRetire = (oldestLayerId != -1); + if (foundTextureToRetire) + removeLayerInternal(oldestLayerId); + + return foundTextureToRetire; +} + +// This is only called in the UI thread, b/c glDeleteTextures need to be called +// on the right context. +void VideoLayerManager::deleteUnusedTextures() +{ + m_retiredTexturesLock.lock(); + int size = m_retiredTextures.size(); + if (size > 0) { + GLuint* textureNames = new GLuint[size]; + int index = 0; + Vector<GLuint>::const_iterator end = m_retiredTextures.end(); + for (Vector<GLuint>::const_iterator it = m_retiredTextures.begin(); + it != end; ++it) { + GLuint textureName = *it; + if (textureName) { + textureNames[index] = textureName; + index++; + XLOG("GL texture %d will be deleted", textureName); + } + } + glDeleteTextures(size, textureNames); + delete textureNames; + m_retiredTextures.clear(); + } + m_retiredTexturesLock.unlock(); + return; +} + +// This can be called in the webcore thread in the media player's dtor. +void VideoLayerManager::removeLayer(const int layerId) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + removeLayerInternal(layerId); +} + +// This can be called on both UI and webcore thread. Since this is a private +// function, it is up to the public function to handle the lock for +// m_videoLayerInfoMap. +void VideoLayerManager::removeLayerInternal(const int layerId) +{ + // Delete the layerInfo corresponding to this layerId and remove from the map. + if (m_videoLayerInfoMap.contains(layerId)) { + GLuint textureId = m_videoLayerInfoMap.get(layerId)->textureId; + if (textureId) { + // Buffer up the retired textures in either UI or webcore thread, + // will be purged at deleteUnusedTextures in the UI thread. + m_retiredTexturesLock.lock(); + m_retiredTextures.append(textureId); + m_retiredTexturesLock.unlock(); + } + delete m_videoLayerInfoMap.get(layerId); + m_videoLayerInfoMap.remove(layerId); + } + return; +} + +} +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/VideoLayerManager.h b/Source/WebCore/platform/graphics/android/VideoLayerManager.h new file mode 100644 index 0000000..de2dafc --- /dev/null +++ b/Source/WebCore/platform/graphics/android/VideoLayerManager.h @@ -0,0 +1,98 @@ +/* + * Copyright 2011 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 VideoLayerManager_h +#define VideoLayerManager_h + +#include "GLUtils.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +#if USE(ACCELERATED_COMPOSITING) + +namespace WebCore { + +// Every video layer can use its uniqueId to query VideoLayerManager about such +// info globally. +struct VideoLayerInfo { + GLuint textureId; // GL texture bound with the surface texture. + int videoSize; // The size of the video. + int timeStamp; // Used to decide which VideoLayerInfo is the oldest one. + GLfloat surfaceMatrix[16]; +}; + + +class VideoLayerManager { + +public: + typedef HashMap<int, VideoLayerInfo*>::const_iterator InfoIterator; + + VideoLayerManager(); + + // Register the texture when we got setSurfaceTexture call. + void registerTexture(const int layerId, const GLuint textureId); + // Update the size when the video is prepared. + void updateVideoLayerSize(const int layerId, const int size); + // At draw time, update the matrix for every video frame update. + void updateMatrix(const int layerId, const GLfloat* matrix); + // Remove the layer info from the mapping. + void removeLayer(const int layerId); + + // Return the texture name corresponding to the layerId + GLuint getTextureId(const int layerId); + // Return the matrix for surface texture corresponding to the layerId + GLfloat* getMatrix(const int layerId); + + // Delete the GL textures + void deleteUnusedTextures(); + +private: + // Get the sum of all the video size stored in m_videoLayerInfoMap. + int getTotalMemUsage(); + // If the memory consumption is out of bound, recycle some textures. + bool recycleTextureMem(); + // The private function to remove layer. + void removeLayerInternal(const int layerId); + + // Indexed by each layer's uniqueId, this map contains the important info + // used for showing the video when playing or the screenshot when paused. + HashMap<int, VideoLayerInfo*> m_videoLayerInfoMap; + android::Mutex m_videoLayerInfoMapLock; + + // Everytime we add one item to the map, we update the timestamp. + int m_currentTimeStamp; + + // The retiredTextures is used to communicate between UI thread and webcore + // thread. Basically, GL textures are determined to retire in the webcore + // thread, and really get deleted in the UI thread. + Vector<GLuint> m_retiredTextures; + android::Mutex m_retiredTexturesLock; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + // +#endif // VideoLayerManager_h diff --git a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp index e6a2710..0a4acad 100644 --- a/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp +++ b/Source/WebKit/android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp @@ -35,6 +35,7 @@ #include "FrameView.h" #include "GraphicsContext.h" #include "SkiaUtils.h" +#include "TilesManager.h" #include "VideoLayerAndroid.h" #include "WebCoreJni.h" #include "WebViewCore.h" @@ -72,6 +73,7 @@ struct MediaPlayerPrivate::JavaGlue { MediaPlayerPrivate::~MediaPlayerPrivate() { + TilesManager::instance()->videoLayerManager()->removeLayer(m_videoLayer->uniqueId()); // m_videoLayer is reference counted, unref is enough here. m_videoLayer->unref(); if (m_glue->m_javaProxy) { @@ -290,6 +292,8 @@ public: m_naturalSizeUnknown = false; m_player->durationChanged(); m_player->sizeChanged(); + TilesManager::instance()->videoLayerManager()->updateVideoLayerSize( + m_player->platformLayer()->uniqueId(), width*height); } virtual bool hasAudio() const { return false; } // do not display the audio UI |