Camera2: various RecordingTest update

1. The duration from MediaPlayer might be longer due to audio track.
   Change it to only check for video track duration
2. Split basic recording test into two. One test resolutions from
   high to low. The other test video frame rate from low to high.
3. Mark two known failing test
4. Also clean up some duplicated code in RobustnessTest.

Bug: 18705837
Bug: 18689511
Change-Id: I855129cf2d2939c783082dccedee56ef88252a16
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index c0875c1..78a0cfe 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -379,5 +379,19 @@
     "com.android.org.conscrypt.SignatureTest#test_getInstance_OpenSSL_ENGINE"
   ],
   bug: 18030049
+},
+{
+  description: "The new recording test is not yet passing on all devices",
+  names: [
+    "android.hardware.camera2.cts.RecordingTest#testRecordingFramerateLowToHigh"
+  ],
+  bug: 18705837
+},
+{
+  description: "The new image reader test is not yet passing on all devices",
+  names: [
+    "android.hardware.camera2.cts.ImageReaderTest#testAllOutputYUVResolutions"
+  ],
+  bug: 18689511
 }
 ]
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 386696c..45d6910 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -30,8 +30,8 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.MediaCodecList;
+import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.MediaPlayer;
 import android.media.MediaRecorder;
 import android.os.Environment;
 import android.os.SystemClock;
@@ -66,15 +66,15 @@
     private static final int VIDEO_FRAME_RATE = 30;
     private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
     private static final int[] mCamcorderProfileList = {
+            CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_2160P,
             CamcorderProfile.QUALITY_1080P,
-            CamcorderProfile.QUALITY_480P,
             CamcorderProfile.QUALITY_720P,
+            CamcorderProfile.QUALITY_480P,
             CamcorderProfile.QUALITY_CIF,
-            CamcorderProfile.QUALITY_LOW,
-            CamcorderProfile.QUALITY_HIGH,
             CamcorderProfile.QUALITY_QCIF,
             CamcorderProfile.QUALITY_QVGA,
+            CamcorderProfile.QUALITY_LOW,
     };
     private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
     private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
@@ -119,7 +119,7 @@
 
                 initSupportedVideoSize(mCameraIds[i]);
 
-                basicRecordingTestByCamera();
+                basicRecordingTestByCamera(mCamcorderProfileList);
             } finally {
                 closeDevice();
                 releaseRecorder();
@@ -222,6 +222,53 @@
     }
 
     /**
+     * <p>
+     * Test recording framerate accuracy when switching from low FPS to high FPS.
+     * </p>
+     * <p>
+     * This test first record a video with profile of lowest framerate then record a video with
+     * profile of highest framerate. Make sure that the video framerate are still accurate.
+     * </p>
+     */
+    public void testRecordingFramerateLowToHigh() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
+                initSupportedVideoSize(mCameraIds[i]);
+
+                int minFpsProfileId = -1, minFps = 1000;
+                int maxFpsProfileId = -1, maxFps = 0;
+                int cameraId = Integer.valueOf(mCamera.getId());
+
+                for (int profileId : mCamcorderProfileList) {
+                    if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+                        continue;
+                    }
+                    CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+                    if (profile.videoFrameRate < minFps) {
+                        minFpsProfileId = profileId;
+                        minFps = profile.videoFrameRate;
+                    }
+                    if (profile.videoFrameRate > maxFps) {
+                        maxFpsProfileId = profileId;
+                        maxFps = profile.videoFrameRate;
+                    }
+                }
+
+                int camcorderProfileList[] = new int[] {minFpsProfileId, maxFpsProfileId};
+                basicRecordingTestByCamera(camcorderProfileList);
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    /**
      * Test slow motion recording where capture rate (camera output) is different with
      * video (playback) frame rate for each camera if high speed recording is supported
      * by both camera and encoder.
@@ -389,8 +436,8 @@
      * Test camera recording by using each available CamcorderProfile for a
      * given camera. preview size is set to the video size.
      */
-    private void basicRecordingTestByCamera() throws Exception {
-        for (int profileId : mCamcorderProfileList) {
+    private void basicRecordingTestByCamera(int[] camcorderProfileList) throws Exception {
+        for (int profileId : camcorderProfileList) {
             int cameraId = Integer.valueOf(mCamera.getId());
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
                     allowedUnsupported(cameraId, profileId)) {
@@ -658,10 +705,11 @@
 
                 // Stop recording and preview
                 stopRecording(/* useMediaRecorder */true);
-                int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+                int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+                        profile.videoFrameRate);
 
                 // Validation recorded video
-                validateRecording(videoSz, duration);
+                validateRecording(videoSz, durationMs);
 
                 if (burstTest) {
                     for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
@@ -854,14 +902,28 @@
         File outFile = new File(mOutMediaFileName);
         assertTrue("No video is recorded", outFile.exists());
 
-        MediaPlayer mediaPlayer = new MediaPlayer();
+        MediaExtractor extractor = new MediaExtractor();
         try {
-            mediaPlayer.setDataSource(mOutMediaFileName);
-            mediaPlayer.prepare();
-            Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
+            extractor.setDataSource(mOutMediaFileName);
+            long durationUs = 0;
+            int width = -1, height = -1;
+            int numTracks = extractor.getTrackCount();
+            final String VIDEO_MIME_TYPE = "video";
+            for (int i = 0; i < numTracks; i++) {
+                MediaFormat format = extractor.getTrackFormat(i);
+                String mime = format.getString(MediaFormat.KEY_MIME);
+                if (mime.contains(VIDEO_MIME_TYPE)) {
+                    Log.i(TAG, "video format is: " + format.toString());
+                    durationUs = format.getLong(MediaFormat.KEY_DURATION);
+                    width = format.getInteger(MediaFormat.KEY_WIDTH);
+                    height = format.getInteger(MediaFormat.KEY_HEIGHT);
+                    break;
+                }
+            }
+            Size videoSz = new Size(width, height);
             assertTrue("Video size doesn't match, expected " + sz.toString() +
                     " got " + videoSz.toString(), videoSz.equals(sz));
-            int duration = mediaPlayer.getDuration();
+            int duration = (int) (durationUs / 1000);
             if (VERBOSE) {
                 Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
                                          duration, durationMs));
@@ -870,12 +932,12 @@
             // TODO: Don't skip this for video snapshot
             if (!mStaticInfo.isHardwareLevelLegacy()) {
                 assertTrue(String.format(
-                        "Camera %s: Video duration doesn't match: recorded %dms, expected %dms",
-                        mCamera.getId(), duration,
-                        durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+                        "Camera %s: Video duration doesn't match: recorded %dms, expected %dms.",
+                        mCamera.getId(), duration, durationMs),
+                        Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
             }
         } finally {
-            mediaPlayer.release();
+            extractor.release();
             if (!DEBUG_DUMP) {
                 outFile.delete();
             }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 30cc58e..397407b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -363,16 +363,6 @@
         }
     }
 
-    private final class ImageCloser implements ImageReader.OnImageAvailableListener {
-        @Override
-        public void onImageAvailable(ImageReader reader) {
-            Image image = reader.acquireLatestImage();
-            if (image != null) {
-                image.close();
-            }
-        }
-    }
-
     private void testOutputCombination(String cameraId, int[] config, MaxOutputSizes maxSizes)
             throws Exception {
 
@@ -382,7 +372,7 @@
         final int TIMEOUT_FOR_RESULT_MS = 1000;
         final int MIN_RESULT_COUNT = 3;
 
-        ImageCloser imageCloser = new ImageCloser();
+        ImageDropperListener imageDropperListener = new ImageDropperListener();
         // Set up outputs
         List<Object> outputTargets = new ArrayList<>();
         List<Surface> outputSurfaces = new ArrayList<>();
@@ -408,7 +398,7 @@
                     Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), JPEG, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     jpegTargets.add(target);
@@ -418,7 +408,7 @@
                     Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), YUV, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     yuvTargets.add(target);
@@ -428,7 +418,7 @@
                     Size targetSize = maxSizes.maxRawSize;
                     ImageReader target = ImageReader.newInstance(
                         targetSize.getWidth(), targetSize.getHeight(), RAW, MIN_RESULT_COUNT);
-                    target.setOnImageAvailableListener(imageCloser, mHandler);
+                    target.setOnImageAvailableListener(imageDropperListener, mHandler);
                     outputTargets.add(target);
                     outputSurfaces.add(target.getSurface());
                     rawTargets.add(target);