1 /*
2 * Copyright 2011 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 "VideoLayerManager.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #ifdef DEBUG
32 #include <cutils/log.h>
33 #include <wtf/text/CString.h>
34
35 #undef XLOG
36 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "VideoLayerManager", __VA_ARGS__)
37
38 #else
39
40 #undef XLOG
41 #define XLOG(...)
42
43 #endif // DEBUG
44
45 // Define the max sum of all the video's sizes.
46 // Note that video_size = width * height. If there is no compression, then the
47 // maximum memory consumption could be 4 * video_size.
48 // Setting this to 2M, means that maximum memory consumption of all the
49 // screenshots would not be above 8M.
50 #define MAX_VIDEOSIZE_SUM 2097152
51
52 namespace WebCore {
53
VideoLayerManager()54 VideoLayerManager::VideoLayerManager()
55 {
56 m_currentTimeStamp = 0;
57 }
58
59 // Getting TextureId for GL draw call, in the UI thread.
getTextureId(const int layerId)60 GLuint VideoLayerManager::getTextureId(const int layerId)
61 {
62 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
63 GLuint result = 0;
64 if (m_videoLayerInfoMap.contains(layerId))
65 result = m_videoLayerInfoMap.get(layerId)->textureId;
66 return result;
67 }
68
69 // Getting matrix for GL draw call, in the UI thread.
getMatrix(const int layerId)70 GLfloat* VideoLayerManager::getMatrix(const int layerId)
71 {
72 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
73 GLfloat* result = 0;
74 if (m_videoLayerInfoMap.contains(layerId))
75 result = m_videoLayerInfoMap.get(layerId)->surfaceMatrix;
76 return result;
77 }
78
getTotalMemUsage()79 int VideoLayerManager::getTotalMemUsage()
80 {
81 int sum = 0;
82 InfoIterator end = m_videoLayerInfoMap.end();
83 for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it)
84 sum += it->second->videoSize;
85 return sum;
86 }
87
88 // When the video start, we know its texture info, so we register when we
89 // recieve the setSurfaceTexture call, this happens on UI thread.
registerTexture(const int layerId,const GLuint textureId)90 void VideoLayerManager::registerTexture(const int layerId, const GLuint textureId)
91 {
92 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
93 // If the texture has been registered, then early return.
94 if (m_videoLayerInfoMap.get(layerId)) {
95 GLuint oldTextureId = m_videoLayerInfoMap.get(layerId)->textureId;
96 if (oldTextureId != textureId)
97 removeLayerInternal(layerId);
98 else
99 return;
100 }
101 // The old info is deleted and now complete the new info and store it.
102 VideoLayerInfo* pInfo = new VideoLayerInfo();
103 pInfo->textureId = textureId;
104 memset(pInfo->surfaceMatrix, 0, sizeof(pInfo->surfaceMatrix));
105 pInfo->videoSize = 0;
106 m_currentTimeStamp++;
107 pInfo->timeStamp = m_currentTimeStamp;
108
109 m_videoLayerInfoMap.add(layerId, pInfo);
110 XLOG("GL texture %d regisered for layerId %d", textureId, layerId);
111
112 return;
113 }
114
115 // Only when the video is prepared, we got the video size. So we should update
116 // the size for the video accordingly.
117 // This is called from webcore thread, from MediaPlayerPrivateAndroid.
updateVideoLayerSize(const int layerId,const int size)118 void VideoLayerManager::updateVideoLayerSize(const int layerId, const int size )
119 {
120 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
121 if (m_videoLayerInfoMap.contains(layerId)) {
122 VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId);
123 if (pInfo)
124 pInfo->videoSize = size;
125 }
126
127 // If the memory usage is out of bound, then just delete the oldest ones.
128 // Because we only recycle the texture before the current timestamp, the
129 // current video's texture will not be deleted.
130 while (getTotalMemUsage() > MAX_VIDEOSIZE_SUM)
131 if (!recycleTextureMem())
132 break;
133 return;
134 }
135
136 // This is called only from UI thread, at drawGL time.
updateMatrix(const int layerId,const GLfloat * matrix)137 void VideoLayerManager::updateMatrix(const int layerId, const GLfloat* matrix)
138 {
139 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
140 if (m_videoLayerInfoMap.contains(layerId)) {
141 // If the existing video layer's matrix is matching the incoming one,
142 // then skip the update.
143 VideoLayerInfo* pInfo = m_videoLayerInfoMap.get(layerId);
144 ASSERT(matrix);
145 if (pInfo && !memcmp(matrix, pInfo->surfaceMatrix, sizeof(pInfo->surfaceMatrix)))
146 return;
147 memcpy(pInfo->surfaceMatrix, matrix, sizeof(pInfo->surfaceMatrix));
148 } else {
149 XLOG("Error: should not reach here, the layerId %d should exist!", layerId);
150 ASSERT(false);
151 }
152 return;
153 }
154
155 // This is called on the webcore thread, save the GL texture for recycle in
156 // the retired queue. They will be deleted in deleteUnusedTextures() in the UI
157 // thread.
158 // Return true when we found one texture to retire.
recycleTextureMem()159 bool VideoLayerManager::recycleTextureMem()
160 {
161 // Find the oldest texture int the m_videoLayerInfoMap, put it in m_retiredTextures
162 int oldestTimeStamp = m_currentTimeStamp;
163 int oldestLayerId = -1;
164
165 InfoIterator end = m_videoLayerInfoMap.end();
166 #ifdef DEBUG
167 XLOG("VideoLayerManager::recycleTextureMem m_videoLayerInfoMap contains");
168 for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it)
169 XLOG(" layerId %d, textureId %d, videoSize %d, timeStamp %d ",
170 it->first, it->second->textureId, it->second->videoSize, it->second->timeStamp);
171 #endif
172 for (InfoIterator it = m_videoLayerInfoMap.begin(); it != end; ++it) {
173 if (it->second->timeStamp < oldestTimeStamp) {
174 oldestTimeStamp = it->second->timeStamp;
175 oldestLayerId = it->first;
176 }
177 }
178
179 bool foundTextureToRetire = (oldestLayerId != -1);
180 if (foundTextureToRetire)
181 removeLayerInternal(oldestLayerId);
182
183 return foundTextureToRetire;
184 }
185
186 // This is only called in the UI thread, b/c glDeleteTextures need to be called
187 // on the right context.
deleteUnusedTextures()188 void VideoLayerManager::deleteUnusedTextures()
189 {
190 m_retiredTexturesLock.lock();
191 int size = m_retiredTextures.size();
192 if (size > 0) {
193 GLuint* textureNames = new GLuint[size];
194 int index = 0;
195 Vector<GLuint>::const_iterator end = m_retiredTextures.end();
196 for (Vector<GLuint>::const_iterator it = m_retiredTextures.begin();
197 it != end; ++it) {
198 GLuint textureName = *it;
199 if (textureName) {
200 textureNames[index] = textureName;
201 index++;
202 XLOG("GL texture %d will be deleted", textureName);
203 }
204 }
205 glDeleteTextures(size, textureNames);
206 delete textureNames;
207 m_retiredTextures.clear();
208 }
209 m_retiredTexturesLock.unlock();
210 return;
211 }
212
213 // This can be called in the webcore thread in the media player's dtor.
removeLayer(const int layerId)214 void VideoLayerManager::removeLayer(const int layerId)
215 {
216 android::Mutex::Autolock lock(m_videoLayerInfoMapLock);
217 removeLayerInternal(layerId);
218 }
219
220 // This can be called on both UI and webcore thread. Since this is a private
221 // function, it is up to the public function to handle the lock for
222 // m_videoLayerInfoMap.
removeLayerInternal(const int layerId)223 void VideoLayerManager::removeLayerInternal(const int layerId)
224 {
225 // Delete the layerInfo corresponding to this layerId and remove from the map.
226 if (m_videoLayerInfoMap.contains(layerId)) {
227 GLuint textureId = m_videoLayerInfoMap.get(layerId)->textureId;
228 if (textureId) {
229 // Buffer up the retired textures in either UI or webcore thread,
230 // will be purged at deleteUnusedTextures in the UI thread.
231 m_retiredTexturesLock.lock();
232 m_retiredTextures.append(textureId);
233 m_retiredTexturesLock.unlock();
234 }
235 delete m_videoLayerInfoMap.get(layerId);
236 m_videoLayerInfoMap.remove(layerId);
237 }
238 return;
239 }
240
241 }
242 #endif // USE(ACCELERATED_COMPOSITING)
243