• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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