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.util.Log; 28 import android.view.Surface; 29 30 31 /** 32 * Holds state associated with a Surface used for MediaCodec encoder input. 33 * <p> 34 * The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses that 35 * to create an EGL window surface. Calls to eglSwapBuffers() cause a frame of data to be sent 36 * to the video encoder. 37 */ 38 class InputSurface implements InputSurfaceInterface { 39 private static final String TAG = "InputSurface"; 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 EGLExt.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 @Override 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 //EGLConfig[] configs = new EGLConfig[1]; 124 int[] surfaceAttribs = { 125 EGL14.EGL_NONE 126 }; 127 mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs[0], mSurface, 128 surfaceAttribs, 0); 129 checkEglError("eglCreateWindowSurface"); 130 if (mEGLSurface == null) { 131 throw new RuntimeException("surface was null"); 132 } 133 } 134 private void releaseEGLSurface() { 135 if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 136 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 137 mEGLSurface = EGL14.EGL_NO_SURFACE; 138 } 139 } 140 /** 141 * Discard all resources held by this class, notably the EGL context. Also releases the 142 * Surface that was passed to our constructor. 143 */ 144 @Override 145 public void release() { 146 if (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 @Override 166 public void makeCurrent() { 167 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 168 throw new RuntimeException("eglMakeCurrent failed"); 169 } 170 } 171 172 public void makeUnCurrent() { 173 if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 174 EGL14.EGL_NO_CONTEXT)) { 175 throw new RuntimeException("eglMakeCurrent failed"); 176 } 177 } 178 179 /** 180 * Calls eglSwapBuffers. Use this to "publish" the current frame. 181 */ 182 @Override 183 public boolean swapBuffers() { 184 return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); 185 } 186 187 /** 188 * Returns the Surface that the MediaCodec receives buffers from. 189 */ 190 public Surface getSurface() { 191 return mSurface; 192 } 193 194 /** 195 * Queries the surface's width. 196 */ 197 public int getWidth() { 198 int[] value = new int[1]; 199 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_WIDTH, value, 0); 200 return value[0]; 201 } 202 203 /** 204 * Queries the surface's height. 205 */ 206 public int getHeight() { 207 int[] value = new int[1]; 208 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_HEIGHT, value, 0); 209 return value[0]; 210 } 211 212 /** 213 * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. 214 */ 215 @Override 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 @Override 231 public void configure(MediaCodec codec) { 232 codec.setInputSurface(mSurface); 233 } 234 235 @Override 236 public void configure(NdkMediaCodec codec) { 237 codec.setInputSurface(mSurface); 238 } 239 240 } 241