1 /* 2 * Copyright (C) 2011 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 17 package android.hardware.cts; 18 19 import android.content.Context; 20 import android.graphics.SurfaceTexture; 21 import android.hardware.Camera; 22 import android.hardware.Camera.Parameters; 23 import android.hardware.Camera.Size; 24 25 import android.opengl.GLES20; 26 import android.opengl.GLSurfaceView; 27 import android.opengl.Matrix; 28 import android.opengl.cts.GLSurfaceViewStubActivity; 29 30 import android.os.ConditionVariable; 31 import android.os.Environment; 32 import android.os.Looper; 33 import android.os.PowerManager; 34 import android.os.PowerManager.WakeLock; 35 36 import android.test.ActivityInstrumentationTestCase2; 37 import android.test.MoreAsserts; 38 import android.test.UiThreadTest; 39 import android.test.suitebuilder.annotation.LargeTest; 40 import android.util.Log; 41 42 import dalvik.annotation.TestLevel; 43 import dalvik.annotation.TestTargetClass; 44 45 import java.io.IOException; 46 import java.nio.ByteBuffer; 47 import java.nio.ByteOrder; 48 import java.nio.FloatBuffer; 49 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.Iterator; 53 import java.util.List; 54 55 import javax.microedition.khronos.egl.EGL10; 56 import javax.microedition.khronos.egl.EGLConfig; 57 import javax.microedition.khronos.egl.EGLContext; 58 import javax.microedition.khronos.egl.EGLDisplay; 59 import javax.microedition.khronos.opengles.GL10; 60 61 /** 62 * This test case must run with hardware. It can't be tested in emulator 63 */ 64 @LargeTest 65 @TestTargetClass(Camera.class) 66 public class CameraGLTest extends ActivityInstrumentationTestCase2<GLSurfaceViewStubActivity> { 67 private static final String TAG = "CameraGLTest"; 68 private static final String PACKAGE = "com.android.cts.stub"; 69 private static final boolean LOGV = false; 70 private static final boolean LOGVV = false; 71 private static final int EGL_OPENGL_ES2_BIT = 0x0004; 72 73 private boolean mSurfaceTextureCallbackResult = false; 74 75 private static final int WAIT_FOR_COMMAND_TO_COMPLETE = 1500; // Milliseconds. 76 private static final int WAIT_FOR_FOCUS_TO_COMPLETE = 3000; 77 private static final int WAIT_FOR_SNAPSHOT_TO_COMPLETE = 5000; 78 79 private SurfaceTextureCallback mSurfaceTextureCallback = new SurfaceTextureCallback(); 80 private SurfaceTextureBurstCallback mSurfaceTextureBurstCallback = new SurfaceTextureBurstCallback(); 81 private PreviewCallback mPreviewCallback = new PreviewCallback(); 82 83 private Looper mLooper = null; 84 private final ConditionVariable mSurfaceTextureDone = new ConditionVariable(); 85 private final ConditionVariable mPreviewDone = new ConditionVariable(); 86 87 Camera mCamera; 88 SurfaceTexture mSurfaceTexture; 89 Renderer mRenderer; 90 GLSurfaceView mGLView; 91 92 public CameraGLTest() { 93 super(PACKAGE, GLSurfaceViewStubActivity.class); 94 if (LOGV) Log.v(TAG, "CameraGLTest Constructor"); 95 } 96 97 @Override 98 protected void setUp() throws Exception { 99 super.setUp(); 100 // Set up renderer instance 101 mRenderer = this.new Renderer(); 102 GLSurfaceViewStubActivity.setRenderer(mRenderer); 103 GLSurfaceViewStubActivity.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); 104 GLSurfaceViewStubActivity.setGlVersion(2); 105 // Start CameraStubActivity. 106 GLSurfaceViewStubActivity stubActivity = getActivity(); 107 // Store a link to the view so we can redraw it when needed 108 mGLView = stubActivity.getView(); 109 } 110 111 @Override 112 protected void tearDown() throws Exception { 113 if (mCamera != null) { 114 terminateMessageLooper(); 115 } 116 // Clean up static values in stub so it can be reused 117 GLSurfaceViewStubActivity.resetRenderMode(); 118 GLSurfaceViewStubActivity.resetRenderer(); 119 GLSurfaceViewStubActivity.resetGlVersion(); 120 121 super.tearDown(); 122 } 123 124 /** 125 * Initializes the message looper so that the Camera object can 126 * receive the callback messages. 127 */ 128 private void initializeMessageLooper(final int cameraId) { 129 final ConditionVariable startDone = new ConditionVariable(); 130 new Thread() { 131 @Override 132 public void run() { 133 Log.v(TAG, "Start camera/surfacetexture thread"); 134 // Set up a looper to be used by camera. 135 Looper.prepare(); 136 // Save the looper so that we can terminate this thread 137 // after we are done with it. 138 mLooper = Looper.myLooper(); 139 // These must be instantiated outside the UI thread, since the 140 // UI thread will be doing a lot of waiting, stopping callbacks. 141 mCamera = Camera.open(cameraId); 142 mSurfaceTexture = new SurfaceTexture(mRenderer.getTextureID()); 143 Log.v(TAG, "Camera " + cameraId + " is opened."); 144 startDone.open(); 145 Looper.loop(); // Blocks forever until Looper.quit() is called. 146 Log.v(TAG, "Stop camera/surfacetexture thread"); 147 } 148 }.start(); 149 150 Log.v(TAG, "start waiting for looper"); 151 if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 152 fail("initializeMessageLooper: start timeout"); 153 } 154 } 155 156 /** 157 * Terminates the message looper thread. 158 */ 159 private void terminateMessageLooper() throws Exception { 160 if (LOGV) Log.v(TAG, "Shutting down camera"); 161 mCamera.release(); 162 mLooper.quit(); 163 // Looper.quit() is asynchronous. The looper may still has some 164 // preview callbacks in the queue after quit is called. The preview 165 // callback still uses the camera object (setHasPreviewCallback). 166 // After camera is released, RuntimeException will be thrown from 167 // the method. So we need to join the looper thread here. 168 mLooper.getThread().join(); 169 mCamera = null; 170 mSurfaceTexture = null; 171 if (LOGV) Log.v(TAG, "Shutdown of camera complete."); 172 } 173 174 /** The camera preview callback. Stops capture after the first callback */ 175 private final class PreviewCallback 176 implements android.hardware.Camera.PreviewCallback { 177 public void onPreviewFrame(byte [] data, Camera camera) { 178 if (LOGV) Log.v(TAG, "PreviewCallback"); 179 assertNotNull(data); 180 Size size = camera.getParameters().getPreviewSize(); 181 assertEquals(size.width * size.height * 3 / 2, data.length); 182 mCamera.stopPreview(); 183 mPreviewDone.open(); 184 } 185 } 186 187 /** A simple SurfaceTexture listener callback, meant to be used together with the camera preview 188 * callback */ 189 private final class SurfaceTextureCallback 190 implements android.graphics.SurfaceTexture.OnFrameAvailableListener { 191 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 192 if (LOGV) Log.v(TAG, "SurfaceTextureCallback"); 193 mSurfaceTextureDone.open(); 194 // Assumes preview is stopped elsewhere 195 } 196 } 197 198 /** A burst SurfaceTexture listener callback, used for multiple-frame capture */ 199 private final class SurfaceTextureBurstCallback 200 implements android.graphics.SurfaceTexture.OnFrameAvailableListener { 201 202 public void setBurstCount(int burstCount) { 203 mBurstCount = burstCount; 204 } 205 206 public int getBurstCount() { 207 return mBurstCount; 208 } 209 210 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 211 if (LOGVV) Log.v(TAG, "SurfaceTextureBurstCallback, frame #" + mBurstCount); 212 mBurstCount--; 213 if (!mSurfaceTextureCallbackResult) { 214 if (mBurstCount <= 0) { 215 if (LOGV) Log.v(TAG, "SurfaceTextureBurstCallback stopping preview."); 216 mCamera.stopPreview(); 217 if (LOGVV) Log.v(TAG, "SurfaceTextureBurstCallback preview stopped."); 218 mSurfaceTextureCallbackResult = true; 219 } 220 mSurfaceTextureDone.open(); 221 } 222 } 223 224 private int mBurstCount = 0; 225 } 226 227 /** Waits until surface texture callbacks have fired */ 228 private boolean waitForSurfaceTextureDone() { 229 if (LOGVV) Log.v(TAG, "Wait for surface texture callback"); 230 if (!mSurfaceTextureDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 231 // timeout could be expected or unexpected. The caller will decide. 232 Log.v(TAG, "waitForSurfaceTextureDone: timeout"); 233 return false; 234 } 235 mSurfaceTextureDone.close(); 236 return true; 237 } 238 239 /** Waits until the camera preview callback has fired */ 240 private boolean waitForPreviewDone() { 241 if (LOGVV) Log.v(TAG, "Wait for preview callback"); 242 if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) { 243 // timeout could be expected or unexpected. The caller will decide. 244 Log.v(TAG, "waitForPreviewDone: timeout"); 245 return false; 246 } 247 mPreviewDone.close(); 248 return true; 249 } 250 251 /** @return OpenGL ES major version 1 or 2 or some negative number for error */ 252 private static int getDetectedVersion() { 253 /* 254 * Get all the device configurations and check if any of the attributes specify the 255 * the EGL_OPENGL_ES2_BIT to determine whether the device supports 2.0. 256 */ 257 EGL10 egl = (EGL10) EGLContext.getEGL(); 258 EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 259 int[] numConfigs = new int[1]; 260 261 if (egl.eglInitialize(display, null)) { 262 try { 263 if (egl.eglGetConfigs(display, null, 0, numConfigs)) { 264 EGLConfig[] configs = new EGLConfig[numConfigs[0]]; 265 if (egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) { 266 int[] value = new int[1]; 267 for (int i = 0; i < numConfigs[0]; i++) { 268 if (egl.eglGetConfigAttrib(display, configs[i], 269 EGL10.EGL_RENDERABLE_TYPE, value)) { 270 if ((value[0] & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT) { 271 return 2; 272 } 273 } else { 274 Log.w(TAG, "Getting config attribute with " 275 + "EGL10#eglGetConfigAttrib failed " 276 + "(" + i + "/" + numConfigs[0] + "): " 277 + egl.eglGetError()); 278 } 279 } 280 return 1; 281 } else { 282 Log.e(TAG, "Getting configs with EGL10#eglGetConfigs failed: " 283 + egl.eglGetError()); 284 return -1; 285 } 286 } else { 287 Log.e(TAG, "Getting number of configs with EGL10#eglGetConfigs failed: " 288 + egl.eglGetError()); 289 return -2; 290 } 291 } finally { 292 egl.eglTerminate(display); 293 } 294 } else { 295 Log.e(TAG, "Couldn't initialize EGL."); 296 return -3; 297 } 298 } 299 300 /** Generic per-camera test interface */ 301 private interface RunPerCamera { 302 void run(int cameraId) throws Exception; 303 } 304 305 /** Generic camera test runner, to minimize boilerplace duplication */ 306 private void runForAllCameras(RunPerCamera test) throws Exception { 307 /* Currently OpenGL ES 2.0 is supported for this test, so just skip it 308 if only 1.0 is available. */ 309 int glVersion = getDetectedVersion(); 310 assertTrue(glVersion > 0); 311 if (glVersion != 2) { 312 Log.w(TAG, "Skipping test because OpenGL ES 2 is not supported"); 313 return; 314 } 315 316 /* Make sure the screen stays on while testing - otherwise the OpenGL context may disappear */ 317 PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE); 318 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "CameraGLTest"); 319 wl.acquire(); 320 try { 321 /* Run the requested test per camera */ 322 int nCameras = Camera.getNumberOfCameras(); 323 for (int id = 0; id < nCameras; id++) { 324 Log.v(TAG, "Camera id=" + id); 325 test.run(id); 326 } 327 } finally { 328 wl.release(); 329 // If an assert failed, camera might still be active. Clean up before next test. 330 if (mCamera != null) { 331 terminateMessageLooper(); 332 } 333 } 334 } 335 336 /** Test Camera.setPreviewTexture in conjunction with the standard Camera preview callback */ 337 @UiThreadTest 338 public void testSetPreviewTexturePreviewCallback() throws Exception { 339 runForAllCameras(testSetPreviewTexturePreviewCallbackByCamera); 340 } 341 342 private RunPerCamera testSetPreviewTexturePreviewCallbackByCamera = new RunPerCamera() { 343 public void run(int cameraId) throws Exception { 344 boolean noTimeout; 345 // Check the order: startPreview->setPreviewTexture, with a PreviewCallback as well 346 mPreviewDone.close(); 347 initializeMessageLooper(cameraId); 348 mCamera.setPreviewCallback(mPreviewCallback); 349 mCamera.startPreview(); 350 mCamera.setPreviewTexture(mSurfaceTexture); 351 noTimeout = waitForPreviewDone(); 352 assertTrue("Timeout waiting for new preview callback!", noTimeout); 353 terminateMessageLooper(); 354 355 // Check the order: setPreviewTexture->startPreview. 356 initializeMessageLooper(cameraId); 357 mCamera.setPreviewCallback(mPreviewCallback); 358 mCamera.setPreviewTexture(mSurfaceTexture); 359 mCamera.startPreview(); 360 noTimeout = waitForPreviewDone(); 361 assertTrue("Timeout waiting for new preview callback!", noTimeout); 362 363 // Check the order: setting preview display to null->startPreview-> 364 // setPreviewTexture. 365 mCamera.setPreviewCallback(mPreviewCallback); 366 mCamera.setPreviewTexture(null); 367 mCamera.startPreview(); 368 mCamera.setPreviewTexture(mSurfaceTexture); 369 noTimeout = waitForPreviewDone(); 370 assertTrue("Timeout waiting for new preview callback!", noTimeout); 371 terminateMessageLooper(); 372 } 373 }; 374 375 /** Test Camera.setPreviewTexture in conjunction with both the standard Camera preview callback, 376 and the SurfaceTexture onFrameAvailable callback */ 377 @UiThreadTest 378 public void testSetPreviewTextureBothCallbacks() throws Exception { 379 runForAllCameras(testSetPreviewTextureBothCallbacksByCamera); 380 } 381 382 private RunPerCamera testSetPreviewTextureBothCallbacksByCamera = new RunPerCamera() { 383 public void run(int cameraId) throws Exception { 384 boolean noTimeout; 385 // Check SurfaceTexture callback together with preview callback 386 // Check the order: setPreviewTexture->startPreview 387 mSurfaceTextureDone.close(); 388 initializeMessageLooper(cameraId); 389 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 390 mCamera.setPreviewCallback(mPreviewCallback); 391 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback); 392 mCamera.setPreviewTexture(mSurfaceTexture); 393 mCamera.startPreview(); 394 395 noTimeout = waitForSurfaceTextureDone(); 396 assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout); 397 noTimeout = waitForPreviewDone(); 398 assertTrue("Timeout waiting for new preview callback!",noTimeout); 399 400 mGLView.requestRender(); 401 terminateMessageLooper(); 402 403 // Check the order: startPreview->setPreviewTexture 404 mSurfaceTextureDone.close(); 405 initializeMessageLooper(cameraId); 406 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 407 mCamera.setPreviewCallback(mPreviewCallback); 408 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback); 409 mCamera.startPreview(); 410 mCamera.setPreviewTexture(mSurfaceTexture); 411 412 noTimeout = waitForSurfaceTextureDone(); 413 assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout); 414 noTimeout = waitForPreviewDone(); 415 assertTrue("Timeout waiting for new preview callback!", noTimeout); 416 417 mGLView.requestRender(); 418 419 // Check the order: setting preview to null->startPreview->setPreviewTexture 420 mCamera.setPreviewCallback(mPreviewCallback); 421 mCamera.setPreviewTexture(null); 422 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback); 423 mCamera.startPreview(); 424 mCamera.setPreviewTexture(mSurfaceTexture); 425 noTimeout = waitForPreviewDone(); 426 assertTrue(noTimeout); 427 terminateMessageLooper(); 428 } 429 }; 430 431 /** Test Camera.setPreviewTexture in conjunction with just the SurfaceTexture onFrameAvailable callback */ 432 @UiThreadTest 433 public void testSetPreviewTextureTextureCallback() throws Exception { 434 runForAllCameras(testSetPreviewTextureTextureCallbackByCamera); 435 } 436 437 private RunPerCamera testSetPreviewTextureTextureCallbackByCamera = new RunPerCamera() { 438 public void run(int cameraId) throws Exception { 439 boolean noTimeout; 440 // Check that SurfaceTexture callbacks work with no standard 441 // preview callback 442 mSurfaceTextureCallbackResult = false; 443 mSurfaceTextureDone.close(); 444 initializeMessageLooper(cameraId); 445 mSurfaceTextureBurstCallback.setBurstCount(1); 446 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback); 447 mCamera.setPreviewTexture(mSurfaceTexture); 448 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 449 mCamera.startPreview(); 450 451 noTimeout = waitForSurfaceTextureDone(); 452 mGLView.requestRender(); 453 assertTrue(noTimeout); 454 455 terminateMessageLooper(); 456 assertTrue(mSurfaceTextureCallbackResult); 457 458 // Check that SurfaceTexture callbacks also work with 459 // startPreview->setPreviewTexture 460 mSurfaceTextureCallbackResult = false; 461 mSurfaceTextureDone.close(); 462 initializeMessageLooper(cameraId); 463 mSurfaceTextureBurstCallback.setBurstCount(1); 464 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback); 465 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 466 mCamera.startPreview(); 467 mCamera.setPreviewTexture(mSurfaceTexture); 468 469 noTimeout = waitForSurfaceTextureDone(); 470 assertTrue(noTimeout); 471 472 terminateMessageLooper(); 473 assertTrue(mSurfaceTextureCallbackResult); 474 475 // Check that SurfaceTexture callbacks also work with 476 // null->startPreview->setPreviewTexture 477 mSurfaceTextureCallbackResult = false; 478 mSurfaceTextureDone.close(); 479 initializeMessageLooper(cameraId); 480 mSurfaceTextureBurstCallback.setBurstCount(1); 481 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback); 482 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 483 mCamera.setPreviewTexture(null); 484 mCamera.startPreview(); 485 mCamera.setPreviewTexture(mSurfaceTexture); 486 487 noTimeout = waitForSurfaceTextureDone(); 488 assertTrue(noTimeout); 489 490 terminateMessageLooper(); 491 assertTrue(mSurfaceTextureCallbackResult); 492 } 493 }; 494 495 /** Test all preview sizes and framerates along with SurfaceTexture-provided metadata (texture 496 * transforms and timestamps) */ 497 @UiThreadTest 498 public void testCameraToSurfaceTextureMetadata() throws Exception { 499 runForAllCameras(testCameraToSurfaceTextureMetadataByCamera); 500 } 501 502 private RunPerCamera testCameraToSurfaceTextureMetadataByCamera = new RunPerCamera() { 503 public void run(int cameraId) throws Exception { 504 // Number of frames to test over 505 int kLoopCount = 100; 506 // Ignore timestamp issues before this frame 507 int kFirstTestedFrame = 10; 508 // Slop in timestamp testing, needed because timestamps are not 509 // currently being set by driver-level code so are subject to 510 // lots of variability 511 float kTestSlopMargin = 30; // ms 512 513 boolean noTimeout; 514 initializeMessageLooper(cameraId); 515 Parameters parameters = mCamera.getParameters(); 516 517 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback); 518 mCamera.setPreviewTexture(mSurfaceTexture); 519 520 for (Size size: parameters.getSupportedPreviewSizes()) { 521 for (int[] fps: parameters.getSupportedPreviewFpsRange()) { 522 if (LOGV) { 523 Log.v(TAG, "Testing camera #" + cameraId + 524 ", preview size:" + size.width + "x" + size.height + 525 ", frame rate range: [" + 526 (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + "," + 527 (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "]"); 528 } 529 parameters.setPreviewSize(size.width, size.height); 530 parameters.setPreviewFpsRange(fps[Parameters.PREVIEW_FPS_MIN_INDEX], 531 fps[Parameters.PREVIEW_FPS_MAX_INDEX]); 532 mCamera.setParameters(parameters); 533 534 assertEquals(size, mCamera.getParameters().getPreviewSize()); 535 536 int[] actualFps = new int[2]; 537 mCamera.getParameters().getPreviewFpsRange(actualFps); 538 assertEquals(fps[Parameters.PREVIEW_FPS_MIN_INDEX], 539 actualFps[Parameters.PREVIEW_FPS_MIN_INDEX]); 540 assertEquals(fps[Parameters.PREVIEW_FPS_MAX_INDEX], 541 actualFps[Parameters.PREVIEW_FPS_MAX_INDEX]); 542 543 mSurfaceTextureBurstCallback.setBurstCount(kLoopCount); 544 mSurfaceTextureCallbackResult = false; 545 mSurfaceTextureDone.close(); 546 547 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 548 if (LOGV) Log.v(TAG, "Starting preview"); 549 mCamera.startPreview(); 550 if (LOGVV) Log.v(TAG, "Preview started"); 551 552 long[] timestamps = new long[kLoopCount]; 553 for (int i = 0; i < kLoopCount; i++) { 554 noTimeout = waitForSurfaceTextureDone(); 555 assertTrue("Timeout waiting for frame " + i + 556 " (burst callback thinks " + 557 (kLoopCount - mSurfaceTextureBurstCallback.getBurstCount()) + 558 ")! (Size " + size.width + "x" + size.height + ", fps [" + 559 (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + ", " + 560 (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "])", 561 noTimeout); 562 563 if (LOGVV) Log.v(TAG, "Frame #" + i + " completed"); 564 // Draw the frame (and update the SurfaceTexture) 565 mGLView.requestRender(); 566 // Wait until frame is drawn, so that the SurfaceTexture has new 567 // metadata 568 noTimeout = mRenderer.waitForDrawDone(); 569 assertTrue(noTimeout); 570 571 // Store timestamps for later 572 timestamps[i] = mSurfaceTexture.getTimestamp(); 573 // Verify that the surfaceTexture transform has at least one non-zero 574 // entry 575 float[] transform = new float[16]; 576 mSurfaceTexture.getTransformMatrix(transform); 577 boolean nonZero = false; 578 for (int k = 0; k < 16; k++) { 579 if (transform[k] != 0.f) { 580 nonZero = true; 581 break; 582 } 583 } 584 assertTrue(nonZero); 585 } 586 assertTrue(mSurfaceTextureCallbackResult); 587 588 float expectedMaxFrameDurationMs = 1000.f * 1000.f / 589 fps[Parameters.PREVIEW_FPS_MIN_INDEX]; 590 float slopMaxFrameDurationMs = expectedMaxFrameDurationMs + 591 kTestSlopMargin; 592 float expectedMinFrameDurationMs = 1000.f * 1000.f / 593 fps[Parameters.PREVIEW_FPS_MAX_INDEX]; 594 float slopMinFrameDurationMs = expectedMinFrameDurationMs - 595 kTestSlopMargin; 596 597 for (int i = kFirstTestedFrame; i < kLoopCount; i++) { 598 float frameDurationMs = (timestamps[i] - timestamps[i - 1]) / 1000000.f; 599 if (LOGVV) { 600 Log.v(TAG, "Frame " + i + " duration: " + frameDurationMs + 601 " ms, expecting [" + expectedMinFrameDurationMs + "," + 602 expectedMaxFrameDurationMs + "], slop range [" + 603 slopMinFrameDurationMs + "," + slopMaxFrameDurationMs + "]."); 604 } 605 assertTrue("Frame " + i + " duration out of bounds! ("+ 606 frameDurationMs + " ms, expecting [" + 607 slopMinFrameDurationMs + "," + 608 slopMaxFrameDurationMs + "] ms)", 609 (frameDurationMs > slopMinFrameDurationMs) && 610 (frameDurationMs < slopMaxFrameDurationMs) ); 611 } 612 } 613 } 614 terminateMessageLooper(); 615 } // void run(int cameraId) 616 }; 617 618 /** Basic OpenGL ES 2.0 renderer to draw SurfaceTexture-sourced frames to the screen */ 619 private class Renderer implements GLSurfaceView.Renderer { 620 public Renderer() { 621 mTriangleVertices = 622 ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES). 623 order(ByteOrder.nativeOrder()).asFloatBuffer(); 624 mTriangleVertices.put(mTriangleVerticesData).position(0); 625 626 Matrix.setIdentityM(mSTMatrix, 0); 627 Matrix.setIdentityM(mMMatrix, 0); 628 629 mTextureID = 0; 630 } 631 632 public void setCameraSizing(Camera.Size previewSize) { 633 mCameraRatio = (float)previewSize.width/previewSize.height; 634 } 635 636 public boolean waitForDrawDone() { 637 if (!mDrawDone.block(WAIT_FOR_COMMAND_TO_COMPLETE) ) { 638 // timeout could be expected or unexpected. The caller will decide. 639 Log.e(TAG, "waitForDrawDone: timeout"); 640 return false; 641 } 642 mDrawDone.close(); 643 return true; 644 } 645 646 private final ConditionVariable mDrawDone = new ConditionVariable(); 647 648 public void onDrawFrame(GL10 glUnused) { 649 if (LOGVV) Log.v(TAG, "onDrawFrame()"); 650 if (CameraGLTest.this.mSurfaceTexture != null) { 651 CameraGLTest.this.mSurfaceTexture.updateTexImage(); 652 CameraGLTest.this.mSurfaceTexture.getTransformMatrix(mSTMatrix); 653 mDrawDone.open(); 654 } 655 656 // Ignore the passed-in GL10 interface, and use the GLES20 657 // class's static methods instead. 658 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 659 GLES20.glUseProgram(mProgram); 660 checkGlError("glUseProgram"); 661 662 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 663 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); 664 665 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 666 GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 667 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 668 checkGlError("glVertexAttribPointer maPosition"); 669 GLES20.glEnableVertexAttribArray(maPositionHandle); 670 checkGlError("glEnableVertexAttribArray maPositionHandle"); 671 672 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 673 GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false, 674 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 675 checkGlError("glVertexAttribPointer maTextureHandle"); 676 GLES20.glEnableVertexAttribArray(maTextureHandle); 677 checkGlError("glEnableVertexAttribArray maTextureHandle"); 678 679 Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0); 680 Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); 681 682 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); 683 GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); 684 GLES20.glUniform1f(muCRatioHandle, mCameraRatio); 685 686 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 687 checkGlError("glDrawArrays"); 688 } 689 690 public void onSurfaceChanged(GL10 glUnused, int width, int height) { 691 if (LOGV) Log.v(TAG, "onSurfaceChanged()"); 692 // Ignore the passed-in GL10 interface, and use the GLES20 693 // class's static methods instead. 694 GLES20.glViewport(0, 0, width, height); 695 mRatio = (float) width / height; 696 Matrix.frustumM(mProjMatrix, 0, -mRatio, mRatio, -1, 1, 3, 7); 697 } 698 699 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { 700 if (LOGV) Log.v(TAG, "onSurfaceCreated()"); 701 // Ignore the passed-in GL10 interface, and use the GLES20 702 // class's static methods instead. 703 704 /* Set up shaders and handles to their variables */ 705 mProgram = createProgram(mVertexShader, mFragmentShader); 706 if (mProgram == 0) { 707 return; 708 } 709 maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); 710 checkGlError("glGetAttribLocation aPosition"); 711 if (maPositionHandle == -1) { 712 throw new RuntimeException("Could not get attrib location for aPosition"); 713 } 714 maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); 715 checkGlError("glGetAttribLocation aTextureCoord"); 716 if (maTextureHandle == -1) { 717 throw new RuntimeException("Could not get attrib location for aTextureCoord"); 718 } 719 720 muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 721 checkGlError("glGetUniformLocation uMVPMatrix"); 722 if (muMVPMatrixHandle == -1) { 723 throw new RuntimeException("Could not get attrib location for uMVPMatrix"); 724 } 725 726 muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix"); 727 checkGlError("glGetUniformLocation uSTMatrix"); 728 if (muMVPMatrixHandle == -1) { 729 throw new RuntimeException("Could not get attrib location for uSTMatrix"); 730 } 731 732 muCRatioHandle = GLES20.glGetUniformLocation(mProgram, "uCRatio"); 733 checkGlError("glGetUniformLocation uCRatio"); 734 if (muMVPMatrixHandle == -1) { 735 throw new RuntimeException("Could not get attrib location for uCRatio"); 736 } 737 738 /* 739 * Create our texture. This has to be done each time the 740 * surface is created. 741 */ 742 743 int[] textures = new int[1]; 744 GLES20.glGenTextures(1, textures, 0); 745 746 mTextureID = textures[0]; 747 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); 748 checkGlError("glBindTexture mTextureID"); 749 750 // Can't do mipmapping with camera source 751 GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, 752 GLES20.GL_NEAREST); 753 GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, 754 GLES20.GL_LINEAR); 755 // Clamp to edge is the only option 756 GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, 757 GLES20.GL_CLAMP_TO_EDGE); 758 GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, 759 GLES20.GL_CLAMP_TO_EDGE); 760 checkGlError("glTexParameteri mTextureID"); 761 762 Matrix.setLookAtM(mVMatrix, 0, 0, 0, 4f, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 763 } 764 765 public int getTextureID() { 766 return mTextureID; 767 } 768 769 private int loadShader(int shaderType, String source) { 770 int shader = GLES20.glCreateShader(shaderType); 771 if (shader != 0) { 772 GLES20.glShaderSource(shader, source); 773 GLES20.glCompileShader(shader); 774 int[] compiled = new int[1]; 775 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 776 if (compiled[0] == 0) { 777 Log.e(TAG, "Could not compile shader " + shaderType + ":"); 778 Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 779 GLES20.glDeleteShader(shader); 780 shader = 0; 781 } 782 } 783 return shader; 784 } 785 786 private int createProgram(String vertexSource, String fragmentSource) { 787 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 788 if (vertexShader == 0) { 789 return 0; 790 } 791 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 792 if (pixelShader == 0) { 793 return 0; 794 } 795 796 int program = GLES20.glCreateProgram(); 797 if (program != 0) { 798 GLES20.glAttachShader(program, vertexShader); 799 checkGlError("glAttachShader"); 800 GLES20.glAttachShader(program, pixelShader); 801 checkGlError("glAttachShader"); 802 GLES20.glLinkProgram(program); 803 int[] linkStatus = new int[1]; 804 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 805 if (linkStatus[0] != GLES20.GL_TRUE) { 806 Log.e(TAG, "Could not link program: "); 807 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 808 GLES20.glDeleteProgram(program); 809 program = 0; 810 } 811 } 812 return program; 813 } 814 815 private void checkGlError(String op) { 816 int error; 817 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 818 Log.e(TAG, op + ": glError " + error); 819 throw new RuntimeException(op + ": glError " + error); 820 } 821 } 822 823 private static final int FLOAT_SIZE_BYTES = 4; 824 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; 825 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; 826 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; 827 private final float[] mTriangleVerticesData = { 828 // X, Y, Z, U, V 829 -1.0f, -1.0f, 0, 0.f, 0.f, 830 1.0f, -1.0f, 0, 1.f, 0.f, 831 -1.0f, 1.0f, 0, 0.f, 1.f, 832 1.0f, 1.0f, 0, 1.f, 1.f, 833 }; 834 835 private FloatBuffer mTriangleVertices; 836 837 private final String mVertexShader = 838 "uniform mat4 uMVPMatrix;\n" + 839 "uniform mat4 uSTMatrix;\n" + 840 "uniform float uCRatio;\n" + 841 "attribute vec4 aPosition;\n" + 842 "attribute vec4 aTextureCoord;\n" + 843 "varying vec2 vTextureCoord;\n" + 844 "void main() {\n" + 845 " gl_Position = vec4(uCRatio,1,1,1) * uMVPMatrix * aPosition;\n" + 846 " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + 847 "}\n"; 848 849 private final String mFragmentShader = 850 "#extension GL_OES_EGL_image_external : require\n" + 851 "precision mediump float;\n" + 852 "varying vec2 vTextureCoord;\n" + 853 "uniform samplerExternalOES sTexture;\n" + 854 "void main() {\n" + 855 " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 856 "}\n"; 857 858 private float[] mMVPMatrix = new float[16]; 859 private float[] mProjMatrix = new float[16]; 860 private float[] mMMatrix = new float[16]; 861 private float[] mVMatrix = new float[16]; 862 private float[] mSTMatrix = new float[16]; 863 864 private int mProgram; 865 private int mTextureID; 866 private int muMVPMatrixHandle; 867 private int muSTMatrixHandle; 868 private int muCRatioHandle; 869 private int maPositionHandle; 870 private int maTextureHandle; 871 872 private float mRatio = 1.0f; 873 private float mCameraRatio = 1.0f; 874 875 private Context mContext; 876 private static final String TAG = "CameraGLTest.Renderer"; 877 878 // Magic key 879 private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; 880 } 881 882 } 883