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