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.stress;
18
19import com.android.ex.camera2.blocking.BlockingSessionCallback;
20import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
21import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
22import com.android.mediaframeworktest.helpers.Camera2Focuser;
23import com.android.mediaframeworktest.helpers.CameraTestUtils;
24import com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
25
26import android.graphics.ImageFormat;
27import android.graphics.Point;
28import android.hardware.camera2.CameraCharacteristics;
29import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
30import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
31import android.hardware.camera2.CameraDevice;
32import android.hardware.camera2.CameraAccessException;
33import android.hardware.camera2.CameraCaptureSession;
34import android.hardware.camera2.CaptureRequest;
35import android.hardware.camera2.CaptureResult;
36import android.hardware.camera2.DngCreator;
37import android.hardware.camera2.params.MeteringRectangle;
38import android.media.Image;
39import android.media.ImageReader;
40import android.media.CamcorderProfile;
41import android.media.MediaExtractor;
42import android.media.MediaFormat;
43import android.media.MediaRecorder;
44import android.os.ConditionVariable;
45import android.os.Environment;
46import android.util.Log;
47import android.util.Pair;
48import android.util.Rational;
49import android.util.Size;
50import android.view.Surface;
51import android.hardware.camera2.params.StreamConfigurationMap;
52import android.test.suitebuilder.annotation.LargeTest;
53import android.util.Log;
54import android.util.Range;
55
56import java.io.ByteArrayOutputStream;
57import java.util.ArrayList;
58import java.util.List;
59import java.io.File;
60import java.util.Arrays;
61import java.util.HashMap;
62
63import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
64import static com.android.mediaframeworktest.helpers.CameraTestUtils.MAX_READER_IMAGES;
65import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
66import static com.android.mediaframeworktest.helpers.CameraTestUtils.basicValidateJpegImage;
67import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
68import static com.android.mediaframeworktest.helpers.CameraTestUtils.dumpFile;
69import static com.android.mediaframeworktest.helpers.CameraTestUtils.getDataFromImage;
70import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
71import static com.android.mediaframeworktest.helpers.CameraTestUtils.makeImageReader;
72import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
73import static com.android.mediaframeworktest.helpers.CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS;
74import static com.android.mediaframeworktest.helpers.CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS;
75import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_1080P;
76import static com.android.mediaframeworktest.helpers.CameraTestUtils.SIZE_BOUND_2160P;
77import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
78
79import com.android.ex.camera2.blocking.BlockingSessionCallback;
80import com.android.mediaframeworktest.Camera2SurfaceViewTestCase;
81import com.android.mediaframeworktest.helpers.CameraTestUtils;
82
83import junit.framework.AssertionFailedError;
84
85/**
86 * <p>Tests Back/Front camera switching and Camera/Video modes witching.</p>
87 *
88 * adb shell am instrument \
89 *    -e class com.android.mediaframeworktest.stress.Camera2SwitchPreviewTest \
90 *    -e iterations 200 \
91 *    -e waitIntervalMs 1000 \
92 *    -e resultToFile false \
93 *    -r -w com.android.mediaframeworktest/.Camera2InstrumentationTestRunner
94 */
95public class Camera2SwitchPreviewTest extends Camera2SurfaceViewTestCase {
96    private static final String TAG = "SwitchPreviewTest";
97    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
98    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
99    // 60 second to accommodate the possible long exposure time.
100    private static final int MAX_REGIONS_AE_INDEX = 0;
101    private static final int MAX_REGIONS_AWB_INDEX = 1;
102    private static final int MAX_REGIONS_AF_INDEX = 2;
103    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
104    private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
105    // 5 percent error margin for resulting metering regions
106    private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
107    private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
108
109    private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
110    private static final int RECORDING_DURATION_MS = 3000;
111    private static final float DURATION_MARGIN = 0.2f;
112    private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
113    private static final int BIT_RATE_1080P = 16000000;
114    private static final int BIT_RATE_MIN = 64000;
115    private static final int BIT_RATE_MAX = 40000000;
116    private static final int VIDEO_FRAME_RATE = 30;
117    private static final int[] mCamcorderProfileList = {
118            CamcorderProfile.QUALITY_HIGH,
119            CamcorderProfile.QUALITY_2160P,
120            CamcorderProfile.QUALITY_1080P,
121            CamcorderProfile.QUALITY_720P,
122            CamcorderProfile.QUALITY_480P,
123            CamcorderProfile.QUALITY_CIF,
124            CamcorderProfile.QUALITY_QCIF,
125            CamcorderProfile.QUALITY_QVGA,
126            CamcorderProfile.QUALITY_LOW,
127    };
128    private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
129    private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
130    private static final int SLOWMO_SLOW_FACTOR = 4;
131    private static final int MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED = 4;
132    private List<Size> mSupportedVideoSizes;
133    private Surface mRecordingSurface;
134    private Surface mPersistentSurface;
135    private MediaRecorder mMediaRecorder;
136    private String mOutMediaFileName;
137    private int mVideoFrameRate;
138    private Size mVideoSize;
139    private long mRecordingStartTime;
140
141    @Override
142    protected void setUp() throws Exception {
143        super.setUp();
144    }
145
146    @Override
147    protected void tearDown() throws Exception {
148        super.tearDown();
149    }
150
151    /**
152     * Test normal still preview switch.
153     * <p>
154     * Preview jpeg output streams are configured. Max still capture
155     * size is used for jpeg capture.
156     * </p>
157     */
158    public void testPreviewSwitchBackFrontCamera() throws Exception {
159        List<String> mCameraColorOutputIds = cameraColorOutputCheck();
160        // Test iteration starts...
161        Log.i(TAG, "Testing preview switch back/front camera in still capture mode");
162        for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
163            for (String id : mCameraColorOutputIds) {
164                try {
165                    openDevice(id);
166                    // Preview for basic still capture:
167                    Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
168                            getIterationCount()));
169                    stillCapturePreviewPreparer(id);
170                    getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
171                } finally {
172                    closeDevice();
173                    closeImageReader();
174                }
175            }
176        }
177    }
178
179    /**
180     * <p>
181     * Test basic video preview switch.
182     * </p>
183     * <p>
184     * This test covers the typical basic use case of video preview switch.
185     * MediaRecorder is used to record the audio and video, CamcorderProfile is
186     * used to configure the MediaRecorder. Preview is set to the video size.
187     * </p>
188     */
189    public void testPreviewSwitchBackFrontVideo() throws Exception {
190        List<String> mCameraColorOutputIds = cameraColorOutputCheck();
191        // Test iteration starts...
192        Log.i(TAG, "Testing preview switch back/front camera in video mode");
193        for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
194            for (String id : mCameraColorOutputIds) {
195                try {
196                    openDevice(id);
197                    // Preview for basic video recording:
198                    Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
199                            getIterationCount()));
200                    recordingPreviewPreparer(id);
201                    getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
202                } finally {
203                    closeDevice();
204                    releaseRecorder();
205                }
206            }
207        }
208    }
209
210
211    /**
212     * Test back camera preview switch between still capture and recording mode.
213     * <p>
214     * This test covers the basic case of preview switch camera mode, between
215     * still capture (photo) and recording (video) mode. The preview settings
216     * are same with the settings in "testPreviewSwitchBackFrontCamera" and
217     * "testPreviewSwitchBackFrontVideo"
218     * </p>
219     */
220    public void testPreviewSwitchBackCameraVideo() throws Exception {
221        String id = mCameraIds[0];
222        openDevice(id);
223        if (!mStaticInfo.isColorOutputSupported()) {
224            Log.i(TAG, "Camera " + id +
225                    " does not support color outputs, skipping");
226            return;
227        }
228        closeDevice();
229        // Test iteration starts...
230        Log.i(TAG, "Testing preview switch between still capture/video modes for back camera");
231        for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
232            try {
233                openDevice(id);
234
235                // Preview for basic still capture:
236                Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
237                        getIterationCount()));
238                stillCapturePreviewPreparer(id);
239                getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
240
241                // Preview for basic video recording:
242                Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
243                        getIterationCount()));
244                recordingPreviewPreparer(id);
245                getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
246            } finally {
247                closeDevice();
248                closeImageReader();
249            }
250        }
251    }
252
253    /**
254     * Test front camera preview switch between still capture and recording mode.
255     * <p>
256     * This test covers the basic case of preview switch camera mode, between
257     * still capture (photo) and recording (video) mode. The preview settings
258     * are same with the settings in "testPreviewSwitchBackFrontCamera" and
259     * "testPreviewSwitchBackFrontVideo"
260     * </p>
261     */
262    public void testPreviewSwitchFrontCameraVideo() throws Exception{
263        String id = mCameraIds[1];
264        openDevice(id);
265        if (!mStaticInfo.isColorOutputSupported()) {
266            Log.i(TAG, "Camera " + id +
267                    " does not support color outputs, skipping");
268            return;
269        }
270        closeDevice();
271        // Test iteration starts...
272        Log.i(TAG, "Testing preview switch between still capture/video modes for front camera");
273        for (int iteration = 0; iteration < getIterationCount(); ++iteration) {
274            try {
275                openDevice(id);
276
277                // Preview for basic still capture:
278                Log.v(TAG, String.format("Preview pictures: %d/%d", iteration + 1,
279                        getIterationCount()));
280                stillCapturePreviewPreparer(id);
281                getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
282
283                // Preview for basic video recording:
284                Log.v(TAG, String.format("Preview for recording videos: %d/%d", iteration + 1,
285                        getIterationCount()));
286                recordingPreviewPreparer(id);
287                getResultPrinter().printStatus(getIterationCount(), iteration + 1, id);
288            } finally {
289                closeDevice();
290                closeImageReader();
291            }
292        }
293    }
294
295    private void stillCapturePreviewPreparer(String id) throws Exception{
296        CaptureResult result;
297        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
298        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
299        CaptureRequest.Builder previewRequest =
300                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
301        CaptureRequest.Builder stillRequest =
302                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
303        // Preview Setup:
304        prepareCapturePreview(previewRequest, stillRequest, resultListener, imageListener);
305
306        Thread.sleep(getTestWaitIntervalMs());
307    }
308
309    private void recordingPreviewPreparer(String id) throws Exception{
310        // Re-use the MediaRecorder object for the same camera device.
311        mMediaRecorder = new MediaRecorder();
312        initSupportedVideoSize(id);
313        // preview Setup:
314        basicRecordingPreviewTestByCamera(mCamcorderProfileList);
315
316        Thread.sleep(getTestWaitIntervalMs());
317    }
318
319
320    /**
321     * Initialize the supported video sizes.
322     */
323    private void initSupportedVideoSize(String cameraId)  throws Exception {
324        Size maxVideoSize = SIZE_BOUND_1080P;
325        if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
326            maxVideoSize = SIZE_BOUND_2160P;
327        }
328        mSupportedVideoSizes =
329                getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize);
330    }
331
332
333    /**
334     * Test camera recording preview by using each available CamcorderProfile for a
335     * given camera. preview size is set to the video size.
336     */
337    private void basicRecordingPreviewTestByCamera(int[] camcorderProfileList)
338            throws Exception {
339        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
340        List<Range<Integer> > fpsRanges = Arrays.asList(
341                mStaticInfo.getAeAvailableTargetFpsRangesChecked());
342        int cameraId = Integer.parseInt(mCamera.getId());
343        int maxVideoFrameRate = -1;
344        int profileId = camcorderProfileList[0];
345        if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
346                allowedUnsupported(cameraId, profileId)) {
347            return;
348        }
349
350        CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
351        Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
352        Range<Integer> fpsRange = new Range(profile.videoFrameRate, profile.videoFrameRate);
353        if (maxVideoFrameRate < profile.videoFrameRate) {
354                maxVideoFrameRate = profile.videoFrameRate;
355        }
356        if (mStaticInfo.isHardwareLevelLegacy() &&
357                (videoSz.getWidth() > maxPreviewSize.getWidth() ||
358                        videoSz.getHeight() > maxPreviewSize.getHeight())) {
359            // Skip. Legacy mode can only do recording up to max preview size
360            return;
361        }
362        assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
363                            " must be one of the camera device supported video size!",
364                    mSupportedVideoSizes.contains(videoSz));
365        assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
366                            ") must be one of the camera device available FPS range!",
367                fpsRanges.contains(fpsRange));
368
369        if (VERBOSE) {
370            Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
371        }
372
373        // Configure preview and recording surfaces.
374        mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
375        if (DEBUG_DUMP) {
376            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
377                    + videoSz.toString() + ".mp4";
378        }
379
380        prepareRecordingWithProfile(profile);
381
382        // prepare preview surface by using video size.
383        updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
384
385        CaptureRequest.Builder previewRequest =
386                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
387        CaptureRequest.Builder recordingRequest =
388                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
389
390        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
391        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
392
393        prepareVideoPreview(previewRequest, recordingRequest, resultListener, imageListener);
394
395        // Can reuse the MediaRecorder object after reset.
396        mMediaRecorder.reset();
397
398        if (maxVideoFrameRate != -1) {
399            // At least one CamcorderProfile is present, check FPS
400            assertTrue("At least one CamcorderProfile must support >= 24 FPS",
401                    maxVideoFrameRate >= 24);
402        }
403    }
404
405    private void releaseRecorder() {
406        if (mMediaRecorder != null) {
407            mMediaRecorder.release();
408            mMediaRecorder = null;
409        }
410    }
411
412    private List<String> cameraColorOutputCheck() throws Exception {
413        List<String> mCameraColorOutputIds = new ArrayList<String>();
414        for (String id : mCameraIds) {
415            openDevice(id);
416            if (!mStaticInfo.isColorOutputSupported()) {
417                Log.i(TAG, "Camera " + id +
418                        " does not support color outputs, skipping");
419                continue;
420            }
421            mCameraColorOutputIds.add(id);
422            closeDevice();
423        }
424        return mCameraColorOutputIds;
425    }
426
427    /**
428     * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported.
429     *
430     * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p>
431     *
432     * @param profileId a {@link CamcorderProfile} ID to check.
433     * @return {@code true} if supported.
434     */
435    private boolean allowedUnsupported(int cameraId, int profileId) {
436        if (!mStaticInfo.isHardwareLevelLegacy()) {
437            return false;
438        }
439
440        switch(profileId) {
441            case CamcorderProfile.QUALITY_2160P:
442            case CamcorderProfile.QUALITY_1080P:
443            case CamcorderProfile.QUALITY_HIGH:
444                return !CamcorderProfile.hasProfile(cameraId, profileId) ||
445                        CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080;
446        }
447        return false;
448    }
449
450    /**
451     * Configure MediaRecorder recording session with CamcorderProfile, prepare
452     * the recording surface.
453     */
454    private void prepareRecordingWithProfile(CamcorderProfile profile)
455            throws Exception {
456        // Prepare MediaRecorder.
457        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
458        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
459        mMediaRecorder.setProfile(profile);
460        mMediaRecorder.setOutputFile(mOutMediaFileName);
461        if (mPersistentSurface != null) {
462            mMediaRecorder.setInputSurface(mPersistentSurface);
463            mRecordingSurface = mPersistentSurface;
464        }
465        mMediaRecorder.prepare();
466        if (mPersistentSurface == null) {
467            mRecordingSurface = mMediaRecorder.getSurface();
468        }
469        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
470        mVideoFrameRate = profile.videoFrameRate;
471        mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
472    }
473
474    /**
475     * Update preview size with video size.
476     *
477     * <p>Preview size will be capped with max preview size.</p>
478     *
479     * @param videoSize The video size used for preview.
480     * @param videoFrameRate The video frame rate
481     *
482     */
483    private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate)  throws Exception {
484        if (mOrderedPreviewSizes == null) {
485            throw new IllegalStateException("supported preview size list is not initialized yet");
486        }
487        final float FRAME_DURATION_TOLERANCE = 0.01f;
488        long videoFrameDuration = (long) (1e9 / videoFrameRate *
489                (1.0 + FRAME_DURATION_TOLERANCE));
490        HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
491                getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
492        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
493        Size previewSize = null;
494        if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
495                videoSize.getHeight() > maxPreviewSize.getHeight()) {
496            for (Size s : mOrderedPreviewSizes) {
497                Long frameDuration = minFrameDurationMap.get(s);
498                if (mStaticInfo.isHardwareLevelLegacy()) {
499                    // Legacy doesn't report min frame duration
500                    frameDuration = new Long(0);
501                }
502                assertTrue("Cannot find minimum frame duration for private size" + s,
503                        frameDuration != null);
504                if (frameDuration <= videoFrameDuration &&
505                        s.getWidth() <= videoSize.getWidth() &&
506                        s.getHeight() <= videoSize.getHeight()) {
507                    Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
508                            " to " + s.toString());
509                    previewSize = s;
510                    break;
511                    // If all preview size doesn't work then we fallback to video size
512                }
513            }
514        }
515        if (previewSize == null) {
516            previewSize = videoSize;
517        }
518
519        updatePreviewSurface(previewSize);
520    }
521
522    protected void prepareVideoPreview(CaptureRequest.Builder previewRequest,
523                                                 CaptureRequest.Builder recordingRequest,
524                                                 CaptureCallback resultListener,
525                                                 ImageReader.OnImageAvailableListener imageListener) throws Exception {
526
527        // Configure output streams with preview and jpeg streams.
528        List<Surface> outputSurfaces = new ArrayList<Surface>();
529        outputSurfaces.add(mPreviewSurface);
530        outputSurfaces.add(mRecordingSurface);
531
532        mSessionListener = new BlockingSessionCallback();
533        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
534
535        previewRequest.addTarget(mPreviewSurface);
536        recordingRequest.addTarget(mPreviewSurface);
537        recordingRequest.addTarget(mRecordingSurface);
538
539        // Start preview.
540        mSession.setRepeatingRequest(previewRequest.build(), null, mHandler);
541    }
542
543    protected void prepareCapturePreview(CaptureRequest.Builder previewRequest,
544                                                 CaptureRequest.Builder stillRequest,
545                                                 CaptureCallback resultListener,
546                                                 ImageReader.OnImageAvailableListener imageListener) throws Exception {
547
548        Size captureSz = mOrderedStillSizes.get(0);
549        Size previewSz = mOrderedPreviewSizes.get(1);
550
551        if (VERBOSE) {
552            Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
553                    captureSz.toString(), previewSz.toString()));
554        }
555
556        // Update preview size.
557        updatePreviewSurface(previewSz);
558
559        // Create ImageReader.
560        createImageReader(captureSz, ImageFormat.JPEG, MAX_READER_IMAGES, imageListener);
561
562        // Configure output streams with preview and jpeg streams.
563        List<Surface> outputSurfaces = new ArrayList<Surface>();
564        outputSurfaces.add(mPreviewSurface);
565        outputSurfaces.add(mReaderSurface);
566        mSessionListener = new BlockingSessionCallback();
567        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
568
569        // Configure the requests.
570        previewRequest.addTarget(mPreviewSurface);
571        stillRequest.addTarget(mPreviewSurface);
572        stillRequest.addTarget(mReaderSurface);
573
574        // Start preview.
575        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
576    }
577
578}
579