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