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_isContentInverted = false;
68 m_newWindowRequest = false;
71 MediaTexture::~MediaTexture()
73 deleteTexture(m_contentTexture);
74 for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
75 deleteTexture(m_videoTextures[i], true);
78 if (m_weakWebViewRef) {
79 JNIEnv* env = JSC::Bindings::getJNIEnv();
80 env->DeleteWeakGlobalRef(m_weakWebViewRef);
84 bool MediaTexture::isContentInverted()
86 android::Mutex::Autolock lock(m_mediaLock);
87 return m_isContentInverted;
89 void MediaTexture::invertContents(bool invertContent)
91 android::Mutex::Autolock lock(m_mediaLock);
92 m_isContentInverted = invertContent;
95 void MediaTexture::initNativeWindowIfNeeded()
98 android::Mutex::Autolock lock(m_mediaLock);
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]);
105 m_unusedTextures.clear();
108 // create a content texture if none exists
109 if (!m_contentTexture) {
110 m_contentTexture = createTexture();
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);
128 // finally create a video texture if needed
129 if (!m_newWindowRequest)
132 // add the texture and add it to the list
133 TextureWrapper* videoTexture = createTexture();
134 m_videoTextures.append(videoTexture);
136 // setup the state variables to signal the other thread
137 m_newWindowRequest = false;
138 m_newWindow = videoTexture->nativeWindow;
141 // signal the WebKit thread in case it is waiting
142 m_newMediaRequestCond.signal();
145 void MediaTexture::draw(const TransformationMatrix& contentMatrix,
146 const TransformationMatrix& videoMatrix,
147 const SkRect& mediaBounds)
149 android::Mutex::Autolock lock(m_mediaLock);
151 if (mediaBounds.isEmpty())
154 // draw all the video textures first
155 for (unsigned int i = 0; i < m_videoTextures.size(); i++) {
157 TextureWrapper* video = m_videoTextures[i];
159 if (!video->surfaceTexture.get() || video->dimensions.isEmpty()
160 || !video->mediaListener->isFrameAvailable())
163 video->surfaceTexture->updateTexImage();
165 float surfaceMatrix[16];
166 video->surfaceTexture->getTransformMatrix(surfaceMatrix);
168 SkRect dimensions = video->dimensions;
169 dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop);
172 if (!mediaBounds.contains(dimensions)) {
173 XLOG("The video exceeds is parent's bounds.");
177 TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix,
178 surfaceMatrix, dimensions, video->textureId);
181 if (!m_contentTexture->mediaListener->isFrameAvailable())
184 m_contentTexture->surfaceTexture->updateTexImage();
186 sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer();
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);
196 TilesManager::instance()->shader()->drawLayerQuad(contentMatrix,
198 m_contentTexture->textureId,
199 1.0f, forceAlphaBlending,
200 GL_TEXTURE_EXTERNAL_OES);
203 ANativeWindow* MediaTexture::requestNativeWindowForVideo()
205 android::Mutex::Autolock lock(m_mediaLock);
207 // the window was not ready before the timeout so return it this time
208 if (ANativeWindow* window = m_newWindow.get()) {
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) {
218 m_newWindowRequest = true;
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);
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;
241 // if the window is ready then return it otherwise return NULL
242 if (ANativeWindow* window = m_newWindow.get()) {
249 ANativeWindow* MediaTexture::getNativeWindowForContent()
251 android::Mutex::Autolock lock(m_mediaLock);
252 if (m_contentTexture)
253 return m_contentTexture->nativeWindow.get();
258 void MediaTexture::releaseNativeWindow(const ANativeWindow* window)
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);
270 void MediaTexture::setDimensions(const ANativeWindow* window,
271 const SkRect& dimensions)
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;
282 void MediaTexture::setFramerateCallback(const ANativeWindow* window,
283 FramerateCallbackProc callback)
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);
295 MediaTexture::TextureWrapper* MediaTexture::createTexture()
297 TextureWrapper* wrapper = new TextureWrapper();
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();
306 wrapper->mediaListener = new MediaListener(m_weakWebViewRef,
307 wrapper->surfaceTexture,
308 wrapper->nativeWindow);
309 wrapper->surfaceTexture->setFrameAvailableListener(wrapper->mediaListener);
314 void MediaTexture::deleteTexture(TextureWrapper* texture, bool force)
316 if (texture->surfaceTexture.get())
317 texture->surfaceTexture->setFrameAvailableListener(0);
320 glDeleteTextures(1, &texture->textureId);
322 m_unusedTextures.append(texture->textureId);
324 // clear the strong pointer references
325 texture->mediaListener.clear();
326 texture->nativeWindow.clear();
327 texture->surfaceTexture.clear();
332 } // namespace WebCore
334 #endif // USE(ACCELERATED_COMPOSITING)