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