Home | History | Annotate | Download | only in webrtc
      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.annotation.TargetApi;
     31 import android.graphics.SurfaceTexture;
     32 import android.opengl.EGL14;
     33 import android.opengl.EGLConfig;
     34 import android.opengl.EGLContext;
     35 import android.opengl.EGLDisplay;
     36 import android.opengl.EGLExt;
     37 import android.opengl.EGLSurface;
     38 import android.view.Surface;
     39 
     40 import org.webrtc.Logging;
     41 
     42 /**
     43  * Holds EGL state and utility methods for handling an EGL14 EGLContext, an EGLDisplay,
     44  * and an EGLSurface.
     45  */
     46 @TargetApi(18)
     47 final class EglBase14 extends EglBase {
     48   private static final String TAG = "EglBase14";
     49   private static final int EGLExt_SDK_VERSION = android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
     50   private static final int CURRENT_SDK_VERSION = android.os.Build.VERSION.SDK_INT;
     51   private EGLContext eglContext;
     52   private EGLConfig eglConfig;
     53   private EGLDisplay eglDisplay;
     54   private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE;
     55 
     56   // EGL 1.4 is supported from API 17. But EGLExt that is used for setting presentation
     57   // time stamp on a surface is supported from 18 so we require 18.
     58   public static boolean isEGL14Supported() {
     59     Logging.d(TAG, "SDK version: " + CURRENT_SDK_VERSION
     60         + ". isEGL14Supported: " + (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION));
     61     return (CURRENT_SDK_VERSION >= EGLExt_SDK_VERSION);
     62   }
     63 
     64   public static class Context extends EglBase.Context {
     65     private final android.opengl.EGLContext egl14Context;
     66 
     67     Context(android.opengl.EGLContext eglContext) {
     68       this.egl14Context = eglContext;
     69     }
     70   }
     71 
     72   // Create a new context with the specified config type, sharing data with sharedContext.
     73   // |sharedContext| may be null.
     74   EglBase14(EglBase14.Context sharedContext, int[] configAttributes) {
     75     eglDisplay = getEglDisplay();
     76     eglConfig = getEglConfig(eglDisplay, configAttributes);
     77     eglContext = createEglContext(sharedContext, eglDisplay, eglConfig);
     78   }
     79 
     80   // Create EGLSurface from the Android Surface.
     81   @Override
     82   public void createSurface(Surface surface) {
     83     createSurfaceInternal(surface);
     84   }
     85 
     86   // Create EGLSurface from the Android SurfaceTexture.
     87   @Override
     88   public void createSurface(SurfaceTexture surfaceTexture) {
     89     createSurfaceInternal(surfaceTexture);
     90   }
     91 
     92   // Create EGLSurface from either Surface or SurfaceTexture.
     93   private void createSurfaceInternal(Object surface) {
     94     if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
     95       throw new IllegalStateException("Input must be either a Surface or SurfaceTexture");
     96     }
     97     checkIsNotReleased();
     98     if (eglSurface != EGL14.EGL_NO_SURFACE) {
     99       throw new RuntimeException("Already has an EGLSurface");
    100     }
    101     int[] surfaceAttribs = {EGL14.EGL_NONE};
    102     eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs, 0);
    103     if (eglSurface == EGL14.EGL_NO_SURFACE) {
    104       throw new RuntimeException("Failed to create window surface");
    105     }
    106   }
    107 
    108   @Override
    109   public void createDummyPbufferSurface() {
    110     createPbufferSurface(1, 1);
    111   }
    112 
    113   @Override
    114   public void createPbufferSurface(int width, int height) {
    115     checkIsNotReleased();
    116     if (eglSurface != EGL14.EGL_NO_SURFACE) {
    117       throw new RuntimeException("Already has an EGLSurface");
    118     }
    119     int[] surfaceAttribs = {EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE};
    120     eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs, 0);
    121     if (eglSurface == EGL14.EGL_NO_SURFACE) {
    122       throw new RuntimeException("Failed to create pixel buffer surface");
    123     }
    124   }
    125 
    126   @Override
    127   public Context getEglBaseContext() {
    128     return new EglBase14.Context(eglContext);
    129   }
    130 
    131   @Override
    132   public boolean hasSurface() {
    133     return eglSurface != EGL14.EGL_NO_SURFACE;
    134   }
    135 
    136   @Override
    137   public int surfaceWidth() {
    138     final int widthArray[] = new int[1];
    139     EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_WIDTH, widthArray, 0);
    140     return widthArray[0];
    141   }
    142 
    143   @Override
    144   public int surfaceHeight() {
    145     final int heightArray[] = new int[1];
    146     EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_HEIGHT, heightArray, 0);
    147     return heightArray[0];
    148   }
    149 
    150   @Override
    151   public void releaseSurface() {
    152     if (eglSurface != EGL14.EGL_NO_SURFACE) {
    153       EGL14.eglDestroySurface(eglDisplay, eglSurface);
    154       eglSurface = EGL14.EGL_NO_SURFACE;
    155     }
    156   }
    157 
    158   private void checkIsNotReleased() {
    159     if (eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT
    160         || eglConfig == null) {
    161       throw new RuntimeException("This object has been released");
    162     }
    163   }
    164 
    165   @Override
    166   public void release() {
    167     checkIsNotReleased();
    168     releaseSurface();
    169     detachCurrent();
    170     EGL14.eglDestroyContext(eglDisplay, eglContext);
    171     EGL14.eglReleaseThread();
    172     EGL14.eglTerminate(eglDisplay);
    173     eglContext = EGL14.EGL_NO_CONTEXT;
    174     eglDisplay = EGL14.EGL_NO_DISPLAY;
    175     eglConfig = null;
    176   }
    177 
    178   @Override
    179   public void makeCurrent() {
    180     checkIsNotReleased();
    181     if (eglSurface == EGL14.EGL_NO_SURFACE) {
    182       throw new RuntimeException("No EGLSurface - can't make current");
    183     }
    184     if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
    185       throw new RuntimeException("eglMakeCurrent failed");
    186     }
    187   }
    188 
    189   // Detach the current EGL context, so that it can be made current on another thread.
    190   @Override
    191   public void detachCurrent() {
    192     if (!EGL14.eglMakeCurrent(
    193         eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
    194       throw new RuntimeException("eglMakeCurrent failed");
    195     }
    196   }
    197 
    198   @Override
    199   public void swapBuffers() {
    200     checkIsNotReleased();
    201     if (eglSurface == EGL14.EGL_NO_SURFACE) {
    202       throw new RuntimeException("No EGLSurface - can't swap buffers");
    203     }
    204     EGL14.eglSwapBuffers(eglDisplay, eglSurface);
    205   }
    206 
    207   public void swapBuffers(long timeStampNs) {
    208     checkIsNotReleased();
    209     if (eglSurface == EGL14.EGL_NO_SURFACE) {
    210       throw new RuntimeException("No EGLSurface - can't swap buffers");
    211     }
    212     // See https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt
    213     EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs);
    214     EGL14.eglSwapBuffers(eglDisplay, eglSurface);
    215   }
    216 
    217   // Return an EGLDisplay, or die trying.
    218   private static EGLDisplay getEglDisplay() {
    219     EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    220     if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
    221       throw new RuntimeException("Unable to get EGL14 display");
    222     }
    223     int[] version = new int[2];
    224     if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) {
    225       throw new RuntimeException("Unable to initialize EGL14");
    226     }
    227     return eglDisplay;
    228   }
    229 
    230   // Return an EGLConfig, or die trying.
    231   private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] configAttributes) {
    232     EGLConfig[] configs = new EGLConfig[1];
    233     int[] numConfigs = new int[1];
    234     if (!EGL14.eglChooseConfig(
    235         eglDisplay, configAttributes, 0, configs, 0, configs.length, numConfigs, 0)) {
    236       throw new RuntimeException("Unable to find any matching EGL config");
    237     }
    238     return configs[0];
    239   }
    240 
    241   // Return an EGLConfig, or die trying.
    242   private static EGLContext createEglContext(
    243       EglBase14.Context sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig) {
    244     int[] contextAttributes = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
    245     EGLContext rootContext =
    246         sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext.egl14Context;
    247     EGLContext eglContext =
    248         EGL14.eglCreateContext(eglDisplay, eglConfig, rootContext, contextAttributes, 0);
    249     if (eglContext == EGL14.EGL_NO_CONTEXT) {
    250       throw new RuntimeException("Failed to create EGL context");
    251     }
    252     return eglContext;
    253   }
    254 }
    255