Home | History | Annotate | Download | only in cts
      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