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 private static final boolean VERBOSE = false; 39 40 private static final int EGL_RECORDABLE_ANDROID = 0x3142; 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 46 private Surface mSurface; 47 48 /** 49 * Creates an InputSurface from a Surface. 50 */ 51 public InputSurface(Surface surface) { 52 if (surface == null) { 53 throw new NullPointerException(); 54 } 55 mSurface = surface; 56 57 eglSetup(); 58 } 59 60 /** 61 * Prepares EGL. We want a GLES 2.0 context and a surface that supports recording. 62 */ 63 private void eglSetup() { 64 mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 65 if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 66 throw new RuntimeException("unable to get EGL14 display"); 67 } 68 int[] version = new int[2]; 69 if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { 70 mEGLDisplay = null; 71 throw new RuntimeException("unable to initialize EGL14"); 72 } 73 74 // Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits 75 // to minimize artifacts from possible YUV conversion. 76 int[] attribList = { 77 EGL14.EGL_RED_SIZE, 8, 78 EGL14.EGL_GREEN_SIZE, 8, 79 EGL14.EGL_BLUE_SIZE, 8, 80 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 81 EGL_RECORDABLE_ANDROID, 1, 82 EGL14.EGL_NONE 83 }; 84 EGLConfig[] configs = new EGLConfig[1]; 85 int[] numConfigs = new int[1]; 86 if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length, 87 numConfigs, 0)) { 88 throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config"); 89 } 90 91 // Configure context for OpenGL ES 2.0. 92 int[] attrib_list = { 93 EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 94 EGL14.EGL_NONE 95 }; 96 mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, 97 attrib_list, 0); 98 checkEglError("eglCreateContext"); 99 if (mEGLContext == null) { 100 throw new RuntimeException("null context"); 101 } 102 103 // Create a window surface, and attach it to the Surface we received. 104 int[] surfaceAttribs = { 105 EGL14.EGL_NONE 106 }; 107 mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface, 108 surfaceAttribs, 0); 109 checkEglError("eglCreateWindowSurface"); 110 if (mEGLSurface == null) { 111 throw new RuntimeException("surface was null"); 112 } 113 } 114 115 /** 116 * Discard all resources held by this class, notably the EGL context. Also releases the 117 * Surface that was passed to our constructor. 118 */ 119 public void release() { 120 if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 121 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); 122 EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 123 EGL14.eglReleaseThread(); 124 EGL14.eglTerminate(mEGLDisplay); 125 } 126 127 mSurface.release(); 128 129 mEGLDisplay = EGL14.EGL_NO_DISPLAY; 130 mEGLContext = EGL14.EGL_NO_CONTEXT; 131 mEGLSurface = EGL14.EGL_NO_SURFACE; 132 133 mSurface = null; 134 } 135 136 /** 137 * Makes our EGL context and surface current. 138 */ 139 public void makeCurrent() { 140 if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { 141 throw new RuntimeException("eglMakeCurrent failed"); 142 } 143 } 144 145 public void makeUnCurrent() { 146 if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 147 EGL14.EGL_NO_CONTEXT)) { 148 throw new RuntimeException("eglMakeCurrent failed"); 149 } 150 } 151 152 /** 153 * Calls eglSwapBuffers. Use this to "publish" the current frame. 154 */ 155 public boolean swapBuffers() { 156 return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); 157 } 158 159 /** 160 * Returns the Surface that the MediaCodec receives buffers from. 161 */ 162 public Surface getSurface() { 163 return mSurface; 164 } 165 166 /** 167 * Queries the surface's width. 168 */ 169 public int getWidth() { 170 int[] value = new int[1]; 171 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_WIDTH, value, 0); 172 return value[0]; 173 } 174 175 /** 176 * Queries the surface's height. 177 */ 178 public int getHeight() { 179 int[] value = new int[1]; 180 EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_HEIGHT, value, 0); 181 return value[0]; 182 } 183 184 /** 185 * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. 186 */ 187 public void setPresentationTime(long nsecs) { 188 EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs); 189 } 190 191 /** 192 * Checks for EGL errors. 193 */ 194 private void checkEglError(String msg) { 195 int error; 196 if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 197 throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); 198 } 199 } 200 } 201