| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.cts; |
| |
| import android.opengl.EGL14; |
| import android.opengl.EGLExt; |
| import android.opengl.EGLConfig; |
| import android.opengl.EGLContext; |
| import android.opengl.EGLDisplay; |
| import android.opengl.EGLSurface; |
| import android.util.Log; |
| import android.view.Surface; |
| |
| |
| /** |
| * Holds state associated with a Surface used for MediaCodec encoder input. |
| * <p> |
| * The constructor takes a Surface obtained from MediaCodec.createInputSurface(), and uses that |
| * to create an EGL window surface. Calls to eglSwapBuffers() cause a frame of data to be sent |
| * to the video encoder. |
| */ |
| class InputSurface { |
| private static final String TAG = "InputSurface"; |
| |
| private static final int EGL_RECORDABLE_ANDROID = 0x3142; |
| |
| private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY; |
| private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT; |
| private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE; |
| private EGLConfig[] mConfigs = new EGLConfig[1]; |
| |
| private Surface mSurface; |
| private int mWidth; |
| private int mHeight; |
| |
| /** |
| * Creates an InputSurface from a Surface. |
| */ |
| public InputSurface(Surface surface) { |
| if (surface == null) { |
| throw new NullPointerException(); |
| } |
| mSurface = surface; |
| |
| eglSetup(); |
| } |
| |
| /** |
| * Prepares EGL. We want a GLES 2.0 context and a surface that supports recording. |
| */ |
| private void eglSetup() { |
| mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); |
| if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { |
| throw new RuntimeException("unable to get EGL14 display"); |
| } |
| int[] version = new int[2]; |
| if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) { |
| mEGLDisplay = null; |
| throw new RuntimeException("unable to initialize EGL14"); |
| } |
| |
| // Configure EGL for recordable and OpenGL ES 2.0. We want enough RGB bits |
| // to minimize artifacts from possible YUV conversion. |
| int[] attribList = { |
| EGL14.EGL_RED_SIZE, 8, |
| EGL14.EGL_GREEN_SIZE, 8, |
| EGL14.EGL_BLUE_SIZE, 8, |
| EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, |
| EGL_RECORDABLE_ANDROID, 1, |
| EGL14.EGL_NONE |
| }; |
| int[] numConfigs = new int[1]; |
| if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, mConfigs, 0, mConfigs.length, |
| numConfigs, 0)) { |
| throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config"); |
| } |
| |
| // Configure context for OpenGL ES 2.0. |
| int[] attrib_list = { |
| EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL14.EGL_NONE |
| }; |
| mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mConfigs[0], EGL14.EGL_NO_CONTEXT, |
| attrib_list, 0); |
| checkEglError("eglCreateContext"); |
| if (mEGLContext == null) { |
| throw new RuntimeException("null context"); |
| } |
| |
| // Create a window surface, and attach it to the Surface we received. |
| createEGLSurface(); |
| |
| mWidth = getWidth(); |
| mHeight = getHeight(); |
| } |
| |
| public void updateSize(int width, int height) { |
| if (width != mWidth || height != mHeight) { |
| Log.d(TAG, "re-create EGLSurface"); |
| releaseEGLSurface(); |
| createEGLSurface(); |
| mWidth = getWidth(); |
| mHeight = getHeight(); |
| } |
| } |
| |
| private void createEGLSurface() { |
| //EGLConfig[] configs = new EGLConfig[1]; |
| int[] surfaceAttribs = { |
| EGL14.EGL_NONE |
| }; |
| mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs[0], mSurface, |
| surfaceAttribs, 0); |
| checkEglError("eglCreateWindowSurface"); |
| if (mEGLSurface == null) { |
| throw new RuntimeException("surface was null"); |
| } |
| } |
| private void releaseEGLSurface() { |
| if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { |
| EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); |
| mEGLSurface = EGL14.EGL_NO_SURFACE; |
| } |
| } |
| /** |
| * Discard all resources held by this class, notably the EGL context. Also releases the |
| * Surface that was passed to our constructor. |
| */ |
| public void release() { |
| if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { |
| EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface); |
| EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); |
| EGL14.eglReleaseThread(); |
| EGL14.eglTerminate(mEGLDisplay); |
| } |
| |
| mSurface.release(); |
| |
| mEGLDisplay = EGL14.EGL_NO_DISPLAY; |
| mEGLContext = EGL14.EGL_NO_CONTEXT; |
| mEGLSurface = EGL14.EGL_NO_SURFACE; |
| |
| mSurface = null; |
| } |
| |
| /** |
| * Makes our EGL context and surface current. |
| */ |
| public void makeCurrent() { |
| if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { |
| throw new RuntimeException("eglMakeCurrent failed"); |
| } |
| } |
| |
| public void makeUnCurrent() { |
| if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, |
| EGL14.EGL_NO_CONTEXT)) { |
| throw new RuntimeException("eglMakeCurrent failed"); |
| } |
| } |
| |
| /** |
| * Calls eglSwapBuffers. Use this to "publish" the current frame. |
| */ |
| public boolean swapBuffers() { |
| return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface); |
| } |
| |
| /** |
| * Returns the Surface that the MediaCodec receives buffers from. |
| */ |
| public Surface getSurface() { |
| return mSurface; |
| } |
| |
| /** |
| * Queries the surface's width. |
| */ |
| public int getWidth() { |
| int[] value = new int[1]; |
| EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_WIDTH, value, 0); |
| return value[0]; |
| } |
| |
| /** |
| * Queries the surface's height. |
| */ |
| public int getHeight() { |
| int[] value = new int[1]; |
| EGL14.eglQuerySurface(mEGLDisplay, mEGLSurface, EGL14.EGL_HEIGHT, value, 0); |
| return value[0]; |
| } |
| |
| /** |
| * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds. |
| */ |
| public void setPresentationTime(long nsecs) { |
| EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs); |
| } |
| |
| /** |
| * Checks for EGL errors. |
| */ |
| private void checkEglError(String msg) { |
| int error; |
| if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { |
| throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error)); |
| } |
| } |
| } |