/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.mediaframeworktest; import com.android.ex.camera2.blocking.BlockingSessionCallback; import com.android.ex.camera2.blocking.BlockingStateCallback; import com.android.ex.camera2.exceptions.TimeoutRuntimeException; import com.android.mediaframeworktest.helpers.CameraErrorCollector; import com.android.mediaframeworktest.helpers.CameraTestResultPrinter; import com.android.mediaframeworktest.helpers.CameraTestUtils; import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback; import com.android.mediaframeworktest.helpers.StaticMetadata; import com.android.mediaframeworktest.helpers.StaticMetadata.CheckLevel; import android.content.Context; import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCaptureSession.CaptureCallback; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.ImageReader; import android.graphics.SurfaceTexture; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.test.ActivityInstrumentationTestCase2; import android.test.InstrumentationTestRunner; import android.util.Log; import android.util.Range; import android.util.Size; import android.view.Surface; import android.view.SurfaceHolder; import android.view.WindowManager; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED; import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS; import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES; import static com.android.mediaframeworktest.helpers.CameraTestUtils.PREVIEW_SIZE_BOUND; import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession; import static com.android.mediaframeworktest.helpers.CameraTestUtils.getPreviewSizeBound; import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedPreviewSizes; import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedStillSizes; import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes; import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSortedSizesForFormat; import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader; /** * Camera2 Preview test case base class by using SurfaceView as rendering target. * * <p>This class encapsulates the SurfaceView based preview common functionalities. * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs * and CameraStateCallback are handled in this class. Some basic preview related utility * functions are provided to facilitate the derived preview-based test classes. * </p> */ /** * (non-Javadoc) * @see android.hardware.camera2.cts.Camera2SurfaceViewTestCase */ public class Camera2SurfaceViewTestCase extends ActivityInstrumentationTestCase2<Camera2SurfaceViewActivity> { private static final String TAG = "SurfaceViewTestCase"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000; // Instrumentation arguments protected static final String ARG_KEY_ITERATIONS = "iterations"; protected static final String ARG_KEY_WAIT_INTERVAL_MS = "waitIntervalMs"; protected static final String ARG_KEY_RESULT_TO_FILE = "resultToFile"; // TODO: Use internal storage for this to make sure the file is only visible to test. protected static final String DEBUG_FILE_NAME_BASE = Environment.getExternalStorageDirectory().getPath(); protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000; protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin. protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100; protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8; protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns protected Context mContext; protected CameraManager mCameraManager; protected String[] mCameraIds; protected HandlerThread mHandlerThread; protected Handler mHandler; protected BlockingStateCallback mCameraListener; protected BlockingSessionCallback mSessionListener; protected CameraErrorCollector mCollector; // Per device fields: protected StaticMetadata mStaticInfo; protected CameraDevice mCamera; protected CameraCaptureSession mSession; protected ImageReader mReader; protected Surface mReaderSurface; protected Surface mPreviewSurface; protected Size mPreviewSize; protected List<Size> mOrderedPreviewSizes; // In descending order. protected List<Size> mOrderedVideoSizes; // In descending order. protected List<Size> mOrderedStillSizes; // In descending order. protected List<Size> mOrderedRAW10Sizes; // In descending order. protected List<Size> mOrderedYUV420888Sizes; // In descending order. protected HashMap<Size, Long> mMinPreviewFrameDurationMap; protected boolean mSupportRAW10; protected WindowManager mWindowManager; // Set the number of iterations to run stress testing. Default to 1. protected int mIterations = 1; // The interval between test iterations used for stress test. protected long mTestWaitIntervalMs = 1 * 1000; // 1 sec protected boolean mWriteToFile = true; protected CameraTestResultPrinter mResultPrinter; public Camera2SurfaceViewTestCase() { super(Camera2SurfaceViewActivity.class); } @Override protected void setUp() throws Exception { /** * Set up the camera preview required environments, including activity, * CameraManager, HandlerThread, Camera IDs, and CameraStateCallback. */ super.setUp(); mContext = getActivity(); /** * Workaround for mockito and JB-MR2 incompatibility * * Avoid java.lang.IllegalArgumentException: dexcache == null * https://code.google.com/p/dexmaker/issues/detail?id=2 */ System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString()); mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); assertNotNull("Unable to get CameraManager", mCameraManager); mCameraIds = mCameraManager.getCameraIdList(); assertNotNull("Unable to get camera ids", mCameraIds); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mCameraListener = new BlockingStateCallback(); mCollector = new CameraErrorCollector(); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mIterations = getArgumentsAsNumber(ARG_KEY_ITERATIONS, 1).intValue(); mTestWaitIntervalMs = getArgumentsAsNumber(ARG_KEY_WAIT_INTERVAL_MS, 1000).longValue(); mWriteToFile = getArgumentsAsBoolean(ARG_KEY_RESULT_TO_FILE, true); Log.i(TAG, "Argument: iteration count=" + mIterations); Log.i(TAG, "Argument: interval (ms)=" + mTestWaitIntervalMs); Log.i(TAG, "Argument: result to file=" + (mWriteToFile ? "true" : "false")); mResultPrinter = new CameraTestResultPrinter(getInstrumentation(), mWriteToFile); } @Override protected void tearDown() throws Exception { // Teardown the camera preview required environments. mHandlerThread.quitSafely(); mHandler = null; mCameraListener = null; try { mCollector.verify(); } catch (Throwable e) { // When new Exception(e) is used, exception info will be printed twice. throw new Exception(e.getMessage()); } finally { super.tearDown(); } } /** * Start camera preview by using the given request, preview size and capture * listener. * <p> * If preview is already started, calling this function will stop the * current preview stream and start a new preview stream with given * parameters. No need to call stopPreview between two startPreview calls. * </p> * * @param request The request builder used to start the preview. * @param previewSz The size of the camera device output preview stream. * @param listener The callbacks the camera device will notify when preview * capture is available. */ protected void startPreview(CaptureRequest.Builder request, Size previewSz, CaptureCallback listener) throws Exception { // Update preview size. updatePreviewSurface(previewSz); if (VERBOSE) { Log.v(TAG, "start preview with size " + mPreviewSize.toString()); } configurePreviewOutput(request); mSession.setRepeatingRequest(request.build(), listener, mHandler); } /** * Configure the preview output stream. * * @param request The request to be configured with preview surface */ protected void configurePreviewOutput(CaptureRequest.Builder request) throws CameraAccessException { List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1); outputSurfaces.add(mPreviewSurface); mSessionListener = new BlockingSessionCallback(); mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); request.addTarget(mPreviewSurface); } /** * Create a {@link CaptureRequest#Builder} and add the default preview surface. * * @return The {@link CaptureRequest#Builder} to be created * @throws CameraAccessException When create capture request from camera fails */ protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException { if (mPreviewSurface == null) { throw new IllegalStateException( "Preview surface is not set yet, call updatePreviewSurface or startPreview" + "first to set the preview surface properly."); } CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); requestBuilder.addTarget(mPreviewSurface); return requestBuilder; } /** * Stop preview for current camera device. */ protected void stopPreview() throws Exception { if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle"); // Stop repeat, wait for captures to complete, and disconnect from surfaces mSession.close(); } /** * Setup still (JPEG) capture configuration and start preview. * <p> * The default max number of image is set to image reader. * </p> * * @param previewRequest The capture request to be used for preview * @param stillRequest The capture request to be used for still capture * @param previewSz Preview size * @param stillSz The still capture size * @param resultListener Capture result listener * @param imageListener The still capture image listener */ protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener) throws Exception { prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener); } /** * Setup still (JPEG) capture configuration and start preview. * * @param previewRequest The capture request to be used for preview * @param stillRequest The capture request to be used for still capture * @param previewSz Preview size * @param stillSz The still capture size * @param resultListener Capture result listener * @param maxNumImages The max number of images set to the image reader * @param imageListener The still capture image listener */ protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception { prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz, ImageFormat.JPEG, resultListener, maxNumImages, imageListener); } /** * Setup raw capture configuration and start preview. * * <p> * The default max number of image is set to image reader. * </p> * * @param previewRequest The capture request to be used for preview * @param rawRequest The capture request to be used for raw capture * @param previewSz Preview size * @param rawSz The raw capture size * @param resultListener Capture result listener * @param imageListener The raw capture image listener */ protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz, CaptureCallback resultListener, ImageReader.OnImageAvailableListener imageListener) throws Exception { prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz, ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener); } /** * Wait for expected result key value available in a certain number of results. * * <p> * Check the result immediately if numFramesWait is 0. * </p> * * @param listener The capture listener to get capture result * @param resultKey The capture result key associated with the result value * @param expectedValue The result value need to be waited for * @param numResultsWait Number of frame to wait before times out * @throws TimeoutRuntimeException If more than numResultsWait results are * seen before the result matching myRequest arrives, or each individual wait * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. */ protected static <T> void waitForResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, T expectedValue, int numResultsWait) { List<T> expectedValues = new ArrayList<T>(); expectedValues.add(expectedValue); waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait); } /** * Wait for any expected result key values available in a certain number of results. * * <p> * Check the result immediately if numFramesWait is 0. * </p> * * @param listener The capture listener to get capture result. * @param resultKey The capture result key associated with the result value. * @param expectedValues The list of result value need to be waited for, * return immediately if the list is empty. * @param numResultsWait Number of frame to wait before times out. * @throws TimeoutRuntimeException If more than numResultsWait results are. * seen before the result matching myRequest arrives, or each individual wait * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms. */ protected static <T> void waitForAnyResultValue(SimpleCaptureCallback listener, CaptureResult.Key<T> resultKey, List<T> expectedValues, int numResultsWait) { if (numResultsWait < 0 || listener == null || expectedValues == null) { throw new IllegalArgumentException( "Input must be non-negative number and listener/expectedValues " + "must be non-null"); } int i = 0; CaptureResult result; do { result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); T value = result.get(resultKey); for ( T expectedValue : expectedValues) { if (VERBOSE) { Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: " + value.toString()); } if (value.equals(expectedValue)) { return; } } } while (i++ < numResultsWait); throw new TimeoutRuntimeException( "Unable to get the expected result value " + expectedValues + " for key " + resultKey.getName() + " after waiting for " + numResultsWait + " results"); } /** * Submit a capture once, then submit additional captures in order to ensure that * the camera will be synchronized. * * <p> * The additional capture count is determined by android.sync.maxLatency (or * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). * </p> * * <p>Returns the number of captures that were submitted (at least 1), which is useful * with {@link #waitForNumResults}.</p> * * @param request capture request to forward to {@link CameraDevice#capture} * @param listener request listener to forward to {@link CameraDevice#capture} * @param handler handler to forward to {@link CameraDevice#capture} * * @return the number of captures that were submitted * * @throws CameraAccessException if capturing failed */ protected int captureRequestsSynchronized( CaptureRequest request, CaptureCallback listener, Handler handler) throws CameraAccessException { return captureRequestsSynchronized(request, /*count*/1, listener, handler); } /** * Submit a capture {@code count} times, then submit additional captures in order to ensure that * the camera will be synchronized. * * <p> * The additional capture count is determined by android.sync.maxLatency (or * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown). * </p> * * <p>Returns the number of captures that were submitted (at least 1), which is useful * with {@link #waitForNumResults}.</p> * * @param request capture request to forward to {@link CameraDevice#capture} * @param count the number of times to submit the request (minimally), must be at least 1 * @param listener request listener to forward to {@link CameraDevice#capture} * @param handler handler to forward to {@link CameraDevice#capture} * * @return the number of captures that were submitted * * @throws IllegalArgumentException if {@code count} was not at least 1 * @throws CameraAccessException if capturing failed */ protected int captureRequestsSynchronized( CaptureRequest request, int count, CaptureCallback listener, Handler handler) throws CameraAccessException { if (count < 1) { throw new IllegalArgumentException("count must be positive"); } int maxLatency = mStaticInfo.getSyncMaxLatency(); if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY; } assertTrue("maxLatency is non-negative", maxLatency >= 0); int numCaptures = maxLatency + count; for (int i = 0; i < numCaptures; ++i) { mSession.capture(request, listener, handler); } return numCaptures; } /** * Wait for numResultWait frames * * @param resultListener The capture listener to get capture result back. * @param numResultsWait Number of frame to wait * * @return the last result, or {@code null} if there was none */ protected static CaptureResult waitForNumResults(SimpleCaptureCallback resultListener, int numResultsWait) { if (numResultsWait < 0 || resultListener == null) { throw new IllegalArgumentException( "Input must be positive number and listener must be non-null"); } CaptureResult result = null; for (int i = 0; i < numResultsWait; i++) { result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); } return result; } /** * Wait for enough results for settings to be applied * * @param resultListener The capture listener to get capture result back. * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is * unknown. */ protected void waitForSettingsApplied(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency) { int maxLatency = mStaticInfo.getSyncMaxLatency(); if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) { maxLatency = numResultWaitForUnknownLatency; } // Wait for settings to take effect waitForNumResults(resultListener, maxLatency); } /** * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED. * * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency * is unknown.</p> * * <p>This is a no-op for {@code LEGACY} devices since they don't report * the {@code aeState} result.</p> * * @param resultListener The capture listener to get capture result back. * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is * unknown. */ protected void waitForAeStable(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency) { waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) { // No-op for metadata return; } List<Integer> expectedAeStates = new ArrayList<Integer>(); expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED)); expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED)); waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, NUM_RESULTS_WAIT_TIMEOUT); } /** * Wait for AE to be: LOCKED * * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency * is unknown.</p> * * <p>This is a no-op for {@code LEGACY} devices since they don't report * the {@code aeState} result.</p> * * @param resultListener The capture listener to get capture result back. * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is * unknown. */ protected void waitForAeLocked(SimpleCaptureCallback resultListener, int numResultWaitForUnknownLatency) { waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency); if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) { // No-op for legacy devices return; } List<Integer> expectedAeStates = new ArrayList<Integer>(); expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED)); waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates, NUM_RESULTS_WAIT_TIMEOUT); } /** * Create an {@link ImageReader} object and get the surface. * * @param size The size of this ImageReader to be created. * @param format The format of this ImageReader to be created * @param maxNumImages The max number of images that can be acquired simultaneously. * @param listener The listener used by this ImageReader to notify callbacks. */ protected void createImageReader(Size size, int format, int maxNumImages, ImageReader.OnImageAvailableListener listener) throws Exception { closeImageReader(); ImageReader r = makeImageReader(size, format, maxNumImages, listener, mHandler); mReader = r; mReaderSurface = r.getSurface(); } /** * Close the pending images then close current active {@link ImageReader} object. */ protected void closeImageReader() { CameraTestUtils.closeImageReader(mReader); mReader = null; mReaderSurface = null; } /** * Open a camera device and get the StaticMetadata for a given camera id. * * @param cameraId The id of the camera device to be opened. */ protected void openDevice(String cameraId) throws Exception { mCamera = CameraTestUtils.openCamera( mCameraManager, cameraId, mCameraListener, mHandler); mCollector.setCameraId(cameraId); CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId); mStaticInfo = new StaticMetadata(properties, CheckLevel.ASSERT, /*collector*/null); StreamConfigurationMap configMap = properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); mSupportRAW10 = configMap.isOutputSupportedFor(ImageFormat.RAW10); if (mStaticInfo.isColorOutputSupported()) { mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND)); mOrderedVideoSizes = getSupportedVideoSizes( cameraId, mCameraManager, PREVIEW_SIZE_BOUND); mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null); if (mSupportRAW10) { mOrderedRAW10Sizes = getSortedSizesForFormat( cameraId, mCameraManager, ImageFormat.RAW10, null); } mOrderedYUV420888Sizes = getSortedSizesForFormat( cameraId, mCameraManager, ImageFormat.YUV_420_888, null); // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview // in public API side. mMinPreviewFrameDurationMap = mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888); } } /** * Close the current actively used camera device. */ protected void closeDevice() { if (mCamera != null) { mCamera.close(); mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS); mCamera = null; mSession = null; mSessionListener = null; mStaticInfo = null; mOrderedPreviewSizes = null; mOrderedVideoSizes = null; mOrderedStillSizes = null; mSupportRAW10 = false; } } /** * Update the preview surface size. * * @param size The preview size to be updated. */ protected void updatePreviewSurface(Size size) { if (size.equals(mPreviewSize) && mPreviewSurface != null) { Log.w(TAG, "Skipping update preview surface size..."); return; } mPreviewSize = size; Camera2SurfaceViewActivity ctsActivity = getActivity(); final SurfaceHolder holder = ctsActivity.getSurfaceView().getHolder(); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); } }); boolean res = ctsActivity.waitForSurfaceSizeChanged( WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), mPreviewSize.getHeight()); assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res); mPreviewSurface = holder.getSurface(); assertNotNull("Preview surface is null", mPreviewSurface); assertTrue("Preview surface is invalid", mPreviewSurface.isValid()); } /** * Setup single capture configuration and start preview. * * @param previewRequest The capture request to be used for preview * @param stillRequest The capture request to be used for still capture * @param previewSz Preview size * @param captureSz Still capture size * @param format The single capture image format * @param resultListener Capture result listener * @param maxNumImages The max number of images set to the image reader * @param imageListener The single capture capture image listener */ protected void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format, CaptureCallback resultListener, int maxNumImages, ImageReader.OnImageAvailableListener imageListener) throws Exception { if (VERBOSE) { Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)", captureSz.toString(), previewSz.toString())); } // Update preview size. updatePreviewSurface(previewSz); // Create ImageReader. createImageReader(captureSz, format, maxNumImages, imageListener); // Configure output streams with preview and jpeg streams. List<Surface> outputSurfaces = new ArrayList<Surface>(); outputSurfaces.add(mPreviewSurface); outputSurfaces.add(mReaderSurface); mSessionListener = new BlockingSessionCallback(); mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler); // Configure the requests. previewRequest.addTarget(mPreviewSurface); stillRequest.addTarget(mPreviewSurface); stillRequest.addTarget(mReaderSurface); // Start preview. mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler); } /** * Get the max preview size that supports the given fpsRange. * * @param fpsRange The fps range the returned size must support. * @return max size that support the given fps range. */ protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) { if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) { throw new IllegalArgumentException("Invalid fps range argument"); } if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) { throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap" + " must be initialized"); } long[] frameDurationRange = new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())}; for (Size size : mOrderedPreviewSizes) { Long minDuration = mMinPreviewFrameDurationMap.get(size); if (minDuration == null || minDuration == 0) { if (mStaticInfo.isCapabilitySupported( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) { throw new IllegalArgumentException( "No min frame duration available for the size " + size); } continue; } if (minDuration <= (frameDurationRange[0] + MIN_FRAME_DURATION_ERROR_MARGIN)) { return size; } } return null; } protected boolean isReprocessSupported(String cameraId, int format) throws CameraAccessException { if (format != ImageFormat.YUV_420_888 && format != ImageFormat.PRIVATE) { throw new IllegalArgumentException( "format " + format + " is not supported for reprocessing"); } StaticMetadata info = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId), CheckLevel.ASSERT, /*collector*/ null); int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING; if (format == ImageFormat.PRIVATE) { cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING; } return info.isCapabilitySupported(cap); } //-------------------------------------------------------------------------------- //---------Below are common functions for Camera framework test run.-------------- //-------------------------------------------------------------------------------- protected Bundle getArguments() { return ((InstrumentationTestRunner)getInstrumentation()).getArguments(); } protected <E extends Number> Number getArgumentsAsNumber(String key, E defaultValue) { String stringValue = getArguments().getString(key); if (stringValue != null) { try { return NumberFormat.getInstance().parse(stringValue); } catch (ParseException e) { Log.w(TAG, "Unable to parse arg " + key + " with value " + stringValue + " to a integer.", e); } } return defaultValue; } protected boolean getArgumentsAsBoolean(String key, boolean defaultValue) { String stringValue = getArguments().getString(key); if (stringValue != null) { try { return Boolean.parseBoolean(stringValue); } catch (Exception e) { Log.w(TAG, "Unable to parse arg " + key + " with value " + stringValue + " to a boolean.", e); } } return defaultValue; } protected int getIterationCount() { return mIterations; } protected long getTestWaitIntervalMs() { return mTestWaitIntervalMs; } public CameraTestResultPrinter getResultPrinter() { return mResultPrinter; } }