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.
isEGL14Supported()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 
Context(android.opengl.EGLContext eglContext)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.
EglBase14(EglBase14.Context sharedContext, int[] configAttributes)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
createSurface(Surface surface)82   public void createSurface(Surface surface) {
83     createSurfaceInternal(surface);
84   }
85 
86   // Create EGLSurface from the Android SurfaceTexture.
87   @Override
createSurface(SurfaceTexture surfaceTexture)88   public void createSurface(SurfaceTexture surfaceTexture) {
89     createSurfaceInternal(surfaceTexture);
90   }
91 
92   // Create EGLSurface from either Surface or SurfaceTexture.
createSurfaceInternal(Object surface)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
createDummyPbufferSurface()109   public void createDummyPbufferSurface() {
110     createPbufferSurface(1, 1);
111   }
112 
113   @Override
createPbufferSurface(int width, int height)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
getEglBaseContext()127   public Context getEglBaseContext() {
128     return new EglBase14.Context(eglContext);
129   }
130 
131   @Override
hasSurface()132   public boolean hasSurface() {
133     return eglSurface != EGL14.EGL_NO_SURFACE;
134   }
135 
136   @Override
surfaceWidth()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
surfaceHeight()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
releaseSurface()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 
checkIsNotReleased()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
release()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
makeCurrent()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
detachCurrent()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
swapBuffers()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 
swapBuffers(long timeStampNs)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.
getEglDisplay()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.
getEglConfig(EGLDisplay eglDisplay, int[] configAttributes)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.
createEglContext( EglBase14.Context sharedContext, EGLDisplay eglDisplay, EGLConfig eglConfig)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