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.setPreviewCallback(mPreviewCallback);
    349             mCamera.startPreview();
    350             mCamera.setPreviewTexture(mSurfaceTexture);
    351             noTimeout = waitForPreviewDone();
    352             assertTrue("Timeout waiting for new preview callback!", noTimeout);
    353             terminateMessageLooper();
    354 
    355             // Check the order: setPreviewTexture->startPreview.
    356             initializeMessageLooper(cameraId);
    357             mCamera.setPreviewCallback(mPreviewCallback);
    358             mCamera.setPreviewTexture(mSurfaceTexture);
    359             mCamera.startPreview();
    360             noTimeout = waitForPreviewDone();
    361             assertTrue("Timeout waiting for new preview callback!", noTimeout);
    362 
    363             // Check the order: setting preview display to null->startPreview->
    364             // setPreviewTexture.
    365             mCamera.setPreviewCallback(mPreviewCallback);
    366             mCamera.setPreviewTexture(null);
    367             mCamera.startPreview();
    368             mCamera.setPreviewTexture(mSurfaceTexture);
    369             noTimeout = waitForPreviewDone();
    370             assertTrue("Timeout waiting for new preview callback!", noTimeout);
    371             terminateMessageLooper();
    372         }
    373     };
    374 
    375     /** Test Camera.setPreviewTexture in conjunction with both the standard Camera preview callback,
    376         and the SurfaceTexture onFrameAvailable callback */
    377     @UiThreadTest
    378     public void testSetPreviewTextureBothCallbacks() throws Exception {
    379         runForAllCameras(testSetPreviewTextureBothCallbacksByCamera);
    380     }
    381 
    382     private RunPerCamera testSetPreviewTextureBothCallbacksByCamera = new RunPerCamera() {
    383         public void run(int cameraId) throws Exception {
    384             boolean noTimeout;
    385             // Check SurfaceTexture callback together with preview callback
    386             // Check the order: setPreviewTexture->startPreview
    387             mSurfaceTextureDone.close();
    388             initializeMessageLooper(cameraId);
    389             mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    390             mCamera.setPreviewCallback(mPreviewCallback);
    391             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback);
    392             mCamera.setPreviewTexture(mSurfaceTexture);
    393             mCamera.startPreview();
    394 
    395             noTimeout = waitForSurfaceTextureDone();
    396             assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout);
    397             noTimeout = waitForPreviewDone();
    398             assertTrue("Timeout waiting for new preview callback!",noTimeout);
    399 
    400             mGLView.requestRender();
    401             terminateMessageLooper();
    402 
    403             // Check the order: startPreview->setPreviewTexture
    404             mSurfaceTextureDone.close();
    405             initializeMessageLooper(cameraId);
    406             mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    407             mCamera.setPreviewCallback(mPreviewCallback);
    408             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback);
    409             mCamera.startPreview();
    410             mCamera.setPreviewTexture(mSurfaceTexture);
    411 
    412             noTimeout = waitForSurfaceTextureDone();
    413             assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout);
    414             noTimeout = waitForPreviewDone();
    415             assertTrue("Timeout waiting for new preview callback!", noTimeout);
    416 
    417             mGLView.requestRender();
    418 
    419             // Check the order: setting preview to null->startPreview->setPreviewTexture
    420             mCamera.setPreviewCallback(mPreviewCallback);
    421             mCamera.setPreviewTexture(null);
    422             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureCallback);
    423             mCamera.startPreview();
    424             mCamera.setPreviewTexture(mSurfaceTexture);
    425             noTimeout = waitForPreviewDone();
    426             assertTrue(noTimeout);
    427             terminateMessageLooper();
    428         }
    429     };
    430 
    431     /** Test Camera.setPreviewTexture in conjunction with just the SurfaceTexture onFrameAvailable callback */
    432     @UiThreadTest
    433     public void testSetPreviewTextureTextureCallback() throws Exception {
    434         runForAllCameras(testSetPreviewTextureTextureCallbackByCamera);
    435     }
    436 
    437     private RunPerCamera testSetPreviewTextureTextureCallbackByCamera = new RunPerCamera() {
    438         public void run(int cameraId) throws Exception {
    439             boolean noTimeout;
    440             // Check that SurfaceTexture callbacks work with no standard
    441             // preview callback
    442             mSurfaceTextureCallbackResult = false;
    443             mSurfaceTextureDone.close();
    444             initializeMessageLooper(cameraId);
    445             mSurfaceTextureBurstCallback.setBurstCount(1);
    446             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback);
    447             mCamera.setPreviewTexture(mSurfaceTexture);
    448             mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    449             mCamera.startPreview();
    450 
    451             noTimeout = waitForSurfaceTextureDone();
    452             mGLView.requestRender();
    453             assertTrue(noTimeout);
    454 
    455             terminateMessageLooper();
    456             assertTrue(mSurfaceTextureCallbackResult);
    457 
    458             // Check that SurfaceTexture callbacks also work with
    459             // startPreview->setPreviewTexture
    460             mSurfaceTextureCallbackResult = false;
    461             mSurfaceTextureDone.close();
    462             initializeMessageLooper(cameraId);
    463             mSurfaceTextureBurstCallback.setBurstCount(1);
    464             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback);
    465             mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    466             mCamera.startPreview();
    467             mCamera.setPreviewTexture(mSurfaceTexture);
    468 
    469             noTimeout = waitForSurfaceTextureDone();
    470             assertTrue(noTimeout);
    471 
    472             terminateMessageLooper();
    473             assertTrue(mSurfaceTextureCallbackResult);
    474 
    475             // Check that SurfaceTexture callbacks also work with
    476             // null->startPreview->setPreviewTexture
    477             mSurfaceTextureCallbackResult = false;
    478             mSurfaceTextureDone.close();
    479             initializeMessageLooper(cameraId);
    480             mSurfaceTextureBurstCallback.setBurstCount(1);
    481             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback);
    482             mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    483             mCamera.setPreviewTexture(null);
    484             mCamera.startPreview();
    485             mCamera.setPreviewTexture(mSurfaceTexture);
    486 
    487             noTimeout = waitForSurfaceTextureDone();
    488             assertTrue(noTimeout);
    489 
    490             terminateMessageLooper();
    491             assertTrue(mSurfaceTextureCallbackResult);
    492         }
    493     };
    494 
    495     /** Test all preview sizes and framerates along with SurfaceTexture-provided metadata (texture
    496      * transforms and timestamps) */
    497     @UiThreadTest
    498     public void testCameraToSurfaceTextureMetadata() throws Exception {
    499         runForAllCameras(testCameraToSurfaceTextureMetadataByCamera);
    500     }
    501 
    502     private RunPerCamera testCameraToSurfaceTextureMetadataByCamera = new RunPerCamera() {
    503         public void run(int cameraId) throws Exception {
    504             // Number of frames to test over
    505             int kLoopCount = 100;
    506             // Ignore timestamp issues before this frame
    507             int kFirstTestedFrame = 10;
    508             // Slop in timestamp testing, needed because timestamps are not
    509             // currently being set by driver-level code so are subject to
    510             // lots of variability
    511             float kTestSlopMargin = 30; // ms
    512 
    513             boolean noTimeout;
    514             initializeMessageLooper(cameraId);
    515             Parameters parameters = mCamera.getParameters();
    516 
    517             mSurfaceTexture.setOnFrameAvailableListener(mSurfaceTextureBurstCallback);
    518             mCamera.setPreviewTexture(mSurfaceTexture);
    519 
    520             for (Size size: parameters.getSupportedPreviewSizes()) {
    521                 for (int[] fps: parameters.getSupportedPreviewFpsRange()) {
    522                     if (LOGV) {
    523                         Log.v(TAG, "Testing camera #" + cameraId +
    524                               ", preview size:" + size.width + "x" + size.height +
    525                               ", frame rate range: [" +
    526                               (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + "," +
    527                               (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "]");
    528                     }
    529                     parameters.setPreviewSize(size.width, size.height);
    530                     parameters.setPreviewFpsRange(fps[Parameters.PREVIEW_FPS_MIN_INDEX],
    531                                                   fps[Parameters.PREVIEW_FPS_MAX_INDEX]);
    532                     mCamera.setParameters(parameters);
    533 
    534                     assertEquals(size, mCamera.getParameters().getPreviewSize());
    535 
    536                     int[] actualFps = new int[2];
    537                     mCamera.getParameters().getPreviewFpsRange(actualFps);
    538                     assertEquals(fps[Parameters.PREVIEW_FPS_MIN_INDEX],
    539                                  actualFps[Parameters.PREVIEW_FPS_MIN_INDEX]);
    540                     assertEquals(fps[Parameters.PREVIEW_FPS_MAX_INDEX],
    541                                  actualFps[Parameters.PREVIEW_FPS_MAX_INDEX]);
    542 
    543                     mSurfaceTextureBurstCallback.setBurstCount(kLoopCount);
    544                     mSurfaceTextureCallbackResult = false;
    545                     mSurfaceTextureDone.close();
    546 
    547                     mRenderer.setCameraSizing(mCamera.getParameters().getPreviewSize());
    548                     if (LOGV) Log.v(TAG, "Starting preview");
    549                     mCamera.startPreview();
    550                     if (LOGVV) Log.v(TAG, "Preview started");
    551 
    552                     long[] timestamps = new long[kLoopCount];
    553                     for (int i = 0; i < kLoopCount; i++) {
    554                         noTimeout = waitForSurfaceTextureDone();
    555                         assertTrue("Timeout waiting for frame " + i +
    556                                    " (burst callback thinks " +
    557                                    (kLoopCount - mSurfaceTextureBurstCallback.getBurstCount()) +
    558                                    ")! (Size " + size.width + "x" + size.height + ", fps [" +
    559                                    (fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.) + ", " +
    560                                    (fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.) + "])",
    561                                    noTimeout);
    562 
    563                         if (LOGVV) Log.v(TAG, "Frame #" + i + " completed");
    564                         // Draw the frame (and update the SurfaceTexture)
    565                         mGLView.requestRender();
    566                         // Wait until frame is drawn, so that the SurfaceTexture has new
    567                         // metadata
    568                         noTimeout = mRenderer.waitForDrawDone();
    569                         assertTrue(noTimeout);
    570 
    571                         // Store timestamps for later
    572                         timestamps[i] = mSurfaceTexture.getTimestamp();
    573                         // Verify that the surfaceTexture transform has at least one non-zero
    574                         // entry
    575                         float[] transform = new float[16];
    576                         mSurfaceTexture.getTransformMatrix(transform);
    577                         boolean nonZero = false;
    578                         for (int k = 0; k < 16; k++) {
    579                             if (transform[k] != 0.f) {
    580                                 nonZero = true;
    581                                 break;
    582                             }
    583                         }
    584                         assertTrue(nonZero);
    585                     }
    586                     assertTrue(mSurfaceTextureCallbackResult);
    587 
    588                     float expectedMaxFrameDurationMs = 1000.f * 1000.f /
    589                             fps[Parameters.PREVIEW_FPS_MIN_INDEX];
    590                     float slopMaxFrameDurationMs = expectedMaxFrameDurationMs +
    591                             kTestSlopMargin;
    592                     float expectedMinFrameDurationMs = 1000.f * 1000.f /
    593                             fps[Parameters.PREVIEW_FPS_MAX_INDEX];
    594                     float slopMinFrameDurationMs = expectedMinFrameDurationMs  -
    595                             kTestSlopMargin;
    596 
    597                     for (int i = kFirstTestedFrame; i < kLoopCount; i++) {
    598                         float frameDurationMs = (timestamps[i] - timestamps[i - 1]) / 1000000.f;
    599                         if (LOGVV) {
    600                             Log.v(TAG, "Frame " + i + " duration: " + frameDurationMs +
    601                                   " ms, expecting [" + expectedMinFrameDurationMs + "," +
    602                                   expectedMaxFrameDurationMs + "], slop range [" +
    603                                   slopMinFrameDurationMs + "," + slopMaxFrameDurationMs + "].");
    604                         }
    605                         assertTrue("Frame " + i + " duration out of bounds! ("+
    606                                    frameDurationMs + " ms, expecting [" +
    607                                    slopMinFrameDurationMs + "," +
    608                                    slopMaxFrameDurationMs + "] ms)",
    609                                    (frameDurationMs > slopMinFrameDurationMs) &&
    610                                    (frameDurationMs < slopMaxFrameDurationMs) );
    611                     }
    612                 }
    613             }
    614             terminateMessageLooper();
    615         } // void run(int cameraId)
    616     };
    617 
    618     /** Basic OpenGL ES 2.0 renderer to draw SurfaceTexture-sourced frames to the screen */
    619     private class Renderer implements GLSurfaceView.Renderer {
    620         public Renderer() {
    621             mTriangleVertices =
    622                     ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES).
    623                     order(ByteOrder.nativeOrder()).asFloatBuffer();
    624             mTriangleVertices.put(mTriangleVerticesData).position(0);
    625 
    626             Matrix.setIdentityM(mSTMatrix, 0);
    627             Matrix.setIdentityM(mMMatrix, 0);
    628 
    629             mTextureID = 0;
    630         }
    631 
    632         public void setCameraSizing(Camera.Size previewSize) {
    633             mCameraRatio = (float)previewSize.width/previewSize.height;
    634         }
    635 
    636         public boolean waitForDrawDone() {
    637             if (!mDrawDone.block(WAIT_FOR_COMMAND_TO_COMPLETE) ) {
    638                 // timeout could be expected or unexpected. The caller will decide.
    639                 Log.e(TAG, "waitForDrawDone: timeout");
    640                 return false;
    641             }
    642             mDrawDone.close();
    643             return true;
    644         }
    645 
    646         private final ConditionVariable mDrawDone = new ConditionVariable();
    647 
    648         public void onDrawFrame(GL10 glUnused) {
    649             if (LOGVV) Log.v(TAG, "onDrawFrame()");
    650             if (CameraGLTest.this.mSurfaceTexture != null) {
    651                 CameraGLTest.this.mSurfaceTexture.updateTexImage();
    652                 CameraGLTest.this.mSurfaceTexture.getTransformMatrix(mSTMatrix);
    653                 mDrawDone.open();
    654             }
    655 
    656             // Ignore the passed-in GL10 interface, and use the GLES20
    657             // class's static methods instead.
    658             GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    659             GLES20.glUseProgram(mProgram);
    660             checkGlError("glUseProgram");
    661 
    662             GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    663             GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
    664 
    665             mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
    666             GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
    667                                          TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
    668             checkGlError("glVertexAttribPointer maPosition");
    669             GLES20.glEnableVertexAttribArray(maPositionHandle);
    670             checkGlError("glEnableVertexAttribArray maPositionHandle");
    671 
    672             mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
    673             GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
    674                                          TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
    675             checkGlError("glVertexAttribPointer maTextureHandle");
    676             GLES20.glEnableVertexAttribArray(maTextureHandle);
    677             checkGlError("glEnableVertexAttribArray maTextureHandle");
    678 
    679             Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
    680             Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
    681 
    682             GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
    683             GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);
    684             GLES20.glUniform1f(muCRatioHandle, mCameraRatio);
    685 
    686             GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    687             checkGlError("glDrawArrays");
    688         }
    689 
    690         public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    691             if (LOGV) Log.v(TAG, "onSurfaceChanged()");
    692             // Ignore the passed-in GL10 interface, and use the GLES20
    693             // class's static methods instead.
    694             GLES20.glViewport(0, 0, width, height);
    695             mRatio = (float) width / height;
    696             Matrix.frustumM(mProjMatrix, 0, -mRatio, mRatio, -1, 1, 3, 7);
    697         }
    698 
    699         public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
    700             if (LOGV) Log.v(TAG, "onSurfaceCreated()");
    701             // Ignore the passed-in GL10 interface, and use the GLES20
    702             // class's static methods instead.
    703 
    704             /* Set up shaders and handles to their variables */
    705             mProgram = createProgram(mVertexShader, mFragmentShader);
    706             if (mProgram == 0) {
    707                 return;
    708             }
    709             maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    710             checkGlError("glGetAttribLocation aPosition");
    711             if (maPositionHandle == -1) {
    712                 throw new RuntimeException("Could not get attrib location for aPosition");
    713             }
    714             maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
    715             checkGlError("glGetAttribLocation aTextureCoord");
    716             if (maTextureHandle == -1) {
    717                 throw new RuntimeException("Could not get attrib location for aTextureCoord");
    718             }
    719 
    720             muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    721             checkGlError("glGetUniformLocation uMVPMatrix");
    722             if (muMVPMatrixHandle == -1) {
    723                 throw new RuntimeException("Could not get attrib location for uMVPMatrix");
    724             }
    725 
    726             muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
    727             checkGlError("glGetUniformLocation uSTMatrix");
    728             if (muMVPMatrixHandle == -1) {
    729                 throw new RuntimeException("Could not get attrib location for uSTMatrix");
    730             }
    731 
    732             muCRatioHandle = GLES20.glGetUniformLocation(mProgram, "uCRatio");
    733             checkGlError("glGetUniformLocation uCRatio");
    734             if (muMVPMatrixHandle == -1) {
    735                 throw new RuntimeException("Could not get attrib location for uCRatio");
    736             }
    737 
    738             /*
    739              * Create our texture. This has to be done each time the
    740              * surface is created.
    741              */
    742 
    743             int[] textures = new int[1];
    744             GLES20.glGenTextures(1, textures, 0);
    745 
    746             mTextureID = textures[0];
    747             GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
    748             checkGlError("glBindTexture mTextureID");
    749 
    750             // Can't do mipmapping with camera source
    751             GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
    752                                    GLES20.GL_NEAREST);
    753             GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
    754                                    GLES20.GL_LINEAR);
    755             // Clamp to edge is the only option
    756             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
    757                                    GLES20.GL_CLAMP_TO_EDGE);
    758             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
    759                                    GLES20.GL_CLAMP_TO_EDGE);
    760             checkGlError("glTexParameteri mTextureID");
    761 
    762             Matrix.setLookAtM(mVMatrix, 0, 0, 0, 4f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    763         }
    764 
    765         public int getTextureID() {
    766             return mTextureID;
    767         }
    768 
    769         private int loadShader(int shaderType, String source) {
    770             int shader = GLES20.glCreateShader(shaderType);
    771             if (shader != 0) {
    772                 GLES20.glShaderSource(shader, source);
    773                 GLES20.glCompileShader(shader);
    774                 int[] compiled = new int[1];
    775                 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    776                 if (compiled[0] == 0) {
    777                     Log.e(TAG, "Could not compile shader " + shaderType + ":");
    778                     Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
    779                     GLES20.glDeleteShader(shader);
    780                     shader = 0;
    781                 }
    782             }
    783             return shader;
    784         }
    785 
    786         private int createProgram(String vertexSource, String fragmentSource) {
    787             int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
    788             if (vertexShader == 0) {
    789                 return 0;
    790             }
    791             int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
    792             if (pixelShader == 0) {
    793                 return 0;
    794             }
    795 
    796             int program = GLES20.glCreateProgram();
    797             if (program != 0) {
    798                 GLES20.glAttachShader(program, vertexShader);
    799                 checkGlError("glAttachShader");
    800                 GLES20.glAttachShader(program, pixelShader);
    801                 checkGlError("glAttachShader");
    802                 GLES20.glLinkProgram(program);
    803                 int[] linkStatus = new int[1];
    804                 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
    805                 if (linkStatus[0] != GLES20.GL_TRUE) {
    806                     Log.e(TAG, "Could not link program: ");
    807                     Log.e(TAG, GLES20.glGetProgramInfoLog(program));
    808                     GLES20.glDeleteProgram(program);
    809                     program = 0;
    810                 }
    811             }
    812             return program;
    813         }
    814 
    815         private void checkGlError(String op) {
    816             int error;
    817             while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
    818                 Log.e(TAG, op + ": glError " + error);
    819                 throw new RuntimeException(op + ": glError " + error);
    820             }
    821         }
    822 
    823         private static final int FLOAT_SIZE_BYTES = 4;
    824         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    825         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    826         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    827         private final float[] mTriangleVerticesData = {
    828             // X, Y, Z, U, V
    829             -1.0f, -1.0f, 0, 0.f, 0.f,
    830             1.0f, -1.0f, 0, 1.f, 0.f,
    831             -1.0f,  1.0f, 0, 0.f, 1.f,
    832             1.0f,   1.0f, 0, 1.f, 1.f,
    833         };
    834 
    835         private FloatBuffer mTriangleVertices;
    836 
    837         private final String mVertexShader =
    838                 "uniform mat4 uMVPMatrix;\n" +
    839                 "uniform mat4 uSTMatrix;\n" +
    840                 "uniform float uCRatio;\n" +
    841                 "attribute vec4 aPosition;\n" +
    842                 "attribute vec4 aTextureCoord;\n" +
    843                 "varying vec2 vTextureCoord;\n" +
    844                 "void main() {\n" +
    845                 "  gl_Position = vec4(uCRatio,1,1,1) * uMVPMatrix * aPosition;\n" +
    846                 "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
    847                 "}\n";
    848 
    849         private final String mFragmentShader =
    850                 "#extension GL_OES_EGL_image_external : require\n" +
    851                 "precision mediump float;\n" +
    852                 "varying vec2 vTextureCoord;\n" +
    853                 "uniform samplerExternalOES sTexture;\n" +
    854                 "void main() {\n" +
    855                 "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
    856                 "}\n";
    857 
    858         private float[] mMVPMatrix = new float[16];
    859         private float[] mProjMatrix = new float[16];
    860         private float[] mMMatrix = new float[16];
    861         private float[] mVMatrix = new float[16];
    862         private float[] mSTMatrix = new float[16];
    863 
    864         private int mProgram;
    865         private int mTextureID;
    866         private int muMVPMatrixHandle;
    867         private int muSTMatrixHandle;
    868         private int muCRatioHandle;
    869         private int maPositionHandle;
    870         private int maTextureHandle;
    871 
    872         private float mRatio = 1.0f;
    873         private float mCameraRatio = 1.0f;
    874 
    875         private Context mContext;
    876         private static final String TAG = "CameraGLTest.Renderer";
    877 
    878         // Magic key
    879         private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
    880     }
    881 
    882 }
    883