1feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk/* 228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * Copyright (C) 2014 The Android Open Source Project 328c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * 428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * Licensed under the Apache License, Version 2.0 (the "License"); 528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * you may not use this file except in compliance with the License. 628c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * You may obtain a copy of the License at 728c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * 828c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * http://www.apache.org/licenses/LICENSE-2.0 928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * 1028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * Unless required by applicable law or agreed to in writing, software 1128c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * distributed under the License is distributed on an "AS IS" BASIS, 1228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1328c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * See the License for the specific language governing permissions and 1428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk * limitations under the License. 1528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk */ 16feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkpackage android.hardware.camera2.legacy; 17feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 18feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.ImageFormat; 19a9bc3559109836efe7479a3279713bd58810b153Ruben Brunkimport android.graphics.RectF; 20feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.SurfaceTexture; 21b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunkimport android.hardware.camera2.CameraCharacteristics; 22e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvalaimport android.os.Environment; 23feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.EGL14; 24feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.EGLConfig; 25feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.EGLContext; 26feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.EGLDisplay; 27feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.EGLSurface; 28feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.GLES11Ext; 29feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.GLES20; 30feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.opengl.Matrix; 31e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvalaimport android.text.format.Time; 32feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.util.Log; 3391838ded36131525312739c0929913b215519c2aRuben Brunkimport android.util.Pair; 34ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunkimport android.util.Size; 35feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.Surface; 36e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvalaimport android.os.SystemProperties; 37feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 38e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvalaimport java.io.File; 39feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.nio.ByteBuffer; 40feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.nio.ByteOrder; 41feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.nio.FloatBuffer; 42feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.util.ArrayList; 43feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.util.Collection; 44feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.util.List; 45feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 46feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk/** 47feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * A renderer class that manages the GL state, and can draw a frame into a set of output 48feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * {@link Surface}s. 49feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 50feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkpublic class SurfaceTextureRenderer { 51feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final String TAG = SurfaceTextureRenderer.class.getSimpleName(); 52a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala private static final boolean DEBUG = false; 53feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int EGL_RECORDABLE_ANDROID = 0x3142; // from EGL/eglext.h 54feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int GL_MATRIX_SIZE = 16; 55feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int VERTEX_POS_SIZE = 3; 56feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int VERTEX_UV_SIZE = 2; 57feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int EGL_COLOR_BITLENGTH = 8; 58feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int GLES_VERSION = 2; 59feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int PBUFFER_PIXEL_BYTES = 4; 60feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 61433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final int FLIP_TYPE_NONE = 0; 62433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final int FLIP_TYPE_HORIZONTAL = 1; 63433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final int FLIP_TYPE_VERTICAL = 2; 64433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL; 65433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 66feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY; 67feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT; 68feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private EGLConfig mConfigs; 69feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 70feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private class EGLSurfaceHolder { 71feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Surface surface; 72feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGLSurface eglSurface; 73feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int width; 74feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int height; 75feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 76feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 77feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private List<EGLSurfaceHolder> mSurfaces = new ArrayList<EGLSurfaceHolder>(); 78feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private List<EGLSurfaceHolder> mConversionSurfaces = new ArrayList<EGLSurfaceHolder>(); 79feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 80feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ByteBuffer mPBufferPixels; 81feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 82feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Hold this to avoid GC 83feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private volatile SurfaceTexture mSurfaceTexture; 84feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 85feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int FLOAT_SIZE_BYTES = 4; 86feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; 87feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; 88feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; 89b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk 90433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // Sampling is mirrored across the horizontal axis 91433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final float[] sHorizontalFlipTriangleVertices = { 92b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk // X, Y, Z, U, V 93b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk -1.0f, -1.0f, 0, 1.f, 0.f, 94b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk 1.0f, -1.0f, 0, 0.f, 0.f, 95b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk -1.0f, 1.0f, 0, 1.f, 1.f, 96b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk 1.0f, 1.0f, 0, 0.f, 1.f, 97b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk }; 98b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk 99433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // Sampling is mirrored across the vertical axis 100433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final float[] sVerticalFlipTriangleVertices = { 101433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // X, Y, Z, U, V 102433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk -1.0f, -1.0f, 0, 0.f, 1.f, 103433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 1.0f, -1.0f, 0, 1.f, 1.f, 104433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk -1.0f, 1.0f, 0, 0.f, 0.f, 105433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 1.0f, 1.0f, 0, 1.f, 0.f, 106433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk }; 107433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 108433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // Sampling is mirrored across the both axes 109433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final float[] sBothFlipTriangleVertices = { 110433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // X, Y, Z, U, V 111433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk -1.0f, -1.0f, 0, 1.f, 1.f, 112433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 1.0f, -1.0f, 0, 0.f, 1.f, 113433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk -1.0f, 1.0f, 0, 1.f, 0.f, 114433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 1.0f, 1.0f, 0, 0.f, 0.f, 115433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk }; 116433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 117b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk // Sampling is 1:1 for a straight copy for the back camera 118433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private static final float[] sRegularTriangleVertices = { 119feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // X, Y, Z, U, V 120feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk -1.0f, -1.0f, 0, 0.f, 0.f, 121feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 1.0f, -1.0f, 0, 1.f, 0.f, 122feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk -1.0f, 1.0f, 0, 0.f, 1.f, 123feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 1.0f, 1.0f, 0, 1.f, 1.f, 124feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }; 125feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 126433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private FloatBuffer mRegularTriangleVertices; 127433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private FloatBuffer mHorizontalFlipTriangleVertices; 128433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private FloatBuffer mVerticalFlipTriangleVertices; 129433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private FloatBuffer mBothFlipTriangleVertices; 130433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk private final int mFacing; 131feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 132feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 133feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * As used in this file, this vertex shader maps a unit square to the view, and 134feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * tells the fragment shader to interpolate over it. Each surface pixel position 135feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * is mapped to a 2D homogeneous texture coordinate of the form (s, t, 0, 1) with 136feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * s and t in the inclusive range [0, 1], and the matrix from 137feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * {@link SurfaceTexture#getTransformMatrix(float[])} is used to map this 138feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * coordinate to a texture location. 139feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 140feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final String VERTEX_SHADER = 141feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk "uniform mat4 uMVPMatrix;\n" + 14228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "uniform mat4 uSTMatrix;\n" + 14328c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "attribute vec4 aPosition;\n" + 14428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "attribute vec4 aTextureCoord;\n" + 14528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "varying vec2 vTextureCoord;\n" + 14628c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "void main() {\n" + 14728c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk " gl_Position = uMVPMatrix * aPosition;\n" + 14828c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + 14928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "}\n"; 150feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 151feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 152feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * This fragment shader simply draws the color in the 2D texture at 153feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * the location from the {@code VERTEX_SHADER}. 154feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 155feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final String FRAGMENT_SHADER = 156feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk "#extension GL_OES_EGL_image_external : require\n" + 15728c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "precision mediump float;\n" + 15828c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "varying vec2 vTextureCoord;\n" + 15928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "uniform samplerExternalOES sTexture;\n" + 16028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "void main() {\n" + 16128c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 16228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk "}\n"; 163feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 164feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private float[] mMVPMatrix = new float[GL_MATRIX_SIZE]; 165feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private float[] mSTMatrix = new float[GL_MATRIX_SIZE]; 166feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 167feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mProgram; 168feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mTextureID = 0; 169feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int muMVPMatrixHandle; 170feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int muSTMatrixHandle; 171feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int maPositionHandle; 172feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int maTextureHandle; 173feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 174e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private PerfMeasurement mPerfMeasurer = null; 175e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private static final String LEGACY_PERF_PROPERTY = "persist.camera.legacy_perf"; 176e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 177b68dd5c8b92d376540ac4ae6ed59671db641685eRuben Brunk public SurfaceTextureRenderer(int facing) { 178433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mFacing = facing; 179433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 180433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mRegularTriangleVertices = ByteBuffer.allocateDirect(sRegularTriangleVertices.length * 181433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); 182433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mRegularTriangleVertices.put(sRegularTriangleVertices).position(0); 183433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 184433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mHorizontalFlipTriangleVertices = ByteBuffer.allocateDirect( 185433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk sHorizontalFlipTriangleVertices.length * FLOAT_SIZE_BYTES). 186433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk order(ByteOrder.nativeOrder()).asFloatBuffer(); 187433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mHorizontalFlipTriangleVertices.put(sHorizontalFlipTriangleVertices).position(0); 188433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 189433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mVerticalFlipTriangleVertices = ByteBuffer.allocateDirect( 190433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk sVerticalFlipTriangleVertices.length * FLOAT_SIZE_BYTES). 191433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk order(ByteOrder.nativeOrder()).asFloatBuffer(); 192433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mVerticalFlipTriangleVertices.put(sVerticalFlipTriangleVertices).position(0); 193433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 194433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mBothFlipTriangleVertices = ByteBuffer.allocateDirect( 195433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk sBothFlipTriangleVertices.length * FLOAT_SIZE_BYTES). 196433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk order(ByteOrder.nativeOrder()).asFloatBuffer(); 197433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk mBothFlipTriangleVertices.put(sBothFlipTriangleVertices).position(0); 198433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 199feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Matrix.setIdentityM(mSTMatrix, 0); 200feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 201feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 202feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int loadShader(int shaderType, String source) { 203feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int shader = GLES20.glCreateShader(shaderType); 204feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glCreateShader type=" + shaderType); 205feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glShaderSource(shader, source); 206feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glCompileShader(shader); 207feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] compiled = new int[1]; 208feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 209feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (compiled[0] == 0) { 210feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, "Could not compile shader " + shaderType + ":"); 211feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader)); 212feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glDeleteShader(shader); 213feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // TODO: handle this more gracefully 214feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not compile shader " + shaderType); 215feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 216feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return shader; 217feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 218feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 219feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int createProgram(String vertexSource, String fragmentSource) { 220feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 221feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (vertexShader == 0) { 222feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return 0; 223feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 224feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 225feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (pixelShader == 0) { 226feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return 0; 227feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 228feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 229feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int program = GLES20.glCreateProgram(); 230feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glCreateProgram"); 231feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (program == 0) { 232feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, "Could not create program"); 233feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 234feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glAttachShader(program, vertexShader); 235feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glAttachShader"); 236feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glAttachShader(program, pixelShader); 237feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glAttachShader"); 238feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glLinkProgram(program); 239feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] linkStatus = new int[1]; 240feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 241feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (linkStatus[0] != GLES20.GL_TRUE) { 242feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, "Could not link program: "); 243feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 244feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glDeleteProgram(program); 245feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // TODO: handle this more gracefully 246feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not link program"); 247feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 248feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return program; 249feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 250feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 251091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh private void drawFrame(SurfaceTexture st, int width, int height, int flipType) 252091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throws LegacyExceptionUtils.BufferQueueAbandonedException { 253feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("onDrawFrame start"); 254feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk st.getTransformMatrix(mSTMatrix); 255feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 256a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0); 257a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk 258a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk // Find intermediate buffer dimensions 25928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk Size dimens; 26028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk try { 26128c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk dimens = LegacyCameraDevice.getTextureSize(st); 26228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 26328c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk // Should never hit this. 26428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e); 26528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk } 26628c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk float texWidth = dimens.getWidth(); 26728c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk float texHeight = dimens.getHeight(); 26828c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 26928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk if (texWidth <= 0 || texHeight <= 0) { 27028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk throw new IllegalStateException("Illegal intermediate texture with dimension of 0"); 27128c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk } 27228c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 273259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // Letterbox or pillar-box output dimensions into intermediate dimensions. 274a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight); 275a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height); 276a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk android.graphics.Matrix boxingXform = new android.graphics.Matrix(); 277a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER); 278a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk boxingXform.mapRect(output); 27928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 280259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate 281a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk // buffer dimensions. 282a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk float scaleX = intermediate.width() / output.width(); 283a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk float scaleY = intermediate.height() / output.height(); 28428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 285259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space 286259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // coordinates in the shader. To avoid stretching, we need to scale the larger dimension 287259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // of the intermediate buffer so that the output buffer is actually letter-boxed 288259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk // or pillar-boxed into the intermediate buffer after clipping. 289259f3d90b8784e150e4b903f9810f089f2d9bd42Ruben Brunk Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1); 29028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 291feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (DEBUG) { 292a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " + 293a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk width + "x" + height + " surface, intermediate buffer size is " + texWidth + 294a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk "x" + texHeight); 29528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk } 29628c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 297a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk // Set viewport to be output buffer dimensions 298a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk GLES20.glViewport(0, 0, width, height); 29928c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 30028c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk if (DEBUG) { 30128c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); 302feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 303feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 304feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 305feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glUseProgram(mProgram); 306feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glUseProgram"); 307feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 308feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 309feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); 310feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 311433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk FloatBuffer triangleVertices; 312433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk switch(flipType) { 313433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk case FLIP_TYPE_HORIZONTAL: 314433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices = mHorizontalFlipTriangleVertices; 315433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk break; 316433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk case FLIP_TYPE_VERTICAL: 317433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices = mVerticalFlipTriangleVertices; 318433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk break; 319433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk case FLIP_TYPE_BOTH: 320433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices = mBothFlipTriangleVertices; 321433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk break; 322433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk default: 323433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices = mRegularTriangleVertices; 324433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk break; 325433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk } 326433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk 327433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 328feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT, 329433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 330feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glVertexAttribPointer maPosition"); 331feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glEnableVertexAttribArray(maPositionHandle); 332feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glEnableVertexAttribArray maPositionHandle"); 333feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 334433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 335feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glVertexAttribPointer(maTextureHandle, VERTEX_UV_SIZE, GLES20.GL_FLOAT, 336433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 337feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glVertexAttribPointer maTextureHandle"); 338feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glEnableVertexAttribArray(maTextureHandle); 339feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glEnableVertexAttribArray maTextureHandle"); 340feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 341feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix, 342feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /*offset*/ 0); 343feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix, 344feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /*offset*/ 0); 345feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 346feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /*offset*/ 0, /*count*/ 4); 347091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh checkGlDrawError("glDrawArrays"); 348feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 349feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 350feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 351feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Initializes GL state. Call this after the EGL surface has been created and made current. 352feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 353feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void initializeGLState() { 354feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER); 355feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mProgram == 0) { 356feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("failed creating program"); 357feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 358feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); 359feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glGetAttribLocation aPosition"); 360feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (maPositionHandle == -1) { 361feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not get attrib location for aPosition"); 362feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 363feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); 364feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glGetAttribLocation aTextureCoord"); 365feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (maTextureHandle == -1) { 366feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not get attrib location for aTextureCoord"); 367feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 368feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 369feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 370feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glGetUniformLocation uMVPMatrix"); 371feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (muMVPMatrixHandle == -1) { 372feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not get attrib location for uMVPMatrix"); 373feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 374feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 375feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix"); 376feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glGetUniformLocation uSTMatrix"); 377feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (muSTMatrixHandle == -1) { 378feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Could not get attrib location for uSTMatrix"); 379feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 380feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 381feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] textures = new int[1]; 382feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glGenTextures(/*n*/ 1, textures, /*offset*/ 0); 383feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 384feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mTextureID = textures[0]; 385feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); 386feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glBindTexture mTextureID"); 387feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 388feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, 389feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.GL_NEAREST); 390feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, 391feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.GL_LINEAR); 392feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, 393feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.GL_CLAMP_TO_EDGE); 394feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, 395feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk GLES20.GL_CLAMP_TO_EDGE); 396feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glTexParameter"); 397feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 398feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 399feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int getTextureId() { 400feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return mTextureID; 401feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 402feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 403feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void clearState() { 404feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mSurfaces.clear(); 40529c3630563452c9635f0510d071839c112d3017dChien-Yu Chen for (EGLSurfaceHolder holder : mConversionSurfaces) { 40629c3630563452c9635f0510d071839c112d3017dChien-Yu Chen try { 40729c3630563452c9635f0510d071839c112d3017dChien-Yu Chen LegacyCameraDevice.disconnectSurface(holder.surface); 40829c3630563452c9635f0510d071839c112d3017dChien-Yu Chen } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 40929c3630563452c9635f0510d071839c112d3017dChien-Yu Chen Log.w(TAG, "Surface abandoned, skipping...", e); 41029c3630563452c9635f0510d071839c112d3017dChien-Yu Chen } 41129c3630563452c9635f0510d071839c112d3017dChien-Yu Chen } 412feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mConversionSurfaces.clear(); 413feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPBufferPixels = null; 4143fe9eba9044c0b20ed349a4b9094bf1fa7942cdfRuben Brunk if (mSurfaceTexture != null) { 4153fe9eba9044c0b20ed349a4b9094bf1fa7942cdfRuben Brunk mSurfaceTexture.release(); 4163fe9eba9044c0b20ed349a4b9094bf1fa7942cdfRuben Brunk } 417feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mSurfaceTexture = null; 418feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 419feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 420feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void configureEGLContext() { 421feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); 422feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) { 423feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("No EGL14 display"); 424feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 425feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] version = new int[2]; 426feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) { 427feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("Cannot initialize EGL14"); 428feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 429feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 430feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] attribList = { 431feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH, 432feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH, 433feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH, 434feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, 435feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL_RECORDABLE_ANDROID, 1, 436feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT, 437feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_NONE 438feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }; 439feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGLConfig[] configs = new EGLConfig[1]; 440feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] numConfigs = new int[1]; 441feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0, 442feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk configs.length, numConfigs, /*offset*/ 0); 443feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkEglError("eglCreateContext RGB888+recordable ES2"); 444feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mConfigs = configs[0]; 445feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] attrib_list = { 446feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, 447feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_NONE 448feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }; 449feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, 450feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk attrib_list, /*offset*/ 0); 451feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkEglError("eglCreateContext"); 452feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if(mEGLContext == EGL14.EGL_NO_CONTEXT) { 453feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("No EGLContext could be made"); 454feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 455feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 456feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 457feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void configureEGLOutputSurfaces(Collection<EGLSurfaceHolder> surfaces) { 458feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (surfaces == null || surfaces.size() == 0) { 459feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("No Surfaces were provided to draw to"); 460feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 461feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int[] surfaceAttribs = { 462feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_NONE 463feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }; 464feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : surfaces) { 465f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs, 466f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.surface, surfaceAttribs, /*offset*/ 0); 467f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk checkEglError("eglCreateWindowSurface"); 468feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 469feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 470feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 471feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void configureEGLPbufferSurfaces(Collection<EGLSurfaceHolder> surfaces) { 472feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (surfaces == null || surfaces.size() == 0) { 473feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException("No Surfaces were provided to draw to"); 474feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 475feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 476feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int maxLength = 0; 477feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : surfaces) { 478f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk int length = holder.width * holder.height; 479f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk // Find max surface size, ensure PBuffer can hold this many pixels 480f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk maxLength = (length > maxLength) ? length : maxLength; 481f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk int[] surfaceAttribs = { 482f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk EGL14.EGL_WIDTH, holder.width, 483f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk EGL14.EGL_HEIGHT, holder.height, 484f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk EGL14.EGL_NONE 485f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk }; 486f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.eglSurface = 487f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0); 488f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk checkEglError("eglCreatePbufferSurface"); 489feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 490feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES) 491feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk .order(ByteOrder.nativeOrder()); 492feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 493feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 494feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void releaseEGLContext() { 495feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) { 496feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, 497feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.EGL_NO_CONTEXT); 498e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala dumpGlTiming(); 499feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mSurfaces != null) { 500feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : mSurfaces) { 501feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (holder.eglSurface != null) { 502feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface); 503feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 504feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 505feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 506feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mConversionSurfaces != null) { 507feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : mConversionSurfaces) { 508feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (holder.eglSurface != null) { 509feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface); 510feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 511feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 512feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 513feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglDestroyContext(mEGLDisplay, mEGLContext); 514feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglReleaseThread(); 515feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglTerminate(mEGLDisplay); 516feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 517feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 518feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mConfigs = null; 519feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mEGLDisplay = EGL14.EGL_NO_DISPLAY; 520feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mEGLContext = EGL14.EGL_NO_CONTEXT; 521feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk clearState(); 522feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 523feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 524feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void makeCurrent(EGLSurface surface) { 525feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext); 526feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkEglError("makeCurrent"); 527feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 528feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 5292da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen private boolean swapBuffers(EGLSurface surface) 5302da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen throws LegacyExceptionUtils.BufferQueueAbandonedException { 531feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface); 5322da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen int error = EGL14.eglGetError(); 5332da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen if (error == EGL14.EGL_BAD_SURFACE) { 5342da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen throw new LegacyExceptionUtils.BufferQueueAbandonedException(); 5352da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen } else if (error != EGL14.EGL_SUCCESS) { 5362da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen throw new IllegalStateException("swapBuffers: EGL error: 0x" + 5372da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen Integer.toHexString(error)); 5382da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen } 539feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return result; 540feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 541feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 542feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void checkEglError(String msg) { 543feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int error; 544feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) { 545feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk throw new IllegalStateException(msg + ": EGL error: 0x" + Integer.toHexString(error)); 546feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 547feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 548feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 549feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void checkGlError(String msg) { 550feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int error; 551feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 552091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throw new IllegalStateException( 553091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh msg + ": GLES20 error: 0x" + Integer.toHexString(error)); 554091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 555091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 556091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh 557091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh private void checkGlDrawError(String msg) 558091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throws LegacyExceptionUtils.BufferQueueAbandonedException { 559091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh int error; 560091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh boolean surfaceAbandoned = false; 561091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh boolean glError = false; 562091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 563091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh if (error == GLES20.GL_OUT_OF_MEMORY) { 564091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh surfaceAbandoned = true; 565091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } else { 566091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh glError = true; 567091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 568091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 569091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh if (glError) { 570091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throw new IllegalStateException( 571091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh msg + ": GLES20 error: 0x" + Integer.toHexString(error)); 572091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 573091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh if (surfaceAbandoned) { 574091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throw new LegacyExceptionUtils.BufferQueueAbandonedException(); 575feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 576feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 577feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 578feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 579e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala * Save a measurement dump to disk, in 580e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala * {@code /sdcard/CameraLegacy/durations_<time>_<width1>x<height1>_...txt} 581e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala */ 582e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private void dumpGlTiming() { 583e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (mPerfMeasurer == null) return; 584e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 585e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala File legacyStorageDir = new File(Environment.getExternalStorageDirectory(), "CameraLegacy"); 586e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (!legacyStorageDir.exists()){ 587e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (!legacyStorageDir.mkdirs()){ 588e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala Log.e(TAG, "Failed to create directory for data dump"); 589e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala return; 590e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 591e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 592e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 593e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala StringBuilder path = new StringBuilder(legacyStorageDir.getPath()); 594e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append(File.separator); 595e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append("durations_"); 596e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 597e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala Time now = new Time(); 598e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala now.setToNow(); 599e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append(now.format2445()); 600e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append("_S"); 601e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala for (EGLSurfaceHolder surface : mSurfaces) { 602e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append(String.format("_%d_%d", surface.width, surface.height)); 603e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 604e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append("_C"); 605e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala for (EGLSurfaceHolder surface : mConversionSurfaces) { 606e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append(String.format("_%d_%d", surface.width, surface.height)); 607e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 608e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala path.append(".txt"); 609e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer.dumpPerformanceData(path.toString()); 610e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 611e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 612e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private void setupGlTiming() { 613e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (PerfMeasurement.isGlTimingSupported()) { 614e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala Log.d(TAG, "Enabling GL performance measurement"); 615e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer = new PerfMeasurement(); 616e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } else { 617e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala Log.d(TAG, "GL performance measurement not supported on this device"); 618e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer = null; 619e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 620e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 621e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 622e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private void beginGlTiming() { 623e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (mPerfMeasurer == null) return; 624e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer.startTimer(); 625e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 626e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 627e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private void addGlTimestamp(long timestamp) { 628e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (mPerfMeasurer == null) return; 629e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer.addTimestamp(timestamp); 630e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 631e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 632e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala private void endGlTiming() { 633e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (mPerfMeasurer == null) return; 634e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala mPerfMeasurer.stopTimer(); 635e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 636e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 637e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala /** 638feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Return the surface texture to draw to - this is the texture use to when producing output 639feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * surface buffers. 640feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 641feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * @return a {@link SurfaceTexture}. 642feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 643feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public SurfaceTexture getSurfaceTexture() { 644feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return mSurfaceTexture; 645feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 646feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 647feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 648feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Set a collection of output {@link Surface}s that can be drawn to. 649feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 650feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * @param surfaces a {@link Collection} of surfaces. 651feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 652f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) { 653feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk releaseEGLContext(); 654feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 655d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6Ruben Brunk if (surfaces == null || surfaces.size() == 0) { 656d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6Ruben Brunk Log.w(TAG, "No output surfaces configured for GL drawing."); 657d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6Ruben Brunk return; 658d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6Ruben Brunk } 659d85e1a6ced452c9bd0d805f6ce19f50c9ea9b0a6Ruben Brunk 660f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk for (Pair<Surface, Size> p : surfaces) { 661f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk Surface s = p.first; 662f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk Size surfaceSize = p.second; 663feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // If pixel conversions aren't handled by egl, use a pbuffer 664ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk try { 665f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk EGLSurfaceHolder holder = new EGLSurfaceHolder(); 666f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.surface = s; 667f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.width = surfaceSize.getWidth(); 668f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.height = surfaceSize.getHeight(); 669ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (LegacyCameraDevice.needsConversion(s)) { 670ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mConversionSurfaces.add(holder); 67129c3630563452c9635f0510d071839c112d3017dChien-Yu Chen // LegacyCameraDevice is the producer of surfaces if it's not handled by EGL, 67229c3630563452c9635f0510d071839c112d3017dChien-Yu Chen // so LegacyCameraDevice needs to connect to the surfaces. 67329c3630563452c9635f0510d071839c112d3017dChien-Yu Chen LegacyCameraDevice.connectSurface(s); 674ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } else { 675ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mSurfaces.add(holder); 676ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 677ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 678ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk Log.w(TAG, "Surface abandoned, skipping configuration... ", e); 679feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 680feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 681feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 682feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Set up egl display 683feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk configureEGLContext(); 684feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 685feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Set up regular egl surfaces if needed 686feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mSurfaces.size() > 0) { 687feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk configureEGLOutputSurfaces(mSurfaces); 688feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 689feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 690feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Set up pbuffer surface if needed 691feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mConversionSurfaces.size() > 0) { 692feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk configureEGLPbufferSurfaces(mConversionSurfaces); 693feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 694feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk makeCurrent((mSurfaces.size() > 0) ? mSurfaces.get(0).eglSurface : 695feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mConversionSurfaces.get(0).eglSurface); 696feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk initializeGLState(); 697feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mSurfaceTexture = new SurfaceTexture(getTextureId()); 698e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 699e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala // Set up performance tracking if enabled 700e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala if (SystemProperties.getBoolean(LEGACY_PERF_PROPERTY, false)) { 701e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala setupGlTiming(); 702e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 703feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 704feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 705feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 706feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Draw the current buffer in the {@link SurfaceTexture} returned from 70791838ded36131525312739c0929913b215519c2aRuben Brunk * {@link #getSurfaceTexture()} into the set of target {@link Surface}s 70891838ded36131525312739c0929913b215519c2aRuben Brunk * in the next request from the given {@link CaptureCollector}, or drop 70991838ded36131525312739c0929913b215519c2aRuben Brunk * the frame if none is available. 710feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 711feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * <p> 71291838ded36131525312739c0929913b215519c2aRuben Brunk * Any {@link Surface}s targeted must be a subset of the {@link Surface}s 71391838ded36131525312739c0929913b215519c2aRuben Brunk * set in the last {@link #configureSurfaces(java.util.Collection)} call. 714feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * </p> 715feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 71691838ded36131525312739c0929913b215519c2aRuben Brunk * @param targetCollector the surfaces to draw to. 717feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 71891838ded36131525312739c0929913b215519c2aRuben Brunk public void drawIntoSurfaces(CaptureCollector targetCollector) { 719feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if ((mSurfaces == null || mSurfaces.size() == 0) 720feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk && (mConversionSurfaces == null || mConversionSurfaces.size() == 0)) { 721feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return; 722feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 7233c8fa3b356fa8f24b55d3dc42d4313297542e9f2Ruben Brunk 72491838ded36131525312739c0929913b215519c2aRuben Brunk boolean doTiming = targetCollector.hasPendingPreviewCaptures(); 725feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("before updateTexImage"); 726e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 72791838ded36131525312739c0929913b215519c2aRuben Brunk if (doTiming) { 72891838ded36131525312739c0929913b215519c2aRuben Brunk beginGlTiming(); 729e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala } 730e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 731feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mSurfaceTexture.updateTexImage(); 732e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 733e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala long timestamp = mSurfaceTexture.getTimestamp(); 73491838ded36131525312739c0929913b215519c2aRuben Brunk 73591838ded36131525312739c0929913b215519c2aRuben Brunk Pair<RequestHolder, Long> captureHolder = targetCollector.previewCaptured(timestamp); 73691838ded36131525312739c0929913b215519c2aRuben Brunk 73791838ded36131525312739c0929913b215519c2aRuben Brunk // No preview request queued, drop frame. 73891838ded36131525312739c0929913b215519c2aRuben Brunk if (captureHolder == null) { 739e663cb77281c4c76241b820f6126543f1c2d859fRuben Brunk if (DEBUG) { 740e663cb77281c4c76241b820f6126543f1c2d859fRuben Brunk Log.d(TAG, "Dropping preview frame."); 741e663cb77281c4c76241b820f6126543f1c2d859fRuben Brunk } 74291838ded36131525312739c0929913b215519c2aRuben Brunk if (doTiming) { 74391838ded36131525312739c0929913b215519c2aRuben Brunk endGlTiming(); 74491838ded36131525312739c0929913b215519c2aRuben Brunk } 74591838ded36131525312739c0929913b215519c2aRuben Brunk return; 74691838ded36131525312739c0929913b215519c2aRuben Brunk } 74791838ded36131525312739c0929913b215519c2aRuben Brunk 74891838ded36131525312739c0929913b215519c2aRuben Brunk RequestHolder request = captureHolder.first; 74991838ded36131525312739c0929913b215519c2aRuben Brunk 75091838ded36131525312739c0929913b215519c2aRuben Brunk Collection<Surface> targetSurfaces = request.getHolderTargets(); 75191838ded36131525312739c0929913b215519c2aRuben Brunk if (doTiming) { 75291838ded36131525312739c0929913b215519c2aRuben Brunk addGlTimestamp(timestamp); 75391838ded36131525312739c0929913b215519c2aRuben Brunk } 754e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 7552da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen List<Long> targetSurfaceIds = new ArrayList(); 7562da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen try { 7572da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces); 7582da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 7592da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen Log.w(TAG, "Surface abandoned, dropping frame. ", e); 7602da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen request.setOutputAbandoned(); 7612da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen } 7622da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen 763feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : mSurfaces) { 7643c8fa3b356fa8f24b55d3dc42d4313297542e9f2Ruben Brunk if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { 765f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk try{ 766a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width, 767a9bc3559109836efe7479a3279713bd58810b153Ruben Brunk holder.height); 768f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk makeCurrent(holder.eglSurface); 769f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk 77091838ded36131525312739c0929913b215519c2aRuben Brunk LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second); 771433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk drawFrame(mSurfaceTexture, holder.width, holder.height, 772433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ? 773433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk FLIP_TYPE_HORIZONTAL : FLIP_TYPE_NONE); 77491838ded36131525312739c0929913b215519c2aRuben Brunk swapBuffers(holder.eglSurface); 77591838ded36131525312739c0929913b215519c2aRuben Brunk } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 77691838ded36131525312739c0929913b215519c2aRuben Brunk Log.w(TAG, "Surface abandoned, dropping frame. ", e); 7772da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen request.setOutputAbandoned(); 77891838ded36131525312739c0929913b215519c2aRuben Brunk } 779feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 780feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 781feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk for (EGLSurfaceHolder holder : mConversionSurfaces) { 7823c8fa3b356fa8f24b55d3dc42d4313297542e9f2Ruben Brunk if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { 783feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk makeCurrent(holder.eglSurface); 784433e715cc0040ce22a31964c71bff71b1fe1a14fRuben Brunk // glReadPixels reads from the bottom of the buffer, so add an extra vertical flip 785091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh try { 786091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh drawFrame(mSurfaceTexture, holder.width, holder.height, 787091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh (mFacing == CameraCharacteristics.LENS_FACING_FRONT) ? 788091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh FLIP_TYPE_BOTH : FLIP_TYPE_VERTICAL); 789091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 790091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh // Should never hit this. 791091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e); 792091661334b25c1bc282463d13622236b1ac79e64Yin-Chia Yeh } 793feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPBufferPixels.clear(); 79428c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, 79528c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels); 796feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk checkGlError("glReadPixels"); 79728c49c9d202a9f4675c1c1e5d4562492d2107b79Ruben Brunk 798ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk try { 799ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk int format = LegacyCameraDevice.detectSurfaceType(holder.surface); 800f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width, 801f4a637d0be2e028d1e78c8bf90ad17ec3f84b5f3Ruben Brunk holder.height); 80291838ded36131525312739c0929913b215519c2aRuben Brunk LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second); 803ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(), 804ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk holder.width, holder.height, format); 805ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { 806ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk Log.w(TAG, "Surface abandoned, dropping frame. ", e); 8072da496f1ce63548486fe28e074f6af90c970db8cChien-Yu Chen request.setOutputAbandoned(); 808ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 809feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 810feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 81191838ded36131525312739c0929913b215519c2aRuben Brunk targetCollector.previewProduced(); 812e1f57d6f44909a66c7ab0af33dbc5289287e823aEino-Ville Talvala 81391838ded36131525312739c0929913b215519c2aRuben Brunk if (doTiming) { 81491838ded36131525312739c0929913b215519c2aRuben Brunk endGlTiming(); 81591838ded36131525312739c0929913b215519c2aRuben Brunk } 816feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 817feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 818feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 819feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Clean up the current GL context. 820feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 821feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void cleanupEGLContext() { 822feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk releaseEGLContext(); 823feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 824feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 825feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 826feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Drop all current GL operations on the floor. 827feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 828feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void flush() { 829feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // TODO: implement flush 830feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Log.e(TAG, "Flush not yet implemented."); 831feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 832feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk} 833