OSDN Git Service

Merge "Refactor the image sharing code" into ics-mr0
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / MediaTexture.cpp
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 #include "MediaTexture.h"
18 #include "TilesManager.h"
19 #include "GLUtils.h"
20 #include "MediaListener.h"
21
22 #if USE(ACCELERATED_COMPOSITING)
23
24 #include <android/native_window.h>
25 #include <gui/SurfaceTexture.h>
26 #include <gui/SurfaceTextureClient.h>
27 #include <wtf/CurrentTime.h>
28 #include <JNIUtility.h>
29 #include "WebCoreJni.h"
30
31 #define LAYER_DEBUG
32 #undef LAYER_DEBUG
33
34 #ifdef DEBUG
35
36 #include <cutils/log.h>
37 #include <wtf/text/CString.h>
38
39 #undef XLOG
40 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "MediaTexture", __VA_ARGS__)
41
42 #else
43
44 #undef XLOG
45 #define XLOG(...)
46
47 #endif // DEBUG
48
49 // Limits the number of ANativeWindows that can be allocated for video playback.
50 // The limit is currently set to 2 as that is the current max number of
51 // simultaneous HW decodes that our OMX implementation allows.  This forces the
52 // media producer to use their own SW decoders for subsequent video streams.
53 #define MAX_WINDOW_COUNT 2
54
55 namespace WebCore {
56
57 MediaTexture::MediaTexture(jobject webViewRef) : android::LightRefBase<MediaTexture>()
58 {
59     if (webViewRef) {
60         JNIEnv* env = JSC::Bindings::getJNIEnv();
61         m_weakWebViewRef = env->NewWeakGlobalRef(webViewRef);
62     } else {
63         m_weakWebViewRef = 0;
64     }
65
66     m_contentTexture = 0;
67     m_isContentInverted = false;
68     m_newWindowRequest = false;
69 }
70
71 MediaTexture::~MediaTexture()
72 {
73     deleteTexture(m_contentTexture);
74     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
75         deleteTexture(m_videoTextures[i], true);
76     }
77
78     if (m_weakWebViewRef) {
79         JNIEnv* env = JSC::Bindings::getJNIEnv();
80         env->DeleteWeakGlobalRef(m_weakWebViewRef);
81     }
82 }
83
84 bool MediaTexture::isContentInverted()
85 {
86     android::Mutex::Autolock lock(m_mediaLock);
87     return m_isContentInverted;
88 }
89 void MediaTexture::invertContents(bool invertContent)
90 {
91     android::Mutex::Autolock lock(m_mediaLock);
92     m_isContentInverted = invertContent;
93 }
94
95 void MediaTexture::initNativeWindowIfNeeded()
96 {
97     {
98         android::Mutex::Autolock lock(m_mediaLock);
99
100         // check to see if there are any unused textures to delete
101         if (m_unusedTextures.size() != 0) {
102             for (unsigned int i = 0; i < m_unusedTextures.size(); i++) {
103                 glDeleteTextures(1, &m_unusedTextures[i]);
104             }
105             m_unusedTextures.clear();
106         }
107
108         // create a content texture if none exists
109         if (!m_contentTexture) {
110             m_contentTexture = createTexture();
111
112             // send a message to the WebKit thread to notify the plugin that it can draw
113             if (m_weakWebViewRef) {
114                 JNIEnv* env = JSC::Bindings::getJNIEnv();
115                 jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
116                 if (localWebViewRef) {
117                     jclass wvClass = env->GetObjectClass(localWebViewRef);
118                     jmethodID sendPluginDrawMsg =
119                             env->GetMethodID(wvClass, "sendPluginDrawMsg", "()V");
120                     env->CallVoidMethod(localWebViewRef, sendPluginDrawMsg);
121                     env->DeleteLocalRef(wvClass);
122                     env->DeleteLocalRef(localWebViewRef);
123                 }
124                 checkException(env);
125             }
126         }
127
128         // finally create a video texture if needed
129         if (!m_newWindowRequest)
130             return;
131
132         // add the texture and add it to the list
133         TextureWrapper* videoTexture = createTexture();
134         m_videoTextures.append(videoTexture);
135
136         // setup the state variables to signal the other thread
137         m_newWindowRequest = false;
138         m_newWindow = videoTexture->nativeWindow;
139     }
140
141     // signal the WebKit thread in case it is waiting
142     m_newMediaRequestCond.signal();
143 }
144
145 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
146           const TransformationMatrix& videoMatrix,
147           const SkRect& mediaBounds)
148 {
149     android::Mutex::Autolock lock(m_mediaLock);
150
151     if (mediaBounds.isEmpty())
152         return;
153
154     // draw all the video textures first
155     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
156
157         TextureWrapper* video = m_videoTextures[i];
158
159         if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
160                 || !video->mediaListener->isFrameAvailable())
161             return;
162
163         video->surfaceTexture->updateTexImage();
164
165         float surfaceMatrix[16];
166         video->surfaceTexture->getTransformMatrix(surfaceMatrix);
167
168         SkRect dimensions = video->dimensions;
169         dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
170
171 #ifdef DEBUG
172         if (!mediaBounds.contains(dimensions)) {
173             XLOG("The video exceeds is parent's bounds.");
174         }
175 #endif // DEBUG
176
177         TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
178                 surfaceMatrix, dimensions, video->textureId);
179     }
180
181     if (!m_contentTexture->mediaListener->isFrameAvailable())
182         return;
183
184     m_contentTexture->surfaceTexture->updateTexImage();
185
186     sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
187
188     PixelFormat f = buf->getPixelFormat();
189     // only attempt to use alpha blending if alpha channel exists
190     bool forceAlphaBlending = !(
191         PIXEL_FORMAT_RGBX_8888 == f ||
192         PIXEL_FORMAT_RGB_888 == f ||
193         PIXEL_FORMAT_RGB_565 == f ||
194         PIXEL_FORMAT_RGB_332 == f);
195
196     TilesManager::instance()->shader()->drawLayerQuad(contentMatrix,
197                                                       mediaBounds,
198                                                       m_contentTexture->textureId,
199                                                       1.0f, forceAlphaBlending,
200                                                       GL_TEXTURE_EXTERNAL_OES);
201 }
202
203 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
204 {
205     android::Mutex::Autolock lock(m_mediaLock);
206
207     // the window was not ready before the timeout so return it this time
208     if (ANativeWindow* window = m_newWindow.get()) {
209         m_newWindow.clear();
210         return window;
211     }
212
213     // we only allow for so many textures, so return NULL if we exceed that limit
214     else if (m_videoTextures.size() >= MAX_WINDOW_COUNT) {
215         return 0;
216     }
217
218     m_newWindowRequest = true;
219
220     // post an inval message to the UI thread to fulfill the request
221     if (m_weakWebViewRef) {
222         JNIEnv* env = JSC::Bindings::getJNIEnv();
223         jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
224         if (localWebViewRef) {
225             jclass wvClass = env->GetObjectClass(localWebViewRef);
226             jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
227             env->CallVoidMethod(localWebViewRef, postInvalMethod);
228             env->DeleteLocalRef(wvClass);
229             env->DeleteLocalRef(localWebViewRef);
230         }
231         checkException(env);
232     }
233
234     //block until the request can be fulfilled or we time out
235     bool timedOut = false;
236     while (m_newWindowRequest && !timedOut) {
237         int ret = m_newMediaRequestCond.waitRelative(m_mediaLock, 500000000); // .5 sec
238         timedOut = ret == TIMED_OUT;
239     }
240
241     // if the window is ready then return it otherwise return NULL
242     if (ANativeWindow* window = m_newWindow.get()) {
243         m_newWindow.clear();
244         return window;
245     }
246     return 0;
247 }
248
249 ANativeWindow* MediaTexture::getNativeWindowForContent()
250 {
251     android::Mutex::Autolock lock(m_mediaLock);
252     if (m_contentTexture)
253         return m_contentTexture->nativeWindow.get();
254     else
255         return 0;
256 }
257
258 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
259 {
260     android::Mutex::Autolock lock(m_mediaLock);
261     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
262         if (m_videoTextures[i]->nativeWindow.get() == window) {
263             deleteTexture(m_videoTextures[i]);
264             m_videoTextures.remove(i);
265             break;
266         }
267     }
268 }
269
270 void MediaTexture::setDimensions(const ANativeWindow* window,
271                                  const SkRect& dimensions)
272 {
273     android::Mutex::Autolock lock(m_mediaLock);
274     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
275         if (m_videoTextures[i]->nativeWindow.get() == window) {
276             m_videoTextures[i]->dimensions = dimensions;
277             break;
278         }
279     }
280 }
281
282 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
283                                         FramerateCallbackProc callback)
284 {
285     XLOG("Release ANW %p (%p):(%p)", this, m_surfaceTexture.get(), m_surfaceTextureClient.get());
286     android::Mutex::Autolock lock(m_mediaLock);
287     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
288         if (m_videoTextures[i]->nativeWindow.get() == window) {
289             m_videoTextures[i]->mediaListener->setFramerateCallback(callback);
290             break;
291         }
292     }
293 }
294
295 MediaTexture::TextureWrapper* MediaTexture::createTexture()
296 {
297     TextureWrapper* wrapper = new TextureWrapper();
298
299     // populate the wrapper
300     glGenTextures(1, &wrapper->textureId);
301     wrapper->surfaceTexture = new android::SurfaceTexture(wrapper->textureId);
302     wrapper->nativeWindow = new android::SurfaceTextureClient(wrapper->surfaceTexture);
303     wrapper->dimensions.setEmpty();
304
305     // setup callback
306     wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
307                                                wrapper->surfaceTexture,
308                                                wrapper->nativeWindow);
309     wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
310
311     return wrapper;
312 }
313
314 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
315 {
316     if (texture->surfaceTexture.get())
317         texture->surfaceTexture->setFrameAvailableListener(0);
318
319     if (force)
320         glDeleteTextures(1, &texture->textureId);
321     else
322         m_unusedTextures.append(texture->textureId);
323
324     // clear the strong pointer references
325     texture->mediaListener.clear();
326     texture->nativeWindow.clear();
327     texture->surfaceTexture.clear();
328
329     delete texture;
330 }
331
332 } // namespace WebCore
333
334 #endif // USE(ACCELERATED_COMPOSITING)