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/SurfaceTexture.h>
33 #include <gui/SurfaceTextureClient.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::SurfaceTexture(wrapper->textureId);
284 wrapper->nativeWindow = new android::SurfaceTextureClient(wrapper->surfaceTexture);
285 wrapper->dimensions.setEmpty();
286
287 // setup callback
288 wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
289 wrapper->surfaceTexture,
290 wrapper->nativeWindow);
291 wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
292
293 return wrapper;
294 }
295
deleteTexture(TextureWrapper * texture,bool force)296 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
297 {
298 if (texture->surfaceTexture.get())
299 texture->surfaceTexture->setFrameAvailableListener(0);
300
301 if (force)
302 glDeleteTextures(1, &texture->textureId);
303 else
304 m_unusedTextures.append(texture->textureId);
305
306 // clear the strong pointer references
307 texture->mediaListener.clear();
308 texture->nativeWindow.clear();
309 texture->surfaceTexture.clear();
310
311 delete texture;
312 }
313
314 } // namespace WebCore
315
316 #endif // USE(ACCELERATED_COMPOSITING)
317