diff options
author | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 11:35:50 -0700 |
---|---|---|
committer | Nicolas Roard <nicolasroard@google.com> | 2012-04-06 14:03:59 -0700 |
commit | 2e510fd5b5a30f1315c272d44ae3aa4cba355498 (patch) | |
tree | db3af5f32855d329856f190c3509ae11ae519851 /Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp | |
parent | c88c88907b618e468ec3928b06a3a31d4f99b9c6 (diff) | |
download | external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.zip external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.gz external_webkit-2e510fd5b5a30f1315c272d44ae3aa4cba355498.tar.bz2 |
Reorganize platform/graphics/android
Change-Id: Idc67155cfa99784dcd931e705336bfa063ecae46
Diffstat (limited to 'Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp new file mode 100644 index 0000000..6501f98 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/layers/VideoLayerManager.cpp @@ -0,0 +1,385 @@ +/* + * 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. + */ + +#define LOG_TAG "VideoLayerManager" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "VideoLayerManager.h" + +#include "AndroidLog.h" +#include "RenderSkinMediaButton.h" +#include "SkCanvas.h" +#include <wtf/CurrentTime.h> + +#if USE(ACCELERATED_COMPOSITING) + +// The animation of the play/pause icon will last for PLAY_PAUSE_ICON_SHOW_TIME +// seconds. +#define PLAY_PAUSE_ICON_SHOW_TIME 1 + +// 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 + +// We don't preload the video data, so we don't have the exact size yet. +// Assuming 16:9 by default, this will be corrected after video prepared. +#define DEFAULT_VIDEO_ASPECT_RATIO 1.78 + +#define VIDEO_TEXTURE_NUMBER 5 +#define VIDEO_BUTTON_SIZE 64 + +namespace WebCore { + +VideoLayerManager::VideoLayerManager() + : m_currentTimeStamp(0) + , m_createdTexture(false) + , m_posterTextureId(0) + , m_spinnerOuterTextureId(0) + , m_spinnerInnerTextureId(0) + , m_playTextureId(0) + , m_pauseTextureId(0) + , m_buttonRect(0, 0, VIDEO_BUTTON_SIZE, VIDEO_BUTTON_SIZE) +{ +} + +int VideoLayerManager::getButtonSize() +{ + return VIDEO_BUTTON_SIZE; +} + +GLuint VideoLayerManager::createTextureFromImage(int buttonType) +{ + SkRect rect = SkRect(m_buttonRect); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + canvas.drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); + RenderSkinMediaButton::Draw(&canvas, m_buttonRect, buttonType, true, 0, + false); + + GLuint texture; + glGenTextures(1, &texture); + + GLUtils::createTextureWithBitmap(texture, bitmap); + bitmap.reset(); + return texture; +} + +// Should be called at the VideoLayerAndroid::drawGL to make sure we allocate +// the GL resources lazily. +void VideoLayerManager::initGLResourcesIfNeeded() +{ + if (!m_createdTexture) { + ALOGD("Reinit GLResource for VideoLayer"); + initGLResources(); + } +} + +void VideoLayerManager::initGLResources() +{ + GLUtils::checkGlError("before initGLResources()"); + if (!m_createdTexture) { + m_spinnerOuterTextureId = + createTextureFromImage(RenderSkinMediaButton::SPINNER_OUTER); + m_spinnerInnerTextureId = + createTextureFromImage(RenderSkinMediaButton::SPINNER_INNER); + m_posterTextureId = + createTextureFromImage(RenderSkinMediaButton::VIDEO); + m_playTextureId = createTextureFromImage(RenderSkinMediaButton::PLAY); + m_pauseTextureId = createTextureFromImage(RenderSkinMediaButton::PAUSE); + } + m_createdTexture = !GLUtils::checkGlError("initGLResources()"); + return; +} + +void VideoLayerManager::cleanupGLResources() +{ + if (m_createdTexture) { + GLuint videoTextures[VIDEO_TEXTURE_NUMBER] = { m_spinnerOuterTextureId, + m_spinnerInnerTextureId, m_posterTextureId, m_playTextureId, + m_pauseTextureId }; + + glDeleteTextures(VIDEO_TEXTURE_NUMBER, videoTextures); + m_createdTexture = false; + } + // Delete the texture in retired mode, but have not hit draw call to be + // removed. + deleteUnusedTextures(); + + // Go over the registered GL textures (screen shot textures) and delete them. + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + InfoIterator end = m_videoLayerInfoMap.end(); + for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) { + // The map include every video has been played, so their textureId can + // be deleted already, like hitting onTrimMemory multiple times. + if (it->second->textureId) { + ALOGV("delete texture from the map %d", it->second->textureId); + glDeleteTextures(1, &it->second->textureId); + // Set the textureID to 0 to show the video icon. + it->second->textureId = 0; + } + } + + GLUtils::checkGlError("cleanupGLResources()"); + return; +} + +// 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 the aspect ratio for GL draw call, in the UI thread. +float VideoLayerManager::getAspectRatio(const int layerId) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + float result = 0; + if (m_videoLayerInfoMap.contains(layerId)) + result = m_videoLayerInfoMap.get(layerId)->aspectRatio; + 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; + pInfo->aspectRatio = DEFAULT_VIDEO_ASPECT_RATIO; + m_currentTimeStamp++; + pInfo->timeStamp = m_currentTimeStamp; + pInfo->lastIconShownTime = 0; + pInfo->iconState = Registered; + + m_videoLayerInfoMap.add(layerId, pInfo); + ALOGV("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, + const float ratio) +{ + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + if (m_videoLayerInfoMap.contains(layerId)) { + VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId); + if (pInfo) { + pInfo->videoSize = size; + pInfo->aspectRatio = ratio; + } + } + + // 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 { + ALOGV("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 + ALOGV("VideoLayerManager::recycleTextureMem m_videoLayerInfoMap contains"); + for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) + ALOGV(" 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++; + ALOGV("GL texture %d will be deleted", textureName); + } + } + glDeleteTextures(size, textureNames); + delete textureNames; + m_retiredTextures.clear(); + } + m_retiredTexturesLock.unlock(); + GLUtils::checkGlError("deleteUnusedTextures"); + 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; +} + +double VideoLayerManager::drawIcon(const int layerId, IconType type) +{ + // When ratio 0 is returned, the Icon should not be drawn. + double ratio = 0; + + android::Mutex::Autolock lock(m_videoLayerInfoMapLock); + if (m_videoLayerInfoMap.contains(layerId)) { + VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId); + // If this is state switching moment, reset the time and state + if ((type == PlayIcon && pInfo->iconState != PlayIconShown) + || (type == PauseIcon && pInfo->iconState != PauseIconShown)) { + pInfo->lastIconShownTime = WTF::currentTime(); + pInfo->iconState = (type == PlayIcon) ? PlayIconShown : PauseIconShown; + } + + // After switching the state, we calculate the ratio depending on the + // time interval. + if ((type == PlayIcon && pInfo->iconState == PlayIconShown) + || (type == PauseIcon && pInfo->iconState == PauseIconShown)) { + double delta = WTF::currentTime() - pInfo->lastIconShownTime; + ratio = 1.0 - (delta / PLAY_PAUSE_ICON_SHOW_TIME); + } + } + + if (ratio > 1 || ratio < 0) + ratio = 0; + return ratio; +} + +} +#endif // USE(ACCELERATED_COMPOSITING) |