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 = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
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 
SurfaceTextureRenderer(int facing)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 
loadShader(int shaderType, String source)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 
createProgram(String vertexSource, String fragmentSource)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 
drawFrame(SurfaceTexture st, int width, int height, int flipType)251     private void drawFrame(SurfaceTexture st, int width, int height, int flipType) {
252         checkGlError("onDrawFrame start");
253         st.getTransformMatrix(mSTMatrix);
254 
255         Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
256 
257         // Find intermediate buffer dimensions
258         Size dimens;
259         try {
260             dimens = LegacyCameraDevice.getTextureSize(st);
261         } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
262             // Should never hit this.
263             throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
264         }
265         float texWidth = dimens.getWidth();
266         float texHeight = dimens.getHeight();
267 
268         if (texWidth <= 0 || texHeight <= 0) {
269             throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
270         }
271 
272         // Letterbox or pillar-box output dimensions into intermediate dimensions.
273         RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
274         RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
275         android.graphics.Matrix boxingXform = new android.graphics.Matrix();
276         boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
277         boxingXform.mapRect(output);
278 
279         // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate
280         // buffer dimensions.
281         float scaleX = intermediate.width() / output.width();
282         float scaleY = intermediate.height() / output.height();
283 
284         // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space
285         // coordinates in the shader.  To avoid stretching, we need to scale the larger dimension
286         // of the intermediate buffer so that the output buffer is actually letter-boxed
287         // or pillar-boxed into the intermediate buffer after clipping.
288         Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1);
289 
290         if (DEBUG) {
291             Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
292                     width + "x" + height + " surface, intermediate buffer size is " + texWidth +
293                     "x" + texHeight);
294         }
295 
296         // Set viewport to be output buffer dimensions
297         GLES20.glViewport(0, 0, width, height);
298 
299         if (DEBUG) {
300             GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
301             GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
302         }
303 
304         GLES20.glUseProgram(mProgram);
305         checkGlError("glUseProgram");
306 
307         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
308         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
309 
310         FloatBuffer triangleVertices;
311         switch(flipType) {
312             case FLIP_TYPE_HORIZONTAL:
313                 triangleVertices = mHorizontalFlipTriangleVertices;
314                 break;
315             case FLIP_TYPE_VERTICAL:
316                 triangleVertices = mVerticalFlipTriangleVertices;
317                 break;
318             case FLIP_TYPE_BOTH:
319                 triangleVertices = mBothFlipTriangleVertices;
320                 break;
321             default:
322                 triangleVertices = mRegularTriangleVertices;
323                 break;
324         }
325 
326         triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
327         GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
328                 /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
329         checkGlError("glVertexAttribPointer maPosition");
330         GLES20.glEnableVertexAttribArray(maPositionHandle);
331         checkGlError("glEnableVertexAttribArray maPositionHandle");
332 
333         triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
334         GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT,
335                 /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
336         checkGlError("glVertexAttribPointer maTextureHandle");
337         GLES20.glEnableVertexAttribArray(maTextureHandle);
338         checkGlError("glEnableVertexAttribArray maTextureHandle");
339 
340         GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
341                 /*offset*/ 0);
342         GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
343                 /*offset*/ 0);
344 
345         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4);
346         checkGlError("glDrawArrays");
347     }
348 
349     /**
350      * Initializes GL state.  Call this after the EGL surface has been created and made current.
351      */
initializeGLState()352     private void initializeGLState() {
353         mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
354         if (mProgram == 0) {
355             throw new IllegalStateException("failed creating program");
356         }
357         maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
358         checkGlError("glGetAttribLocation aPosition");
359         if (maPositionHandle == -1) {
360             throw new IllegalStateException("Could not get attrib location for aPosition");
361         }
362         maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
363         checkGlError("glGetAttribLocation aTextureCoord");
364         if (maTextureHandle == -1) {
365             throw new IllegalStateException("Could not get attrib location for aTextureCoord");
366         }
367 
368         muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
369         checkGlError("glGetUniformLocation uMVPMatrix");
370         if (muMVPMatrixHandle == -1) {
371             throw new IllegalStateException("Could not get attrib location for uMVPMatrix");
372         }
373 
374         muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
375         checkGlError("glGetUniformLocation uSTMatrix");
376         if (muSTMatrixHandle == -1) {
377             throw new IllegalStateException("Could not get attrib location for uSTMatrix");
378         }
379 
380         int[] textures = new int[1];
381         GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0);
382 
383         mTextureID = textures[0];
384         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
385         checkGlError("glBindTexture mTextureID");
386 
387         GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
388                 GLES20.GL_NEAREST);
389         GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
390                 GLES20.GL_LINEAR);
391         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
392                 GLES20.GL_CLAMP_TO_EDGE);
393         GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
394                 GLES20.GL_CLAMP_TO_EDGE);
395         checkGlError("glTexParameter");
396     }
397 
getTextureId()398     private int getTextureId() {
399         return mTextureID;
400     }
401 
clearState()402     private void clearState() {
403         mSurfaces.clear();
404         mConversionSurfaces.clear();
405         mPBufferPixels = null;
406         if (mSurfaceTexture != null) {
407             mSurfaceTexture.release();
408         }
409         mSurfaceTexture = null;
410     }
411 
configureEGLContext()412     private void configureEGLContext() {
413         mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
414         if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
415             throw new IllegalStateException("No EGL14 display");
416         }
417         int[] version = new int[2];
418         if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) {
419             throw new IllegalStateException("Cannot initialize EGL14");
420         }
421 
422         int[] attribList = {
423                 EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
424                 EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
425                 EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
426                 EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
427                 EGL_RECORDABLE_ANDROID, 1,
428                 EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
429                 EGL14.EGL_NONE
430         };
431         EGLConfig[] configs = new EGLConfig[1];
432         int[] numConfigs = new int[1];
433         EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0,
434                 configs.length, numConfigs, /*offset*/ 0);
435         checkEglError("eglCreateContext RGB888+recordable ES2");
436         mConfigs = configs[0];
437         int[] attrib_list = {
438                 EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
439                 EGL14.EGL_NONE
440         };
441         mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
442                 attrib_list, /*offset*/ 0);
443         checkEglError("eglCreateContext");
444         if(mEGLContext == EGL14.EGL_NO_CONTEXT) {
445             throw new IllegalStateException("No EGLContext could be made");
446         }
447     }
448 
configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces)449     private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) {
450         if (surfaces == null || surfaces.size() == 0) {
451             throw new IllegalStateException("No Surfaces were provided to draw to");
452         }
453         int[] surfaceAttribs = {
454                 EGL14.EGL_NONE
455         };
456         for (EGLSurfaceHolder holder : surfaces) {
457             holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs,
458                     holder.surface, surfaceAttribs, /*offset*/ 0);
459             checkEglError("eglCreateWindowSurface");
460         }
461     }
462 
configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces)463     private void configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces) {
464         if (surfaces == null || surfaces.size() == 0) {
465             throw new IllegalStateException("No Surfaces were provided to draw to");
466         }
467 
468         int maxLength = 0;
469         for (EGLSurfaceHolder holder : surfaces) {
470             int length = holder.width * holder.height;
471             // Find max surface size, ensure PBuffer can hold this many pixels
472             maxLength = (length > maxLength) ? length : maxLength;
473             int[] surfaceAttribs = {
474                     EGL14.EGL_WIDTH, holder.width,
475                     EGL14.EGL_HEIGHT, holder.height,
476                     EGL14.EGL_NONE
477             };
478             holder.eglSurface =
479                     EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0);
480             checkEglError("eglCreatePbufferSurface");
481         }
482         mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES)
483                 .order(ByteOrder.nativeOrder());
484     }
485 
releaseEGLContext()486     private void releaseEGLContext() {
487         if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
488             EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
489                     EGL14.EGL_NO_CONTEXT);
490             dumpGlTiming();
491             if (mSurfaces != null) {
492                 for (EGLSurfaceHolder holder : mSurfaces) {
493                     if (holder.eglSurface != null) {
494                         EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
495                     }
496                 }
497             }
498             if (mConversionSurfaces != null) {
499                 for (EGLSurfaceHolder holder : mConversionSurfaces) {
500                     if (holder.eglSurface != null) {
501                         EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
502                     }
503                 }
504             }
505             EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
506             EGL14.eglReleaseThread();
507             EGL14.eglTerminate(mEGLDisplay);
508         }
509 
510         mConfigs = null;
511         mEGLDisplay = EGL14.EGL_NO_DISPLAY;
512         mEGLContext = EGL14.EGL_NO_CONTEXT;
513         clearState();
514     }
515 
makeCurrent(EGLSurface surface)516     private void makeCurrent(EGLSurface surface) {
517         EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
518         checkEglError("makeCurrent");
519     }
520 
swapBuffers(EGLSurface surface)521     private boolean swapBuffers(EGLSurface surface) {
522         boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
523         checkEglError("swapBuffers");
524         return result;
525     }
526 
checkEglError(String msg)527     private void checkEglError(String msg) {
528         int error;
529         if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
530             throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error));
531         }
532     }
533 
checkGlError(String msg)534     private void checkGlError(String msg) {
535         int error;
536         while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
537             throw new IllegalStateException(msg + ": GLES20 error: 0x" + Integer.toHexString(error));
538         }
539     }
540 
541     /**
542      * Save a measurement dump to disk, in
543      * {@code /sdcard/CameraLegacy/durations_<time>_<width1>x<height1>_...txt}
544      */
dumpGlTiming()545     private void dumpGlTiming() {
546         if (mPerfMeasurer == null) return;
547 
548         File legacyStorageDir = new File(Environment.getExternalStorageDirectory(), "CameraLegacy");
549         if (!legacyStorageDir.exists()){
550             if (!legacyStorageDir.mkdirs()){
551                 Log.e(TAG, "Failed to create directory for data dump");
552                 return;
553             }
554         }
555 
556         StringBuilder path = new StringBuilder(legacyStorageDir.getPath());
557         path.append(File.separator);
558         path.append("durations_");
559 
560         Time now = new Time();
561         now.setToNow();
562         path.append(now.format2445());
563         path.append("_S");
564         for (EGLSurfaceHolder surface : mSurfaces) {
565             path.append(String.format("_%d_%d", surface.width, surface.height));
566         }
567         path.append("_C");
568         for (EGLSurfaceHolder surface : mConversionSurfaces) {
569             path.append(String.format("_%d_%d", surface.width, surface.height));
570         }
571         path.append(".txt");
572         mPerfMeasurer.dumpPerformanceData(path.toString());
573     }
574 
setupGlTiming()575     private void setupGlTiming() {
576         if (PerfMeasurement.isGlTimingSupported()) {
577             Log.d(TAG, "Enabling GL performance measurement");
578             mPerfMeasurer = new PerfMeasurement();
579         } else {
580             Log.d(TAG, "GL performance measurement not supported on this device");
581             mPerfMeasurer = null;
582         }
583     }
584 
beginGlTiming()585     private void beginGlTiming() {
586         if (mPerfMeasurer == null) return;
587         mPerfMeasurer.startTimer();
588     }
589 
addGlTimestamp(long timestamp)590     private void addGlTimestamp(long timestamp) {
591         if (mPerfMeasurer == null) return;
592         mPerfMeasurer.addTimestamp(timestamp);
593     }
594 
endGlTiming()595     private void endGlTiming() {
596         if (mPerfMeasurer == null) return;
597         mPerfMeasurer.stopTimer();
598     }
599 
600     /**
601      * Return the surface texture to draw to - this is the texture use to when producing output
602      * surface buffers.
603      *
604      * @return a {@link SurfaceTexture}.
605      */
getSurfaceTexture()606     public SurfaceTexture getSurfaceTexture() {
607         return mSurfaceTexture;
608     }
609 
610     /**
611      * Set a collection of output {@link Surface}s that can be drawn to.
612      *
613      * @param surfaces a {@link Collection} of surfaces.
614      */
configureSurfaces(Collection<Pair<Surface, Size>> surfaces)615     public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) {
616         releaseEGLContext();
617 
618         if (surfaces == null || surfaces.size() == 0) {
619             Log.w(TAG, "No output surfaces configured for GL drawing.");
620             return;
621         }
622 
623         for (Pair<Surface, Size> p : surfaces) {
624             Surface s = p.first;
625             Size surfaceSize = p.second;
626             // If pixel conversions aren't handled by egl, use a pbuffer
627             try {
628                 EGLSurfaceHolder holder = new EGLSurfaceHolder();
629                 holder.surface = s;
630                 holder.width = surfaceSize.getWidth();
631                 holder.height = surfaceSize.getHeight();
632                 if (LegacyCameraDevice.needsConversion(s)) {
633                     // Always override to YV12 output for YUV surface formats.
634                     LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12);
635                     mConversionSurfaces.add(holder);
636                 } else {
637                     mSurfaces.add(holder);
638                 }
639             } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
640                 Log.w(TAG, "Surface abandoned, skipping configuration... ", e);
641             }
642         }
643 
644         // Set up egl display
645         configureEGLContext();
646 
647         // Set up regular egl surfaces if needed
648         if (mSurfaces.size() > 0) {
649             configureEGLOutputSurfaces(mSurfaces);
650         }
651 
652         // Set up pbuffer surface if needed
653         if (mConversionSurfaces.size() > 0) {
654             configureEGLPbufferSurfaces(mConversionSurfaces);
655         }
656         makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface :
657                 mConversionSurfaces.get(0).eglSurface);
658         initializeGLState();
659         mSurfaceTexture = new SurfaceTexture(getTextureId());
660 
661         // Set up performance tracking if enabled
662         if (SystemProperties.getBoolean(LEGACY_PERF_PROPERTY, false)) {
663             setupGlTiming();
664         }
665     }
666 
667     /**
668      * Draw the current buffer in the {@link SurfaceTexture} returned from
669      * {@link #getSurfaceTexture()} into the set of target {@link Surface}s
670      * in the next request from the given {@link CaptureCollector}, or drop
671      * the frame if none is available.
672      *
673      * <p>
674      * Any {@link Surface}s targeted must be a subset of the {@link Surface}s
675      * set in the last {@link #configureSurfaces(java.util.Collection)} call.
676      * </p>
677      *
678      * @param targetCollector the surfaces to draw to.
679      */
drawIntoSurfaces(CaptureCollector targetCollector)680     public void drawIntoSurfaces(CaptureCollector targetCollector) {
681         if ((mSurfaces == null || mSurfaces.size() == 0)
682                 && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) {
683             return;
684         }
685 
686         boolean doTiming = targetCollector.hasPendingPreviewCaptures();
687         checkGlError("before updateTexImage");
688 
689         if (doTiming) {
690             beginGlTiming();
691         }
692 
693         mSurfaceTexture.updateTexImage();
694 
695         long timestamp = mSurfaceTexture.getTimestamp();
696 
697         Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp);
698 
699         // No preview request queued, drop frame.
700         if (captureHolder == null) {
701             if (DEBUG) {
702                 Log.d(TAG, "Dropping preview frame.");
703             }
704             if (doTiming) {
705                 endGlTiming();
706             }
707             return;
708         }
709 
710         RequestHolder request = captureHolder.first;
711 
712         Collection<Surface> targetSurfaces = request.getHolderTargets();
713         if (doTiming) {
714             addGlTimestamp(timestamp);
715         }
716 
717         List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
718         for (EGLSurfaceHolder holder : mSurfaces) {
719             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
720                 try{
721                     LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
722                             holder.height);
723                     makeCurrent(holder.eglSurface);
724 
725                     LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
726                     drawFrame(mSurfaceTexture, holder.width, holder.height,
727                             (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
728                                     FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE);
729                     swapBuffers(holder.eglSurface);
730                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
731                     Log.w(TAG, "Surface abandoned, dropping frame. ", e);
732                 }
733             }
734         }
735         for (EGLSurfaceHolder holder : mConversionSurfaces) {
736             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
737                 makeCurrent(holder.eglSurface);
738                 // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip
739                 drawFrame(mSurfaceTexture, holder.width, holder.height,
740                         (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
741                                 FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL);
742                 mPBufferPixels.clear();
743                 GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
744                         GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
745                 checkGlError("glReadPixels");
746 
747                 try {
748                     int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
749                     LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
750                             holder.height);
751                     LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
752                     LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
753                             holder.width, holder.height, format);
754                 } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
755                     Log.w(TAG, "Surface abandoned, dropping frame. ", e);
756                 }
757             }
758         }
759         targetCollector.previewProduced();
760 
761         if (doTiming) {
762             endGlTiming();
763         }
764     }
765 
766     /**
767      * Clean up the current GL context.
768      */
cleanupEGLContext()769     public void cleanupEGLContext() {
770         releaseEGLContext();
771     }
772 
773     /**
774      * Drop all current GL operations on the floor.
775      */
flush()776     public void flush() {
777         // TODO: implement flush
778         Log.e(TAG, "Flush not yet implemented.");
779     }
780 }
781