2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "MediaTexture.h"
18 #include "TilesManager.h"
20 #include "MediaListener.h"
22 #if USE(ACCELERATED_COMPOSITING)
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"
36 #include <cutils/log.h>
37 #include <wtf/text/CString.h>
40 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "MediaTexture", __VA_ARGS__)
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
57 MediaTexture::MediaTexture(jobject webViewRef) : android::LightRefBase<MediaTexture>()
60 JNIEnv* env = JSC::Bindings::getJNIEnv();
61 m_weakWebViewRef = env->NewWeakGlobalRef(webViewRef);
67 m_newWindowRequest = false;
70 MediaTexture::~MediaTexture()
72 deleteTexture(m_contentTexture);
73 for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
74 deleteTexture(m_videoTextures[i], true);
77 if (m_weakWebViewRef) {
78 JNIEnv* env = JSC::Bindings::getJNIEnv();
79 env->DeleteWeakGlobalRef(m_weakWebViewRef);
83 void MediaTexture::initNativeWindowIfNeeded()
86 android::Mutex::Autolock lock(m_mediaLock);
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]);
93 m_unusedTextures.clear();
96 // create a content texture if none exists
97 if (!m_contentTexture) {
98 m_contentTexture = createTexture();
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);
116 // finally create a video texture if needed
117 if (!m_newWindowRequest)
120 // add the texture and add it to the list
121 TextureWrapper* videoTexture = createTexture();
122 m_videoTextures.append(videoTexture);
124 // setup the state variables to signal the other thread
125 m_newWindowRequest = false;
126 m_newWindow = videoTexture->nativeWindow;
129 // signal the WebKit thread in case it is waiting
130 m_newMediaRequestCond.signal();
133 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
134 const TransformationMatrix& videoMatrix,
135 const SkRect& mediaBounds)
137 android::Mutex::Autolock lock(m_mediaLock);
139 if (mediaBounds.isEmpty())
142 // draw all the video textures first
143 for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
145 TextureWrapper* video = m_videoTextures[i];
147 if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
148 || !video->mediaListener->isFrameAvailable())
151 video->surfaceTexture->updateTexImage();
153 float surfaceMatrix[16];
154 video->surfaceTexture->getTransformMatrix(surfaceMatrix);
156 SkRect dimensions = video->dimensions;
157 dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
160 if (!mediaBounds.contains(dimensions)) {
161 XLOG("The video exceeds is parent's bounds.");
165 TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
166 surfaceMatrix, dimensions, video->textureId);
169 if (!m_contentTexture->mediaListener->isFrameAvailable())
172 m_contentTexture->surfaceTexture->updateTexImage();
174 sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
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);
184 TilesManager::instance()->shader()->drawLayerQuad(contentMatrix,
186 m_contentTexture->textureId,
187 1.0f, forceAlphaBlending,
188 GL_TEXTURE_EXTERNAL_OES);
191 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
193 android::Mutex::Autolock lock(m_mediaLock);
195 // the window was not ready before the timeout so return it this time
196 if (ANativeWindow* window = m_newWindow.get()) {
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) {
206 m_newWindowRequest = true;
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);
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;
229 // if the window is ready then return it otherwise return NULL
230 if (ANativeWindow* window = m_newWindow.get()) {
237 ANativeWindow* MediaTexture::getNativeWindowForContent()
239 android::Mutex::Autolock lock(m_mediaLock);
240 if (m_contentTexture)
241 return m_contentTexture->nativeWindow.get();
246 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
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);
258 void MediaTexture::setDimensions(const ANativeWindow* window,
259 const SkRect& dimensions)
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;
270 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
271 FramerateCallbackProc callback)
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);
283 MediaTexture::TextureWrapper* MediaTexture::createTexture()
285 TextureWrapper* wrapper = new TextureWrapper();
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();
294 wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
295 wrapper->surfaceTexture,
296 wrapper->nativeWindow);
297 wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
302 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
304 if (texture->surfaceTexture.get())
305 texture->surfaceTexture->setFrameAvailableListener(0);
308 glDeleteTextures(1, &texture->textureId);
310 m_unusedTextures.append(texture->textureId);
312 // clear the strong pointer references
313 texture->mediaListener.clear();
314 texture->nativeWindow.clear();
315 texture->surfaceTexture.clear();
320 } // namespace WebCore
322 #endif // USE(ACCELERATED_COMPOSITING)