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.setOneShotPreviewCallback(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.setOneShotPreviewCallback(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.setOneShotPreviewCallback(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.setOneShotPreviewCallback(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.setOneShotPreviewCallback(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.setOneShotPreviewCallback(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 * TODO: This should be made stricter once SurfaceTexture timestamps are generated by the drivers. 498 */ 499 @UiThreadTest 500 public void testCameraToSurfaceTextureMetadata() throws Exception { 501 runForAllCameras(testCameraToSurfaceTextureMetadataByCamera); 502 } 503 504 private RunPerCamera testCameraToSurfaceTextureMetadataByCamera = new RunPerCamera() { 505 public void run(int cameraId) throws Exception { 506 // Number of frames to test over 507 int kLoopCount = 100; 508 // Number of frames that can be out of bounds before calling this a failure 509 int kMaxOutOfBoundsFrames = kLoopCount / 25; // 4% of frames 510 // Ignore timestamp issues before this frame 511 int kFirstTestedFrame = 10; 512 // Slop in timestamp testing, needed because timestamps are not 513 // currently being set by driver-level code so are subject to 514 // user-space timing variability 515 float kTestSlopMargin = 20; // ms 516 517 boolean noTimeout; 518 initializeMessageLooper(cameraId); 519 Parameters parameters = mCamera.getParameters(); 520 521 mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback); 522 mCamera.setPreviewTexture(mSurfaceTexture); 523 524 for (Size size: parameters.getSupportedPreviewSizes()) { 525 for (int[] fps: parameters.getSupportedPreviewFpsRange()) { 526 if (LOGV) { 527 Log.v(TAG, "Testing camera #" + cameraId + 528 ", preview size:" + size.width + "x" + size.height + 529 ", frame rate range: [" + 530 (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + "," + 531 (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "]"); 532 } 533 parameters.setPreviewSize(size.width, size.height); 534 parameters.setPreviewFpsRange(fps[Parameters.PREVIEW_FPS_MIN_INDEX], 535 fps[Parameters.PREVIEW_FPS_MAX_INDEX]); 536 mCamera.setParameters(parameters); 537 538 assertEquals(size, mCamera.getParameters().getPreviewSize()); 539 540 int[] actualFps = new int[2]; 541 mCamera.getParameters().getPreviewFpsRange(actualFps); 542 assertEquals(fps[Parameters.PREVIEW_FPS_MIN_INDEX], 543 actualFps[Parameters.PREVIEW_FPS_MIN_INDEX]); 544 assertEquals(fps[Parameters.PREVIEW_FPS_MAX_INDEX], 545 actualFps[Parameters.PREVIEW_FPS_MAX_INDEX]); 546 547 mSurfaceTextureBurstCallback. 548 setBurstCount(kLoopCount + kFirstTestedFrame); 549 mSurfaceTextureCallbackResult = false; 550 mSurfaceTextureDone.close(); 551 552 mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize()); 553 if (LOGV) Log.v(TAG, "Starting preview"); 554 mCamera.startPreview(); 555 if (LOGVV) Log.v(TAG, "Preview started"); 556 557 long[] timestamps = new long[kLoopCount]; 558 for (int i = 0; i < kLoopCount + kFirstTestedFrame; i++) { 559 noTimeout = waitForSurfaceTextureDone(); 560 assertTrue("Timeout waiting for frame " + i + 561 " (burst callback thinks " + 562 (kLoopCount - mSurfaceTextureBurstCallback.getBurstCount()) + 563 ")! (Size " + size.width + "x" + size.height + ", fps [" + 564 (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + ", " + 565 (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "])", 566 noTimeout); 567 568 if (LOGVV) Log.v(TAG, "Frame #" + i + " completed"); 569 // Draw the frame (and update the SurfaceTexture) 570 mGLView.requestRender(); 571 // Wait until frame is drawn, so that the SurfaceTexture has new 572 // metadata 573 noTimeout = mRenderer.waitForDrawDone(); 574 assertTrue(noTimeout); 575 576 // Store timestamps for later 577 if (i >= kFirstTestedFrame) { 578 timestamps[i - kFirstTestedFrame] = 579 mSurfaceTexture.getTimestamp(); 580 } 581 // Verify that the surfaceTexture transform has at least one non-zero 582 // entry 583 float[] transform = new float[16]; 584 mSurfaceTexture.getTransformMatrix(transform); 585 boolean nonZero = false; 586 for (int k = 0; k < 16; k++) { 587 if (transform[k] != 0.f) { 588 nonZero = true; 589 break; 590 } 591 } 592 assertTrue(nonZero); 593 } 594 assertTrue(mSurfaceTextureCallbackResult); 595 596 float expectedMaxFrameDurationMs = 1000.f * 1000.f / 597 fps[Parameters.PREVIEW_FPS_MIN_INDEX]; 598 float slopMaxFrameDurationMs = expectedMaxFrameDurationMs + 599 kTestSlopMargin; 600 float expectedMinFrameDurationMs = 1000.f * 1000.f / 601 fps[Parameters.PREVIEW_FPS_MAX_INDEX]; 602 float slopMinFrameDurationMs = expectedMinFrameDurationMs - 603 kTestSlopMargin; 604 605 int outOfBoundsCount = 0; 606 for (int i = 1; i < kLoopCount; i++) { 607 float frameDurationMs = 608 (timestamps[i] - timestamps[i-1]) / 1000000.f; 609 if (LOGVV) { 610 Log.v(TAG, "Frame " + i + " duration: " + frameDurationMs + 611 " ms, expecting [" + expectedMinFrameDurationMs + "," + 612 expectedMaxFrameDurationMs + "], slop range [" + 613 slopMinFrameDurationMs + "," + slopMaxFrameDurationMs + "]."); 614 } 615 if ( frameDurationMs > slopMaxFrameDurationMs || 616 frameDurationMs < slopMinFrameDurationMs ) { 617 if (LOGVV) { 618 Log.v(TAG, " Out of bounds!!"); 619 } 620 outOfBoundsCount++; 621 } 622 } 623 assertTrue( 624 "Too many frame intervals out of frame rate bounds: " 625 + outOfBoundsCount + 626 ", limit " + kMaxOutOfBoundsFrames, 627 outOfBoundsCount <= kMaxOutOfBoundsFrames); 628 } 629 } 630 terminateMessageLooper(); 631 } // void run(int cameraId) 632 }; 633 634 /** Basic OpenGL ES 2.0 renderer to draw SurfaceTexture-sourced frames to the screen */ 635 private class Renderer implements GLSurfaceView.Renderer { 636 public Renderer() { 637 mTriangleVertices = 638 ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES). 639 order(ByteOrder.nativeOrder()).asFloatBuffer(); 640 mTriangleVertices.put(mTriangleVerticesData).position(0); 641 642 Matrix.setIdentityM(mSTMatrix, 0); 643 Matrix.setIdentityM(mMMatrix, 0); 644 645 mTextureID = 0; 646 } 647 648 public void setCameraSizing(Camera.Size previewSize) { 649 mCameraRatio = (float)previewSize.width/previewSize.height; 650 } 651 652 public boolean waitForDrawDone() { 653 if (!mDrawDone.block(WAIT_FOR_COMMAND_TO_COMPLETE) ) { 654 // timeout could be expected or unexpected. The caller will decide. 655 Log.e(TAG, "waitForDrawDone: timeout"); 656 return false; 657 } 658 mDrawDone.close(); 659 return true; 660 } 661 662 private final ConditionVariable mDrawDone = new ConditionVariable(); 663 664 public void onDrawFrame(GL10 glUnused) { 665 if (LOGVV) Log.v(TAG, "onDrawFrame()"); 666 if (CameraGLTest.this.mSurfaceTexture != null) { 667 CameraGLTest.this.mSurfaceTexture.updateTexImage(); 668 CameraGLTest.this.mSurfaceTexture.getTransformMatrix(mSTMatrix); 669 mDrawDone.open(); 670 } 671 672 // Ignore the passed-in GL10 interface, and use the GLES20 673 // class's static methods instead. 674 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 675 GLES20.glUseProgram(mProgram); 676 checkGlError("glUseProgram"); 677 678 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 679 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); 680 681 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 682 GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 683 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 684 checkGlError("glVertexAttribPointer maPosition"); 685 GLES20.glEnableVertexAttribArray(maPositionHandle); 686 checkGlError("glEnableVertexAttribArray maPositionHandle"); 687 688 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 689 GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false, 690 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 691 checkGlError("glVertexAttribPointer maTextureHandle"); 692 GLES20.glEnableVertexAttribArray(maTextureHandle); 693 checkGlError("glEnableVertexAttribArray maTextureHandle"); 694 695 Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0); 696 Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); 697 698 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); 699 GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); 700 GLES20.glUniform1f(muCRatioHandle, mCameraRatio); 701 702 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 703 checkGlError("glDrawArrays"); 704 } 705 706 public void onSurfaceChanged(GL10 glUnused, int width, int height) { 707 if (LOGV) Log.v(TAG, "onSurfaceChanged()"); 708 // Ignore the passed-in GL10 interface, and use the GLES20 709 // class's static methods instead. 710 GLES20.glViewport(0, 0, width, height); 711 mRatio = (float) width / height; 712 Matrix.frustumM(mProjMatrix, 0, -mRatio, mRatio, -1, 1, 3, 7); 713 } 714 715 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { 716 if (LOGV) Log.v(TAG, "onSurfaceCreated()"); 717 // Ignore the passed-in GL10 interface, and use the GLES20 718 // class's static methods instead. 719 720 /* Set up shaders and handles to their variables */ 721 mProgram = createProgram(mVertexShader, mFragmentShader); 722 if (mProgram == 0) { 723 return; 724 } 725 maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); 726 checkGlError("glGetAttribLocation aPosition"); 727 if (maPositionHandle == -1) { 728 throw new RuntimeException("Could not get attrib location for aPosition"); 729 } 730 maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); 731 checkGlError("glGetAttribLocation aTextureCoord"); 732 if (maTextureHandle == -1) { 733 throw new RuntimeException("Could not get attrib location for aTextureCoord"); 734 } 735 736 muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 737 checkGlError("glGetUniformLocation uMVPMatrix"); 738 if (muMVPMatrixHandle == -1) { 739 throw new RuntimeException("Could not get attrib location for uMVPMatrix"); 740 } 741 742 muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix"); 743 checkGlError("glGetUniformLocation uSTMatrix"); 744 if (muMVPMatrixHandle == -1) { 745 throw new RuntimeException("Could not get attrib location for uSTMatrix"); 746 } 747 748 muCRatioHandle = GLES20.glGetUniformLocation(mProgram, "uCRatio"); 749 checkGlError("glGetUniformLocation uCRatio"); 750 if (muMVPMatrixHandle == -1) { 751 throw new RuntimeException("Could not get attrib location for uCRatio"); 752 } 753 754 /* 755 * Create our texture. This has to be done each time the 756 * surface is created. 757 */ 758 759 int[] textures = new int[1]; 760 GLES20.glGenTextures(1, textures, 0); 761 762 mTextureID = textures[0]; 763 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); 764 checkGlError("glBindTexture mTextureID"); 765 766 // Can't do mipmapping with camera source 767 GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, 768 GLES20.GL_NEAREST); 769 GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, 770 GLES20.GL_LINEAR); 771 // Clamp to edge is the only option 772 GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, 773 GLES20.GL_CLAMP_TO_EDGE); 774 GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, 775 GLES20.GL_CLAMP_TO_EDGE); 776 checkGlError("glTexParameteri mTextureID"); 777 778 Matrix.setLookAtM(mVMatrix, 0, 0, 0, 4f, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 779 } 780 781 public int getTextureID() { 782 return mTextureID; 783 } 784 785 private int loadShader(int shaderType, String source) { 786 int shader = GLES20.glCreateShader(shaderType); 787 if (shader != 0) { 788 GLES20.glShaderSource(shader, source); 789 GLES20.glCompileShader(shader); 790 int[] compiled = new int[1]; 791 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 792 if (compiled[0] == 0) { 793 Log.e(TAG, "Could not compile shader " + shaderType + ":"); 794 Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 795 GLES20.glDeleteShader(shader); 796 shader = 0; 797 } 798 } 799 return shader; 800 } 801 802 private int createProgram(String vertexSource, String fragmentSource) { 803 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 804 if (vertexShader == 0) { 805 return 0; 806 } 807 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 808 if (pixelShader == 0) { 809 return 0; 810 } 811 812 int program = GLES20.glCreateProgram(); 813 if (program != 0) { 814 GLES20.glAttachShader(program, vertexShader); 815 checkGlError("glAttachShader"); 816 GLES20.glAttachShader(program, pixelShader); 817 checkGlError("glAttachShader"); 818 GLES20.glLinkProgram(program); 819 int[] linkStatus = new int[1]; 820 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 821 if (linkStatus[0] != GLES20.GL_TRUE) { 822 Log.e(TAG, "Could not link program: "); 823 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 824 GLES20.glDeleteProgram(program); 825 program = 0; 826 } 827 } 828 return program; 829 } 830 831 private void checkGlError(String op) { 832 int error; 833 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 834 Log.e(TAG, op + ": glError " + error); 835 throw new RuntimeException(op + ": glError " + error); 836 } 837 } 838 839 private static final int FLOAT_SIZE_BYTES = 4; 840 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; 841 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; 842 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; 843 private final float[] mTriangleVerticesData = { 844 // X, Y, Z, U, V 845 -1.0f, -1.0f, 0, 0.f, 0.f, 846 1.0f, -1.0f, 0, 1.f, 0.f, 847 -1.0f, 1.0f, 0, 0.f, 1.f, 848 1.0f, 1.0f, 0, 1.f, 1.f, 849 }; 850 851 private FloatBuffer mTriangleVertices; 852 853 private final String mVertexShader = 854 "uniform mat4 uMVPMatrix;\n" + 855 "uniform mat4 uSTMatrix;\n" + 856 "uniform float uCRatio;\n" + 857 "attribute vec4 aPosition;\n" + 858 "attribute vec4 aTextureCoord;\n" + 859 "varying vec2 vTextureCoord;\n" + 860 "void main() {\n" + 861 " gl_Position = vec4(uCRatio,1,1,1) * uMVPMatrix * aPosition;\n" + 862 " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + 863 "}\n"; 864 865 private final String mFragmentShader = 866 "#extension GL_OES_EGL_image_external : require\n" + 867 "precision mediump float;\n" + 868 "varying vec2 vTextureCoord;\n" + 869 "uniform samplerExternalOES sTexture;\n" + 870 "void main() {\n" + 871 " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 872 "}\n"; 873 874 private float[] mMVPMatrix = new float[16]; 875 private float[] mProjMatrix = new float[16]; 876 private float[] mMMatrix = new float[16]; 877 private float[] mVMatrix = new float[16]; 878 private float[] mSTMatrix = new float[16]; 879 880 private int mProgram; 881 private int mTextureID; 882 private int muMVPMatrixHandle; 883 private int muSTMatrixHandle; 884 private int muCRatioHandle; 885 private int maPositionHandle; 886 private int maTextureHandle; 887 888 private float mRatio = 1.0f; 889 private float mCameraRatio = 1.0f; 890 891 private Context mContext; 892 private static final String TAG = "CameraGLTest.Renderer"; 893 894 // Magic key 895 private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; 896 } 897 898 } 899