1/* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.heifwriter; 18 19import android.opengl.EGL14; 20import android.opengl.EGLConfig; 21import android.opengl.EGLContext; 22import android.opengl.EGLDisplay; 23import android.opengl.EGLExt; 24import android.opengl.EGLSurface; 25import android.util.Log; 26import android.view.Surface; 27 28import java.util.Objects; 29 30/** 31 * Holds state associated with a Surface used for MediaCodec encoder input. 32 * <p> 33 * The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses that 34 * to create an EGL window surface. Calls to eglSwapBuffers() cause a frame of data to be sent 35 * to the video encoder. 36 * 37 * @hide 38 */ 39public class EglWindowSurface { 40 private static final String TAG = "EglWindowSurface"; 41 42 private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY; 43 private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT; 44 private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE; 45 private EGLConfig[] mConfigs = new EGLConfig[1]; 46 47 private Surface mSurface; 48 private int mWidth; 49 private int mHeight; 50 51 /** 52 * Creates an EglWindowSurface from a Surface. 53 */ 54 public EglWindowSurface(Surface surface) { 55 if (surface == null) { 56 throw new NullPointerException(); 57 } 58 mSurface = surface; 59 60 eglSetup(); 61 } 62 63 /** 64 * Prepares EGL. We want a GLES 2.0 context and a surface that supports recording. 65 */ 66 private void eglSetup() { 67 mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 68 if (Objects.equals(mEGLDisplay, EGL14.EGL_NO_DISPLAY)) { 69 throw new RuntimeException("unable to get EGL14 display"); 70 } 71 int[] version = new int[2]; 72 if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { 73 mEGLDisplay = null; 74 throw new RuntimeException("unable to initialize EGL14"); 75 } 76 77 // Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits 78 // to minimize artifacts from possible YUV conversion. 79 int[] attribList = { 80 EGL14.EGL_RED_SIZE, 8, 81 EGL14.EGL_GREEN_SIZE, 8, 82 EGL14.EGL_BLUE_SIZE, 8, 83 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 84 EGLExt.EGL_RECORDABLE_ANDROID, 1, 85 EGL14.EGL_NONE 86 }; 87 int[] numConfigs = new int[1]; 88 if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, mConfigs, 0, mConfigs.length, 89 numConfigs, 0)) { 90 throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config"); 91 } 92 93 // Configure context for OpenGL ES 2.0. 94 int[] attrib_list = { 95 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 96 EGL14.EGL_NONE 97 }; 98 mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mConfigs[0], EGL14.EGL_NO_CONTEXT, 99 attrib_list, 0); 100 checkEglError("eglCreateContext"); 101 if (mEGLContext == null) { 102 throw new RuntimeException("null context"); 103 } 104 105 // Create a window surface, and attach it to the Surface we received. 106 createEGLSurface(); 107 108 mWidth = getWidth(); 109 mHeight = getHeight(); 110 } 111 112 public void updateSize(int width, int height) { 113 if (width != mWidth || height != mHeight) { 114 Log.d(TAG, "re-create EGLSurface"); 115 releaseEGLSurface(); 116 createEGLSurface(); 117 mWidth = getWidth(); 118 mHeight = getHeight(); 119 } 120 } 121 122 private void createEGLSurface() { 123 int[] surfaceAttribs = { 124 EGL14.EGL_NONE 125 }; 126 mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs[0], mSurface, 127 surfaceAttribs, 0); 128 checkEglError("eglCreateWindowSurface"); 129 if (mEGLSurface == null) { 130 throw new RuntimeException("surface was null"); 131 } 132 } 133 134 private void releaseEGLSurface() { 135 if (!Objects.equals(mEGLDisplay, EGL14.EGL_NO_DISPLAY)) { 136 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 137 mEGLSurface = EGL14.EGL_NO_SURFACE; 138 } 139 } 140 141 /** 142 * Discard all resources held by this class, notably the EGL context. Also releases the 143 * Surface that was passed to our constructor. 144 */ 145 public void release() { 146 if (!Objects.equals(mEGLDisplay, EGL14.EGL_NO_DISPLAY)) { 147 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 148 EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 149 EGL14.eglReleaseThread(); 150 EGL14.eglTerminate(mEGLDisplay); 151 } 152 153 mSurface.release(); 154 155 mEGLDisplay = EGL14.EGL_NO_DISPLAY; 156 mEGLContext = EGL14.EGL_NO_CONTEXT; 157 mEGLSurface = EGL14.EGL_NO_SURFACE; 158 159 mSurface = null; 160 } 161 162 /** 163 * Makes our EGL context and surface current. 164 */ 165 public void makeCurrent() { 166 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 167 throw new RuntimeException("eglMakeCurrent failed"); 168 } 169 } 170 171 /** 172 * Makes our EGL context and surface not current. 173 */ 174 public void makeUnCurrent() { 175 if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 176 EGL14.EGL_NO_CONTEXT)) { 177 throw new RuntimeException("eglMakeCurrent failed"); 178 } 179 } 180 181 /** 182 * Calls eglSwapBuffers. Use this to "publish" the current frame. 183 */ 184 public boolean swapBuffers() { 185 return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); 186 } 187 188 /** 189 * Returns the Surface that the MediaCodec receives buffers from. 190 */ 191 public Surface getSurface() { 192 return mSurface; 193 } 194 195 /** 196 * Queries the surface's width. 197 */ 198 public int getWidth() { 199 int[] value = new int[1]; 200 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_WIDTH, value, 0); 201 return value[0]; 202 } 203 204 /** 205 * Queries the surface's height. 206 */ 207 public int getHeight() { 208 int[] value = new int[1]; 209 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_HEIGHT, value, 0); 210 return value[0]; 211 } 212 213 /** 214 * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. 215 */ 216 public void setPresentationTime(long nsecs) { 217 EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs); 218 } 219 220 /** 221 * Checks for EGL errors. 222 */ 223 private void checkEglError(String msg) { 224 int error; 225 if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 226 throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); 227 } 228 } 229} 230