Home | History | Annotate | Download | only in legacy
      1 /*
      2  * Copyright (C) 2014 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 package android.hardware.camera2.legacy;
     17 
     18 import android.graphics.ImageFormat;
     19 import android.graphics.RectF;
     20 import android.graphics.SurfaceTexture;
     21 import android.hardware.camera2.CameraCharacteristics;
     22 import android.os.Environment;
     23 import android.opengl.EGL14;
     24 import android.opengl.EGLConfig;
     25 import android.opengl.EGLContext;
     26 import android.opengl.EGLDisplay;
     27 import android.opengl.EGLSurface;
     28 import android.opengl.GLES11Ext;
     29 import android.opengl.GLES20;
     30 import android.opengl.Matrix;
     31 import android.text.format.Time;
     32 import android.util.Log;
     33 import android.util.Pair;
     34 import android.util.Size;
     35 import android.view.Surface;
     36 import android.os.SystemProperties;
     37 
     38 import java.io.File;
     39 import java.nio.ByteBuffer;
     40 import java.nio.ByteOrder;
     41 import java.nio.FloatBuffer;
     42 import java.util.ArrayList;
     43 import java.util.Collection;
     44 import java.util.List;
     45 
     46 /**
     47  * A renderer class that manages the GL state, and can draw a frame into a set of output
     48  * {@link Surface}s.
     49  */
     50 public class SurfaceTextureRenderer {
     51     private static final String TAG = SurfaceTextureRenderer.class.getSimpleName();
     52     private static final boolean DEBUG = false;
     53     private static final int EGL_RECORDABLE_ANDROID = 0x3142; // from EGL/eglext.h
     54     private static final int GL_MATRIX_SIZE = 16;
     55     private static final int VERTEX_POS_SIZE = 3;
     56     private static final int VERTEX_UV_SIZE = 2;
     57     private static final int EGL_COLOR_BITLENGTH = 8;
     58     private static final int GLES_VERSION = 2;
     59     private static final int PBUFFER_PIXEL_BYTES = 4;
     60 
     61     private static final int FLIP_TYPE_NONE = 0;
     62     private static final int FLIP_TYPE_HORIZONTAL = 1;
     63     private static final int FLIP_TYPE_VERTICAL = 2;
     64     private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL;
     65 
     66     private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
     67     private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
     68     private EGLConfig mConfigs;
     69 
     70     private class EGLSurfaceHolder {
     71         Surface surface;
     72         EGLSurface eglSurface;
     73         int width;
     74         int height;
     75     }
     76 
     77     private List<EGLSurfaceHolder> mSurfaces = new ArrayList<EGLSurfaceHolder>();
     78     private List<EGLSurfaceHolder> mConversionSurfaces = new ArrayList<EGLSurfaceHolder>();
     79 
     80     private ByteBuffer mPBufferPixels;
     81 
     82     // Hold this to avoid GC
     83     private volatile SurfaceTexture mSurfaceTexture;
     84 
     85     private static final int FLOAT_SIZE_BYTES = 4;
     86     private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
     87     private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
     88     private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
     89 
     90     // Sampling is mirrored across the horizontal axis
     91     private static final float[] sHorizontalFlipTriangleVertices = {
     92             // X, Y, Z, U, V
     93             -1.0f, -1.0f, 0, 1.f, 0.f,
     94             1.0f, -1.0f, 0, 0.f, 0.f,
     95             -1.0f,  1.0f, 0, 1.f, 1.f,
     96             1.0f,  1.0f, 0, 0.f, 1.f,
     97     };
     98 
     99     // Sampling is mirrored across the vertical axis
    100     private static final float[] sVerticalFlipTriangleVertices = {
    101             // X, Y, Z, U, V
    102             -1.0f, -1.0f, 0, 0.f, 1.f,
    103             1.0f, -1.0f, 0, 1.f, 1.f,
    104             -1.0f,  1.0f, 0, 0.f, 0.f,
    105             1.0f,  1.0f, 0, 1.f, 0.f,
    106     };
    107 
    108     // Sampling is mirrored across the both axes
    109     private static final float[] sBothFlipTriangleVertices = {
    110             // X, Y, Z, U, V
    111             -1.0f, -1.0f, 0, 1.f, 1.f,
    112             1.0f, -1.0f, 0, 0.f, 1.f,
    113             -1.0f,  1.0f, 0, 1.f, 0.f,
    114             1.0f,  1.0f, 0, 0.f, 0.f,
    115     };
    116 
    117     // Sampling is 1:1 for a straight copy for the back camera
    118     private static final float[] sRegularTriangleVertices = {
    119             // X, Y, Z, U, V
    120             -1.0f, -1.0f, 0, 0.f, 0.f,
    121             1.0f, -1.0f, 0, 1.f, 0.f,
    122             -1.0f,  1.0f, 0, 0.f, 1.f,
    123             1.0f,  1.0f, 0, 1.f, 1.f,
    124     };
    125 
    126     private FloatBuffer mRegularTriangleVertices;
    127     private FloatBuffer mHorizontalFlipTriangleVertices;
    128     private FloatBuffer mVerticalFlipTriangleVertices;
    129     private FloatBuffer mBothFlipTriangleVertices;
    130     private final int mFacing;
    131 
    132     /**
    133      * As used in this file, this vertex shader maps a unit square to the view, and
    134      * tells the fragment shader to interpolate over it.  Each surface pixel position
    135      * is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with
    136      * s and t in the inclusive range [0, 1], and the matrix from
    137      * {@link SurfaceTexture#getTransformMatrix(float[])} is used to map this
    138      * coordinate to a texture location.
    139      */
    140     private static final String VERTEX_SHADER =
    141             "uniform mat4 uMVPMatrix;\n" +
    142             "uniform mat4 uSTMatrix;\n" +
    143             "attribute vec4 aPosition;\n" +
    144             "attribute vec4 aTextureCoord;\n" +
    145             "varying vec2 vTextureCoord;\n" +
    146             "void main() {\n" +
    147             "  gl_Position = uMVPMatrix * aPosition;\n" +
    148             "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
    149             "}\n";
    150 
    151     /**
    152      * This fragment shader simply draws the color in the 2D texture at
    153      * the location from the {@code VERTEX_SHADER}.
    154      */
    155     private static final String FRAGMENT_SHADER =
    156             "#extension GL_OES_EGL_image_external : require\n" +
    157             "precision mediump float;\n" +
    158             "varying vec2 vTextureCoord;\n" +
    159             "uniform samplerExternalOES sTexture;\n" +
    160             "void main() {\n" +
    161             "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
    162             "}\n";
    163 
    164     private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
    165     private float[] mSTMatrix = new float[GL_MATRIX_SIZE];
    166 
    167     private int mProgram;
    168     private int mTextureID = 0;
    169     private int muMVPMatrixHandle;
    170     private int muSTMatrixHandle;
    171     private int maPositionHandle;
    172     private int maTextureHandle;
    173 
    174     private PerfMeasurement mPerfMeasurer = null;
    175     private static final String LEGACY_PERF_PROPERTY = "persist.camera.legacy_perf";
    176 
    177     public SurfaceTextureRenderer(int facing) {
    178         mFacing = facing;
    179 
    180         mRegularTriangleVertices = ByteBuffer.allocateDirect(sRegularTriangleVertices.length *
    181                 FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
    182         mRegularTriangleVertices.put(sRegularTriangleVertices).position(0);
    183 
    184         mHorizontalFlipTriangleVertices = ByteBuffer.allocateDirect(
    185                 sHorizontalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
    186                 order(ByteOrder.nativeOrder()).asFloatBuffer();
    187         mHorizontalFlipTriangleVertices.put(sHorizontalFlipTriangleVertices).position(0);
    188 
    189         mVerticalFlipTriangleVertices = ByteBuffer.allocateDirect(
    190                 sVerticalFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
    191                 order(ByteOrder.nativeOrder()).asFloatBuffer();
    192         mVerticalFlipTriangleVertices.put(sVerticalFlipTriangleVertices).position(0);
    193 
    194         mBothFlipTriangleVertices = ByteBuffer.allocateDirect(
    195                 sBothFlipTriangleVertices.length * FLOAT_SIZE_BYTES).
    196                 order(ByteOrder.nativeOrder()).asFloatBuffer();
    197         mBothFlipTriangleVertices.put(sBothFlipTriangleVertices).position(0);
    198 
    199         Matrix.setIdentityM(mSTMatrix, 0);
    200     }
    201 
    202     private int loadShader(int shaderType, String source) {
    203         int shader = GLES20.glCreateShader(shaderType);
    204         checkGlError("glCreateShader type=" + shaderType);
    205         GLES20.glShaderSource(shader, source);
    206         GLES20.glCompileShader(shader);
    207         int[] compiled = new int[1];
    208         GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    209         if (compiled[0] == 0) {
    210             Log.e(TAG, "Could not compile shader " + shaderType + ":");
    211             Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
    212             GLES20.glDeleteShader(shader);
    213             // TODO: handle this more gracefully
    214             throw new IllegalStateException("Could not compile shader " + shaderType);
    215         }
    216         return shader;
    217     }
    218 
    219     private int createProgram(String vertexSource, String fragmentSource) {
    220         int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
    221         if (vertexShader == 0) {
    222             return 0;
    223         }
    224         int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
    225         if (pixelShader == 0) {
    226             return 0;
    227         }
    228 
    229         int program = GLES20.glCreateProgram();
    230         checkGlError("glCreateProgram");
    231         if (program == 0) {
    232             Log.e(TAG, "Could not create program");
    233         }
    234         GLES20.glAttachShader(program, vertexShader);
    235         checkGlError("glAttachShader");
    236         GLES20.glAttachShader(program, pixelShader);
    237         checkGlError("glAttachShader");
    238         GLES20.glLinkProgram(program);
    239         int[] linkStatus = new int[1];
    240         GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
    241         if (linkStatus[0] != GLES20.GL_TRUE) {
    242             Log.e(TAG, "Could not link program: ");
    243             Log.e(TAG, GLES20.glGetProgramInfoLog(program));
    244             GLES20.glDeleteProgram(program);
    245             // TODO: handle this more gracefully
    246             throw new IllegalStateException("Could not link program");
    247         }
    248         return program;
    249     }
    250 
    251     private void drawFrame(SurfaceTexture st, int width, int height, int flipType)
    252             throws LegacyExceptionUtils.BufferQueueAbandonedException {
    253         checkGlError("onDrawFrame start");
    254         st.getTransformMatrix(mSTMatrix);
    255 
    256         Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
    257 
    258         // Find intermediate buffer dimensions
    259         Size dimens;
    260         try {
    261             dimens = LegacyCameraDevice.getTextureSize(st);
    262         } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    263             // Should never hit this.
    264             throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
    265         }
    266         float texWidth = dimens.getWidth();
    267         float texHeight = dimens.getHeight();
    268 
    269         if (texWidth <= 0 || texHeight <= 0) {
    270             throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
    271         }
    272 
    273         // Letterbox or pillar-box output dimensions into intermediate dimensions.
    274         RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
    275         RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
    276         android.graphics.Matrix boxingXform = new android.graphics.Matrix();
    277         boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
    278         boxingXform.mapRect(output);
    279 
    280         // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate
    281         // buffer dimensions.
    282         float scaleX = intermediate.width() / output.width();
    283         float scaleY = intermediate.height() / output.height();
    284 
    285         // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space
    286         // coordinates in the shader.  To avoid stretching, we need to scale the larger dimension
    287         // of the intermediate buffer so that the output buffer is actually letter-boxed
    288         // or pillar-boxed into the intermediate buffer after clipping.
    289         Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1);
    290 
    291         if (DEBUG) {
    292             Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
    293                     width + "x" + height + " surface, intermediate buffer size is " + texWidth +
    294                     "x" + texHeight);
    295         }
    296 
    297         // Set viewport to be output buffer dimensions
    298         GLES20.glViewport(0, 0, width, height);
    299 
    300         if (DEBUG) {
    301             GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    302             GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    303         }
    304 
    305         GLES20.glUseProgram(mProgram);
    306         checkGlError("glUseProgram");
    307 
    308         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    309         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
    310 
    311         FloatBuffer triangleVertices;
    312         switch(flipType) {
    313             case FLIP_TYPE_HORIZONTAL:
    314                 triangleVertices = mHorizontalFlipTriangleVertices;
    315                 break;
    316             case FLIP_TYPE_VERTICAL:
    317                 triangleVertices = mVerticalFlipTriangleVertices;
    318                 break;
    319             case FLIP_TYPE_BOTH:
    320                 triangleVertices = mBothFlipTriangleVertices;
    321                 break;
    322             default:
    323                 triangleVertices = mRegularTriangleVertices;
    324                 break;
    325         }
    326 
    327         triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
    328         GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
    329                 /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    330         checkGlError("glVertexAttribPointer maPosition");
    331         GLES20.glEnableVertexAttribArray(maPositionHandle);
    332         checkGlError("glEnableVertexAttribArray maPositionHandle");
    333 
    334         triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
    335         GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
    336                 /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    337         checkGlError("glVertexAttribPointer maTextureHandle");
    338         GLES20.glEnableVertexAttribArray(maTextureHandle);
    339         checkGlError("glEnableVertexAttribArray maTextureHandle");
    340 
    341         GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
    342                 /*offset*/ 0);
    343         GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
    344                 /*offset*/ 0);
    345 
    346         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
    347         checkGlDrawError("glDrawArrays");
    348     }
    349 
    350     /**
    351      * Initializes GL state.  Call this after the EGL surface has been created and made current.
    352      */
    353     private void initializeGLState() {
    354         mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
    355         if (mProgram == 0) {
    356             throw new IllegalStateException("failed creating program");
    357         }
    358         maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    359         checkGlError("glGetAttribLocation aPosition");
    360         if (maPositionHandle == -1) {
    361             throw new IllegalStateException("Could not get attrib location for aPosition");
    362         }
    363         maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
    364         checkGlError("glGetAttribLocation aTextureCoord");
    365         if (maTextureHandle == -1) {
    366             throw new IllegalStateException("Could not get attrib location for aTextureCoord");
    367         }
    368 
    369         muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    370         checkGlError("glGetUniformLocation uMVPMatrix");
    371         if (muMVPMatrixHandle == -1) {
    372             throw new IllegalStateException("Could not get attrib location for uMVPMatrix");
    373         }
    374 
    375         muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
    376         checkGlError("glGetUniformLocation uSTMatrix");
    377         if (muSTMatrixHandle == -1) {
    378             throw new IllegalStateException("Could not get attrib location for uSTMatrix");
    379         }
    380 
    381         int[] textures = new int[1];
    382         GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0);
    383 
    384         mTextureID = textures[0];
    385         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
    386         checkGlError("glBindTexture mTextureID");
    387 
    388         GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
    389                 GLES20.GL_NEAREST);
    390         GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
    391                 GLES20.GL_LINEAR);
    392         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
    393                 GLES20.GL_CLAMP_TO_EDGE);
    394         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
    395                 GLES20.GL_CLAMP_TO_EDGE);
    396         checkGlError("glTexParameter");
    397     }
    398 
    399     private int getTextureId() {
    400         return mTextureID;
    401     }
    402 
    403     private void clearState() {
    404         mSurfaces.clear();
    405         for (EGLSurfaceHolder holder : mConversionSurfaces) {
    406             try {
    407                 LegacyCameraDevice.disconnectSurface(holder.surface);
    408             } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    409                 Log.w(TAG, "Surface abandoned, skipping...", e);
    410             }
    411         }
    412         mConversionSurfaces.clear();
    413         mPBufferPixels = null;
    414         if (mSurfaceTexture != null) {
    415             mSurfaceTexture.release();
    416         }
    417         mSurfaceTexture = null;
    418     }
    419 
    420     private void configureEGLContext() {
    421         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
    422         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
    423             throw new IllegalStateException("No EGL14 display");
    424         }
    425         int[] version = new int[2];
    426         if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) {
    427             throw new IllegalStateException("Cannot initialize EGL14");
    428         }
    429 
    430         int[] attribList = {
    431                 EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
    432                 EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
    433                 EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
    434                 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
    435                 EGL_RECORDABLE_ANDROID, 1,
    436                 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
    437                 EGL14.EGL_NONE
    438         };
    439         EGLConfig[] configs = new EGLConfig[1];
    440         int[] numConfigs = new int[1];
    441         EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0,
    442                 configs.length, numConfigs, /*offset*/ 0);
    443         checkEglError("eglCreateContext RGB888+recordable ES2");
    444         mConfigs = configs[0];
    445         int[] attrib_list = {
    446                 EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
    447                 EGL14.EGL_NONE
    448         };
    449         mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
    450                 attrib_list, /*offset*/ 0);
    451         checkEglError("eglCreateContext");
    452         if(mEGLContext == EGL14.EGL_NO_CONTEXT) {
    453             throw new IllegalStateException("No EGLContext could be made");
    454         }
    455     }
    456 
    457     private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {
    458         if (surfaces == null || surfaces.size() == 0) {
    459             throw new IllegalStateException("No Surfaces were provided to draw to");
    460         }
    461         int[] surfaceAttribs = {
    462                 EGL14.EGL_NONE
    463         };
    464         for (EGLSurfaceHolder holder : surfaces) {
    465             holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
    466                     holder.surface, surfaceAttribs, /*offset*/ 0);
    467             checkEglError("eglCreateWindowSurface");
    468         }
    469     }
    470 
    471     private void configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces) {
    472         if (surfaces == null || surfaces.size() == 0) {
    473             throw new IllegalStateException("No Surfaces were provided to draw to");
    474         }
    475 
    476         int maxLength = 0;
    477         for (EGLSurfaceHolder holder : surfaces) {
    478             int length = holder.width * holder.height;
    479             // Find max surface size, ensure PBuffer can hold this many pixels
    480             maxLength = (length > maxLength) ? length : maxLength;
    481             int[] surfaceAttribs = {
    482                     EGL14.EGL_WIDTH, holder.width,
    483                     EGL14.EGL_HEIGHT, holder.height,
    484                     EGL14.EGL_NONE
    485             };
    486             holder.eglSurface =
    487                     EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
    488             checkEglError("eglCreatePbufferSurface");
    489         }
    490         mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
    491                 .order(ByteOrder.nativeOrder());
    492     }
    493 
    494     private void releaseEGLContext() {
    495         if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
    496             EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
    497                     EGL14.EGL_NO_CONTEXT);
    498             dumpGlTiming();
    499             if (mSurfaces != null) {
    500                 for (EGLSurfaceHolder holder : mSurfaces) {
    501                     if (holder.eglSurface != null) {
    502                         EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
    503                     }
    504                 }
    505             }
    506             if (mConversionSurfaces != null) {
    507                 for (EGLSurfaceHolder holder : mConversionSurfaces) {
    508                     if (holder.eglSurface != null) {
    509                         EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
    510                     }
    511                 }
    512             }
    513             EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
    514             EGL14.eglReleaseThread();
    515             EGL14.eglTerminate(mEGLDisplay);
    516         }
    517 
    518         mConfigs = null;
    519         mEGLDisplay = EGL14.EGL_NO_DISPLAY;
    520         mEGLContext = EGL14.EGL_NO_CONTEXT;
    521         clearState();
    522     }
    523 
    524     private void makeCurrent(EGLSurface surface) {
    525         EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
    526         checkEglError("makeCurrent");
    527     }
    528 
    529     private boolean swapBuffers(EGLSurface surface)
    530             throws LegacyExceptionUtils.BufferQueueAbandonedException {
    531         boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
    532 
    533         int error = EGL14.eglGetError();
    534         switch (error) {
    535             case EGL14.EGL_SUCCESS:
    536                 return result;
    537 
    538             // Check for an abandoned buffer queue, or other error conditions out
    539             // of the user's control.
    540             //
    541             // From the EGL 1.4 spec (2013-12-04), Section 3.9.4 Posting Errors:
    542             //
    543             //   If eglSwapBuffers is called and the native window associated with
    544             //   surface is no longer valid, an EGL_BAD_NATIVE_WINDOW error is
    545             //   generated.
    546             //
    547             // We also interpret EGL_BAD_SURFACE as indicating an abandoned
    548             // surface, even though the EGL spec does not document it as such, for
    549             // backwards compatibility with older versions of this file.
    550             case EGL14.EGL_BAD_NATIVE_WINDOW:
    551             case EGL14.EGL_BAD_SURFACE:
    552                 throw new LegacyExceptionUtils.BufferQueueAbandonedException();
    553 
    554             default:
    555                 throw new IllegalStateException(
    556                         "swapBuffers: EGL error: 0x" + Integer.toHexString(error));
    557         }
    558     }
    559 
    560     private void checkEglError(String msg) {
    561         int error;
    562         if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
    563             throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
    564         }
    565     }
    566 
    567     private void checkGlError(String msg) {
    568         int error;
    569         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    570             throw new IllegalStateException(
    571                     msg + ": GLES20 error: 0x" + Integer.toHexString(error));
    572         }
    573     }
    574 
    575     private void checkGlDrawError(String msg)
    576             throws LegacyExceptionUtils.BufferQueueAbandonedException {
    577         int error;
    578         boolean surfaceAbandoned = false;
    579         boolean glError = false;
    580         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    581             if (error == GLES20.GL_OUT_OF_MEMORY) {
    582                 surfaceAbandoned = true;
    583             } else {
    584                 glError = true;
    585             }
    586         }
    587         if (glError) {
    588             throw new IllegalStateException(
    589                     msg + ": GLES20 error: 0x" + Integer.toHexString(error));
    590         }
    591         if (surfaceAbandoned) {
    592             throw new LegacyExceptionUtils.BufferQueueAbandonedException();
    593         }
    594     }
    595 
    596     /**
    597      * Save a measurement dump to disk, in
    598      * {@code /sdcard/CameraLegacy/durations_<time>_<width1>x<height1>_...txt}
    599      */
    600     private void dumpGlTiming() {
    601         if (mPerfMeasurer == null) return;
    602 
    603         File legacyStorageDir = new File(Environment.getExternalStorageDirectory(), "CameraLegacy");
    604         if (!legacyStorageDir.exists()){
    605             if (!legacyStorageDir.mkdirs()){
    606                 Log.e(TAG, "Failed to create directory for data dump");
    607                 return;
    608             }
    609         }
    610 
    611         StringBuilder path = new StringBuilder(legacyStorageDir.getPath());
    612         path.append(File.separator);
    613         path.append("durations_");
    614 
    615         Time now = new Time();
    616         now.setToNow();
    617         path.append(now.format2445());
    618         path.append("_S");
    619         for (EGLSurfaceHolder surface : mSurfaces) {
    620             path.append(String.format("_%d_%d", surface.width, surface.height));
    621         }
    622         path.append("_C");
    623         for (EGLSurfaceHolder surface : mConversionSurfaces) {
    624             path.append(String.format("_%d_%d", surface.width, surface.height));
    625         }
    626         path.append(".txt");
    627         mPerfMeasurer.dumpPerformanceData(path.toString());
    628     }
    629 
    630     private void setupGlTiming() {
    631         if (PerfMeasurement.isGlTimingSupported()) {
    632             Log.d(TAG, "Enabling GL performance measurement");
    633             mPerfMeasurer = new PerfMeasurement();
    634         } else {
    635             Log.d(TAG, "GL performance measurement not supported on this device");
    636             mPerfMeasurer = null;
    637         }
    638     }
    639 
    640     private void beginGlTiming() {
    641         if (mPerfMeasurer == null) return;
    642         mPerfMeasurer.startTimer();
    643     }
    644 
    645     private void addGlTimestamp(long timestamp) {
    646         if (mPerfMeasurer == null) return;
    647         mPerfMeasurer.addTimestamp(timestamp);
    648     }
    649 
    650     private void endGlTiming() {
    651         if (mPerfMeasurer == null) return;
    652         mPerfMeasurer.stopTimer();
    653     }
    654 
    655     /**
    656      * Return the surface texture to draw to - this is the texture use to when producing output
    657      * surface buffers.
    658      *
    659      * @return a {@link SurfaceTexture}.
    660      */
    661     public SurfaceTexture getSurfaceTexture() {
    662         return mSurfaceTexture;
    663     }
    664 
    665     /**
    666      * Set a collection of output {@link Surface}s that can be drawn to.
    667      *
    668      * @param surfaces a {@link Collection} of surfaces.
    669      */
    670     public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) {
    671         releaseEGLContext();
    672 
    673         if (surfaces == null || surfaces.size() == 0) {
    674             Log.w(TAG, "No output surfaces configured for GL drawing.");
    675             return;
    676         }
    677 
    678         for (Pair<Surface, Size> p : surfaces) {
    679             Surface s = p.first;
    680             Size surfaceSize = p.second;
    681             // If pixel conversions aren't handled by egl, use a pbuffer
    682             try {
    683                 EGLSurfaceHolder holder = new EGLSurfaceHolder();
    684                 holder.surface = s;
    685                 holder.width = surfaceSize.getWidth();
    686                 holder.height = surfaceSize.getHeight();
    687                 if (LegacyCameraDevice.needsConversion(s)) {
    688                     mConversionSurfaces.add(holder);
    689                     // LegacyCameraDevice is the producer of surfaces if it's not handled by EGL,
    690                     // so LegacyCameraDevice needs to connect to the surfaces.
    691                     LegacyCameraDevice.connectSurface(s);
    692                 } else {
    693                     mSurfaces.add(holder);
    694                 }
    695             } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    696                 Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
    697             }
    698         }
    699 
    700         // Set up egl display
    701         configureEGLContext();
    702 
    703         // Set up regular egl surfaces if needed
    704         if (mSurfaces.size() > 0) {
    705             configureEGLOutputSurfaces(mSurfaces);
    706         }
    707 
    708         // Set up pbuffer surface if needed
    709         if (mConversionSurfaces.size() > 0) {
    710             configureEGLPbufferSurfaces(mConversionSurfaces);
    711         }
    712         makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
    713                 mConversionSurfaces.get(0).eglSurface);
    714         initializeGLState();
    715         mSurfaceTexture = new SurfaceTexture(getTextureId());
    716 
    717         // Set up performance tracking if enabled
    718         if (SystemProperties.getBoolean(LEGACY_PERF_PROPERTY, false)) {
    719             setupGlTiming();
    720         }
    721     }
    722 
    723     /**
    724      * Draw the current buffer in the {@link SurfaceTexture} returned from
    725      * {@link #getSurfaceTexture()} into the set of target {@link Surface}s
    726      * in the next request from the given {@link CaptureCollector}, or drop
    727      * the frame if none is available.
    728      *
    729      * <p>
    730      * Any {@link Surface}s targeted must be a subset of the {@link Surface}s
    731      * set in the last {@link #configureSurfaces(java.util.Collection)} call.
    732      * </p>
    733      *
    734      * @param targetCollector the surfaces to draw to.
    735      */
    736     public void drawIntoSurfaces(CaptureCollector targetCollector) {
    737         if ((mSurfaces == null || mSurfaces.size() == 0)
    738                 && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
    739             return;
    740         }
    741 
    742         boolean doTiming = targetCollector.hasPendingPreviewCaptures();
    743         checkGlError("before updateTexImage");
    744 
    745         if (doTiming) {
    746             beginGlTiming();
    747         }
    748 
    749         mSurfaceTexture.updateTexImage();
    750 
    751         long timestamp = mSurfaceTexture.getTimestamp();
    752 
    753         Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp);
    754 
    755         // No preview request queued, drop frame.
    756         if (captureHolder == null) {
    757             if (DEBUG) {
    758                 Log.d(TAG, "Dropping preview frame.");
    759             }
    760             if (doTiming) {
    761                 endGlTiming();
    762             }
    763             return;
    764         }
    765 
    766         RequestHolder request = captureHolder.first;
    767 
    768         Collection<Surface> targetSurfaces = request.getHolderTargets();
    769         if (doTiming) {
    770             addGlTimestamp(timestamp);
    771         }
    772 
    773         List<Long> targetSurfaceIds = new ArrayList();
    774         try {
    775             targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
    776         } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    777             Log.w(TAG, "Surface abandoned, dropping frame. ", e);
    778             request.setOutputAbandoned();
    779         }
    780 
    781         for (EGLSurfaceHolder holder : mSurfaces) {
    782             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
    783                 try{
    784                     LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
    785                             holder.height);
    786                     makeCurrent(holder.eglSurface);
    787 
    788                     LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
    789                     drawFrame(mSurfaceTexture, holder.width, holder.height,
    790                             (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
    791                                     FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);
    792                     swapBuffers(holder.eglSurface);
    793                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    794                     Log.w(TAG, "Surface abandoned, dropping frame. ", e);
    795                     request.setOutputAbandoned();
    796                 }
    797             }
    798         }
    799         for (EGLSurfaceHolder holder : mConversionSurfaces) {
    800             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
    801                 makeCurrent(holder.eglSurface);
    802                 // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
    803                 try {
    804                     drawFrame(mSurfaceTexture, holder.width, holder.height,
    805                             (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
    806                                     FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
    807                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    808                     // Should never hit this.
    809                     throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
    810                 }
    811                 mPBufferPixels.clear();
    812                 GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
    813                         GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
    814                 checkGlError("glReadPixels");
    815 
    816                 try {
    817                     int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
    818                     LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
    819                             holder.height);
    820                     LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
    821                     LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
    822                             holder.width, holder.height, format);
    823                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
    824                     Log.w(TAG, "Surface abandoned, dropping frame. ", e);
    825                     request.setOutputAbandoned();
    826                 }
    827             }
    828         }
    829         targetCollector.previewProduced();
    830 
    831         if (doTiming) {
    832             endGlTiming();
    833         }
    834     }
    835 
    836     /**
    837      * Clean up the current GL context.
    838      */
    839     public void cleanupEGLContext() {
    840         releaseEGLContext();
    841     }
    842 
    843     /**
    844      * Drop all current GL operations on the floor.
    845      */
    846     public void flush() {
    847         // TODO: implement flush
    848         Log.e(TAG, "Flush not yet implemented.");
    849     }
    850 }
    851