Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project Licensed under the Apache
      3  * License, Version 2.0 (the "License"); you may not use this file except in
      4  * compliance with the License. You may obtain a copy of the License at
      5  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
      6  * or agreed to in writing, software distributed under the License is
      7  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
      8  * KIND, either express or implied. See the License for the specific language
      9  * governing permissions and limitations under the License.
     10  */
     11 
     12 package android.hardware.camera2.cts;
     13 
     14 import static android.hardware.camera2.cts.CameraTestUtils.*;
     15 import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
     16 
     17 import android.graphics.ImageFormat;
     18 import android.hardware.camera2.CameraCharacteristics;
     19 import android.hardware.camera2.CameraCaptureSession;
     20 import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
     21 import android.hardware.camera2.CameraDevice;
     22 import android.hardware.camera2.CaptureRequest;
     23 import android.hardware.camera2.CaptureResult;
     24 import android.hardware.camera2.params.StreamConfigurationMap;
     25 import android.util.Size;
     26 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
     27 import android.media.CamcorderProfile;
     28 import android.media.MediaCodec;
     29 import android.media.MediaCodecInfo;
     30 import android.media.MediaCodecInfo.CodecCapabilities;
     31 import android.media.MediaCodecInfo.CodecProfileLevel;
     32 import android.media.Image;
     33 import android.media.ImageReader;
     34 import android.media.MediaCodecList;
     35 import android.media.MediaExtractor;
     36 import android.media.MediaFormat;
     37 import android.media.MediaRecorder;
     38 import android.os.Environment;
     39 import android.os.SystemClock;
     40 import android.test.suitebuilder.annotation.LargeTest;
     41 import android.util.Log;
     42 import android.util.Range;
     43 import android.view.Surface;
     44 
     45 import com.android.ex.camera2.blocking.BlockingSessionCallback;
     46 
     47 import junit.framework.AssertionFailedError;
     48 
     49 import java.io.File;
     50 import java.util.ArrayList;
     51 import java.util.Arrays;
     52 import java.util.List;
     53 import java.util.HashMap;
     54 
     55 /**
     56  * CameraDevice video recording use case tests by using MediaRecorder and
     57  * MediaCodec.
     58  */
     59 @LargeTest
     60 public class RecordingTest extends Camera2SurfaceViewTestCase {
     61     private static final String TAG = "RecordingTest";
     62     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     63     private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
     64     private static final int RECORDING_DURATION_MS = 3000;
     65     private static final float DURATION_MARGIN = 0.2f;
     66     private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
     67     private static final int BIT_RATE_1080P = 16000000;
     68     private static final int BIT_RATE_MIN = 64000;
     69     private static final int BIT_RATE_MAX = 40000000;
     70     private static final int VIDEO_FRAME_RATE = 30;
     71     private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
     72     private static final int[] mCamcorderProfileList = {
     73             CamcorderProfile.QUALITY_HIGH,
     74             CamcorderProfile.QUALITY_2160P,
     75             CamcorderProfile.QUALITY_1080P,
     76             CamcorderProfile.QUALITY_720P,
     77             CamcorderProfile.QUALITY_480P,
     78             CamcorderProfile.QUALITY_CIF,
     79             CamcorderProfile.QUALITY_QCIF,
     80             CamcorderProfile.QUALITY_QVGA,
     81             CamcorderProfile.QUALITY_LOW,
     82     };
     83     private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
     84     private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
     85     private static final int SLOWMO_SLOW_FACTOR = 4;
     86     private static final int MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED = 4;
     87     private List<Size> mSupportedVideoSizes;
     88     private Surface mRecordingSurface;
     89     private Surface mPersistentSurface;
     90     private MediaRecorder mMediaRecorder;
     91     private String mOutMediaFileName;
     92     private int mVideoFrameRate;
     93     private Size mVideoSize;
     94     private long mRecordingStartTime;
     95 
     96     @Override
     97     protected void setUp() throws Exception {
     98         super.setUp();
     99     }
    100 
    101     @Override
    102     protected void tearDown() throws Exception {
    103         super.tearDown();
    104     }
    105 
    106     private void doBasicRecording() throws Exception {
    107         for (int i = 0; i < mCameraIds.length; i++) {
    108             try {
    109                 Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
    110                 // Re-use the MediaRecorder object for the same camera device.
    111                 mMediaRecorder = new MediaRecorder();
    112                 openDevice(mCameraIds[i]);
    113                 if (!mStaticInfo.isColorOutputSupported()) {
    114                     Log.i(TAG, "Camera " + mCameraIds[i] +
    115                             " does not support color outputs, skipping");
    116                     continue;
    117                 }
    118                 initSupportedVideoSize(mCameraIds[i]);
    119 
    120                 basicRecordingTestByCamera(mCamcorderProfileList);
    121             } finally {
    122                 closeDevice();
    123                 releaseRecorder();
    124             }
    125         }
    126     }
    127 
    128     /**
    129      * <p>
    130      * Test basic camera recording.
    131      * </p>
    132      * <p>
    133      * This test covers the typical basic use case of camera recording.
    134      * MediaRecorder is used to record the audio and video, CamcorderProfile is
    135      * used to configure the MediaRecorder. It goes through the pre-defined
    136      * CamcorderProfile list, test each profile configuration and validate the
    137      * recorded video. Preview is set to the video size.
    138      * </p>
    139      */
    140     public void testBasicRecording() throws Exception {
    141         doBasicRecording();
    142     }
    143 
    144     /**
    145      * <p>
    146      * Test basic camera recording from a persistent input surface.
    147      * </p>
    148      * <p>
    149      * This test is similar to testBasicRecording except that MediaRecorder records
    150      * from a persistent input surface that's used across multiple recording sessions.
    151      * </p>
    152      */
    153     public void testRecordingFromPersistentSurface() throws Exception {
    154         mPersistentSurface = MediaCodec.createPersistentInputSurface();
    155         assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
    156 
    157         try {
    158             doBasicRecording();
    159         } finally {
    160             mPersistentSurface.release();
    161             mPersistentSurface = null;
    162         }
    163     }
    164 
    165     /**
    166      * <p>
    167      * Test camera recording for all supported sizes by using MediaRecorder.
    168      * </p>
    169      * <p>
    170      * This test covers camera recording for all supported sizes by camera. MediaRecorder
    171      * is used to encode the video. Preview is set to the video size. Recorded videos are
    172      * validated according to the recording configuration.
    173      * </p>
    174      */
    175     public void testSupportedVideoSizes() throws Exception {
    176         for (int i = 0; i < mCameraIds.length; i++) {
    177             try {
    178                 Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
    179                 // Re-use the MediaRecorder object for the same camera device.
    180                 mMediaRecorder = new MediaRecorder();
    181                 openDevice(mCameraIds[i]);
    182                 if (!mStaticInfo.isColorOutputSupported()) {
    183                     Log.i(TAG, "Camera " + mCameraIds[i] +
    184                             " does not support color outputs, skipping");
    185                     continue;
    186                 }
    187                 initSupportedVideoSize(mCameraIds[i]);
    188 
    189                 recordingSizeTestByCamera();
    190             } finally {
    191                 closeDevice();
    192                 releaseRecorder();
    193             }
    194         }
    195     }
    196 
    197     /**
    198      * Test different start/stop orders of Camera and Recorder.
    199      *
    200      * <p>The recording should be working fine for any kind of start/stop orders.</p>
    201      */
    202     public void testCameraRecorderOrdering() {
    203         // TODO: need implement
    204     }
    205 
    206     /**
    207      * <p>
    208      * Test camera recording for all supported sizes by using MediaCodec.
    209      * </p>
    210      * <p>
    211      * This test covers video only recording for all supported sizes (camera and
    212      * encoder). MediaCodec is used to encode the video. The recorded videos are
    213      * validated according to the recording configuration.
    214      * </p>
    215      */
    216     public void testMediaCodecRecording() throws Exception {
    217         // TODO. Need implement.
    218     }
    219 
    220     /**
    221      * <p>
    222      * Test video snapshot for each camera.
    223      * </p>
    224      * <p>
    225      * This test covers video snapshot typical use case. The MediaRecorder is used to record the
    226      * video for each available video size. The largest still capture size is selected to
    227      * capture the JPEG image. The still capture images are validated according to the capture
    228      * configuration. The timestamp of capture result before and after video snapshot is also
    229      * checked to make sure no frame drop caused by video snapshot.
    230      * </p>
    231      */
    232     public void testVideoSnapshot() throws Exception {
    233         videoSnapshotHelper(/*burstTest*/false);
    234     }
    235 
    236     /**
    237      * <p>
    238      * Test burst video snapshot for each camera.
    239      * </p>
    240      * <p>
    241      * This test covers burst video snapshot capture. The MediaRecorder is used to record the
    242      * video for each available video size. The largest still capture size is selected to
    243      * capture the JPEG image. {@value #BURST_VIDEO_SNAPSHOT_NUM} video snapshot requests will be
    244      * sent during the test. The still capture images are validated according to the capture
    245      * configuration.
    246      * </p>
    247      */
    248     public void testBurstVideoSnapshot() throws Exception {
    249         videoSnapshotHelper(/*burstTest*/true);
    250     }
    251 
    252     /**
    253      * Test timelapse recording, where capture rate is slower than video (playback) frame rate.
    254      */
    255     public void testTimelapseRecording() throws Exception {
    256         // TODO. Need implement.
    257     }
    258 
    259     public void testSlowMotionRecording() throws Exception {
    260         slowMotionRecording();
    261     }
    262 
    263     public void testConstrainedHighSpeedRecording() throws Exception {
    264         constrainedHighSpeedRecording();
    265     }
    266 
    267     /**
    268      * <p>
    269      * Test recording framerate accuracy when switching from low FPS to high FPS.
    270      * </p>
    271      * <p>
    272      * This test first record a video with profile of lowest framerate then record a video with
    273      * profile of highest framerate. Make sure that the video framerate are still accurate.
    274      * </p>
    275      */
    276     public void testRecordingFramerateLowToHigh() throws Exception {
    277         for (int i = 0; i < mCameraIds.length; i++) {
    278             try {
    279                 Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
    280                 // Re-use the MediaRecorder object for the same camera device.
    281                 mMediaRecorder = new MediaRecorder();
    282                 openDevice(mCameraIds[i]);
    283                 if (!mStaticInfo.isColorOutputSupported()) {
    284                     Log.i(TAG, "Camera " + mCameraIds[i] +
    285                             " does not support color outputs, skipping");
    286                     continue;
    287                 }
    288                 initSupportedVideoSize(mCameraIds[i]);
    289 
    290                 int minFpsProfileId = -1, minFps = 1000;
    291                 int maxFpsProfileId = -1, maxFps = 0;
    292                 int cameraId = Integer.valueOf(mCamera.getId());
    293 
    294                 for (int profileId : mCamcorderProfileList) {
    295                     if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
    296                         continue;
    297                     }
    298                     CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
    299                     if (profile.videoFrameRate < minFps) {
    300                         minFpsProfileId = profileId;
    301                         minFps = profile.videoFrameRate;
    302                     }
    303                     if (profile.videoFrameRate > maxFps) {
    304                         maxFpsProfileId = profileId;
    305                         maxFps = profile.videoFrameRate;
    306                     }
    307                 }
    308 
    309                 int camcorderProfileList[] = new int[] {minFpsProfileId, maxFpsProfileId};
    310                 basicRecordingTestByCamera(camcorderProfileList);
    311             } finally {
    312                 closeDevice();
    313                 releaseRecorder();
    314             }
    315         }
    316     }
    317 
    318     /**
    319      * Test slow motion recording where capture rate (camera output) is different with
    320      * video (playback) frame rate for each camera if high speed recording is supported
    321      * by both camera and encoder.
    322      *
    323      * <p>
    324      * Normal recording use cases make the capture rate (camera output frame
    325      * rate) the same as the video (playback) frame rate. This guarantees that
    326      * the motions in the scene play at the normal speed. If the capture rate is
    327      * faster than video frame rate, for a given time duration, more number of
    328      * frames are captured than it can be played in the same time duration. This
    329      * generates "slow motion" effect during playback.
    330      * </p>
    331      */
    332     private void slowMotionRecording() throws Exception {
    333         for (String id : mCameraIds) {
    334             try {
    335                 Log.i(TAG, "Testing slow motion recording for camera " + id);
    336                 // Re-use the MediaRecorder object for the same camera device.
    337                 mMediaRecorder = new MediaRecorder();
    338                 openDevice(id);
    339                 if (!mStaticInfo.isColorOutputSupported()) {
    340                     Log.i(TAG, "Camera " + id +
    341                             " does not support color outputs, skipping");
    342                     continue;
    343                 }
    344                 if (!mStaticInfo.isHighSpeedVideoSupported()) {
    345                     continue;
    346                 }
    347 
    348                 StreamConfigurationMap config =
    349                         mStaticInfo.getValueFromKeyNonNull(
    350                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    351                 Size[] highSpeedVideoSizes = config.getHighSpeedVideoSizes();
    352                 for (Size size : highSpeedVideoSizes) {
    353                     Range<Integer> fpsRange = getHighestHighSpeedFixedFpsRangeForSize(config, size);
    354                     mCollector.expectNotNull("Unable to find the fixed frame rate fps range for " +
    355                             "size " + size, fpsRange);
    356                     if (fpsRange == null) {
    357                         continue;
    358                     }
    359 
    360                     int captureRate = fpsRange.getLower();
    361                     int videoFramerate = captureRate / SLOWMO_SLOW_FACTOR;
    362                     // Skip the test if the highest recording FPS supported by CamcorderProfile
    363                     if (fpsRange.getUpper() > getFpsFromHighSpeedProfileForSize(size)) {
    364                         Log.w(TAG, "high speed recording " + size + "@" + captureRate + "fps"
    365                                 + " is not supported by CamcorderProfile");
    366                         continue;
    367                     }
    368 
    369                     mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video.mp4";
    370                     if (DEBUG_DUMP) {
    371                         mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video_" + id + "_"
    372                                 + size.toString() + ".mp4";
    373                     }
    374 
    375                     prepareRecording(size, videoFramerate, captureRate);
    376 
    377                     // prepare preview surface by using video size.
    378                     updatePreviewSurfaceWithVideo(size, captureRate);
    379 
    380                     // Start recording
    381                     SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    382                     startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
    383                             fpsRange, resultListener, /*useHighSpeedSession*/false);
    384 
    385                     // Record certain duration.
    386                     SystemClock.sleep(RECORDING_DURATION_MS);
    387 
    388                     // Stop recording and preview
    389                     stopRecording(/*useMediaRecorder*/true);
    390                     // Convert number of frames camera produced into the duration in unit of ms.
    391                     int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
    392                                     videoFramerate);
    393 
    394                     // Validation.
    395                     validateRecording(size, durationMs);
    396                 }
    397 
    398             } finally {
    399                 closeDevice();
    400                 releaseRecorder();
    401             }
    402         }
    403     }
    404 
    405     private void constrainedHighSpeedRecording() throws Exception {
    406         for (String id : mCameraIds) {
    407             try {
    408                 Log.i(TAG, "Testing constrained high speed recording for camera " + id);
    409                 // Re-use the MediaRecorder object for the same camera device.
    410                 mMediaRecorder = new MediaRecorder();
    411                 openDevice(id);
    412 
    413                 if (!mStaticInfo.isConstrainedHighSpeedVideoSupported()) {
    414                     continue;
    415                 }
    416 
    417                 StreamConfigurationMap config =
    418                         mStaticInfo.getValueFromKeyNonNull(
    419                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    420                 Size[] highSpeedVideoSizes = config.getHighSpeedVideoSizes();
    421                 for (Size size : highSpeedVideoSizes) {
    422                     List<Range<Integer>> fixedFpsRanges =
    423                             getHighSpeedFixedFpsRangeForSize(config, size);
    424                     mCollector.expectTrue("Unable to find the fixed frame rate fps range for " +
    425                             "size " + size, fixedFpsRanges.size() > 0);
    426                     // Test recording for each FPS range
    427                     for (Range<Integer> fpsRange : fixedFpsRanges) {
    428                         int captureRate = fpsRange.getLower();
    429                         final int VIDEO_FRAME_RATE = 30;
    430                         // Skip the test if the highest recording FPS supported by CamcorderProfile
    431                         if (fpsRange.getUpper() > getFpsFromHighSpeedProfileForSize(size)) {
    432                             Log.w(TAG, "high speed recording " + size + "@" + captureRate + "fps"
    433                                     + " is not supported by CamcorderProfile");
    434                             continue;
    435                         }
    436 
    437                         mOutMediaFileName = VIDEO_FILE_PATH + "/test_cslowMo_video_" + captureRate +
    438                                 "fps_" + id + "_" + size.toString() + ".mp4";
    439 
    440                         prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
    441 
    442                         // prepare preview surface by using video size.
    443                         updatePreviewSurfaceWithVideo(size, captureRate);
    444 
    445                         // Start recording
    446                         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    447                         startSlowMotionRecording(/*useMediaRecorder*/true, VIDEO_FRAME_RATE,
    448                                 captureRate, fpsRange, resultListener,
    449                                 /*useHighSpeedSession*/true);
    450 
    451                         // Record certain duration.
    452                         SystemClock.sleep(RECORDING_DURATION_MS);
    453 
    454                         // Stop recording and preview
    455                         stopRecording(/*useMediaRecorder*/true);
    456                         // Convert number of frames camera produced into the duration in unit of ms.
    457                         int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
    458                                         VIDEO_FRAME_RATE);
    459 
    460                         // Validation.
    461                         validateRecording(size, durationMs);
    462                     }
    463                 }
    464 
    465             } finally {
    466                 closeDevice();
    467                 releaseRecorder();
    468             }
    469         }
    470     }
    471 
    472     /**
    473      * Get high speed FPS from CamcorderProfiles for a given size.
    474      *
    475      * @param size The size used to search the CamcorderProfiles for the FPS.
    476      * @return high speed video FPS, 0 if the given size is not supported by the CamcorderProfiles.
    477      */
    478     private int getFpsFromHighSpeedProfileForSize(Size size) {
    479         for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_480P;
    480                 quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
    481             if (CamcorderProfile.hasProfile(quality)) {
    482                 CamcorderProfile profile = CamcorderProfile.get(quality);
    483                 if (size.equals(new Size(profile.videoFrameWidth, profile.videoFrameHeight))){
    484                     return profile.videoFrameRate;
    485                 }
    486             }
    487         }
    488 
    489         return 0;
    490     }
    491 
    492     private Range<Integer> getHighestHighSpeedFixedFpsRangeForSize(StreamConfigurationMap config,
    493             Size size) {
    494         Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
    495         Range<Integer> maxRange = availableFpsRanges[0];
    496         boolean foundRange = false;
    497         for (Range<Integer> range : availableFpsRanges) {
    498             if (range.getLower().equals(range.getUpper()) && range.getLower() >= maxRange.getLower()) {
    499                 foundRange = true;
    500                 maxRange = range;
    501             }
    502         }
    503 
    504         if (!foundRange) {
    505             return null;
    506         }
    507         return maxRange;
    508     }
    509 
    510     private List<Range<Integer>> getHighSpeedFixedFpsRangeForSize(StreamConfigurationMap config,
    511             Size size) {
    512         Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
    513         List<Range<Integer>> fixedRanges = new ArrayList<Range<Integer>>();
    514         for (Range<Integer> range : availableFpsRanges) {
    515             if (range.getLower().equals(range.getUpper())) {
    516                 fixedRanges.add(range);
    517             }
    518         }
    519         return fixedRanges;
    520     }
    521 
    522     private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
    523             int captureRate, Range<Integer> fpsRange,
    524             CameraCaptureSession.CaptureCallback listener, boolean useHighSpeedSession) throws Exception {
    525         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
    526         assertTrue("Both preview and recording surfaces should be valid",
    527                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
    528         outputSurfaces.add(mPreviewSurface);
    529         outputSurfaces.add(mRecordingSurface);
    530         // Video snapshot surface
    531         if (mReaderSurface != null) {
    532             outputSurfaces.add(mReaderSurface);
    533         }
    534         mSessionListener = new BlockingSessionCallback();
    535         mSession = configureCameraSession(mCamera, outputSurfaces, useHighSpeedSession,
    536                 mSessionListener, mHandler);
    537 
    538         // Create slow motion request list
    539         List<CaptureRequest> slowMoRequests = null;
    540         if (useHighSpeedSession) {
    541             CaptureRequest.Builder requestBuilder =
    542                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    543             requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    544             requestBuilder.addTarget(mPreviewSurface);
    545             requestBuilder.addTarget(mRecordingSurface);
    546             slowMoRequests = ((CameraConstrainedHighSpeedCaptureSession) mSession).
    547                     createHighSpeedRequestList(requestBuilder.build());
    548         } else {
    549             CaptureRequest.Builder recordingRequestBuilder =
    550                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    551             recordingRequestBuilder.set(CaptureRequest.CONTROL_MODE,
    552                     CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
    553             recordingRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
    554                     CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
    555 
    556             CaptureRequest.Builder recordingOnlyBuilder =
    557                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    558             recordingOnlyBuilder.set(CaptureRequest.CONTROL_MODE,
    559                     CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
    560             recordingOnlyBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
    561                     CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
    562             int slowMotionFactor = captureRate / videoFrameRate;
    563 
    564             // Make sure camera output frame rate is set to correct value.
    565             recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    566             recordingRequestBuilder.addTarget(mRecordingSurface);
    567             recordingRequestBuilder.addTarget(mPreviewSurface);
    568             recordingOnlyBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    569             recordingOnlyBuilder.addTarget(mRecordingSurface);
    570 
    571             slowMoRequests = new ArrayList<CaptureRequest>();
    572             slowMoRequests.add(recordingRequestBuilder.build());// Preview + recording.
    573 
    574             for (int i = 0; i < slowMotionFactor - 1; i++) {
    575                 slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
    576             }
    577         }
    578 
    579         mSession.setRepeatingBurst(slowMoRequests, listener, mHandler);
    580 
    581         if (useMediaRecorder) {
    582             mMediaRecorder.start();
    583         } else {
    584             // TODO: need implement MediaCodec path.
    585         }
    586 
    587     }
    588 
    589     /**
    590      * Test camera recording by using each available CamcorderProfile for a
    591      * given camera. preview size is set to the video size.
    592      */
    593     private void basicRecordingTestByCamera(int[] camcorderProfileList) throws Exception {
    594         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
    595         List<Range<Integer> > fpsRanges = Arrays.asList(
    596                 mStaticInfo.getAeAvailableTargetFpsRangesChecked());
    597         int cameraId = Integer.valueOf(mCamera.getId());
    598         for (int profileId : camcorderProfileList) {
    599             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
    600                     allowedUnsupported(cameraId, profileId)) {
    601                 continue;
    602             }
    603 
    604             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
    605             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
    606             Range<Integer> fpsRange = new Range(profile.videoFrameRate, profile.videoFrameRate);
    607             if (mStaticInfo.isHardwareLevelLegacy() &&
    608                     (videoSz.getWidth() > maxPreviewSize.getWidth() ||
    609                      videoSz.getHeight() > maxPreviewSize.getHeight())) {
    610                 // Skip. Legacy mode can only do recording up to max preview size
    611                 continue;
    612             }
    613             assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
    614                             " must be one of the camera device supported video size!",
    615                             mSupportedVideoSizes.contains(videoSz));
    616             assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
    617                     ") must be one of the camera device available FPS range!",
    618                     fpsRanges.contains(fpsRange));
    619 
    620             if (VERBOSE) {
    621                 Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
    622             }
    623 
    624             // Configure preview and recording surfaces.
    625             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    626             if (DEBUG_DUMP) {
    627                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
    628                         + videoSz.toString() + ".mp4";
    629             }
    630 
    631             prepareRecordingWithProfile(profile);
    632 
    633             // prepare preview surface by using video size.
    634             updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
    635 
    636             // Start recording
    637             SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    638             startRecording(/* useMediaRecorder */true, resultListener);
    639 
    640             // Record certain duration.
    641             SystemClock.sleep(RECORDING_DURATION_MS);
    642 
    643             // Stop recording and preview
    644             stopRecording(/* useMediaRecorder */true);
    645             // Convert number of frames camera produced into the duration in unit of ms.
    646             int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
    647                             profile.videoFrameRate);
    648 
    649             if (VERBOSE) {
    650                 Log.v(TAG, "video frame rate: " + profile.videoFrameRate +
    651                                 ", num of frames produced: " + resultListener.getTotalNumFrames());
    652             }
    653 
    654             // Validation.
    655             validateRecording(videoSz, durationMs);
    656         }
    657     }
    658 
    659     /**
    660      * Test camera recording for each supported video size by camera, preview
    661      * size is set to the video size.
    662      */
    663     private void recordingSizeTestByCamera() throws Exception {
    664         for (Size sz : mSupportedVideoSizes) {
    665             if (!isSupported(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE)) {
    666                 continue;
    667             }
    668 
    669             if (VERBOSE) {
    670                 Log.v(TAG, "Testing camera recording with video size " + sz.toString());
    671             }
    672 
    673             // Configure preview and recording surfaces.
    674             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    675             if (DEBUG_DUMP) {
    676                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + mCamera.getId() + "_"
    677                         + sz.toString() + ".mp4";
    678             }
    679 
    680             // Use AVC and AAC a/v compression format.
    681             prepareRecording(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE);
    682 
    683             // prepare preview surface by using video size.
    684             updatePreviewSurfaceWithVideo(sz, VIDEO_FRAME_RATE);
    685 
    686             // Start recording
    687             SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    688             startRecording(/* useMediaRecorder */true, resultListener);
    689 
    690             // Record certain duration.
    691             SystemClock.sleep(RECORDING_DURATION_MS);
    692 
    693             // Stop recording and preview
    694             stopRecording(/* useMediaRecorder */true);
    695             // Convert number of frames camera produced into the duration in unit of ms.
    696             int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
    697                             VIDEO_FRAME_RATE);
    698 
    699             // Validation.
    700             validateRecording(sz, durationMs);
    701         }
    702     }
    703 
    704     /**
    705      * Initialize the supported video sizes.
    706      */
    707     private void initSupportedVideoSize(String cameraId)  throws Exception {
    708         Size maxVideoSize = SIZE_BOUND_1080P;
    709         if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
    710             maxVideoSize = SIZE_BOUND_2160P;
    711         }
    712         mSupportedVideoSizes =
    713                 getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize);
    714     }
    715 
    716     /**
    717      * Simple wrapper to wrap normal/burst video snapshot tests
    718      */
    719     private void videoSnapshotHelper(boolean burstTest) throws Exception {
    720             for (String id : mCameraIds) {
    721                 try {
    722                     Log.i(TAG, "Testing video snapshot for camera " + id);
    723                     // Re-use the MediaRecorder object for the same camera device.
    724                     mMediaRecorder = new MediaRecorder();
    725 
    726                     openDevice(id);
    727 
    728                     if (!mStaticInfo.isColorOutputSupported()) {
    729                         Log.i(TAG, "Camera " + id +
    730                                 " does not support color outputs, skipping");
    731                         continue;
    732                     }
    733 
    734                     initSupportedVideoSize(id);
    735 
    736                     videoSnapshotTestByCamera(burstTest);
    737                 } finally {
    738                     closeDevice();
    739                     releaseRecorder();
    740                 }
    741             }
    742     }
    743 
    744     /**
    745      * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported.
    746      *
    747      * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p>
    748      *
    749      * @param profileId a {@link CamcorderProfile} ID to check.
    750      * @return {@code true} if supported.
    751      */
    752     private boolean allowedUnsupported(int cameraId, int profileId) {
    753         if (!mStaticInfo.isHardwareLevelLegacy()) {
    754             return false;
    755         }
    756 
    757         switch(profileId) {
    758             case CamcorderProfile.QUALITY_2160P:
    759             case CamcorderProfile.QUALITY_1080P:
    760             case CamcorderProfile.QUALITY_HIGH:
    761                 return !CamcorderProfile.hasProfile(cameraId, profileId) ||
    762                         CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080;
    763         }
    764         return false;
    765     }
    766 
    767     /**
    768      * Test video snapshot for each  available CamcorderProfile for a given camera.
    769      *
    770      * <p>
    771      * Preview size is set to the video size. For the burst test, frame drop and jittering
    772      * is not checked.
    773      * </p>
    774      *
    775      * @param burstTest Perform burst capture or single capture. For burst capture
    776      *                  {@value #BURST_VIDEO_SNAPSHOT_NUM} capture requests will be sent.
    777      */
    778     private void videoSnapshotTestByCamera(boolean burstTest)
    779             throws Exception {
    780         final int NUM_SINGLE_SHOT_TEST = 5;
    781         final int FRAMEDROP_TOLERANCE = 8;
    782         final int FRAME_SIZE_15M = 15000000;
    783         final float FRAME_DROP_TOLERENCE_FACTOR = 1.5f;
    784         int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
    785 
    786         for (int profileId : mCamcorderProfileList) {
    787             int cameraId = Integer.valueOf(mCamera.getId());
    788             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
    789                     allowedUnsupported(cameraId, profileId)) {
    790                 continue;
    791             }
    792 
    793             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
    794             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
    795             Size maxPreviewSize = mOrderedPreviewSizes.get(0);
    796 
    797             if (mStaticInfo.isHardwareLevelLegacy() &&
    798                     (videoSz.getWidth() > maxPreviewSize.getWidth() ||
    799                      videoSz.getHeight() > maxPreviewSize.getHeight())) {
    800                 // Skip. Legacy mode can only do recording up to max preview size
    801                 continue;
    802             }
    803 
    804             if (!mSupportedVideoSizes.contains(videoSz)) {
    805                 mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
    806                         profileId + " must be one of the camera device supported video size!");
    807                 continue;
    808             }
    809 
    810             // For LEGACY, find closest supported smaller or equal JPEG size to the current video
    811             // size; if no size is smaller than the video, pick the smallest JPEG size.  The assert
    812             // for video size above guarantees that for LIMITED or FULL, we select videoSz here.
    813             // Also check for minFrameDuration here to make sure jpeg stream won't slow down
    814             // video capture
    815             Size videoSnapshotSz = mOrderedStillSizes.get(mOrderedStillSizes.size() - 1);
    816             // Allow a bit tolerance so we don't fail for a few nano seconds of difference
    817             final float FRAME_DURATION_TOLERANCE = 0.01f;
    818             long videoFrameDuration = (long) (1e9 / profile.videoFrameRate *
    819                     (1.0 + FRAME_DURATION_TOLERANCE));
    820             HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
    821                     getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG);
    822             for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
    823                 Size candidateSize = mOrderedStillSizes.get(i);
    824                 if (mStaticInfo.isHardwareLevelLegacy()) {
    825                     // Legacy level doesn't report min frame duration
    826                     if (candidateSize.getWidth() <= videoSz.getWidth() &&
    827                             candidateSize.getHeight() <= videoSz.getHeight()) {
    828                         videoSnapshotSz = candidateSize;
    829                     }
    830                 } else {
    831                     Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
    832                     assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
    833                             jpegFrameDuration != null);
    834                     if (candidateSize.getWidth() <= videoSz.getWidth() &&
    835                             candidateSize.getHeight() <= videoSz.getHeight() &&
    836                             jpegFrameDuration <= videoFrameDuration) {
    837                         videoSnapshotSz = candidateSize;
    838                     }
    839                 }
    840             }
    841 
    842             /**
    843              * Only test full res snapshot when below conditions are all true.
    844              * 1. Camera is a FULL device
    845              * 2. video size is up to max preview size, which will be bounded by 1080p.
    846              * 3. Full resolution jpeg stream can keep up to video stream speed.
    847              *    When full res jpeg stream cannot keep up to video stream speed, search
    848              *    the largest jpeg size that can susptain video speed instead.
    849              */
    850             if (mStaticInfo.isHardwareLevelFull() &&
    851                     videoSz.getWidth() <= maxPreviewSize.getWidth() &&
    852                     videoSz.getHeight() <= maxPreviewSize.getHeight()) {
    853                 for (Size jpegSize : mOrderedStillSizes) {
    854                     Long jpegFrameDuration = minFrameDurationMap.get(jpegSize);
    855                     assertTrue("Cannot find minimum frame duration for jpeg size " + jpegSize,
    856                             jpegFrameDuration != null);
    857                     if (jpegFrameDuration <= videoFrameDuration) {
    858                         videoSnapshotSz = jpegSize;
    859                         break;
    860                     }
    861                     if (jpegSize.equals(videoSz)) {
    862                         throw new AssertionFailedError(
    863                                 "Cannot find adequate video snapshot size for video size" +
    864                                         videoSz);
    865                     }
    866                 }
    867             }
    868 
    869             Log.i(TAG, "Testing video snapshot size " + videoSnapshotSz +
    870                     " for video size " + videoSz);
    871             if (videoSnapshotSz.getWidth() * videoSnapshotSz.getHeight() > FRAME_SIZE_15M)
    872                 kFrameDrop_Tolerence = (int)(FRAMEDROP_TOLERANCE * FRAME_DROP_TOLERENCE_FACTOR);
    873 
    874             createImageReader(
    875                     videoSnapshotSz, ImageFormat.JPEG,
    876                     MAX_VIDEO_SNAPSHOT_IMAGES, /*listener*/null);
    877 
    878             if (VERBOSE) {
    879                 Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
    880             }
    881 
    882             // Configure preview and recording surfaces.
    883             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    884             if (DEBUG_DUMP) {
    885                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
    886                         + videoSz.toString() + ".mp4";
    887             }
    888 
    889             int numTestIterations = burstTest ? 1 : NUM_SINGLE_SHOT_TEST;
    890             int totalDroppedFrames = 0;
    891 
    892             for (int numTested = 0; numTested < numTestIterations; numTested++) {
    893                 prepareRecordingWithProfile(profile);
    894 
    895                 // prepare video snapshot
    896                 SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    897                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
    898                 CaptureRequest.Builder videoSnapshotRequestBuilder =
    899                         mCamera.createCaptureRequest((mStaticInfo.isHardwareLevelLegacy()) ?
    900                                 CameraDevice.TEMPLATE_RECORD :
    901                                 CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
    902 
    903                 // prepare preview surface by using video size.
    904                 updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
    905 
    906                 prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
    907                 CaptureRequest request = videoSnapshotRequestBuilder.build();
    908 
    909                 // Start recording
    910                 startRecording(/* useMediaRecorder */true, resultListener);
    911                 long startTime = SystemClock.elapsedRealtime();
    912 
    913                 // Record certain duration.
    914                 SystemClock.sleep(RECORDING_DURATION_MS / 2);
    915 
    916                 // take video snapshot
    917                 if (burstTest) {
    918                     List<CaptureRequest> requests =
    919                             new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
    920                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
    921                         requests.add(request);
    922                     }
    923                     mSession.captureBurst(requests, resultListener, mHandler);
    924                 } else {
    925                     mSession.capture(request, resultListener, mHandler);
    926                 }
    927 
    928                 // make sure recording is still going after video snapshot
    929                 SystemClock.sleep(RECORDING_DURATION_MS / 2);
    930 
    931                 // Stop recording and preview
    932                 int durationMs = stopRecording(/* useMediaRecorder */true);
    933                 // For non-burst test, use number of frames to also double check video frame rate.
    934                 // Burst video snapshot is allowed to cause frame rate drop, so do not use number
    935                 // of frames to estimate duration
    936                 if (!burstTest) {
    937                     durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
    938                         profile.videoFrameRate);
    939                 }
    940 
    941                 // Validation recorded video
    942                 validateRecording(videoSz, durationMs);
    943 
    944                 if (burstTest) {
    945                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
    946                         Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
    947                         validateVideoSnapshotCapture(image, videoSnapshotSz);
    948                         image.close();
    949                     }
    950                 } else {
    951                     // validate video snapshot image
    952                     Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
    953                     validateVideoSnapshotCapture(image, videoSnapshotSz);
    954 
    955                     // validate if there is framedrop around video snapshot
    956                     totalDroppedFrames +=  validateFrameDropAroundVideoSnapshot(
    957                             resultListener, image.getTimestamp());
    958 
    959                     //TODO: validate jittering. Should move to PTS
    960                     //validateJittering(resultListener);
    961 
    962                     image.close();
    963                 }
    964             }
    965 
    966             if (!burstTest) {
    967                 Log.w(TAG, String.format("Camera %d Video size %s: Number of dropped frames " +
    968                         "detected in %d trials is %d frames.", cameraId, videoSz.toString(),
    969                         numTestIterations, totalDroppedFrames));
    970                 mCollector.expectLessOrEqual(
    971                         String.format(
    972                                 "Camera %d Video size %s: Number of dropped frames %d must not"
    973                                 + " be larger than %d",
    974                                 cameraId, videoSz.toString(), totalDroppedFrames,
    975                                 kFrameDrop_Tolerence),
    976                         kFrameDrop_Tolerence, totalDroppedFrames);
    977             }
    978             closeImageReader();
    979         }
    980     }
    981 
    982     /**
    983      * Configure video snapshot request according to the still capture size
    984      */
    985     private void prepareVideoSnapshot(
    986             CaptureRequest.Builder requestBuilder,
    987             ImageReader.OnImageAvailableListener imageListener)
    988             throws Exception {
    989         mReader.setOnImageAvailableListener(imageListener, mHandler);
    990         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
    991         requestBuilder.addTarget(mRecordingSurface);
    992         assertNotNull("Preview surface must be non-null!", mPreviewSurface);
    993         requestBuilder.addTarget(mPreviewSurface);
    994         assertNotNull("Reader surface must be non-null!", mReaderSurface);
    995         requestBuilder.addTarget(mReaderSurface);
    996     }
    997 
    998     /**
    999      * Update preview size with video size.
   1000      *
   1001      * <p>Preview size will be capped with max preview size.</p>
   1002      *
   1003      * @param videoSize The video size used for preview.
   1004      * @param videoFrameRate The video frame rate
   1005      *
   1006      */
   1007     private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
   1008         if (mOrderedPreviewSizes == null) {
   1009             throw new IllegalStateException("supported preview size list is not initialized yet");
   1010         }
   1011         final float FRAME_DURATION_TOLERANCE = 0.01f;
   1012         long videoFrameDuration = (long) (1e9 / videoFrameRate *
   1013                 (1.0 + FRAME_DURATION_TOLERANCE));
   1014         HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
   1015                 getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
   1016         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
   1017         Size previewSize = null;
   1018         if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
   1019                 videoSize.getHeight() > maxPreviewSize.getHeight()) {
   1020             for (Size s : mOrderedPreviewSizes) {
   1021                 Long frameDuration = minFrameDurationMap.get(s);
   1022                 if (mStaticInfo.isHardwareLevelLegacy()) {
   1023                     // Legacy doesn't report min frame duration
   1024                     frameDuration = new Long(0);
   1025                 }
   1026                 assertTrue("Cannot find minimum frame duration for private size" + s,
   1027                         frameDuration != null);
   1028                 if (frameDuration <= videoFrameDuration &&
   1029                         s.getWidth() <= videoSize.getWidth() &&
   1030                         s.getHeight() <= videoSize.getHeight()) {
   1031                     Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
   1032                             " to " + s.toString());
   1033                     previewSize = s;
   1034                     break;
   1035                     // If all preview size doesn't work then we fallback to video size
   1036                 }
   1037             }
   1038         }
   1039         if (previewSize == null) {
   1040             previewSize = videoSize;
   1041         }
   1042         updatePreviewSurface(previewSize);
   1043     }
   1044 
   1045     /**
   1046      * Configure MediaRecorder recording session with CamcorderProfile, prepare
   1047      * the recording surface.
   1048      */
   1049     private void prepareRecordingWithProfile(CamcorderProfile profile)
   1050             throws Exception {
   1051         // Prepare MediaRecorder.
   1052         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
   1053         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
   1054         mMediaRecorder.setProfile(profile);
   1055         mMediaRecorder.setOutputFile(mOutMediaFileName);
   1056         if (mPersistentSurface != null) {
   1057             mMediaRecorder.setInputSurface(mPersistentSurface);
   1058             mRecordingSurface = mPersistentSurface;
   1059         }
   1060         mMediaRecorder.prepare();
   1061         if (mPersistentSurface == null) {
   1062             mRecordingSurface = mMediaRecorder.getSurface();
   1063         }
   1064         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
   1065         mVideoFrameRate = profile.videoFrameRate;
   1066         mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
   1067     }
   1068 
   1069     /**
   1070      * Configure MediaRecorder recording session with CamcorderProfile, prepare
   1071      * the recording surface. Use AVC for video compression, AAC for audio compression.
   1072      * Both are required for android devices by android CDD.
   1073      */
   1074     private void prepareRecording(Size sz, int videoFrameRate, int captureRate)
   1075             throws Exception {
   1076         // Prepare MediaRecorder.
   1077         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
   1078         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
   1079         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
   1080         mMediaRecorder.setOutputFile(mOutMediaFileName);
   1081         mMediaRecorder.setVideoEncodingBitRate(getVideoBitRate(sz));
   1082         mMediaRecorder.setVideoFrameRate(videoFrameRate);
   1083         mMediaRecorder.setCaptureRate(captureRate);
   1084         mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
   1085         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
   1086         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
   1087         if (mPersistentSurface != null) {
   1088             mMediaRecorder.setInputSurface(mPersistentSurface);
   1089             mRecordingSurface = mPersistentSurface;
   1090         }
   1091         mMediaRecorder.prepare();
   1092         if (mPersistentSurface == null) {
   1093             mRecordingSurface = mMediaRecorder.getSurface();
   1094         }
   1095         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
   1096         mVideoFrameRate = videoFrameRate;
   1097         mVideoSize = sz;
   1098     }
   1099 
   1100     private void startRecording(boolean useMediaRecorder,
   1101             CameraCaptureSession.CaptureCallback listener) throws Exception {
   1102         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
   1103         assertTrue("Both preview and recording surfaces should be valid",
   1104                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
   1105         outputSurfaces.add(mPreviewSurface);
   1106         outputSurfaces.add(mRecordingSurface);
   1107         // Video snapshot surface
   1108         if (mReaderSurface != null) {
   1109             outputSurfaces.add(mReaderSurface);
   1110         }
   1111         mSessionListener = new BlockingSessionCallback();
   1112         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
   1113 
   1114         CaptureRequest.Builder recordingRequestBuilder =
   1115                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
   1116         // Make sure camera output frame rate is set to correct value.
   1117         Range<Integer> fpsRange = Range.create(mVideoFrameRate, mVideoFrameRate);
   1118         recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
   1119         recordingRequestBuilder.addTarget(mRecordingSurface);
   1120         recordingRequestBuilder.addTarget(mPreviewSurface);
   1121         mSession.setRepeatingRequest(recordingRequestBuilder.build(), listener, mHandler);
   1122 
   1123         if (useMediaRecorder) {
   1124             mMediaRecorder.start();
   1125         } else {
   1126             // TODO: need implement MediaCodec path.
   1127         }
   1128         mRecordingStartTime = SystemClock.elapsedRealtime();
   1129     }
   1130 
   1131     private void startRecording(boolean useMediaRecorder)  throws Exception {
   1132         startRecording(useMediaRecorder, null);
   1133     }
   1134 
   1135     private void stopCameraStreaming() throws Exception {
   1136         if (VERBOSE) {
   1137             Log.v(TAG, "Stopping camera streaming and waiting for idle");
   1138         }
   1139         // Stop repeating, wait for captures to complete, and disconnect from
   1140         // surfaces
   1141         mSession.close();
   1142         mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
   1143     }
   1144 
   1145     // Stop recording and return the estimated video duration in milliseconds.
   1146     private int stopRecording(boolean useMediaRecorder) throws Exception {
   1147         long stopRecordingTime = SystemClock.elapsedRealtime();
   1148         if (useMediaRecorder) {
   1149             stopCameraStreaming();
   1150 
   1151             mMediaRecorder.stop();
   1152             // Can reuse the MediaRecorder object after reset.
   1153             mMediaRecorder.reset();
   1154         } else {
   1155             // TODO: need implement MediaCodec path.
   1156         }
   1157         if (mPersistentSurface == null && mRecordingSurface != null) {
   1158             mRecordingSurface.release();
   1159             mRecordingSurface = null;
   1160         }
   1161         return (int) (stopRecordingTime - mRecordingStartTime);
   1162     }
   1163 
   1164     private void releaseRecorder() {
   1165         if (mMediaRecorder != null) {
   1166             mMediaRecorder.release();
   1167             mMediaRecorder = null;
   1168         }
   1169     }
   1170 
   1171     private void validateRecording(Size sz, int expectedDurationMs) throws Exception {
   1172         File outFile = new File(mOutMediaFileName);
   1173         assertTrue("No video is recorded", outFile.exists());
   1174 
   1175         MediaExtractor extractor = new MediaExtractor();
   1176         try {
   1177             extractor.setDataSource(mOutMediaFileName);
   1178             long durationUs = 0;
   1179             int width = -1, height = -1;
   1180             int numTracks = extractor.getTrackCount();
   1181             final String VIDEO_MIME_TYPE = "video";
   1182             for (int i = 0; i < numTracks; i++) {
   1183                 MediaFormat format = extractor.getTrackFormat(i);
   1184                 String mime = format.getString(MediaFormat.KEY_MIME);
   1185                 if (mime.contains(VIDEO_MIME_TYPE)) {
   1186                     Log.i(TAG, "video format is: " + format.toString());
   1187                     durationUs = format.getLong(MediaFormat.KEY_DURATION);
   1188                     width = format.getInteger(MediaFormat.KEY_WIDTH);
   1189                     height = format.getInteger(MediaFormat.KEY_HEIGHT);
   1190                     break;
   1191                 }
   1192             }
   1193             Size videoSz = new Size(width, height);
   1194             assertTrue("Video size doesn't match, expected " + sz.toString() +
   1195                     " got " + videoSz.toString(), videoSz.equals(sz));
   1196             int duration = (int) (durationUs / 1000);
   1197             if (VERBOSE) {
   1198                 Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
   1199                                          duration, expectedDurationMs));
   1200             }
   1201 
   1202             // TODO: Don't skip this for video snapshot
   1203             if (!mStaticInfo.isHardwareLevelLegacy()) {
   1204                 assertTrue(String.format(
   1205                         "Camera %s: Video duration doesn't match: recorded %dms, expected %dms.",
   1206                         mCamera.getId(), duration, expectedDurationMs),
   1207                         Math.abs(duration - expectedDurationMs) <
   1208                         DURATION_MARGIN * expectedDurationMs);
   1209             }
   1210         } finally {
   1211             extractor.release();
   1212             if (!DEBUG_DUMP) {
   1213                 outFile.delete();
   1214             }
   1215         }
   1216     }
   1217 
   1218     /**
   1219      * Validate video snapshot capture image object sanity and test.
   1220      *
   1221      * <p> Check for size, format and jpeg decoding</p>
   1222      *
   1223      * @param image The JPEG image to be verified.
   1224      * @param size The JPEG capture size to be verified against.
   1225      */
   1226     private void validateVideoSnapshotCapture(Image image, Size size) {
   1227         CameraTestUtils.validateImage(image, size.getWidth(), size.getHeight(),
   1228                 ImageFormat.JPEG, /*filePath*/null);
   1229     }
   1230 
   1231     /**
   1232      * Validate if video snapshot causes frame drop.
   1233      * Here frame drop is defined as frame duration >= 2 * expected frame duration.
   1234      * Return the estimated number of frames dropped during video snapshot
   1235      */
   1236     private int validateFrameDropAroundVideoSnapshot(
   1237             SimpleCaptureCallback resultListener, long imageTimeStamp) {
   1238         double expectedDurationMs = 1000.0 / mVideoFrameRate;
   1239         CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
   1240         long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
   1241         while (!resultListener.hasMoreResults()) {
   1242             CaptureResult currentResult =
   1243                     resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
   1244             long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
   1245             if (currentTS == imageTimeStamp) {
   1246                 // validate the timestamp before and after, then return
   1247                 CaptureResult nextResult =
   1248                         resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
   1249                 long nextTS = getValueNotNull(nextResult, CaptureResult.SENSOR_TIMESTAMP);
   1250                 double durationMs = (currentTS - prevTS) / 1000000.0;
   1251                 int totalFramesDropped = 0;
   1252 
   1253                 // Snapshots in legacy mode pause the preview briefly.  Skip the duration
   1254                 // requirements for legacy mode unless this is fixed.
   1255                 if (!mStaticInfo.isHardwareLevelLegacy()) {
   1256                     mCollector.expectTrue(
   1257                             String.format(
   1258                                     "Video %dx%d Frame drop detected before video snapshot: " +
   1259                                             "duration %.2fms (expected %.2fms)",
   1260                                     mVideoSize.getWidth(), mVideoSize.getHeight(),
   1261                                     durationMs, expectedDurationMs
   1262                             ),
   1263                             durationMs <= (expectedDurationMs * MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED)
   1264                     );
   1265                     // Log a warning is there is any frame drop detected.
   1266                     if (durationMs >= expectedDurationMs * 2) {
   1267                         Log.w(TAG, String.format(
   1268                                 "Video %dx%d Frame drop detected before video snapshot: " +
   1269                                         "duration %.2fms (expected %.2fms)",
   1270                                 mVideoSize.getWidth(), mVideoSize.getHeight(),
   1271                                 durationMs, expectedDurationMs
   1272                         ));
   1273                     }
   1274 
   1275                     durationMs = (nextTS - currentTS) / 1000000.0;
   1276                     mCollector.expectTrue(
   1277                             String.format(
   1278                                     "Video %dx%d Frame drop detected after video snapshot: " +
   1279                                             "duration %.2fms (expected %.2fms)",
   1280                                     mVideoSize.getWidth(), mVideoSize.getHeight(),
   1281                                     durationMs, expectedDurationMs
   1282                             ),
   1283                             durationMs <= (expectedDurationMs * MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED)
   1284                     );
   1285                     // Log a warning is there is any frame drop detected.
   1286                     if (durationMs >= expectedDurationMs * 2) {
   1287                         Log.w(TAG, String.format(
   1288                                 "Video %dx%d Frame drop detected after video snapshot: " +
   1289                                         "duration %fms (expected %fms)",
   1290                                 mVideoSize.getWidth(), mVideoSize.getHeight(),
   1291                                 durationMs, expectedDurationMs
   1292                         ));
   1293                     }
   1294 
   1295                     double totalDurationMs = (nextTS - prevTS) / 1000000.0;
   1296                     // Minus 2 for the expected 2 frames interval
   1297                     totalFramesDropped = (int) (totalDurationMs / expectedDurationMs) - 2;
   1298                     if (totalFramesDropped < 0) {
   1299                         Log.w(TAG, "totalFrameDropped is " + totalFramesDropped +
   1300                                 ". Video frame rate might be too fast.");
   1301                     }
   1302                     totalFramesDropped = Math.max(0, totalFramesDropped);
   1303                 }
   1304                 return totalFramesDropped;
   1305             }
   1306             prevTS = currentTS;
   1307         }
   1308         throw new AssertionFailedError(
   1309                 "Video snapshot timestamp does not match any of capture results!");
   1310     }
   1311 
   1312     /**
   1313      * Validate frame jittering from the input simple listener's buffered results
   1314      */
   1315     private void validateJittering(SimpleCaptureCallback resultListener) {
   1316         double expectedDurationMs = 1000.0 / mVideoFrameRate;
   1317         CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
   1318         long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
   1319         while (!resultListener.hasMoreResults()) {
   1320             CaptureResult currentResult =
   1321                     resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
   1322             long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
   1323             double durationMs = (currentTS - prevTS) / 1000000.0;
   1324             double durationError = Math.abs(durationMs - expectedDurationMs);
   1325             long frameNumber = currentResult.getFrameNumber();
   1326             mCollector.expectTrue(
   1327                     String.format(
   1328                             "Resolution %dx%d Frame %d: jittering (%.2fms) exceeds bound [%.2fms,%.2fms]",
   1329                             mVideoSize.getWidth(), mVideoSize.getHeight(),
   1330                             frameNumber, durationMs,
   1331                             expectedDurationMs - FRAME_DURATION_ERROR_TOLERANCE_MS,
   1332                             expectedDurationMs + FRAME_DURATION_ERROR_TOLERANCE_MS),
   1333                     durationError <= FRAME_DURATION_ERROR_TOLERANCE_MS);
   1334             prevTS = currentTS;
   1335         }
   1336     }
   1337 
   1338     /**
   1339      * Calculate a video bit rate based on the size. The bit rate is scaled
   1340      * based on ratio of video size to 1080p size.
   1341      */
   1342     private int getVideoBitRate(Size sz) {
   1343         int rate = BIT_RATE_1080P;
   1344         float scaleFactor = sz.getHeight() * sz.getWidth() / (float)(1920 * 1080);
   1345         rate = (int)(rate * scaleFactor);
   1346 
   1347         // Clamp to the MIN, MAX range.
   1348         return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
   1349     }
   1350 
   1351     /**
   1352      * Check if the encoder and camera are able to support this size and frame rate.
   1353      * Assume the video compression format is AVC.
   1354      */
   1355     private boolean isSupported(Size sz, int captureRate, int encodingRate) throws Exception {
   1356         // Check camera capability.
   1357         if (!isSupportedByCamera(sz, captureRate)) {
   1358             return false;
   1359         }
   1360 
   1361         // Check encode capability.
   1362         if (!isSupportedByAVCEncoder(sz, encodingRate)){
   1363             return false;
   1364         }
   1365 
   1366         if(VERBOSE) {
   1367             Log.v(TAG, "Both encoder and camera support " + sz.toString() + "@" + encodingRate + "@"
   1368                     + getVideoBitRate(sz) / 1000 + "Kbps");
   1369         }
   1370 
   1371         return true;
   1372     }
   1373 
   1374     private boolean isSupportedByCamera(Size sz, int frameRate) {
   1375         // Check if camera can support this sz and frame rate combination.
   1376         StreamConfigurationMap config = mStaticInfo.
   1377                 getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1378 
   1379         long minDuration = config.getOutputMinFrameDuration(MediaRecorder.class, sz);
   1380         if (minDuration == 0) {
   1381             return false;
   1382         }
   1383 
   1384         int maxFrameRate = (int) (1e9f / minDuration);
   1385         return maxFrameRate >= frameRate;
   1386     }
   1387 
   1388     /**
   1389      * Check if encoder can support this size and frame rate combination by querying
   1390      * MediaCodec capability. Check is based on size and frame rate. Ignore the bit rate
   1391      * as the bit rates targeted in this test are well below the bit rate max value specified
   1392      * by AVC specification for certain level.
   1393      */
   1394     private static boolean isSupportedByAVCEncoder(Size sz, int frameRate) {
   1395         MediaFormat format = MediaFormat.createVideoFormat(
   1396                 MediaFormat.MIMETYPE_VIDEO_AVC, sz.getWidth(), sz.getHeight());
   1397         format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
   1398         MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
   1399         return mcl.findEncoderForFormat(format) != null;
   1400     }
   1401 }
   1402