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
28package org.webrtc;
29
30import android.annotation.TargetApi;
31import android.graphics.SurfaceTexture;
32import android.opengl.EGL14;
33import android.opengl.EGLConfig;
34import android.opengl.EGLContext;
35import android.opengl.EGLDisplay;
36import android.opengl.EGLExt;
37import android.opengl.EGLSurface;
38import android.view.Surface;
39
40import 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)
47final 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