• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #define LOG_TAG "MediaTexture"
18 #define LOG_NDEBUG 1
19 
20 #include "config.h"
21 #include "MediaTexture.h"
22 
23 #include "AndroidLog.h"
24 #include "DrawQuadData.h"
25 #include "TilesManager.h"
26 #include "GLUtils.h"
27 #include "MediaListener.h"
28 
29 #if USE(ACCELERATED_COMPOSITING)
30 
31 #include <android/native_window.h>
32 #include <gui/GLConsumer.h>
33 #include <gui/Surface.h>
34 #include <JNIUtility.h>
35 #include "WebCoreJni.h"
36 
37 // Limits the number of ANativeWindows that can be allocated for video playback.
38 // The limit is currently set to 2 as that is the current max number of
39 // simultaneous HW decodes that our OMX implementation allows.  This forces the
40 // media producer to use their own SW decoders for subsequent video streams.
41 #define MAX_WINDOW_COUNT 2
42 
43 namespace WebCore {
44 
MediaTexture(jobject webViewRef,jobject webViewCoreRef)45 MediaTexture::MediaTexture(jobject webViewRef, jobject webViewCoreRef) : android::LightRefBase<MediaTexture>()
46 {
47     JNIEnv* env = JSC::Bindings::getJNIEnv();
48     m_weakWebViewRef = env->NewWeakGlobalRef(webViewRef);
49     m_weakWebViewCoreRef = env->NewWeakGlobalRef(webViewCoreRef);
50 
51     m_contentTexture = 0;
52     m_isContentInverted = false;
53     m_newWindowRequest = false;
54 }
55 
~MediaTexture()56 MediaTexture::~MediaTexture()
57 {
58     if (m_contentTexture)
59         deleteTexture(m_contentTexture, true);
60     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
61         deleteTexture(m_videoTextures[i], true);
62     }
63 
64     JNIEnv* env = JSC::Bindings::getJNIEnv();
65     env->DeleteWeakGlobalRef(m_weakWebViewRef);
66     env->DeleteWeakGlobalRef(m_weakWebViewCoreRef);
67 }
68 
isContentInverted()69 bool MediaTexture::isContentInverted()
70 {
71     android::Mutex::Autolock lock(m_mediaLock);
72     return m_isContentInverted;
73 }
invertContents(bool invertContent)74 void MediaTexture::invertContents(bool invertContent)
75 {
76     android::Mutex::Autolock lock(m_mediaLock);
77     m_isContentInverted = invertContent;
78 }
79 
initNativeWindowIfNeeded()80 void MediaTexture::initNativeWindowIfNeeded()
81 {
82     {
83         android::Mutex::Autolock lock(m_mediaLock);
84 
85         // check to see if there are any unused textures to delete
86         if (m_unusedTextures.size() != 0) {
87             for (unsigned int i = 0; i < m_unusedTextures.size(); i++) {
88                 glDeleteTextures(1, &m_unusedTextures[i]);
89             }
90             m_unusedTextures.clear();
91         }
92 
93         // create a content texture if none exists
94         if (!m_contentTexture) {
95             m_contentTexture = createTexture();
96 
97             // send a message to the WebKit thread to notify the plugin that it can draw
98             if (m_weakWebViewCoreRef) {
99                 JNIEnv* env = JSC::Bindings::getJNIEnv();
100                 jobject localWebViewCoreRef = env->NewLocalRef(m_weakWebViewCoreRef);
101                 if (localWebViewCoreRef) {
102                     jclass wvClass = env->GetObjectClass(localWebViewCoreRef);
103                     jmethodID sendPluginDrawMsg =
104                             env->GetMethodID(wvClass, "sendPluginDrawMsg", "()V");
105                     env->CallVoidMethod(localWebViewCoreRef, sendPluginDrawMsg);
106                     env->DeleteLocalRef(wvClass);
107                     env->DeleteLocalRef(localWebViewCoreRef);
108                 }
109                 checkException(env);
110             }
111         }
112 
113         // finally create a video texture if needed
114         if (!m_newWindowRequest)
115             return;
116 
117         // add the texture and add it to the list
118         TextureWrapper* videoTexture = createTexture();
119         m_videoTextures.append(videoTexture);
120 
121         // setup the state variables to signal the other thread
122         m_newWindowRequest = false;
123         m_newWindow = videoTexture->nativeWindow;
124     }
125 
126     // signal the WebKit thread in case it is waiting
127     m_newMediaRequestCond.signal();
128 }
129 
draw(const TransformationMatrix & contentMatrix,const TransformationMatrix & videoMatrix,const SkRect & mediaBounds)130 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
131           const TransformationMatrix& videoMatrix,
132           const SkRect& mediaBounds)
133 {
134     android::Mutex::Autolock lock(m_mediaLock);
135 
136     if (mediaBounds.isEmpty())
137         return;
138 
139     // draw all the video textures first
140     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
141 
142         TextureWrapper* video = m_videoTextures[i];
143 
144         if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
145                 || !video->mediaListener->isFrameAvailable())
146             continue;
147 
148         video->surfaceTexture->updateTexImage();
149 
150         float surfaceMatrix[16];
151         video->surfaceTexture->getTransformMatrix(surfaceMatrix);
152 
153         SkRect dimensions = video->dimensions;
154         dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
155 
156 #ifdef DEBUG
157         if (!mediaBounds.contains(dimensions)) {
158             ALOGV("The video exceeds is parent's bounds.");
159         }
160 #endif // DEBUG
161 
162         TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
163                 surfaceMatrix, dimensions, video->textureId);
164     }
165 
166     if (!m_contentTexture->mediaListener->isFrameAvailable())
167         return;
168 
169     m_contentTexture->surfaceTexture->updateTexImage();
170 
171     sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
172 
173     PixelFormat f = buf->getPixelFormat();
174     // only attempt to use alpha blending if alpha channel exists
175     bool forceAlphaBlending = !(
176         PIXEL_FORMAT_RGBX_8888 == f ||
177         PIXEL_FORMAT_RGB_888 == f ||
178         PIXEL_FORMAT_RGB_565 == f);
179 
180     TextureQuadData data(m_contentTexture->textureId, GL_TEXTURE_EXTERNAL_OES,
181                          GL_LINEAR, LayerQuad, &contentMatrix, &mediaBounds,
182                          1.0f, forceAlphaBlending);
183     TilesManager::instance()->shader()->drawQuad(&data);
184 }
185 
requestNativeWindowForVideo()186 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
187 {
188     android::Mutex::Autolock lock(m_mediaLock);
189 
190     // the window was not ready before the timeout so return it this time
191     if (ANativeWindow* window = m_newWindow.get()) {
192         m_newWindow.clear();
193         return window;
194     }
195 
196     // we only allow for so many textures, so return NULL if we exceed that limit
197     else if (m_videoTextures.size() >= MAX_WINDOW_COUNT) {
198         return 0;
199     }
200 
201     m_newWindowRequest = true;
202 
203     // post an inval message to the UI thread to fulfill the request
204     if (m_weakWebViewRef) {
205         JNIEnv* env = JSC::Bindings::getJNIEnv();
206         jobject localWebViewRef = env->NewLocalRef(m_weakWebViewRef);
207         if (localWebViewRef) {
208             jclass wvClass = env->GetObjectClass(localWebViewRef);
209             jmethodID postInvalMethod = env->GetMethodID(wvClass, "postInvalidate", "()V");
210             env->CallVoidMethod(localWebViewRef, postInvalMethod);
211             env->DeleteLocalRef(wvClass);
212             env->DeleteLocalRef(localWebViewRef);
213         }
214         checkException(env);
215     }
216 
217     //block until the request can be fulfilled or we time out
218     bool timedOut = false;
219     while (m_newWindowRequest && !timedOut) {
220         int ret = m_newMediaRequestCond.waitRelative(m_mediaLock, 500000000); // .5 sec
221         timedOut = ret == TIMED_OUT;
222     }
223 
224     // if the window is ready then return it otherwise return NULL
225     if (ANativeWindow* window = m_newWindow.get()) {
226         m_newWindow.clear();
227         return window;
228     }
229     return 0;
230 }
231 
getNativeWindowForContent()232 ANativeWindow* MediaTexture::getNativeWindowForContent()
233 {
234     android::Mutex::Autolock lock(m_mediaLock);
235     if (m_contentTexture)
236         return m_contentTexture->nativeWindow.get();
237     else
238         return 0;
239 }
240 
releaseNativeWindow(const ANativeWindow * window)241 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
242 {
243     android::Mutex::Autolock lock(m_mediaLock);
244     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
245         if (m_videoTextures[i]->nativeWindow.get() == window) {
246             deleteTexture(m_videoTextures[i]);
247             m_videoTextures.remove(i);
248             break;
249         }
250     }
251 }
252 
setDimensions(const ANativeWindow * window,const SkRect & dimensions)253 void MediaTexture::setDimensions(const ANativeWindow* window,
254                                  const SkRect& dimensions)
255 {
256     android::Mutex::Autolock lock(m_mediaLock);
257     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
258         if (m_videoTextures[i]->nativeWindow.get() == window) {
259             m_videoTextures[i]->dimensions = dimensions;
260             break;
261         }
262     }
263 }
264 
setFramerateCallback(const ANativeWindow * window,FramerateCallbackProc callback)265 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
266                                         FramerateCallbackProc callback)
267 {
268     android::Mutex::Autolock lock(m_mediaLock);
269     for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
270         if (m_videoTextures[i]->nativeWindow.get() == window) {
271             m_videoTextures[i]->mediaListener->setFramerateCallback(callback);
272             break;
273         }
274     }
275 }
276 
createTexture()277 MediaTexture::TextureWrapper* MediaTexture::createTexture()
278 {
279     TextureWrapper* wrapper = new TextureWrapper();
280 
281     // populate the wrapper
282     glGenTextures(1, &wrapper->textureId);
283     wrapper->surfaceTexture = new android::GLConsumer(wrapper->textureId);
284     wrapper->nativeWindow = new android::Surface(
285             wrapper->surfaceTexture->getBufferQueue());
286     wrapper->dimensions.setEmpty();
287 
288     // setup callback
289     wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
290                                                wrapper->surfaceTexture,
291                                                wrapper->nativeWindow);
292     wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
293 
294     return wrapper;
295 }
296 
deleteTexture(TextureWrapper * texture,bool force)297 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
298 {
299     if (texture->surfaceTexture.get())
300         texture->surfaceTexture->setFrameAvailableListener(0);
301 
302     if (force)
303         glDeleteTextures(1, &texture->textureId);
304     else
305         m_unusedTextures.append(texture->textureId);
306 
307     // clear the strong pointer references
308     texture->mediaListener.clear();
309     texture->nativeWindow.clear();
310     texture->surfaceTexture.clear();
311 
312     delete texture;
313 }
314 
315 } // namespace WebCore
316 
317 #endif // USE(ACCELERATED_COMPOSITING)
318