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