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