1/*
2 * Copyright (C) 2016 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
17package com.android.mediaframeworktest;
18
19import com.android.ex.camera2.blocking.BlockingSessionCallback;
20import com.android.ex.camera2.blocking.BlockingStateCallback;
21import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
22import com.android.mediaframeworktest.helpers.CameraErrorCollector;
23import com.android.mediaframeworktest.helpers.CameraTestResultPrinter;
24import com.android.mediaframeworktest.helpers.CameraTestUtils;
25import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
26import com.android.mediaframeworktest.helpers.StaticMetadata;
27import com.android.mediaframeworktest.helpers.StaticMetadata.CheckLevel;
28
29import android.content.Context;
30import android.graphics.ImageFormat;
31import android.hardware.camera2.CameraAccessException;
32import android.hardware.camera2.CameraCaptureSession;
33import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
34import android.hardware.camera2.CameraCharacteristics;
35import android.hardware.camera2.CameraDevice;
36import android.hardware.camera2.CameraManager;
37import android.hardware.camera2.CameraMetadata;
38import android.hardware.camera2.CaptureRequest;
39import android.hardware.camera2.CaptureResult;
40import android.hardware.camera2.params.StreamConfigurationMap;
41import android.media.ImageReader;
42import android.graphics.SurfaceTexture;
43import android.os.Bundle;
44import android.os.Environment;
45import android.os.Handler;
46import android.os.HandlerThread;
47import android.os.Looper;
48import android.test.ActivityInstrumentationTestCase2;
49import android.test.InstrumentationTestRunner;
50import android.util.Log;
51import android.util.Range;
52import android.util.Size;
53import android.view.Surface;
54import android.view.SurfaceHolder;
55import android.view.WindowManager;
56
57import java.text.NumberFormat;
58import java.text.ParseException;
59import java.util.ArrayList;
60import java.util.HashMap;
61import java.util.List;
62
63import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
64import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS;
65import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
66import static com.android.mediaframeworktest.helpers.CameraTestUtils.PREVIEW_SIZE_BOUND;
67import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
68import static com.android.mediaframeworktest.helpers.CameraTestUtils.getPreviewSizeBound;
69import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedPreviewSizes;
70import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedStillSizes;
71import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
72import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSortedSizesForFormat;
73import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
74
75/**
76 * Camera2 Preview test case base class by using SurfaceView as rendering target.
77 *
78 * <p>This class encapsulates the SurfaceView based preview common functionalities.
79 * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
80 * and CameraStateCallback are handled in this class. Some basic preview related utility
81 * functions are provided to facilitate the derived preview-based test classes.
82 * </p>
83 */
84/**
85 * (non-Javadoc)
86 * @see android.hardware.camera2.cts.Camera2SurfaceViewTestCase
87 */
88public class Camera2SurfaceViewTestCase extends
89        ActivityInstrumentationTestCase2<Camera2SurfaceViewActivity> {
90
91    private static final String TAG = "SurfaceViewTestCase";
92    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
93    private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
94
95    // Instrumentation arguments
96    protected static final String ARG_KEY_ITERATIONS = "iterations";
97    protected static final String ARG_KEY_WAIT_INTERVAL_MS = "waitIntervalMs";
98    protected static final String ARG_KEY_RESULT_TO_FILE = "resultToFile";
99
100    // TODO: Use internal storage for this to make sure the file is only visible to test.
101    protected static final String DEBUG_FILE_NAME_BASE =
102            Environment.getExternalStorageDirectory().getPath();
103    protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
104    protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
105    protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
106    protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
107    protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns
108
109    protected Context mContext;
110    protected CameraManager mCameraManager;
111    protected String[] mCameraIds;
112    protected HandlerThread mHandlerThread;
113    protected Handler mHandler;
114    protected BlockingStateCallback mCameraListener;
115    protected BlockingSessionCallback mSessionListener;
116    protected CameraErrorCollector mCollector;
117    // Per device fields:
118    protected StaticMetadata mStaticInfo;
119    protected CameraDevice mCamera;
120    protected CameraCaptureSession mSession;
121    protected ImageReader mReader;
122    protected Surface mReaderSurface;
123    protected Surface mPreviewSurface;
124    protected Size mPreviewSize;
125    protected List<Size> mOrderedPreviewSizes; // In descending order.
126    protected List<Size> mOrderedVideoSizes; // In descending order.
127    protected List<Size> mOrderedStillSizes; // In descending order.
128    protected List<Size> mOrderedRAW10Sizes; // In descending order.
129    protected List<Size> mOrderedYUV420888Sizes; // In descending order.
130    protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
131    protected boolean mSupportRAW10;
132
133    protected WindowManager mWindowManager;
134
135    // Set the number of iterations to run stress testing. Default to 1.
136    protected int mIterations = 1;
137    // The interval between test iterations used for stress test.
138    protected long mTestWaitIntervalMs = 1 * 1000;  // 1 sec
139    protected boolean mWriteToFile = true;
140    protected CameraTestResultPrinter mResultPrinter;
141
142
143    public Camera2SurfaceViewTestCase() {
144        super(Camera2SurfaceViewActivity.class);
145    }
146
147    @Override
148    protected void setUp() throws Exception {
149        /**
150         * Set up the camera preview required environments, including activity,
151         * CameraManager, HandlerThread, Camera IDs, and CameraStateCallback.
152         */
153        super.setUp();
154        mContext = getActivity();
155        /**
156         * Workaround for mockito and JB-MR2 incompatibility
157         *
158         * Avoid java.lang.IllegalArgumentException: dexcache == null
159         * https://code.google.com/p/dexmaker/issues/detail?id=2
160         */
161        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
162        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
163        assertNotNull("Unable to get CameraManager", mCameraManager);
164        mCameraIds = mCameraManager.getCameraIdList();
165        assertNotNull("Unable to get camera ids", mCameraIds);
166        mHandlerThread = new HandlerThread(TAG);
167        mHandlerThread.start();
168        mHandler = new Handler(mHandlerThread.getLooper());
169        mCameraListener = new BlockingStateCallback();
170        mCollector = new CameraErrorCollector();
171
172        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
173
174        mIterations = getArgumentsAsNumber(ARG_KEY_ITERATIONS, 1).intValue();
175        mTestWaitIntervalMs = getArgumentsAsNumber(ARG_KEY_WAIT_INTERVAL_MS, 1000).longValue();
176        mWriteToFile = getArgumentsAsBoolean(ARG_KEY_RESULT_TO_FILE, true);
177        Log.i(TAG, "Argument: iteration count=" + mIterations);
178        Log.i(TAG, "Argument: interval (ms)=" + mTestWaitIntervalMs);
179        Log.i(TAG, "Argument: result to file=" + (mWriteToFile ? "true" : "false"));
180        mResultPrinter = new CameraTestResultPrinter(getInstrumentation(), mWriteToFile);
181    }
182
183    @Override
184    protected void tearDown() throws Exception {
185        // Teardown the camera preview required environments.
186        mHandlerThread.quitSafely();
187        mHandler = null;
188        mCameraListener = null;
189
190        try {
191            mCollector.verify();
192        } catch (Throwable e) {
193            // When new Exception(e) is used, exception info will be printed twice.
194            throw new Exception(e.getMessage());
195        } finally {
196            super.tearDown();
197        }
198    }
199
200    /**
201     * Start camera preview by using the given request, preview size and capture
202     * listener.
203     * <p>
204     * If preview is already started, calling this function will stop the
205     * current preview stream and start a new preview stream with given
206     * parameters. No need to call stopPreview between two startPreview calls.
207     * </p>
208     *
209     * @param request The request builder used to start the preview.
210     * @param previewSz The size of the camera device output preview stream.
211     * @param listener The callbacks the camera device will notify when preview
212     *            capture is available.
213     */
214    protected void startPreview(CaptureRequest.Builder request, Size previewSz,
215            CaptureCallback listener) throws Exception {
216        // Update preview size.
217        updatePreviewSurface(previewSz);
218        if (VERBOSE) {
219            Log.v(TAG, "start preview with size " + mPreviewSize.toString());
220        }
221
222        configurePreviewOutput(request);
223
224        mSession.setRepeatingRequest(request.build(), listener, mHandler);
225    }
226
227    /**
228     * Configure the preview output stream.
229     *
230     * @param request The request to be configured with preview surface
231     */
232    protected void configurePreviewOutput(CaptureRequest.Builder request)
233            throws CameraAccessException {
234        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
235        outputSurfaces.add(mPreviewSurface);
236        mSessionListener = new BlockingSessionCallback();
237        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
238
239        request.addTarget(mPreviewSurface);
240    }
241
242    /**
243     * Create a {@link CaptureRequest#Builder} and add the default preview surface.
244     *
245     * @return The {@link CaptureRequest#Builder} to be created
246     * @throws CameraAccessException When create capture request from camera fails
247     */
248    protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException {
249        if (mPreviewSurface == null) {
250            throw new IllegalStateException(
251                    "Preview surface is not set yet, call updatePreviewSurface or startPreview"
252                    + "first to set the preview surface properly.");
253        }
254        CaptureRequest.Builder requestBuilder =
255                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
256        requestBuilder.addTarget(mPreviewSurface);
257        return requestBuilder;
258    }
259
260    /**
261     * Stop preview for current camera device.
262     */
263    protected void stopPreview() throws Exception {
264        if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
265        // Stop repeat, wait for captures to complete, and disconnect from surfaces
266        mSession.close();
267    }
268
269    /**
270     * Setup still (JPEG) capture configuration and start preview.
271     * <p>
272     * The default max number of image is set to image reader.
273     * </p>
274     *
275     * @param previewRequest The capture request to be used for preview
276     * @param stillRequest The capture request to be used for still capture
277     * @param previewSz Preview size
278     * @param stillSz The still capture size
279     * @param resultListener Capture result listener
280     * @param imageListener The still capture image listener
281     */
282    protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
283            CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
284            CaptureCallback resultListener,
285            ImageReader.OnImageAvailableListener imageListener) throws Exception {
286        prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
287                ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener);
288    }
289
290    /**
291     * Setup still (JPEG) capture configuration and start preview.
292     *
293     * @param previewRequest The capture request to be used for preview
294     * @param stillRequest The capture request to be used for still capture
295     * @param previewSz Preview size
296     * @param stillSz The still capture size
297     * @param resultListener Capture result listener
298     * @param maxNumImages The max number of images set to the image reader
299     * @param imageListener The still capture image listener
300     */
301    protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
302            CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
303            CaptureCallback resultListener, int maxNumImages,
304            ImageReader.OnImageAvailableListener imageListener) throws Exception {
305        prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
306                ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
307    }
308
309    /**
310     * Setup raw capture configuration and start preview.
311     *
312     * <p>
313     * The default max number of image is set to image reader.
314     * </p>
315     *
316     * @param previewRequest The capture request to be used for preview
317     * @param rawRequest The capture request to be used for raw capture
318     * @param previewSz Preview size
319     * @param rawSz The raw capture size
320     * @param resultListener Capture result listener
321     * @param imageListener The raw capture image listener
322     */
323    protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
324            CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz,
325            CaptureCallback resultListener,
326            ImageReader.OnImageAvailableListener imageListener) throws Exception {
327        prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz,
328                ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener);
329    }
330
331    /**
332     * Wait for expected result key value available in a certain number of results.
333     *
334     * <p>
335     * Check the result immediately if numFramesWait is 0.
336     * </p>
337     *
338     * @param listener The capture listener to get capture result
339     * @param resultKey The capture result key associated with the result value
340     * @param expectedValue The result value need to be waited for
341     * @param numResultsWait Number of frame to wait before times out
342     * @throws TimeoutRuntimeException If more than numResultsWait results are
343     * seen before the result matching myRequest arrives, or each individual wait
344     * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
345     */
346    protected static <T> void waitForResultValue(SimpleCaptureCallback listener,
347            CaptureResult.Key<T> resultKey,
348            T expectedValue, int numResultsWait) {
349        List<T> expectedValues = new ArrayList<T>();
350        expectedValues.add(expectedValue);
351        waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait);
352    }
353
354    /**
355     * Wait for any expected result key values available in a certain number of results.
356     *
357     * <p>
358     * Check the result immediately if numFramesWait is 0.
359     * </p>
360     *
361     * @param listener The capture listener to get capture result.
362     * @param resultKey The capture result key associated with the result value.
363     * @param expectedValues The list of result value need to be waited for,
364     * return immediately if the list is empty.
365     * @param numResultsWait Number of frame to wait before times out.
366     * @throws TimeoutRuntimeException If more than numResultsWait results are.
367     * seen before the result matching myRequest arrives, or each individual wait
368     * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
369     */
370    protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener,
371            CaptureResult.Key<T> resultKey,
372            List<T> expectedValues, int numResultsWait) {
373        if (numResultsWait < 0 || listener == null || expectedValues == null) {
374            throw new IllegalArgumentException(
375                    "Input must be non-negative number and listener/expectedValues "
376                    + "must be non-null");
377        }
378
379        int i = 0;
380        CaptureResult result;
381        do {
382            result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
383            T value = result.get(resultKey);
384            for ( T expectedValue : expectedValues) {
385                if (VERBOSE) {
386                    Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: "
387                            + value.toString());
388                }
389                if (value.equals(expectedValue)) {
390                    return;
391                }
392            }
393        } while (i++ < numResultsWait);
394
395        throw new TimeoutRuntimeException(
396                "Unable to get the expected result value " + expectedValues + " for key " +
397                        resultKey.getName() + " after waiting for " + numResultsWait + " results");
398    }
399
400    /**
401     * Submit a capture once, then submit additional captures in order to ensure that
402     * the camera will be synchronized.
403     *
404     * <p>
405     * The additional capture count is determined by android.sync.maxLatency (or
406     * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
407     * </p>
408     *
409     * <p>Returns the number of captures that were submitted (at least 1), which is useful
410     * with {@link #waitForNumResults}.</p>
411     *
412     * @param request capture request to forward to {@link CameraDevice#capture}
413     * @param listener request listener to forward to {@link CameraDevice#capture}
414     * @param handler handler to forward to {@link CameraDevice#capture}
415     *
416     * @return the number of captures that were submitted
417     *
418     * @throws CameraAccessException if capturing failed
419     */
420    protected int captureRequestsSynchronized(
421            CaptureRequest request, CaptureCallback listener, Handler handler)
422                    throws CameraAccessException {
423        return captureRequestsSynchronized(request, /*count*/1, listener, handler);
424    }
425
426    /**
427     * Submit a capture {@code count} times, then submit additional captures in order to ensure that
428     * the camera will be synchronized.
429     *
430     * <p>
431     * The additional capture count is determined by android.sync.maxLatency (or
432     * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
433     * </p>
434     *
435     * <p>Returns the number of captures that were submitted (at least 1), which is useful
436     * with {@link #waitForNumResults}.</p>
437     *
438     * @param request capture request to forward to {@link CameraDevice#capture}
439     * @param count the number of times to submit the request (minimally), must be at least 1
440     * @param listener request listener to forward to {@link CameraDevice#capture}
441     * @param handler handler to forward to {@link CameraDevice#capture}
442     *
443     * @return the number of captures that were submitted
444     *
445     * @throws IllegalArgumentException if {@code count} was not at least 1
446     * @throws CameraAccessException if capturing failed
447     */
448    protected int captureRequestsSynchronized(
449            CaptureRequest request, int count, CaptureCallback listener, Handler handler)
450                    throws CameraAccessException {
451        if (count < 1) {
452            throw new IllegalArgumentException("count must be positive");
453        }
454
455        int maxLatency = mStaticInfo.getSyncMaxLatency();
456        if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
457            maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
458        }
459
460        assertTrue("maxLatency is non-negative", maxLatency >= 0);
461
462        int numCaptures = maxLatency + count;
463
464        for (int i = 0; i < numCaptures; ++i) {
465            mSession.capture(request, listener, handler);
466        }
467
468        return numCaptures;
469    }
470
471    /**
472     * Wait for numResultWait frames
473     *
474     * @param resultListener The capture listener to get capture result back.
475     * @param numResultsWait Number of frame to wait
476     *
477     * @return the last result, or {@code null} if there was none
478     */
479    protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener,
480            int numResultsWait) {
481        if (numResultsWait < 0 || resultListener == null) {
482            throw new IllegalArgumentException(
483                    "Input must be positive number and listener must be non-null");
484        }
485
486        CaptureResult result = null;
487        for (int i = 0; i < numResultsWait; i++) {
488            result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
489        }
490
491        return result;
492    }
493
494    /**
495     * Wait for enough results for settings to be applied
496     *
497     * @param resultListener The capture listener to get capture result back.
498     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
499     *                                       unknown.
500     */
501    protected void waitForSettingsApplied(SimpleCaptureCallback resultListener,
502            int numResultWaitForUnknownLatency) {
503        int maxLatency = mStaticInfo.getSyncMaxLatency();
504        if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
505            maxLatency = numResultWaitForUnknownLatency;
506        }
507        // Wait for settings to take effect
508        waitForNumResults(resultListener, maxLatency);
509    }
510
511
512    /**
513     * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
514     *
515     * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
516     * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
517     * is unknown.</p>
518     *
519     * <p>This is a no-op for {@code LEGACY} devices since they don't report
520     * the {@code aeState} result.</p>
521     *
522     * @param resultListener The capture listener to get capture result back.
523     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
524     *                                       unknown.
525     */
526    protected void waitForAeStable(SimpleCaptureCallback resultListener,
527            int numResultWaitForUnknownLatency) {
528        waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
529
530        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
531            // No-op for metadata
532            return;
533        }
534        List<Integer> expectedAeStates = new ArrayList<Integer>();
535        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED));
536        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED));
537        waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
538                NUM_RESULTS_WAIT_TIMEOUT);
539    }
540
541    /**
542     * Wait for AE to be: LOCKED
543     *
544     * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
545     * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
546     * is unknown.</p>
547     *
548     * <p>This is a no-op for {@code LEGACY} devices since they don't report
549     * the {@code aeState} result.</p>
550     *
551     * @param resultListener The capture listener to get capture result back.
552     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
553     *                                       unknown.
554     */
555    protected void waitForAeLocked(SimpleCaptureCallback resultListener,
556            int numResultWaitForUnknownLatency) {
557
558        waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
559
560        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
561            // No-op for legacy devices
562            return;
563        }
564
565        List<Integer> expectedAeStates = new ArrayList<Integer>();
566        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
567        waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
568                NUM_RESULTS_WAIT_TIMEOUT);
569    }
570
571    /**
572     * Create an {@link ImageReader} object and get the surface.
573     *
574     * @param size The size of this ImageReader to be created.
575     * @param format The format of this ImageReader to be created
576     * @param maxNumImages The max number of images that can be acquired simultaneously.
577     * @param listener The listener used by this ImageReader to notify callbacks.
578     */
579    protected void createImageReader(Size size, int format, int maxNumImages,
580            ImageReader.OnImageAvailableListener listener) throws Exception {
581        closeImageReader();
582
583        ImageReader r = makeImageReader(size, format, maxNumImages, listener,
584                mHandler);
585        mReader = r;
586        mReaderSurface = r.getSurface();
587    }
588
589    /**
590     * Close the pending images then close current active {@link ImageReader} object.
591     */
592    protected void closeImageReader() {
593        CameraTestUtils.closeImageReader(mReader);
594        mReader = null;
595        mReaderSurface = null;
596    }
597
598
599    /**
600     * Open a camera device and get the StaticMetadata for a given camera id.
601     *
602     * @param cameraId The id of the camera device to be opened.
603     */
604    protected void openDevice(String cameraId) throws Exception {
605        mCamera = CameraTestUtils.openCamera(
606                mCameraManager, cameraId, mCameraListener, mHandler);
607        mCollector.setCameraId(cameraId);
608        CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId);
609        mStaticInfo = new StaticMetadata(properties, CheckLevel.ASSERT, /*collector*/null);
610        StreamConfigurationMap configMap =
611                properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
612        mSupportRAW10 = configMap.isOutputSupportedFor(ImageFormat.RAW10);
613        if (mStaticInfo.isColorOutputSupported()) {
614            mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
615                    getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
616            mOrderedVideoSizes = getSupportedVideoSizes(
617                    cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
618            mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
619            if (mSupportRAW10) {
620                mOrderedRAW10Sizes = getSortedSizesForFormat(
621                        cameraId, mCameraManager, ImageFormat.RAW10, null);
622            }
623            mOrderedYUV420888Sizes = getSortedSizesForFormat(
624                    cameraId, mCameraManager, ImageFormat.YUV_420_888, null);
625            // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
626            // in public API side.
627            mMinPreviewFrameDurationMap =
628                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
629        }
630    }
631
632    /**
633     * Close the current actively used camera device.
634     */
635    protected void closeDevice() {
636        if (mCamera != null) {
637            mCamera.close();
638            mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
639            mCamera = null;
640            mSession = null;
641            mSessionListener = null;
642            mStaticInfo = null;
643            mOrderedPreviewSizes = null;
644            mOrderedVideoSizes = null;
645            mOrderedStillSizes = null;
646            mSupportRAW10 = false;
647        }
648    }
649
650    /**
651     * Update the preview surface size.
652     *
653     * @param size The preview size to be updated.
654     */
655    protected void updatePreviewSurface(Size size) {
656        if (size.equals(mPreviewSize) && mPreviewSurface != null) {
657            Log.w(TAG, "Skipping update preview surface size...");
658            return;
659        }
660
661        mPreviewSize = size;
662        Camera2SurfaceViewActivity ctsActivity = getActivity();
663        final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder();
664        Handler handler = new Handler(Looper.getMainLooper());
665        handler.post(new Runnable() {
666            @Override
667            public void run() {
668                holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
669            }
670        });
671
672        boolean res = ctsActivity.waitForSurfaceSizeChanged(
673                WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
674                mPreviewSize.getHeight());
675        assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
676        mPreviewSurface = holder.getSurface();
677        assertNotNull("Preview surface is null", mPreviewSurface);
678        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
679    }
680
681    /**
682     * Setup single capture configuration and start preview.
683     *
684     * @param previewRequest The capture request to be used for preview
685     * @param stillRequest The capture request to be used for still capture
686     * @param previewSz Preview size
687     * @param captureSz Still capture size
688     * @param format The single capture image format
689     * @param resultListener Capture result listener
690     * @param maxNumImages The max number of images set to the image reader
691     * @param imageListener The single capture capture image listener
692     */
693    protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
694            CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
695            CaptureCallback resultListener, int maxNumImages,
696            ImageReader.OnImageAvailableListener imageListener) throws Exception {
697        if (VERBOSE) {
698            Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
699                    captureSz.toString(), previewSz.toString()));
700        }
701
702        // Update preview size.
703        updatePreviewSurface(previewSz);
704
705        // Create ImageReader.
706        createImageReader(captureSz, format, maxNumImages, imageListener);
707
708        // Configure output streams with preview and jpeg streams.
709        List<Surface> outputSurfaces = new ArrayList<Surface>();
710        outputSurfaces.add(mPreviewSurface);
711        outputSurfaces.add(mReaderSurface);
712        mSessionListener = new BlockingSessionCallback();
713        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
714
715        // Configure the requests.
716        previewRequest.addTarget(mPreviewSurface);
717        stillRequest.addTarget(mPreviewSurface);
718        stillRequest.addTarget(mReaderSurface);
719
720        // Start preview.
721        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
722    }
723
724    /**
725     * Get the max preview size that supports the given fpsRange.
726     *
727     * @param fpsRange The fps range the returned size must support.
728     * @return max size that support the given fps range.
729     */
730    protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) {
731        if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) {
732            throw new IllegalArgumentException("Invalid fps range argument");
733        }
734        if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
735            throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
736                    + " must be initialized");
737        }
738
739        long[] frameDurationRange =
740                new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
741        for (Size size : mOrderedPreviewSizes) {
742            Long minDuration = mMinPreviewFrameDurationMap.get(size);
743            if (minDuration == null ||
744                    minDuration == 0) {
745                if (mStaticInfo.isCapabilitySupported(
746                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
747                    throw new IllegalArgumentException(
748                            "No min frame duration available for the size " + size);
749                }
750                continue;
751            }
752            if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) {
753                return size;
754            }
755        }
756
757        return null;
758    }
759
760    protected boolean isReprocessSupported(String cameraId, int format)
761            throws CameraAccessException {
762        if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) {
763            throw new IllegalArgumentException(
764                    "format " + format + " is not supported for reprocessing");
765        }
766
767        StaticMetadata info =
768                new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
769                                   CheckLevel.ASSERT, /*collector*/ null);
770        int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
771        if (format == ImageFormat.PRIVATE) {
772            cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
773        }
774        return info.isCapabilitySupported(cap);
775    }
776
777    //--------------------------------------------------------------------------------
778    //---------Below are common functions for Camera framework test run.--------------
779    //--------------------------------------------------------------------------------
780
781    protected Bundle getArguments() {
782        return ((InstrumentationTestRunner)getInstrumentation()).getArguments();
783    }
784
785    protected <E extends Number> Number getArgumentsAsNumber(String key, E defaultValue) {
786        String stringValue = getArguments().getString(key);
787        if (stringValue != null) {
788            try {
789                return NumberFormat.getInstance().parse(stringValue);
790            } catch (ParseException e) {
791                Log.w(TAG, "Unable to parse arg " + key + " with value " + stringValue
792                        + " to a integer.", e);
793            }
794        }
795        return defaultValue;
796    }
797
798    protected boolean getArgumentsAsBoolean(String key, boolean defaultValue) {
799        String stringValue = getArguments().getString(key);
800        if (stringValue != null) {
801            try {
802                return Boolean.parseBoolean(stringValue);
803            } catch (Exception e) {
804                Log.w(TAG, "Unable to parse arg " + key + " with value " + stringValue
805                        + " to a boolean.", e);
806            }
807        }
808        return defaultValue;
809    }
810
811    protected int getIterationCount() {
812        return mIterations;
813    }
814
815    protected long getTestWaitIntervalMs() {
816        return mTestWaitIntervalMs;
817    }
818
819    public CameraTestResultPrinter getResultPrinter() {
820        return mResultPrinter;
821    }
822}
823