Home | History | Annotate | Download | only in cts
      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