• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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