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