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