1/* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11package org.webrtc.videoengine; 12 13import java.util.concurrent.locks.ReentrantLock; 14 15import javax.microedition.khronos.egl.EGL10; 16import javax.microedition.khronos.egl.EGLConfig; 17import javax.microedition.khronos.egl.EGLContext; 18import javax.microedition.khronos.egl.EGLDisplay; 19import javax.microedition.khronos.opengles.GL10; 20 21import android.app.ActivityManager; 22import android.content.Context; 23import android.content.pm.ConfigurationInfo; 24import android.graphics.PixelFormat; 25import android.opengl.GLSurfaceView; 26import android.util.Log; 27 28public class ViEAndroidGLES20 extends GLSurfaceView 29 implements GLSurfaceView.Renderer { 30 private static String TAG = "WEBRTC-JR"; 31 private static final boolean DEBUG = false; 32 // True if onSurfaceCreated has been called. 33 private boolean surfaceCreated = false; 34 private boolean openGLCreated = false; 35 // True if NativeFunctionsRegistered has been called. 36 private boolean nativeFunctionsRegisted = false; 37 private ReentrantLock nativeFunctionLock = new ReentrantLock(); 38 // Address of Native object that will do the drawing. 39 private long nativeObject = 0; 40 private int viewWidth = 0; 41 private int viewHeight = 0; 42 43 public static boolean UseOpenGL2(Object renderWindow) { 44 return ViEAndroidGLES20.class.isInstance(renderWindow); 45 } 46 47 public ViEAndroidGLES20(Context context) { 48 super(context); 49 init(false, 0, 0); 50 } 51 52 public ViEAndroidGLES20(Context context, boolean translucent, 53 int depth, int stencil) { 54 super(context); 55 init(translucent, depth, stencil); 56 } 57 58 private void init(boolean translucent, int depth, int stencil) { 59 60 // By default, GLSurfaceView() creates a RGB_565 opaque surface. 61 // If we want a translucent one, we should change the surface's 62 // format here, using PixelFormat.TRANSLUCENT for GL Surfaces 63 // is interpreted as any 32-bit surface with alpha by SurfaceFlinger. 64 if (translucent) { 65 this.getHolder().setFormat(PixelFormat.TRANSLUCENT); 66 } 67 68 // Setup the context factory for 2.0 rendering. 69 // See ContextFactory class definition below 70 setEGLContextFactory(new ContextFactory()); 71 72 // We need to choose an EGLConfig that matches the format of 73 // our surface exactly. This is going to be done in our 74 // custom config chooser. See ConfigChooser class definition 75 // below. 76 setEGLConfigChooser( translucent ? 77 new ConfigChooser(8, 8, 8, 8, depth, stencil) : 78 new ConfigChooser(5, 6, 5, 0, depth, stencil) ); 79 80 // Set the renderer responsible for frame rendering 81 this.setRenderer(this); 82 this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 83 } 84 85 private static class ContextFactory implements GLSurfaceView.EGLContextFactory { 86 private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 87 public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { 88 Log.w(TAG, "creating OpenGL ES 2.0 context"); 89 checkEglError("Before eglCreateContext", egl); 90 int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 91 EGLContext context = egl.eglCreateContext(display, eglConfig, 92 EGL10.EGL_NO_CONTEXT, attrib_list); 93 checkEglError("After eglCreateContext", egl); 94 return context; 95 } 96 97 public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { 98 egl.eglDestroyContext(display, context); 99 } 100 } 101 102 private static void checkEglError(String prompt, EGL10 egl) { 103 int error; 104 while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { 105 Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); 106 } 107 } 108 109 private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { 110 111 public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { 112 mRedSize = r; 113 mGreenSize = g; 114 mBlueSize = b; 115 mAlphaSize = a; 116 mDepthSize = depth; 117 mStencilSize = stencil; 118 } 119 120 // This EGL config specification is used to specify 2.0 rendering. 121 // We use a minimum size of 4 bits for red/green/blue, but will 122 // perform actual matching in chooseConfig() below. 123 private static int EGL_OPENGL_ES2_BIT = 4; 124 private static int[] s_configAttribs2 = 125 { 126 EGL10.EGL_RED_SIZE, 4, 127 EGL10.EGL_GREEN_SIZE, 4, 128 EGL10.EGL_BLUE_SIZE, 4, 129 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 130 EGL10.EGL_NONE 131 }; 132 133 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 134 135 // Get the number of minimally matching EGL configurations 136 int[] num_config = new int[1]; 137 egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config); 138 139 int numConfigs = num_config[0]; 140 141 if (numConfigs <= 0) { 142 throw new IllegalArgumentException("No configs match configSpec"); 143 } 144 145 // Allocate then read the array of minimally matching EGL configs 146 EGLConfig[] configs = new EGLConfig[numConfigs]; 147 egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config); 148 149 if (DEBUG) { 150 printConfigs(egl, display, configs); 151 } 152 // Now return the "best" one 153 return chooseConfig(egl, display, configs); 154 } 155 156 public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 157 EGLConfig[] configs) { 158 for(EGLConfig config : configs) { 159 int d = findConfigAttrib(egl, display, config, 160 EGL10.EGL_DEPTH_SIZE, 0); 161 int s = findConfigAttrib(egl, display, config, 162 EGL10.EGL_STENCIL_SIZE, 0); 163 164 // We need at least mDepthSize and mStencilSize bits 165 if (d < mDepthSize || s < mStencilSize) 166 continue; 167 168 // We want an *exact* match for red/green/blue/alpha 169 int r = findConfigAttrib(egl, display, config, 170 EGL10.EGL_RED_SIZE, 0); 171 int g = findConfigAttrib(egl, display, config, 172 EGL10.EGL_GREEN_SIZE, 0); 173 int b = findConfigAttrib(egl, display, config, 174 EGL10.EGL_BLUE_SIZE, 0); 175 int a = findConfigAttrib(egl, display, config, 176 EGL10.EGL_ALPHA_SIZE, 0); 177 178 if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) 179 return config; 180 } 181 return null; 182 } 183 184 private int findConfigAttrib(EGL10 egl, EGLDisplay display, 185 EGLConfig config, int attribute, int defaultValue) { 186 187 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { 188 return mValue[0]; 189 } 190 return defaultValue; 191 } 192 193 private void printConfigs(EGL10 egl, EGLDisplay display, 194 EGLConfig[] configs) { 195 int numConfigs = configs.length; 196 Log.w(TAG, String.format("%d configurations", numConfigs)); 197 for (int i = 0; i < numConfigs; i++) { 198 Log.w(TAG, String.format("Configuration %d:\n", i)); 199 printConfig(egl, display, configs[i]); 200 } 201 } 202 203 private void printConfig(EGL10 egl, EGLDisplay display, 204 EGLConfig config) { 205 int[] attributes = { 206 EGL10.EGL_BUFFER_SIZE, 207 EGL10.EGL_ALPHA_SIZE, 208 EGL10.EGL_BLUE_SIZE, 209 EGL10.EGL_GREEN_SIZE, 210 EGL10.EGL_RED_SIZE, 211 EGL10.EGL_DEPTH_SIZE, 212 EGL10.EGL_STENCIL_SIZE, 213 EGL10.EGL_CONFIG_CAVEAT, 214 EGL10.EGL_CONFIG_ID, 215 EGL10.EGL_LEVEL, 216 EGL10.EGL_MAX_PBUFFER_HEIGHT, 217 EGL10.EGL_MAX_PBUFFER_PIXELS, 218 EGL10.EGL_MAX_PBUFFER_WIDTH, 219 EGL10.EGL_NATIVE_RENDERABLE, 220 EGL10.EGL_NATIVE_VISUAL_ID, 221 EGL10.EGL_NATIVE_VISUAL_TYPE, 222 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, 223 EGL10.EGL_SAMPLES, 224 EGL10.EGL_SAMPLE_BUFFERS, 225 EGL10.EGL_SURFACE_TYPE, 226 EGL10.EGL_TRANSPARENT_TYPE, 227 EGL10.EGL_TRANSPARENT_RED_VALUE, 228 EGL10.EGL_TRANSPARENT_GREEN_VALUE, 229 EGL10.EGL_TRANSPARENT_BLUE_VALUE, 230 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, 231 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, 232 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, 233 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, 234 EGL10.EGL_LUMINANCE_SIZE, 235 EGL10.EGL_ALPHA_MASK_SIZE, 236 EGL10.EGL_COLOR_BUFFER_TYPE, 237 EGL10.EGL_RENDERABLE_TYPE, 238 0x3042 // EGL10.EGL_CONFORMANT 239 }; 240 String[] names = { 241 "EGL_BUFFER_SIZE", 242 "EGL_ALPHA_SIZE", 243 "EGL_BLUE_SIZE", 244 "EGL_GREEN_SIZE", 245 "EGL_RED_SIZE", 246 "EGL_DEPTH_SIZE", 247 "EGL_STENCIL_SIZE", 248 "EGL_CONFIG_CAVEAT", 249 "EGL_CONFIG_ID", 250 "EGL_LEVEL", 251 "EGL_MAX_PBUFFER_HEIGHT", 252 "EGL_MAX_PBUFFER_PIXELS", 253 "EGL_MAX_PBUFFER_WIDTH", 254 "EGL_NATIVE_RENDERABLE", 255 "EGL_NATIVE_VISUAL_ID", 256 "EGL_NATIVE_VISUAL_TYPE", 257 "EGL_PRESERVED_RESOURCES", 258 "EGL_SAMPLES", 259 "EGL_SAMPLE_BUFFERS", 260 "EGL_SURFACE_TYPE", 261 "EGL_TRANSPARENT_TYPE", 262 "EGL_TRANSPARENT_RED_VALUE", 263 "EGL_TRANSPARENT_GREEN_VALUE", 264 "EGL_TRANSPARENT_BLUE_VALUE", 265 "EGL_BIND_TO_TEXTURE_RGB", 266 "EGL_BIND_TO_TEXTURE_RGBA", 267 "EGL_MIN_SWAP_INTERVAL", 268 "EGL_MAX_SWAP_INTERVAL", 269 "EGL_LUMINANCE_SIZE", 270 "EGL_ALPHA_MASK_SIZE", 271 "EGL_COLOR_BUFFER_TYPE", 272 "EGL_RENDERABLE_TYPE", 273 "EGL_CONFORMANT" 274 }; 275 int[] value = new int[1]; 276 for (int i = 0; i < attributes.length; i++) { 277 int attribute = attributes[i]; 278 String name = names[i]; 279 if (egl.eglGetConfigAttrib(display, config, attribute, value)) { 280 Log.w(TAG, String.format(" %s: %d\n", name, value[0])); 281 } else { 282 // Log.w(TAG, String.format(" %s: failed\n", name)); 283 while (egl.eglGetError() != EGL10.EGL_SUCCESS); 284 } 285 } 286 } 287 288 // Subclasses can adjust these values: 289 protected int mRedSize; 290 protected int mGreenSize; 291 protected int mBlueSize; 292 protected int mAlphaSize; 293 protected int mDepthSize; 294 protected int mStencilSize; 295 private int[] mValue = new int[1]; 296 } 297 298 // IsSupported 299 // Return true if this device support Open GL ES 2.0 rendering. 300 public static boolean IsSupported(Context context) { 301 ActivityManager am = 302 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 303 ConfigurationInfo info = am.getDeviceConfigurationInfo(); 304 if(info.reqGlEsVersion >= 0x20000) { 305 // Open GL ES 2.0 is supported. 306 return true; 307 } 308 return false; 309 } 310 311 public void onDrawFrame(GL10 gl) { 312 nativeFunctionLock.lock(); 313 if(!nativeFunctionsRegisted || !surfaceCreated) { 314 nativeFunctionLock.unlock(); 315 return; 316 } 317 318 if(!openGLCreated) { 319 if(0 != CreateOpenGLNative(nativeObject, viewWidth, viewHeight)) { 320 return; // Failed to create OpenGL 321 } 322 openGLCreated = true; // Created OpenGL successfully 323 } 324 DrawNative(nativeObject); // Draw the new frame 325 nativeFunctionLock.unlock(); 326 } 327 328 public void onSurfaceChanged(GL10 gl, int width, int height) { 329 surfaceCreated = true; 330 viewWidth = width; 331 viewHeight = height; 332 333 nativeFunctionLock.lock(); 334 if(nativeFunctionsRegisted) { 335 if(CreateOpenGLNative(nativeObject,width,height) == 0) 336 openGLCreated = true; 337 } 338 nativeFunctionLock.unlock(); 339 } 340 341 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 342 } 343 344 public void RegisterNativeObject(long nativeObject) { 345 nativeFunctionLock.lock(); 346 this.nativeObject = nativeObject; 347 nativeFunctionsRegisted = true; 348 nativeFunctionLock.unlock(); 349 } 350 351 public void DeRegisterNativeObject() { 352 nativeFunctionLock.lock(); 353 nativeFunctionsRegisted = false; 354 openGLCreated = false; 355 this.nativeObject = 0; 356 nativeFunctionLock.unlock(); 357 } 358 359 public void ReDraw() { 360 if(surfaceCreated) { 361 // Request the renderer to redraw using the render thread context. 362 this.requestRender(); 363 } 364 } 365 366 private native int CreateOpenGLNative(long nativeObject, 367 int width, int height); 368 private native void DrawNative(long nativeObject); 369 370} 371