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 20 import android.media.MediaCodec; 21 import android.opengl.EGL14; 22 import android.opengl.EGLConfig; 23 import android.opengl.EGLContext; 24 import android.opengl.EGLDisplay; 25 import android.opengl.EGLExt; 26 import android.opengl.EGLSurface; 27 import android.opengl.GLES20; 28 import android.util.Log; 29 import android.view.Surface; 30 31 32 /** 33 * Holds state associated with a Surface used for MediaCodec encoder input. 34 * <p> 35 * The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses that 36 * to create an EGL window surface. Calls to eglSwapBuffers() cause a frame of data to be sent 37 * to the video encoder. 38 */ 39 class InputSurface implements InputSurfaceInterface { 40 private static final String TAG = "InputSurface"; 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 boolean mReleaseSurface; 48 private Surface mSurface; 49 private int mWidth; 50 private int mHeight; 51 52 /** 53 * Creates an InputSurface from a Surface. 54 */ InputSurface(Surface surface, boolean releaseSurface)55 public InputSurface(Surface surface, boolean releaseSurface) { 56 if (surface == null) { 57 throw new NullPointerException(); 58 } 59 mSurface = surface; 60 mReleaseSurface = releaseSurface; 61 62 eglSetup(); 63 } 64 65 /** 66 * Creates an InputSurface from a Surface. 67 */ InputSurface(Surface surface)68 public InputSurface(Surface surface) { 69 this(surface, true); 70 } 71 72 /** 73 * Prepares EGL. We want a GLES 2.0 context and a surface that supports recording. 74 */ eglSetup()75 private void eglSetup() { 76 mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 77 if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 78 throw new RuntimeException("unable to get EGL14 display"); 79 } 80 int[] version = new int[2]; 81 if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { 82 mEGLDisplay = null; 83 throw new RuntimeException("unable to initialize EGL14"); 84 } 85 86 // Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits 87 // to minimize artifacts from possible YUV conversion. 88 int[] attribList = { 89 EGL14.EGL_RED_SIZE, 8, 90 EGL14.EGL_GREEN_SIZE, 8, 91 EGL14.EGL_BLUE_SIZE, 8, 92 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 93 EGLExt.EGL_RECORDABLE_ANDROID, 1, 94 EGL14.EGL_NONE 95 }; 96 int[] numConfigs = new int[1]; 97 if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, mConfigs, 0, mConfigs.length, 98 numConfigs, 0)) { 99 throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config"); 100 } 101 102 // Configure context for OpenGL ES 2.0. 103 int[] attrib_list = { 104 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 105 EGL14.EGL_NONE 106 }; 107 mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mConfigs[0], EGL14.EGL_NO_CONTEXT, 108 attrib_list, 0); 109 checkEglError("eglCreateContext"); 110 if (mEGLContext == null) { 111 throw new RuntimeException("null context"); 112 } 113 114 // Create a window surface, and attach it to the Surface we received. 115 createEGLSurface(); 116 117 mWidth = getWidth(); 118 mHeight = getHeight(); 119 } 120 121 @Override updateSize(int width, int height)122 public void updateSize(int width, int height) { 123 if (width != mWidth || height != mHeight) { 124 Log.d(TAG, "re-create EGLSurface"); 125 releaseEGLSurface(); 126 createEGLSurface(); 127 mWidth = getWidth(); 128 mHeight = getHeight(); 129 } 130 } 131 createEGLSurface()132 private void createEGLSurface() { 133 //EGLConfig[] configs = new EGLConfig[1]; 134 int[] surfaceAttribs = { 135 EGL14.EGL_NONE 136 }; 137 mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs[0], mSurface, 138 surfaceAttribs, 0); 139 checkEglError("eglCreateWindowSurface"); 140 if (mEGLSurface == null) { 141 throw new RuntimeException("surface was null"); 142 } 143 } releaseEGLSurface()144 private void releaseEGLSurface() { 145 if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 146 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 147 mEGLSurface = EGL14.EGL_NO_SURFACE; 148 } 149 } 150 /** 151 * Discard all resources held by this class, notably the EGL context. Also releases the 152 * Surface that was passed to our constructor. 153 */ 154 @Override release()155 public void release() { 156 if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 157 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 158 EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 159 EGL14.eglReleaseThread(); 160 EGL14.eglTerminate(mEGLDisplay); 161 } 162 163 if (mReleaseSurface) { 164 mSurface.release(); 165 } 166 167 mEGLDisplay = EGL14.EGL_NO_DISPLAY; 168 mEGLContext = EGL14.EGL_NO_CONTEXT; 169 mEGLSurface = EGL14.EGL_NO_SURFACE; 170 171 mSurface = null; 172 } 173 174 /** 175 * Makes our EGL context and surface current. 176 */ 177 @Override makeCurrent()178 public void makeCurrent() { 179 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 180 throw new RuntimeException("eglMakeCurrent failed"); 181 } 182 } 183 makeUnCurrent()184 public void makeUnCurrent() { 185 if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 186 EGL14.EGL_NO_CONTEXT)) { 187 throw new RuntimeException("eglMakeCurrent failed"); 188 } 189 } 190 191 /** 192 * Calls eglSwapBuffers. Use this to "publish" the current frame. 193 */ 194 @Override swapBuffers()195 public boolean swapBuffers() { 196 return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); 197 } 198 199 /** 200 * Returns the Surface that the MediaCodec receives buffers from. 201 */ getSurface()202 public Surface getSurface() { 203 return mSurface; 204 } 205 206 /** 207 * Queries the surface's width. 208 */ getWidth()209 public int getWidth() { 210 int[] value = new int[1]; 211 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_WIDTH, value, 0); 212 return value[0]; 213 } 214 215 /** 216 * Queries the surface's height. 217 */ getHeight()218 public int getHeight() { 219 int[] value = new int[1]; 220 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_HEIGHT, value, 0); 221 return value[0]; 222 } 223 224 /** 225 * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. 226 */ 227 @Override setPresentationTime(long nsecs)228 public void setPresentationTime(long nsecs) { 229 EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs); 230 } 231 232 /** 233 * Checks for EGL errors. 234 */ checkEglError(String msg)235 private void checkEglError(String msg) { 236 int error; 237 if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 238 throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); 239 } 240 } 241 242 @Override configure(MediaCodec codec)243 public void configure(MediaCodec codec) { 244 codec.setInputSurface(mSurface); 245 } 246 247 @Override configure(NdkMediaCodec codec)248 public void configure(NdkMediaCodec codec) { 249 codec.setInputSurface(mSurface); 250 } 251 252 /** 253 * Clears the surface to black. 254 * <p> 255 * Ported from https://github.com/google/grafika 256 */ clearSurface(Surface surface)257 public static void clearSurface(Surface surface) { 258 // We need to do this with OpenGL ES (*not* Canvas -- the "software render" bits 259 // are sticky). We can't stay connected to the Surface after we're done because 260 // that'd prevent the video encoder from attaching. 261 // 262 // If the Surface is resized to be larger, the new portions will be black, so 263 // clearing to something other than black may look weird unless we do the clear 264 // post-resize. 265 InputSurface win = new InputSurface(surface, false /* release */); 266 win.makeCurrent(); 267 GLES20.glClearColor(0, 0, 0, 0); 268 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 269 win.swapBuffers(); 270 win.release(); 271 } 272 } 273