1 /* 2 * libjingle 3 * Copyright 2015 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 package org.webrtc; 29 30 import android.graphics.Canvas; 31 import android.graphics.SurfaceTexture; 32 import android.graphics.Rect; 33 import android.view.Surface; 34 import android.view.SurfaceHolder; 35 36 import javax.microedition.khronos.egl.EGL10; 37 import javax.microedition.khronos.egl.EGLConfig; 38 import javax.microedition.khronos.egl.EGLContext; 39 import javax.microedition.khronos.egl.EGLDisplay; 40 import javax.microedition.khronos.egl.EGLSurface; 41 42 /** 43 * Holds EGL state and utility methods for handling an egl 1.0 EGLContext, an EGLDisplay, 44 * and an EGLSurface. 45 */ 46 final class EglBase10 extends EglBase { 47 // This constant is taken from EGL14.EGL_CONTEXT_CLIENT_VERSION. 48 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 49 50 private final EGL10 egl; 51 private EGLContext eglContext; 52 private EGLConfig eglConfig; 53 private EGLDisplay eglDisplay; 54 private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; 55 56 // EGL wrapper for an actual EGLContext. 57 public static class Context extends EglBase.Context { 58 private final EGLContext eglContext; 59 Context(EGLContext eglContext)60 public Context(EGLContext eglContext) { 61 this.eglContext = eglContext; 62 } 63 } 64 65 // Create a new context with the specified config type, sharing data with sharedContext. EglBase10(Context sharedContext, int[] configAttributes)66 EglBase10(Context sharedContext, int[] configAttributes) { 67 this.egl = (EGL10) EGLContext.getEGL(); 68 eglDisplay = getEglDisplay(); 69 eglConfig = getEglConfig(eglDisplay, configAttributes); 70 eglContext = createEglContext(sharedContext, eglDisplay, eglConfig); 71 } 72 73 @Override createSurface(Surface surface)74 public void createSurface(Surface surface) { 75 /** 76 * We have to wrap Surface in a SurfaceHolder because for some reason eglCreateWindowSurface 77 * couldn't actually take a Surface object until API 17. Older versions fortunately just call 78 * SurfaceHolder.getSurface(), so we'll do that. No other methods are relevant. 79 */ 80 class FakeSurfaceHolder implements SurfaceHolder { 81 private final Surface surface; 82 83 FakeSurfaceHolder(Surface surface) { 84 this.surface = surface; 85 } 86 87 @Override 88 public void addCallback(Callback callback) {} 89 90 @Override 91 public void removeCallback(Callback callback) {} 92 93 @Override 94 public boolean isCreating() { 95 return false; 96 } 97 98 @Deprecated 99 @Override 100 public void setType(int i) {} 101 102 @Override 103 public void setFixedSize(int i, int i2) {} 104 105 @Override 106 public void setSizeFromLayout() {} 107 108 @Override 109 public void setFormat(int i) {} 110 111 @Override 112 public void setKeepScreenOn(boolean b) {} 113 114 @Override 115 public Canvas lockCanvas() { 116 return null; 117 } 118 119 @Override 120 public Canvas lockCanvas(Rect rect) { 121 return null; 122 } 123 124 @Override 125 public void unlockCanvasAndPost(Canvas canvas) {} 126 127 @Override 128 public Rect getSurfaceFrame() { 129 return null; 130 } 131 132 @Override 133 public Surface getSurface() { 134 return surface; 135 } 136 } 137 138 createSurfaceInternal(new FakeSurfaceHolder(surface)); 139 } 140 141 // Create EGLSurface from the Android SurfaceTexture. 142 @Override createSurface(SurfaceTexture surfaceTexture)143 public void createSurface(SurfaceTexture surfaceTexture) { 144 createSurfaceInternal(surfaceTexture); 145 } 146 147 // Create EGLSurface from either a SurfaceHolder or a SurfaceTexture. createSurfaceInternal(Object nativeWindow)148 private void createSurfaceInternal(Object nativeWindow) { 149 if (!(nativeWindow instanceof SurfaceHolder) && !(nativeWindow instanceof SurfaceTexture)) { 150 throw new IllegalStateException("Input must be either a SurfaceHolder or SurfaceTexture"); 151 } 152 checkIsNotReleased(); 153 if (eglSurface != EGL10.EGL_NO_SURFACE) { 154 throw new RuntimeException("Already has an EGLSurface"); 155 } 156 int[] surfaceAttribs = {EGL10.EGL_NONE}; 157 eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, surfaceAttribs); 158 if (eglSurface == EGL10.EGL_NO_SURFACE) { 159 throw new RuntimeException("Failed to create window surface"); 160 } 161 } 162 163 // Create dummy 1x1 pixel buffer surface so the context can be made current. 164 @Override createDummyPbufferSurface()165 public void createDummyPbufferSurface() { 166 createPbufferSurface(1, 1); 167 } 168 169 @Override createPbufferSurface(int width, int height)170 public void createPbufferSurface(int width, int height) { 171 checkIsNotReleased(); 172 if (eglSurface != EGL10.EGL_NO_SURFACE) { 173 throw new RuntimeException("Already has an EGLSurface"); 174 } 175 int[] surfaceAttribs = {EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE}; 176 eglSurface = egl.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs); 177 if (eglSurface == EGL10.EGL_NO_SURFACE) { 178 throw new RuntimeException("Failed to create pixel buffer surface"); 179 } 180 } 181 182 @Override getEglBaseContext()183 public org.webrtc.EglBase.Context getEglBaseContext() { 184 return new EglBase10.Context(eglContext); 185 } 186 187 @Override hasSurface()188 public boolean hasSurface() { 189 return eglSurface != EGL10.EGL_NO_SURFACE; 190 } 191 192 @Override surfaceWidth()193 public int surfaceWidth() { 194 final int widthArray[] = new int[1]; 195 egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, widthArray); 196 return widthArray[0]; 197 } 198 199 @Override surfaceHeight()200 public int surfaceHeight() { 201 final int heightArray[] = new int[1]; 202 egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, heightArray); 203 return heightArray[0]; 204 } 205 206 @Override releaseSurface()207 public void releaseSurface() { 208 if (eglSurface != EGL10.EGL_NO_SURFACE) { 209 egl.eglDestroySurface(eglDisplay, eglSurface); 210 eglSurface = EGL10.EGL_NO_SURFACE; 211 } 212 } 213 checkIsNotReleased()214 private void checkIsNotReleased() { 215 if (eglDisplay == EGL10.EGL_NO_DISPLAY || eglContext == EGL10.EGL_NO_CONTEXT 216 || eglConfig == null) { 217 throw new RuntimeException("This object has been released"); 218 } 219 } 220 221 @Override release()222 public void release() { 223 checkIsNotReleased(); 224 releaseSurface(); 225 detachCurrent(); 226 egl.eglDestroyContext(eglDisplay, eglContext); 227 egl.eglTerminate(eglDisplay); 228 eglContext = EGL10.EGL_NO_CONTEXT; 229 eglDisplay = EGL10.EGL_NO_DISPLAY; 230 eglConfig = null; 231 } 232 233 @Override makeCurrent()234 public void makeCurrent() { 235 checkIsNotReleased(); 236 if (eglSurface == EGL10.EGL_NO_SURFACE) { 237 throw new RuntimeException("No EGLSurface - can't make current"); 238 } 239 if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { 240 throw new RuntimeException("eglMakeCurrent failed"); 241 } 242 } 243 244 // Detach the current EGL context, so that it can be made current on another thread. 245 @Override detachCurrent()246 public void detachCurrent() { 247 if (!egl.eglMakeCurrent( 248 eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) { 249 throw new RuntimeException("eglMakeCurrent failed"); 250 } 251 } 252 253 @Override swapBuffers()254 public void swapBuffers() { 255 checkIsNotReleased(); 256 if (eglSurface == EGL10.EGL_NO_SURFACE) { 257 throw new RuntimeException("No EGLSurface - can't swap buffers"); 258 } 259 egl.eglSwapBuffers(eglDisplay, eglSurface); 260 } 261 262 // Return an EGLDisplay, or die trying. getEglDisplay()263 private EGLDisplay getEglDisplay() { 264 EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 265 if (eglDisplay == EGL10.EGL_NO_DISPLAY) { 266 throw new RuntimeException("Unable to get EGL10 display"); 267 } 268 int[] version = new int[2]; 269 if (!egl.eglInitialize(eglDisplay, version)) { 270 throw new RuntimeException("Unable to initialize EGL10"); 271 } 272 return eglDisplay; 273 } 274 275 // Return an EGLConfig, or die trying. getEglConfig(EGLDisplay eglDisplay, int[] configAttributes)276 private EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] configAttributes) { 277 EGLConfig[] configs = new EGLConfig[1]; 278 int[] numConfigs = new int[1]; 279 if (!egl.eglChooseConfig( 280 eglDisplay, configAttributes, configs, configs.length, numConfigs)) { 281 throw new RuntimeException("Unable to find any matching EGL config"); 282 } 283 return configs[0]; 284 } 285 286 // Return an EGLConfig, or die trying. createEglContext( Context sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig)287 private EGLContext createEglContext( 288 Context sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig) { 289 int[] contextAttributes = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; 290 EGLContext rootContext = 291 sharedContext == null ? EGL10.EGL_NO_CONTEXT : sharedContext.eglContext; 292 EGLContext eglContext = 293 egl.eglCreateContext(eglDisplay, eglConfig, rootContext, contextAttributes); 294 if (eglContext == EGL10.EGL_NO_CONTEXT) { 295 throw new RuntimeException("Failed to create EGL context"); 296 } 297 return eglContext; 298 } 299 } 300