OSDN Git Service

Delay sending Plugin Draw event until the surface is ready.
[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_newWindowRequest = false;
68 }
69
70 MediaTexture::~MediaTexture()
71 {
72     deleteTexture(m_contentTexture);
73     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
74         deleteTexture(m_videoTextures[i], true);
75     }
76
77     if (m_weakWebViewRef) {
78         JNIEnv* env = JSC::Bindings::getJNIEnv();
79         env->DeleteWeakGlobalRef(m_weakWebViewRef);
80     }
81 }
82
83 void MediaTexture::initNativeWindowIfNeeded()
84 {
85     {
86         android::Mutex::Autolock lock(m_mediaLock);
87
88         // check to see if there are any unused textures to delete
89         if (m_unusedTextures.size() != 0) {
90             for (unsigned int i = 0; i < m_unusedTextures.size(); i++) {
91                 glDeleteTextures(1, &m_unusedTextures[i]);
92             }
93             m_unusedTextures.clear();
94         }
95
96         // create a content texture if none exists
97         if (!m_contentTexture) {
98             m_contentTexture = createTexture();
99
100             // send a message to the WebKit thread to notify the plugin that it can draw
101             if (m_weakWebViewRef) {
102                 JNIEnv* env = JSC::Bindings::getJNIEnv();
103                 jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
104                 if (localWebViewRef) {
105                     jclass wvClass = env->GetObjectClass(localWebViewRef);
106                     jmethodID sendPluginDrawMsg =
107                             env->GetMethodID(wvClass, "sendPluginDrawMsg", "()V");
108                     env->CallVoidMethod(localWebViewRef, sendPluginDrawMsg);
109                     env->DeleteLocalRef(wvClass);
110                     env->DeleteLocalRef(localWebViewRef);
111                 }
112                 checkException(env);
113             }
114         }
115
116         // finally create a video texture if needed
117         if (!m_newWindowRequest)
118             return;
119
120         // add the texture and add it to the list
121         TextureWrapper* videoTexture = createTexture();
122         m_videoTextures.append(videoTexture);
123
124         // setup the state variables to signal the other thread
125         m_newWindowRequest = false;
126         m_newWindow = videoTexture->nativeWindow;
127     }
128
129     // signal the WebKit thread in case it is waiting
130     m_newMediaRequestCond.signal();
131 }
132
133 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
134           const TransformationMatrix& videoMatrix,
135           const SkRect& mediaBounds)
136 {
137     android::Mutex::Autolock lock(m_mediaLock);
138
139     if (mediaBounds.isEmpty())
140         return;
141
142     // draw all the video textures first
143     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
144
145         TextureWrapper* video = m_videoTextures[i];
146
147         if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
148                 || !video->mediaListener->isFrameAvailable())
149             return;
150
151         video->surfaceTexture->updateTexImage();
152
153         float surfaceMatrix[16];
154         video->surfaceTexture->getTransformMatrix(surfaceMatrix);
155
156         SkRect dimensions = video->dimensions;
157         dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
158
159 #ifdef DEBUG
160         if (!mediaBounds.contains(dimensions)) {
161             XLOG("The video exceeds is parent's bounds.");
162         }
163 #endif // DEBUG
164
165         TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
166                 surfaceMatrix, dimensions, video->textureId);
167     }
168
169     if (!m_contentTexture->mediaListener->isFrameAvailable())
170         return;
171
172     m_contentTexture->surfaceTexture->updateTexImage();
173
174     sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
175
176     PixelFormat f = buf->getPixelFormat();
177     // only attempt to use alpha blending if alpha channel exists
178     bool forceAlphaBlending = !(
179         PIXEL_FORMAT_RGBX_8888 == f ||
180         PIXEL_FORMAT_RGB_888 == f ||
181         PIXEL_FORMAT_RGB_565 == f ||
182         PIXEL_FORMAT_RGB_332 == f);
183
184     TilesManager::instance()->shader()->drawLayerQuad(contentMatrix,
185                                                       mediaBounds,
186                                                       m_contentTexture->textureId,
187                                                       1.0f, forceAlphaBlending,
188                                                       GL_TEXTURE_EXTERNAL_OES);
189 }
190
191 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
192 {
193     android::Mutex::Autolock lock(m_mediaLock);
194
195     // the window was not ready before the timeout so return it this time
196     if (ANativeWindow* window = m_newWindow.get()) {
197         m_newWindow.clear();
198         return window;
199     }
200
201     // we only allow for so many textures, so return NULL if we exceed that limit
202     else if (m_videoTextures.size() >= MAX_WINDOW_COUNT) {
203         return 0;
204     }
205
206     m_newWindowRequest = true;
207
208     // post an inval message to the UI thread to fulfill the request
209     if (m_weakWebViewRef) {
210         JNIEnv* env = JSC::Bindings::getJNIEnv();
211         jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
212         if (localWebViewRef) {
213             jclass wvClass = env->GetObjectClass(localWebViewRef);
214             jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
215             env->CallVoidMethod(localWebViewRef, postInvalMethod);
216             env->DeleteLocalRef(wvClass);
217             env->DeleteLocalRef(localWebViewRef);
218         }
219         checkException(env);
220     }
221
222     //block until the request can be fulfilled or we time out
223     bool timedOut = false;
224     while (m_newWindowRequest && !timedOut) {
225         int ret = m_newMediaRequestCond.waitRelative(m_mediaLock, 500000000); // .5 sec
226         timedOut = ret == TIMED_OUT;
227     }
228
229     // if the window is ready then return it otherwise return NULL
230     if (ANativeWindow* window = m_newWindow.get()) {
231         m_newWindow.clear();
232         return window;
233     }
234     return 0;
235 }
236
237 ANativeWindow* MediaTexture::getNativeWindowForContent()
238 {
239     android::Mutex::Autolock lock(m_mediaLock);
240     if (m_contentTexture)
241         return m_contentTexture->nativeWindow.get();
242     else
243         return 0;
244 }
245
246 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
247 {
248     android::Mutex::Autolock lock(m_mediaLock);
249     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
250         if (m_videoTextures[i]->nativeWindow.get() == window) {
251             deleteTexture(m_videoTextures[i]);
252             m_videoTextures.remove(i);
253             break;
254         }
255     }
256 }
257
258 void MediaTexture::setDimensions(const ANativeWindow* window,
259                                  const SkRect& dimensions)
260 {
261     android::Mutex::Autolock lock(m_mediaLock);
262     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
263         if (m_videoTextures[i]->nativeWindow.get() == window) {
264             m_videoTextures[i]->dimensions = dimensions;
265             break;
266         }
267     }
268 }
269
270 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
271                                         FramerateCallbackProc callback)
272 {
273     XLOG("Release ANW %p (%p):(%p)", this, m_surfaceTexture.get(), m_surfaceTextureClient.get());
274     android::Mutex::Autolock lock(m_mediaLock);
275     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
276         if (m_videoTextures[i]->nativeWindow.get() == window) {
277             m_videoTextures[i]->mediaListener->setFramerateCallback(callback);
278             break;
279         }
280     }
281 }
282
283 MediaTexture::TextureWrapper* MediaTexture::createTexture()
284 {
285     TextureWrapper* wrapper = new TextureWrapper();
286
287     // populate the wrapper
288     glGenTextures(1, &wrapper->textureId);
289     wrapper->surfaceTexture = new android::SurfaceTexture(wrapper->textureId);
290     wrapper->nativeWindow = new android::SurfaceTextureClient(wrapper->surfaceTexture);
291     wrapper->dimensions.setEmpty();
292
293     // setup callback
294     wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
295                                                wrapper->surfaceTexture,
296                                                wrapper->nativeWindow);
297     wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
298
299     return wrapper;
300 }
301
302 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
303 {
304     if (texture->surfaceTexture.get())
305         texture->surfaceTexture->setFrameAvailableListener(0);
306
307     if (force)
308         glDeleteTextures(1, &texture->textureId);
309     else
310         m_unusedTextures.append(texture->textureId);
311
312     // clear the strong pointer references
313     texture->mediaListener.clear();
314     texture->nativeWindow.clear();
315     texture->surfaceTexture.clear();
316
317     delete texture;
318 }
319
320 } // namespace WebCore
321
322 #endif // USE(ACCELERATED_COMPOSITING)