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.CameraDevice;
     21 import android.hardware.camera2.CaptureRequest;
     22 import android.hardware.camera2.CaptureResult;
     23 import android.hardware.camera2.params.StreamConfigurationMap;
     24 import android.util.Size;
     25 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
     26 import android.media.CamcorderProfile;
     27 import android.media.MediaCodecInfo;
     28 import android.media.MediaCodecInfo.CodecCapabilities;
     29 import android.media.MediaCodecInfo.CodecProfileLevel;
     30 import android.media.Image;
     31 import android.media.ImageReader;
     32 import android.media.MediaCodecList;
     33 import android.media.MediaPlayer;
     34 import android.media.MediaRecorder;
     35 import android.os.Environment;
     36 import android.os.SystemClock;
     37 import android.test.suitebuilder.annotation.LargeTest;
     38 import android.util.Log;
     39 import android.util.Range;
     40 import android.view.Surface;
     41 
     42 import com.android.ex.camera2.blocking.BlockingSessionCallback;
     43 
     44 import junit.framework.AssertionFailedError;
     45 
     46 import java.io.File;
     47 import java.util.ArrayList;
     48 import java.util.List;
     49 
     50 /**
     51  * CameraDevice video recording use case tests by using MediaRecorder and
     52  * MediaCodec.
     53  */
     54 @LargeTest
     55 public class RecordingTest extends Camera2SurfaceViewTestCase {
     56     private static final String TAG = "RecordingTest";
     57     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     58     private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
     59     private static final int RECORDING_DURATION_MS = 3000;
     60     private static final int DURATION_MARGIN_MS = 600;
     61     private static final int FRAME_DURATION_ERROR_TOLERANCE_MS = 3;
     62     private static final int BIT_RATE_1080P = 16000000;
     63     private static final int BIT_RATE_MIN = 64000;
     64     private static final int BIT_RATE_MAX = 40000000;
     65     private static final int VIDEO_FRAME_RATE = 30;
     66     private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
     67     private static final int[] mCamcorderProfileList = {
     68             CamcorderProfile.QUALITY_2160P,
     69             CamcorderProfile.QUALITY_1080P,
     70             CamcorderProfile.QUALITY_480P,
     71             CamcorderProfile.QUALITY_720P,
     72             CamcorderProfile.QUALITY_CIF,
     73             CamcorderProfile.QUALITY_LOW,
     74             CamcorderProfile.QUALITY_HIGH,
     75             CamcorderProfile.QUALITY_QCIF,
     76             CamcorderProfile.QUALITY_QVGA,
     77     };
     78     private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
     79     private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
     80     private static final int SLOWMO_SLOW_FACTOR = 4;
     81     private List<Size> mSupportedVideoSizes;
     82     private Surface mRecordingSurface;
     83     private MediaRecorder mMediaRecorder;
     84     private String mOutMediaFileName;
     85     private int mVideoFrameRate;
     86     private Size mVideoSize;
     87 
     88     @Override
     89     protected void setUp() throws Exception {
     90         super.setUp();
     91     }
     92 
     93     @Override
     94     protected void tearDown() throws Exception {
     95         super.tearDown();
     96     }
     97 
     98     /**
     99      * <p>
    100      * Test basic camera recording.
    101      * </p>
    102      * <p>
    103      * This test covers the typical basic use case of camera recording.
    104      * MediaRecorder is used to record the audio and video, CamcorderProfile is
    105      * used to configure the MediaRecorder. It goes through the pre-defined
    106      * CamcorderProfile list, test each profile configuration and validate the
    107      * recorded video. Preview is set to the video size.
    108      * </p>
    109      */
    110     public void testBasicRecording() throws Exception {
    111         for (int i = 0; i < mCameraIds.length; i++) {
    112             try {
    113                 Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
    114                 // Re-use the MediaRecorder object for the same camera device.
    115                 mMediaRecorder = new MediaRecorder();
    116                 openDevice(mCameraIds[i]);
    117 
    118                 initSupportedVideoSize(mCameraIds[i]);
    119 
    120                 basicRecordingTestByCamera();
    121             } finally {
    122                 closeDevice();
    123                 releaseRecorder();
    124             }
    125         }
    126     }
    127 
    128     /**
    129      * <p>
    130      * Test camera recording for all supported sizes by using MediaRecorder.
    131      * </p>
    132      * <p>
    133      * This test covers camera recording for all supported sizes by camera. MediaRecorder
    134      * is used to encode the video. Preview is set to the video size. Recorded videos are
    135      * validated according to the recording configuration.
    136      * </p>
    137      */
    138     public void testSupportedVideoSizes() throws Exception {
    139         for (int i = 0; i < mCameraIds.length; i++) {
    140             try {
    141                 Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
    142                 // Re-use the MediaRecorder object for the same camera device.
    143                 mMediaRecorder = new MediaRecorder();
    144                 openDevice(mCameraIds[i]);
    145 
    146                 initSupportedVideoSize(mCameraIds[i]);
    147 
    148                 recordingSizeTestByCamera();
    149             } finally {
    150                 closeDevice();
    151                 releaseRecorder();
    152             }
    153         }
    154     }
    155 
    156     /**
    157      * Test different start/stop orders of Camera and Recorder.
    158      *
    159      * <p>The recording should be working fine for any kind of start/stop orders.</p>
    160      */
    161     public void testCameraRecorderOrdering() {
    162         // TODO: need implement
    163     }
    164 
    165     /**
    166      * <p>
    167      * Test camera recording for all supported sizes by using MediaCodec.
    168      * </p>
    169      * <p>
    170      * This test covers video only recording for all supported sizes (camera and
    171      * encoder). MediaCodec is used to encode the video. The recorded videos are
    172      * validated according to the recording configuration.
    173      * </p>
    174      */
    175     public void testMediaCodecRecording() throws Exception {
    176         // TODO. Need implement.
    177     }
    178 
    179     /**
    180      * <p>
    181      * Test video snapshot for each camera.
    182      * </p>
    183      * <p>
    184      * This test covers video snapshot typical use case. The MediaRecorder is used to record the
    185      * video for each available video size. The largest still capture size is selected to
    186      * capture the JPEG image. The still capture images are validated according to the capture
    187      * configuration. The timestamp of capture result before and after video snapshot is also
    188      * checked to make sure no frame drop caused by video snapshot.
    189      * </p>
    190      */
    191     public void testVideoSnapshot() throws Exception {
    192         videoSnapshotHelper(/*burstTest*/false);
    193     }
    194 
    195     /**
    196      * <p>
    197      * Test burst video snapshot for each camera.
    198      * </p>
    199      * <p>
    200      * This test covers burst video snapshot capture. The MediaRecorder is used to record the
    201      * video for each available video size. The largest still capture size is selected to
    202      * capture the JPEG image. {@value #BURST_VIDEO_SNAPSHOT_NUM} video snapshot requests will be
    203      * sent during the test. The still capture images are validated according to the capture
    204      * configuration.
    205      * </p>
    206      */
    207     public void testBurstVideoSnapshot() throws Exception {
    208         videoSnapshotHelper(/*burstTest*/true);
    209     }
    210 
    211     /**
    212      * Test timelapse recording, where capture rate is slower than video (playback) frame rate.
    213      */
    214     public void testTimelapseRecording() throws Exception {
    215         // TODO. Need implement.
    216     }
    217 
    218     public void testSlowMotionRecording() throws Exception {
    219         slowMotionRecording();
    220     }
    221 
    222     /**
    223      * Test slow motion recording where capture rate (camera output) is different with
    224      * video (playback) frame rate for each camera if high speed recording is supported
    225      * by both camera and encoder.
    226      *
    227      * <p>
    228      * Normal recording use cases make the capture rate (camera output frame
    229      * rate) the same as the video (playback) frame rate. This guarantees that
    230      * the motions in the scene play at the normal speed. If the capture rate is
    231      * faster than video frame rate, for a given time duration, more number of
    232      * frames are captured than it can be played in the same time duration. This
    233      * generates "slow motion" effect during playback.
    234      * </p>
    235      */
    236     private void slowMotionRecording() throws Exception {
    237         for (String id : mCameraIds) {
    238             try {
    239                 Log.i(TAG, "Testing slow motion recording for camera " + id);
    240                 // Re-use the MediaRecorder object for the same camera device.
    241                 mMediaRecorder = new MediaRecorder();
    242                 openDevice(id);
    243 
    244                 if (!mStaticInfo.isHighSpeedVideoSupported()) {
    245                     continue;
    246                 }
    247 
    248                 StreamConfigurationMap config =
    249                         mStaticInfo.getValueFromKeyNonNull(
    250                                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    251                 Size[] highSpeedVideoSizes = config.getHighSpeedVideoSizes();
    252                 for (Size size : highSpeedVideoSizes) {
    253                     Range<Integer> fpsRange = getHighestHighSpeedFixedFpsRangeForSize(config, size);
    254                     mCollector.expectNotNull("Unable to find the fixed frame rate fps range for " +
    255                             "size " + size, fpsRange);
    256                     if (fpsRange == null) {
    257                         continue;
    258                     }
    259 
    260                     int captureRate = fpsRange.getLower();
    261                     int videoFramerate = captureRate / SLOWMO_SLOW_FACTOR;
    262                     /**
    263                      * Check if encoder support this. TODO: use HIGH_SPEED_720p
    264                      * CamCorderProfile to get the performance guarantee. Also
    265                      * add the test in StaticMetadataTest to check: 1. Camera
    266                      * high speed recording metadata is correctly reported 2.
    267                      * Encoder profile/level info is correctly reported. After
    268                      * that, we only need check the CamcorderProfile before
    269                      * skipping the test.
    270                      */
    271                     if (!isSupportedByAVCEncoder(size, captureRate)) {
    272                         Log.i(TAG, "high speed recording " + size + "@" + captureRate + "fps"
    273                                 + " is not supported by AVC encoder");
    274                         continue;
    275                     }
    276 
    277                     mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video.mp4";
    278                     if (DEBUG_DUMP) {
    279                         mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video_" + id + "_"
    280                                 + size.toString() + ".mp4";
    281                     }
    282 
    283                     prepareRecording(size, videoFramerate, captureRate);
    284 
    285                     // prepare preview surface by using video size.
    286                     updatePreviewSurfaceWithVideoSize(size);
    287 
    288                     // Start recording
    289                     startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
    290                             fpsRange);
    291                     long startTime = SystemClock.elapsedRealtime();
    292 
    293                     // Record certain duration.
    294                     SystemClock.sleep(RECORDING_DURATION_MS);
    295 
    296                     // Stop recording and preview
    297                     stopRecording(/*useMediaRecorder*/true);
    298                     int duration = (int) (SystemClock.elapsedRealtime() - startTime);
    299 
    300                     // Validation.
    301                     validateRecording(size, duration * SLOWMO_SLOW_FACTOR);
    302 
    303                 }
    304 
    305             } finally {
    306                 closeDevice();
    307                 releaseRecorder();
    308             }
    309         }
    310     }
    311 
    312     private Range<Integer> getHighestHighSpeedFixedFpsRangeForSize(StreamConfigurationMap config,
    313             Size size) {
    314         Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
    315         Range<Integer> maxRange = availableFpsRanges[0];
    316         boolean foundRange = false;
    317         for (Range<Integer> range : availableFpsRanges) {
    318             if (range.getLower() == range.getUpper() && range.getLower() >= maxRange.getLower()) {
    319                 foundRange = true;
    320                 maxRange = range;
    321             }
    322         }
    323 
    324         if (!foundRange) {
    325             return null;
    326         }
    327         return maxRange;
    328     }
    329 
    330     private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
    331             int captureRate, Range<Integer> fpsRange) throws Exception {
    332         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
    333         assertTrue("Both preview and recording surfaces should be valid",
    334                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
    335         outputSurfaces.add(mPreviewSurface);
    336         outputSurfaces.add(mRecordingSurface);
    337         // Video snapshot surface
    338         if (mReaderSurface != null) {
    339             outputSurfaces.add(mReaderSurface);
    340         }
    341         mSessionListener = new BlockingSessionCallback();
    342         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
    343 
    344         CaptureRequest.Builder recordingRequestBuilder =
    345                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    346         recordingRequestBuilder.set(CaptureRequest.CONTROL_MODE,
    347                 CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
    348         recordingRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
    349                 CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
    350 
    351         CaptureRequest.Builder recordingOnlyBuilder =
    352                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    353         recordingOnlyBuilder.set(CaptureRequest.CONTROL_MODE,
    354                 CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
    355         recordingOnlyBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
    356                 CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
    357         int slowMotionFactor = captureRate / videoFrameRate;
    358 
    359         // Make sure camera output frame rate is set to correct value.
    360         recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    361         recordingRequestBuilder.addTarget(mRecordingSurface);
    362         recordingRequestBuilder.addTarget(mPreviewSurface);
    363         recordingOnlyBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    364         recordingOnlyBuilder.addTarget(mRecordingSurface);
    365 
    366         List<CaptureRequest> slowMoRequests = new ArrayList<CaptureRequest>();
    367         slowMoRequests.add(recordingRequestBuilder.build());// Preview + recording.
    368 
    369         for (int i = 0; i < slowMotionFactor - 1; i++) {
    370             slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
    371         }
    372         mSession.setRepeatingBurst(slowMoRequests, null, null);
    373 
    374         if (useMediaRecorder) {
    375             mMediaRecorder.start();
    376         } else {
    377             // TODO: need implement MediaCodec path.
    378         }
    379 
    380     }
    381 
    382     /**
    383      * Test camera recording by using each available CamcorderProfile for a
    384      * given camera. preview size is set to the video size.
    385      */
    386     private void basicRecordingTestByCamera() throws Exception {
    387         for (int profileId : mCamcorderProfileList) {
    388             int cameraId = Integer.valueOf(mCamera.getId());
    389             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
    390                     allowedUnsupported(cameraId, profileId)) {
    391                 continue;
    392             }
    393 
    394             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
    395             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
    396             assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
    397                             " must be one of the camera device supported video size!",
    398                             mSupportedVideoSizes.contains(videoSz));
    399 
    400             if (VERBOSE) {
    401                 Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
    402             }
    403 
    404             // Configure preview and recording surfaces.
    405             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    406             if (DEBUG_DUMP) {
    407                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
    408                         + videoSz.toString() + ".mp4";
    409             }
    410 
    411             prepareRecordingWithProfile(profile);
    412 
    413             // prepare preview surface by using video size.
    414             updatePreviewSurfaceWithVideoSize(videoSz);
    415 
    416             // Start recording
    417             startRecording(/* useMediaRecorder */true);
    418             long startTime = SystemClock.elapsedRealtime();
    419 
    420             // Record certain duration.
    421             SystemClock.sleep(RECORDING_DURATION_MS);
    422 
    423             // Stop recording and preview
    424             stopRecording(/* useMediaRecorder */true);
    425             int duration = (int) (SystemClock.elapsedRealtime() - startTime);
    426 
    427             // Validation.
    428             validateRecording(videoSz, duration);
    429         }
    430     }
    431 
    432     /**
    433      * Test camera recording for each supported video size by camera, preview
    434      * size is set to the video size.
    435      */
    436     private void recordingSizeTestByCamera() throws Exception {
    437         for (Size sz : mSupportedVideoSizes) {
    438             if (!isSupported(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE)) {
    439                 continue;
    440             }
    441 
    442             if (VERBOSE) {
    443                 Log.v(TAG, "Testing camera recording with video size " + sz.toString());
    444             }
    445 
    446             // Configure preview and recording surfaces.
    447             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    448             if (DEBUG_DUMP) {
    449                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + mCamera.getId() + "_"
    450                         + sz.toString() + ".mp4";
    451             }
    452 
    453             // Use AVC and AAC a/v compression format.
    454             prepareRecording(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE);
    455 
    456             // prepare preview surface by using video size.
    457             updatePreviewSurfaceWithVideoSize(sz);
    458 
    459             // Start recording
    460             startRecording(/* useMediaRecorder */true);
    461             long startTime = SystemClock.elapsedRealtime();
    462 
    463             // Record certain duration.
    464             SystemClock.sleep(RECORDING_DURATION_MS);
    465 
    466             // Stop recording and preview
    467             stopRecording(/* useMediaRecorder */true);
    468             int duration = (int) (SystemClock.elapsedRealtime() - startTime);
    469 
    470             // Validation.
    471             validateRecording(sz, duration);
    472         }
    473     }
    474 
    475     /**
    476      * Initialize the supported video sizes.
    477      */
    478     private void initSupportedVideoSize(String cameraId)  throws Exception {
    479         Size maxVideoSize = SIZE_BOUND_1080P;
    480         if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
    481             maxVideoSize = SIZE_BOUND_2160P;
    482         }
    483         mSupportedVideoSizes =
    484                 getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize);
    485     }
    486 
    487     /**
    488      * Simple wrapper to wrap normal/burst video snapshot tests
    489      */
    490     private void videoSnapshotHelper(boolean burstTest) throws Exception {
    491             for (String id : mCameraIds) {
    492                 try {
    493                     Log.i(TAG, "Testing video snapshot for camera " + id);
    494                     // Re-use the MediaRecorder object for the same camera device.
    495                     mMediaRecorder = new MediaRecorder();
    496 
    497                     openDevice(id);
    498 
    499                     if (mStaticInfo.isHardwareLevelLegacy()) {
    500                         Log.i(TAG, "Skipping test on legacy devices");
    501                         continue;
    502                     }
    503 
    504                     initSupportedVideoSize(id);
    505 
    506                     videoSnapshotTestByCamera(burstTest);
    507                 } finally {
    508                     closeDevice();
    509                     releaseRecorder();
    510                 }
    511             }
    512     }
    513 
    514     /**
    515      * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported.
    516      *
    517      * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p>
    518      *
    519      * @param profileId a {@link CamcorderProfile} ID to check.
    520      * @return {@code true} if supported.
    521      */
    522     private boolean allowedUnsupported(int cameraId, int profileId) {
    523         if (!mStaticInfo.isHardwareLevelLegacy()) {
    524             return false;
    525         }
    526 
    527         switch(profileId) {
    528             case CamcorderProfile.QUALITY_2160P:
    529             case CamcorderProfile.QUALITY_1080P:
    530             case CamcorderProfile.QUALITY_HIGH:
    531                 return !CamcorderProfile.hasProfile(cameraId, profileId) ||
    532                         CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080;
    533         }
    534         return false;
    535     }
    536 
    537     /**
    538      * Test video snapshot for each  available CamcorderProfile for a given camera.
    539      *
    540      * <p>
    541      * Preview size is set to the video size. For the burst test, frame drop and jittering
    542      * is not checked.
    543      * </p>
    544      *
    545      * @param burstTest Perform burst capture or single capture. For burst capture
    546      *                  {@value #BURST_VIDEO_SNAPSHOT_NUM} capture requests will be sent.
    547      */
    548     private void videoSnapshotTestByCamera(boolean burstTest)
    549             throws Exception {
    550         final int NUM_SINGLE_SHOT_TEST = 5;
    551         final int FRAMEDROP_TOLERANCE = 8;
    552         for (int profileId : mCamcorderProfileList) {
    553             int cameraId = Integer.valueOf(mCamera.getId());
    554             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
    555                     allowedUnsupported(cameraId, profileId)) {
    556                 continue;
    557             }
    558 
    559             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
    560             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
    561             if (!mSupportedVideoSizes.contains(videoSz)) {
    562                 mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
    563                         profileId + " must be one of the camera device supported video size!");
    564                 continue;
    565             }
    566 
    567             Size maxPreviewSize = mOrderedPreviewSizes.get(0);
    568 
    569             // For LEGACY, find closest supported smaller or equal JPEG size to the current video
    570             // size; if no size is smaller than the video, pick the smallest JPEG size.  The assert
    571             // for video size above guarantees that for LIMITED or FULL, we select videoSz here.
    572             Size videoSnapshotSz = mOrderedStillSizes.get(mOrderedStillSizes.size() - 1);
    573             for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
    574                 Size candidateSize = mOrderedStillSizes.get(i);
    575                 if (candidateSize.getWidth() <= videoSz.getWidth() &&
    576                         candidateSize.getHeight() <= videoSz.getHeight()) {
    577                     videoSnapshotSz = candidateSize;
    578                 }
    579             }
    580 
    581             /**
    582              * Only test full res snapshot when below conditions are all true.
    583              * 1. Camera is a FULL device
    584              * 2. video size is up to max preview size, which will be bounded by 1080p.
    585              */
    586             if (mStaticInfo.isHardwareLevelFull() &&
    587                     videoSz.getWidth() <= maxPreviewSize.getWidth() &&
    588                     videoSz.getHeight() <= maxPreviewSize.getHeight()) {
    589                 videoSnapshotSz = mOrderedStillSizes.get(0);
    590             }
    591 
    592             createImageReader(
    593                     videoSnapshotSz, ImageFormat.JPEG,
    594                     MAX_VIDEO_SNAPSHOT_IMAGES, /*listener*/null);
    595 
    596             if (VERBOSE) {
    597                 Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
    598             }
    599 
    600             // Configure preview and recording surfaces.
    601             mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
    602             if (DEBUG_DUMP) {
    603                 mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
    604                         + videoSz.toString() + ".mp4";
    605             }
    606 
    607             int numTestIterations = burstTest ? 1 : NUM_SINGLE_SHOT_TEST;
    608             int totalDroppedFrames = 0;
    609 
    610             for (int numTested = 0; numTested < numTestIterations; numTested++) {
    611                 prepareRecordingWithProfile(profile);
    612 
    613                 // prepare video snapshot
    614                 SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
    615                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
    616                 CaptureRequest.Builder videoSnapshotRequestBuilder =
    617                         mCamera.createCaptureRequest((mStaticInfo.isHardwareLevelLegacy()) ?
    618                                 CameraDevice.TEMPLATE_RECORD :
    619                                 CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
    620 
    621                 // prepare preview surface by using video size.
    622                 updatePreviewSurfaceWithVideoSize(videoSz);
    623 
    624                 prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
    625                 CaptureRequest request = videoSnapshotRequestBuilder.build();
    626 
    627                 // Start recording
    628                 startRecording(/* useMediaRecorder */true, resultListener);
    629                 long startTime = SystemClock.elapsedRealtime();
    630 
    631                 // Record certain duration.
    632                 SystemClock.sleep(RECORDING_DURATION_MS / 2);
    633 
    634                 // take video snapshot
    635                 if (burstTest) {
    636                     List<CaptureRequest> requests =
    637                             new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
    638                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
    639                         requests.add(request);
    640                     }
    641                     mSession.captureBurst(requests, resultListener, mHandler);
    642                 } else {
    643                     mSession.capture(request, resultListener, mHandler);
    644                 }
    645 
    646                 // make sure recording is still going after video snapshot
    647                 SystemClock.sleep(RECORDING_DURATION_MS / 2);
    648 
    649                 // Stop recording and preview
    650                 stopRecording(/* useMediaRecorder */true);
    651                 int duration = (int) (SystemClock.elapsedRealtime() - startTime);
    652 
    653                 // Validation recorded video
    654                 validateRecording(videoSz, duration);
    655 
    656                 if (burstTest) {
    657                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
    658                         Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
    659                         validateVideoSnapshotCapture(image, videoSnapshotSz);
    660                         image.close();
    661                     }
    662                 } else {
    663                     // validate video snapshot image
    664                     Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
    665                     validateVideoSnapshotCapture(image, videoSnapshotSz);
    666 
    667                     // validate if there is framedrop around video snapshot
    668                     totalDroppedFrames +=  validateFrameDropAroundVideoSnapshot(
    669                             resultListener, image.getTimestamp());
    670 
    671                     //TODO: validate jittering. Should move to PTS
    672                     //validateJittering(resultListener);
    673 
    674                     image.close();
    675                 }
    676             }
    677 
    678             if (!burstTest) {
    679                 Log.w(TAG, String.format("Camera %d Video size %s: Number of dropped frames " +
    680                         "detected in %d trials is %d frames.", cameraId, videoSz.toString(),
    681                         numTestIterations, totalDroppedFrames));
    682                 mCollector.expectLessOrEqual(
    683                         String.format(
    684                                 "Camera %d Video size %s: Number of dropped frames %d must not"
    685                                 + " be larger than %d",
    686                                 cameraId, videoSz.toString(), totalDroppedFrames,
    687                                 FRAMEDROP_TOLERANCE),
    688                         FRAMEDROP_TOLERANCE, totalDroppedFrames);
    689             }
    690             closeImageReader();
    691         }
    692     }
    693 
    694     /**
    695      * Configure video snapshot request according to the still capture size
    696      */
    697     private void prepareVideoSnapshot(
    698             CaptureRequest.Builder requestBuilder,
    699             ImageReader.OnImageAvailableListener imageListener)
    700             throws Exception {
    701         mReader.setOnImageAvailableListener(imageListener, mHandler);
    702         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
    703         requestBuilder.addTarget(mRecordingSurface);
    704         assertNotNull("Preview surface must be non-null!", mPreviewSurface);
    705         requestBuilder.addTarget(mPreviewSurface);
    706         assertNotNull("Reader surface must be non-null!", mReaderSurface);
    707         requestBuilder.addTarget(mReaderSurface);
    708     }
    709 
    710     /**
    711      * Update preview size with video size.
    712      *
    713      * <p>Preview size will be capped with max preview size.</p>
    714      *
    715      * @param videoSize The video size used for preview.
    716      */
    717     private void updatePreviewSurfaceWithVideoSize(Size videoSize) {
    718         if (mOrderedPreviewSizes == null) {
    719             throw new IllegalStateException("supported preview size list is not initialized yet");
    720         }
    721         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
    722         Size previewSize = videoSize;
    723         if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
    724                 videoSize.getHeight() > maxPreviewSize.getHeight()) {
    725             Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
    726                     " to " + maxPreviewSize.toString());
    727             previewSize = maxPreviewSize;
    728         }
    729 
    730         updatePreviewSurface(previewSize);
    731     }
    732 
    733     /**
    734      * Configure MediaRecorder recording session with CamcorderProfile, prepare
    735      * the recording surface.
    736      */
    737     private void prepareRecordingWithProfile(CamcorderProfile profile)
    738             throws Exception {
    739         // Prepare MediaRecorder.
    740         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    741         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    742         mMediaRecorder.setProfile(profile);
    743         mMediaRecorder.setOutputFile(mOutMediaFileName);
    744         mMediaRecorder.prepare();
    745         mRecordingSurface = mMediaRecorder.getSurface();
    746         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
    747         mVideoFrameRate = profile.videoFrameRate;
    748         mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
    749     }
    750 
    751     /**
    752      * Configure MediaRecorder recording session with CamcorderProfile, prepare
    753      * the recording surface. Use AVC for video compression, AAC for audio compression.
    754      * Both are required for android devices by android CDD.
    755      */
    756     private void prepareRecording(Size sz, int videoFrameRate, int captureRate)
    757             throws Exception {
    758         // Prepare MediaRecorder.
    759         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    760         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    761         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    762         mMediaRecorder.setOutputFile(mOutMediaFileName);
    763         mMediaRecorder.setVideoEncodingBitRate(getVideoBitRate(sz));
    764         mMediaRecorder.setVideoFrameRate(videoFrameRate);
    765         mMediaRecorder.setCaptureRate(captureRate);
    766         mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
    767         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
    768         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    769         mMediaRecorder.prepare();
    770         mRecordingSurface = mMediaRecorder.getSurface();
    771         assertNotNull("Recording surface must be non-null!", mRecordingSurface);
    772         mVideoFrameRate = videoFrameRate;
    773         mVideoSize = sz;
    774     }
    775 
    776     private void startRecording(boolean useMediaRecorder,
    777             CameraCaptureSession.CaptureCallback listener) throws Exception {
    778         List<Surface> outputSurfaces = new ArrayList<Surface>(2);
    779         assertTrue("Both preview and recording surfaces should be valid",
    780                 mPreviewSurface.isValid() && mRecordingSurface.isValid());
    781         outputSurfaces.add(mPreviewSurface);
    782         outputSurfaces.add(mRecordingSurface);
    783         // Video snapshot surface
    784         if (mReaderSurface != null) {
    785             outputSurfaces.add(mReaderSurface);
    786         }
    787         mSessionListener = new BlockingSessionCallback();
    788         mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
    789 
    790         CaptureRequest.Builder recordingRequestBuilder =
    791                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    792         // Make sure camera output frame rate is set to correct value.
    793         Range<Integer> fpsRange = Range.create(mVideoFrameRate, mVideoFrameRate);
    794         recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
    795         recordingRequestBuilder.addTarget(mRecordingSurface);
    796         recordingRequestBuilder.addTarget(mPreviewSurface);
    797         mSession.setRepeatingRequest(recordingRequestBuilder.build(), listener, mHandler);
    798 
    799         if (useMediaRecorder) {
    800             mMediaRecorder.start();
    801         } else {
    802             // TODO: need implement MediaCodec path.
    803         }
    804     }
    805 
    806     private void startRecording(boolean useMediaRecorder)  throws Exception {
    807         startRecording(useMediaRecorder, null);
    808     }
    809 
    810     private void stopCameraStreaming() throws Exception {
    811         if (VERBOSE) {
    812             Log.v(TAG, "Stopping camera streaming and waiting for idle");
    813         }
    814         // Stop repeating, wait for captures to complete, and disconnect from
    815         // surfaces
    816         mSession.close();
    817         mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
    818     }
    819 
    820     private void stopRecording(boolean useMediaRecorder) throws Exception {
    821         if (useMediaRecorder) {
    822             stopCameraStreaming();
    823 
    824             mMediaRecorder.stop();
    825             // Can reuse the MediaRecorder object after reset.
    826             mMediaRecorder.reset();
    827         } else {
    828             // TODO: need implement MediaCodec path.
    829         }
    830         if (mRecordingSurface != null) {
    831             mRecordingSurface.release();
    832             mRecordingSurface = null;
    833         }
    834     }
    835 
    836     private void releaseRecorder() {
    837         if (mMediaRecorder != null) {
    838             mMediaRecorder.release();
    839             mMediaRecorder = null;
    840         }
    841     }
    842 
    843     private void validateRecording(Size sz, int durationMs) throws Exception {
    844         File outFile = new File(mOutMediaFileName);
    845         assertTrue("No video is recorded", outFile.exists());
    846 
    847         MediaPlayer mediaPlayer = new MediaPlayer();
    848         try {
    849             mediaPlayer.setDataSource(mOutMediaFileName);
    850             mediaPlayer.prepare();
    851             Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
    852             assertTrue("Video size doesn't match, expected " + sz.toString() +
    853                     " got " + videoSz.toString(), videoSz.equals(sz));
    854             int duration = mediaPlayer.getDuration();
    855             if (VERBOSE) {
    856                 Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
    857                                          duration, durationMs));
    858             }
    859 
    860             // TODO: Don't skip this for video snapshot
    861             if (!mStaticInfo.isHardwareLevelLegacy()) {
    862                 assertTrue(String.format(
    863                         "Video duration doesn't match: recorded %dms, expected %dms", duration,
    864                         durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
    865             }
    866         } finally {
    867             mediaPlayer.release();
    868             if (!DEBUG_DUMP) {
    869                 outFile.delete();
    870             }
    871         }
    872     }
    873 
    874     /**
    875      * Validate video snapshot capture image object sanity and test.
    876      *
    877      * <p> Check for size, format and jpeg decoding</p>
    878      *
    879      * @param image The JPEG image to be verified.
    880      * @param size The JPEG capture size to be verified against.
    881      */
    882     private void validateVideoSnapshotCapture(Image image, Size size) {
    883         CameraTestUtils.validateImage(image, size.getWidth(), size.getHeight(),
    884                 ImageFormat.JPEG, /*filePath*/null);
    885     }
    886 
    887     /**
    888      * Validate if video snapshot causes frame drop.
    889      * Here frame drop is defined as frame duration >= 2 * expected frame duration.
    890      * Return the estimated number of frames dropped during video snapshot
    891      */
    892     private int validateFrameDropAroundVideoSnapshot(
    893             SimpleCaptureCallback resultListener, long imageTimeStamp) {
    894         int expectedDurationMs = 1000 / mVideoFrameRate;
    895         CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
    896         long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
    897         while (!resultListener.hasMoreResults()) {
    898             CaptureResult currentResult =
    899                     resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
    900             long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
    901             if (currentTS == imageTimeStamp) {
    902                 // validate the timestamp before and after, then return
    903                 CaptureResult nextResult =
    904                         resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
    905                 long nextTS = getValueNotNull(nextResult, CaptureResult.SENSOR_TIMESTAMP);
    906                 int durationMs = (int) (currentTS - prevTS) / 1000000;
    907                 int totalFramesDropped = 0;
    908 
    909                 // Snapshots in legacy mode pause the preview briefly.  Skip the duration
    910                 // requirements for legacy mode unless this is fixed.
    911                 if (!mStaticInfo.isHardwareLevelLegacy()) {
    912                     // Log a warning is there is any frame drop detected.
    913                     if (durationMs >= expectedDurationMs * 2) {
    914                         Log.w(TAG, String.format(
    915                                 "Video %dx%d Frame drop detected before video snapshot: " +
    916                                         "duration %dms (expected %dms)",
    917                                 mVideoSize.getWidth(), mVideoSize.getHeight(),
    918                                 durationMs, expectedDurationMs
    919                         ));
    920                     }
    921 
    922                     durationMs = (int) (nextTS - currentTS) / 1000000;
    923                     // Log a warning is there is any frame drop detected.
    924                     if (durationMs >= expectedDurationMs * 2) {
    925                         Log.w(TAG, String.format(
    926                                 "Video %dx%d Frame drop detected after video snapshot: " +
    927                                         "duration %dms (expected %dms)",
    928                                 mVideoSize.getWidth(), mVideoSize.getHeight(),
    929                                 durationMs, expectedDurationMs
    930                         ));
    931                     }
    932 
    933                     int totalDurationMs = (int) (nextTS - prevTS) / 1000000;
    934                     // Rounding and minus 2 for the expected 2 frames interval
    935                     totalFramesDropped =
    936                             (totalDurationMs + expectedDurationMs / 2) /expectedDurationMs - 2;
    937                     if (totalFramesDropped < 0) {
    938                         Log.w(TAG, "totalFrameDropped is " + totalFramesDropped +
    939                                 ". Video frame rate might be too fast.");
    940                     }
    941                     totalFramesDropped = Math.max(0, totalFramesDropped);
    942                 }
    943                 return totalFramesDropped;
    944             }
    945             prevTS = currentTS;
    946         }
    947         throw new AssertionFailedError(
    948                 "Video snapshot timestamp does not match any of capture results!");
    949     }
    950 
    951     /**
    952      * Validate frame jittering from the input simple listener's buffered results
    953      */
    954     private void validateJittering(SimpleCaptureCallback resultListener) {
    955         int expectedDurationMs = 1000 / mVideoFrameRate;
    956         CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
    957         long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
    958         while (!resultListener.hasMoreResults()) {
    959             CaptureResult currentResult =
    960                     resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
    961             long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
    962             int durationMs = (int) (currentTS - prevTS) / 1000000;
    963             int durationError = Math.abs(durationMs - expectedDurationMs);
    964             long frameNumber = currentResult.getFrameNumber();
    965             mCollector.expectTrue(
    966                     String.format(
    967                             "Resolution %dx%d Frame %d: jittering (%dms) exceeds bound [%dms,%dms]",
    968                             mVideoSize.getWidth(), mVideoSize.getHeight(),
    969                             frameNumber, durationMs,
    970                             expectedDurationMs - FRAME_DURATION_ERROR_TOLERANCE_MS,
    971                             expectedDurationMs + FRAME_DURATION_ERROR_TOLERANCE_MS),
    972                     durationError <= FRAME_DURATION_ERROR_TOLERANCE_MS);
    973             prevTS = currentTS;
    974         }
    975     }
    976 
    977     /**
    978      * Calculate a video bit rate based on the size. The bit rate is scaled
    979      * based on ratio of video size to 1080p size.
    980      */
    981     private int getVideoBitRate(Size sz) {
    982         int rate = BIT_RATE_1080P;
    983         float scaleFactor = sz.getHeight() * sz.getWidth() / (float)(1920 * 1080);
    984         rate = (int)(rate * scaleFactor);
    985 
    986         // Clamp to the MIN, MAX range.
    987         return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
    988     }
    989 
    990     /**
    991      * Check if the encoder and camera are able to support this size and frame rate.
    992      * Assume the video compression format is AVC.
    993      */
    994     private boolean isSupported(Size sz, int captureRate, int encodingRate) throws Exception {
    995         // Check camera capability.
    996         if (!isSupportedByCamera(sz, captureRate)) {
    997             return false;
    998         }
    999 
   1000         // Check encode capability.
   1001         if (!isSupportedByAVCEncoder(sz, encodingRate)){
   1002             return false;
   1003         }
   1004 
   1005         if(VERBOSE) {
   1006             Log.v(TAG, "Both encoder and camera support " + sz.toString() + "@" + encodingRate + "@"
   1007                     + getVideoBitRate(sz) / 1000 + "Kbps");
   1008         }
   1009 
   1010         return true;
   1011     }
   1012 
   1013     private boolean isSupportedByCamera(Size sz, int frameRate) {
   1014         // Check if camera can support this sz and frame rate combination.
   1015         StreamConfigurationMap config = mStaticInfo.
   1016                 getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
   1017 
   1018         long minDuration = config.getOutputMinFrameDuration(MediaRecorder.class, sz);
   1019         if (minDuration == 0) {
   1020             return false;
   1021         }
   1022 
   1023         int maxFrameRate = (int) (1e9f / minDuration);
   1024         return maxFrameRate >= frameRate;
   1025     }
   1026 
   1027     /**
   1028      * Check if encoder can support this size and frame rate combination by querying
   1029      * MediaCodec capability. Check is based on size and frame rate. Ignore the bit rate
   1030      * as the bit rates targeted in this test are well below the bit rate max value specified
   1031      * by AVC specification for certain level.
   1032      */
   1033     private static boolean isSupportedByAVCEncoder(Size sz, int frameRate) {
   1034         String mimeType = "video/avc";
   1035         MediaCodecInfo codecInfo = getEncoderInfo(mimeType);
   1036         if (codecInfo == null) {
   1037             return false;
   1038         }
   1039         CodecCapabilities cap = codecInfo.getCapabilitiesForType(mimeType);
   1040         if (cap == null) {
   1041             return false;
   1042         }
   1043 
   1044         int highestLevel = 0;
   1045         for (CodecProfileLevel lvl : cap.profileLevels) {
   1046             if (lvl.level > highestLevel) {
   1047                 highestLevel = lvl.level;
   1048             }
   1049         }
   1050         // Don't support anything meaningful for level 1 or 2.
   1051         if (highestLevel <= CodecProfileLevel.AVCLevel2) {
   1052             return false;
   1053         }
   1054 
   1055         if(VERBOSE) {
   1056             Log.v(TAG, "The highest level supported by encoder is: " + highestLevel);
   1057         }
   1058 
   1059         // Put bitRate here for future use.
   1060         int maxW, maxH, bitRate;
   1061         // Max encoding speed.
   1062         int maxMacroblocksPerSecond = 0;
   1063         switch(highestLevel) {
   1064             case CodecProfileLevel.AVCLevel21:
   1065                 maxW = 352;
   1066                 maxH = 576;
   1067                 bitRate = 4000000;
   1068                 maxMacroblocksPerSecond = 19800;
   1069                 break;
   1070             case CodecProfileLevel.AVCLevel22:
   1071                 maxW = 720;
   1072                 maxH = 480;
   1073                 bitRate = 4000000;
   1074                 maxMacroblocksPerSecond = 20250;
   1075                 break;
   1076             case CodecProfileLevel.AVCLevel3:
   1077                 maxW = 720;
   1078                 maxH = 480;
   1079                 bitRate = 10000000;
   1080                 maxMacroblocksPerSecond = 40500;
   1081                 break;
   1082             case CodecProfileLevel.AVCLevel31:
   1083                 maxW = 1280;
   1084                 maxH = 720;
   1085                 bitRate = 14000000;
   1086                 maxMacroblocksPerSecond = 108000;
   1087                 break;
   1088             case CodecProfileLevel.AVCLevel32:
   1089                 maxW = 1280;
   1090                 maxH = 720;
   1091                 bitRate = 20000000;
   1092                 maxMacroblocksPerSecond = 216000;
   1093                 break;
   1094             case CodecProfileLevel.AVCLevel4:
   1095                 maxW = 1920;
   1096                 maxH = 1088; // It should be 1088 in terms of AVC capability.
   1097                 bitRate = 20000000;
   1098                 maxMacroblocksPerSecond = 245760;
   1099                 break;
   1100             case CodecProfileLevel.AVCLevel41:
   1101                 maxW = 1920;
   1102                 maxH = 1088; // It should be 1088 in terms of AVC capability.
   1103                 bitRate = 50000000;
   1104                 maxMacroblocksPerSecond = 245760;
   1105                 break;
   1106             case CodecProfileLevel.AVCLevel42:
   1107                 maxW = 2048;
   1108                 maxH = 1088; // It should be 1088 in terms of AVC capability.
   1109                 bitRate = 50000000;
   1110                 maxMacroblocksPerSecond = 522240;
   1111                 break;
   1112             case CodecProfileLevel.AVCLevel5:
   1113                 maxW = 3672;
   1114                 maxH = 1536;
   1115                 bitRate = 135000000;
   1116                 maxMacroblocksPerSecond = 589824;
   1117                 break;
   1118             case CodecProfileLevel.AVCLevel51:
   1119             default:
   1120                 maxW = 4096;
   1121                 maxH = 2304;
   1122                 bitRate = 240000000;
   1123                 maxMacroblocksPerSecond = 983040;
   1124                 break;
   1125         }
   1126 
   1127         // Check size limit.
   1128         if (sz.getWidth() > maxW || sz.getHeight() > maxH) {
   1129             Log.i(TAG, "Requested resolution " + sz.toString() + " exceeds (" +
   1130                     maxW + "," + maxH + ")");
   1131             return false;
   1132         }
   1133 
   1134         // Check frame rate limit.
   1135         Size sizeInMb = new Size((sz.getWidth() + 15) / 16, (sz.getHeight() + 15) / 16);
   1136         int maxFps = maxMacroblocksPerSecond / (sizeInMb.getWidth() * sizeInMb.getHeight());
   1137         if (frameRate > maxFps) {
   1138             Log.i(TAG, "Requested frame rate " + frameRate + " exceeds " + maxFps);
   1139             return false;
   1140         }
   1141 
   1142         return true;
   1143     }
   1144 
   1145     private static MediaCodecInfo getEncoderInfo(String mimeType) {
   1146         int numCodecs = MediaCodecList.getCodecCount();
   1147         for (int i = 0; i < numCodecs; i++) {
   1148             MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
   1149 
   1150             if (!codecInfo.isEncoder()) {
   1151                 continue;
   1152             }
   1153 
   1154             String[] types = codecInfo.getSupportedTypes();
   1155             for (int j = 0; j < types.length; j++) {
   1156                 if (types[j].equalsIgnoreCase(mimeType)) {
   1157                     return codecInfo;
   1158                 }
   1159             }
   1160         }
   1161         return null;
   1162     }
   1163 }
   1164