diff options
Diffstat (limited to 'talk/app/webrtc/java/android/org/webrtc/EglBase.java')
-rw-r--r-- | talk/app/webrtc/java/android/org/webrtc/EglBase.java | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/talk/app/webrtc/java/android/org/webrtc/EglBase.java b/talk/app/webrtc/java/android/org/webrtc/EglBase.java new file mode 100644 index 0000000000..2ee36882e8 --- /dev/null +++ b/talk/app/webrtc/java/android/org/webrtc/EglBase.java @@ -0,0 +1,271 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.webrtc; + +import android.graphics.SurfaceTexture; +import android.view.SurfaceHolder; + +import org.webrtc.Logging; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + +/** + * Holds EGL state and utility methods for handling an EGLContext, an EGLDisplay, and an EGLSurface. + */ +public final class EglBase { + private static final String TAG = "EglBase"; + // These constants are taken from EGL14.EGL_OPENGL_ES2_BIT and EGL14.EGL_CONTEXT_CLIENT_VERSION. + // https://android.googlesource.com/platform/frameworks/base/+/master/opengl/java/android/opengl/EGL14.java + // This is similar to how GlSurfaceView does: + // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/opengl/GLSurfaceView.java#760 + private static final int EGL_OPENGL_ES2_BIT = 4; + private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + // Android-specific extension. + private static final int EGL_RECORDABLE_ANDROID = 0x3142; + + private final EGL10 egl; + private EGLContext eglContext; + private ConfigType configType; + private EGLConfig eglConfig; + private EGLDisplay eglDisplay; + private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; + + // EGLConfig constructor type. Influences eglChooseConfig arguments. + public static enum ConfigType { + // No special parameters. + PLAIN, + // Configures with EGL_SURFACE_TYPE = EGL_PBUFFER_BIT. + PIXEL_BUFFER, + // Configures with EGL_RECORDABLE_ANDROID = 1. + // Discourages EGL from using pixel formats that cannot efficiently be + // converted to something usable by the video encoder. + RECORDABLE + } + + // Create root context without any EGLSurface or parent EGLContext. This can be used for branching + // new contexts that share data. + public EglBase() { + this(EGL10.EGL_NO_CONTEXT, ConfigType.PLAIN); + } + + // Create a new context with the specified config type, sharing data with sharedContext. + public EglBase(EGLContext sharedContext, ConfigType configType) { + this.egl = (EGL10) EGLContext.getEGL(); + this.configType = configType; + eglDisplay = getEglDisplay(); + eglConfig = getEglConfig(eglDisplay, configType); + eglContext = createEglContext(sharedContext, eglDisplay, eglConfig); + } + + // Create EGLSurface from the Android SurfaceHolder. + public void createSurface(SurfaceHolder surfaceHolder) { + createSurfaceInternal(surfaceHolder); + } + + // Create EGLSurface from the Android SurfaceTexture. + public void createSurface(SurfaceTexture surfaceTexture) { + createSurfaceInternal(surfaceTexture); + } + + // Create EGLSurface from either a SurfaceHolder or a SurfaceTexture. + private void createSurfaceInternal(Object nativeWindow) { + if (!(nativeWindow instanceof SurfaceHolder) && !(nativeWindow instanceof SurfaceTexture)) { + throw new IllegalStateException("Input must be either a SurfaceHolder or SurfaceTexture"); + } + checkIsNotReleased(); + if (configType == ConfigType.PIXEL_BUFFER) { + Logging.w(TAG, "This EGL context is configured for PIXEL_BUFFER, but uses regular Surface"); + } + if (eglSurface != EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("Already has an EGLSurface"); + } + int[] surfaceAttribs = {EGL10.EGL_NONE}; + eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, surfaceAttribs); + if (eglSurface == EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("Failed to create window surface"); + } + } + + // Create dummy 1x1 pixel buffer surface so the context can be made current. + public void createDummyPbufferSurface() { + createPbufferSurface(1, 1); + } + + public void createPbufferSurface(int width, int height) { + checkIsNotReleased(); + if (configType != ConfigType.PIXEL_BUFFER) { + throw new RuntimeException( + "This EGL context is not configured to use a pixel buffer: " + configType); + } + if (eglSurface != EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("Already has an EGLSurface"); + } + int[] surfaceAttribs = {EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE}; + eglSurface = egl.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs); + if (eglSurface == EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("Failed to create pixel buffer surface"); + } + } + + public EGLContext getContext() { + return eglContext; + } + + public boolean hasSurface() { + return eglSurface != EGL10.EGL_NO_SURFACE; + } + + public int surfaceWidth() { + final int widthArray[] = new int[1]; + egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, widthArray); + return widthArray[0]; + } + + public int surfaceHeight() { + final int heightArray[] = new int[1]; + egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, heightArray); + return heightArray[0]; + } + + public void releaseSurface() { + if (eglSurface != EGL10.EGL_NO_SURFACE) { + egl.eglDestroySurface(eglDisplay, eglSurface); + eglSurface = EGL10.EGL_NO_SURFACE; + } + } + + private void checkIsNotReleased() { + if (eglDisplay == EGL10.EGL_NO_DISPLAY || eglContext == EGL10.EGL_NO_CONTEXT + || eglConfig == null) { + throw new RuntimeException("This object has been released"); + } + } + + public void release() { + checkIsNotReleased(); + releaseSurface(); + detachCurrent(); + egl.eglDestroyContext(eglDisplay, eglContext); + egl.eglTerminate(eglDisplay); + eglContext = EGL10.EGL_NO_CONTEXT; + eglDisplay = EGL10.EGL_NO_DISPLAY; + eglConfig = null; + } + + public void makeCurrent() { + checkIsNotReleased(); + if (eglSurface == EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("No EGLSurface - can't make current"); + } + if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + throw new RuntimeException("eglMakeCurrent failed"); + } + } + + // Detach the current EGL context, so that it can be made current on another thread. + public void detachCurrent() { + if (!egl.eglMakeCurrent( + eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) { + throw new RuntimeException("eglMakeCurrent failed"); + } + } + + public void swapBuffers() { + checkIsNotReleased(); + if (eglSurface == EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("No EGLSurface - can't swap buffers"); + } + egl.eglSwapBuffers(eglDisplay, eglSurface); + } + + // Return an EGLDisplay, or die trying. + private EGLDisplay getEglDisplay() { + EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if (eglDisplay == EGL10.EGL_NO_DISPLAY) { + throw new RuntimeException("Unable to get EGL10 display"); + } + int[] version = new int[2]; + if (!egl.eglInitialize(eglDisplay, version)) { + throw new RuntimeException("Unable to initialize EGL10"); + } + return eglDisplay; + } + + // Return an EGLConfig, or die trying. + private EGLConfig getEglConfig(EGLDisplay eglDisplay, ConfigType configType) { + // Always RGB888, GLES2. + int[] configAttributes = { + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE, 0, // Allocate dummy fields for specific options. + EGL10.EGL_NONE + }; + + // Fill in dummy fields based on configType. + switch (configType) { + case PLAIN: + break; + case PIXEL_BUFFER: + configAttributes[configAttributes.length - 3] = EGL10.EGL_SURFACE_TYPE; + configAttributes[configAttributes.length - 2] = EGL10.EGL_PBUFFER_BIT; + break; + case RECORDABLE: + configAttributes[configAttributes.length - 3] = EGL_RECORDABLE_ANDROID; + configAttributes[configAttributes.length - 2] = 1; + break; + default: + throw new IllegalArgumentException(); + } + + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + if (!egl.eglChooseConfig( + eglDisplay, configAttributes, configs, configs.length, numConfigs)) { + throw new RuntimeException("Unable to find RGB888 " + configType + " EGL config"); + } + return configs[0]; + } + + // Return an EGLConfig, or die trying. + private EGLContext createEglContext( + EGLContext sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig) { + int[] contextAttributes = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; + EGLContext eglContext = + egl.eglCreateContext(eglDisplay, eglConfig, sharedContext, contextAttributes); + if (eglContext == EGL10.EGL_NO_CONTEXT) { + throw new RuntimeException("Failed to create EGL context"); + } + return eglContext; + } +} |