Camera: add video frame drop check logic
Bug: 29181095
Change-Id: Id6d613573c1451742bf2db06b8b03702b50d30ff
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 1a09a43..d101036 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -50,6 +50,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.HashMap;
@@ -65,6 +66,9 @@
private static final int RECORDING_DURATION_MS = 3000;
private static final float DURATION_MARGIN = 0.2f;
private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
+ private static final float FRAMEDURATION_MARGIN = 0.2f;
+ private static final float VID_SNPSHT_FRMDRP_RATE_TOLERANCE = 10.0f;
+ private static final float FRMDRP_RATE_TOLERANCE = 5.0f;
private static final int BIT_RATE_1080P = 16000000;
private static final int BIT_RATE_MIN = 64000;
private static final int BIT_RATE_MAX = 40000000;
@@ -417,11 +421,11 @@
// Stop recording and preview
stopRecording(/*useMediaRecorder*/true);
// Convert number of frames camera produced into the duration in unit of ms.
- int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
- videoFramerate);
+ float frameDurationMs = 1000.0f / videoFramerate;
+ float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
// Validation.
- validateRecording(size, durationMs);
+ validateRecording(size, durationMs, frameDurationMs, FRMDRP_RATE_TOLERANCE);
}
} finally {
@@ -484,11 +488,11 @@
// Stop recording and preview
stopRecording(/*useMediaRecorder*/true);
// Convert number of frames camera produced into the duration in unit of ms.
- int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
- VIDEO_FRAME_RATE);
+ float frameDurationMs = 1000.0f / VIDEO_FRAME_RATE;
+ float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
// Validation.
- validateRecording(size, durationMs);
+ validateRecording(size, durationMs, frameDurationMs, FRMDRP_RATE_TOLERANCE);
}
}
@@ -682,8 +686,8 @@
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
// Convert number of frames camera produced into the duration in unit of ms.
- int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
- profile.videoFrameRate);
+ float frameDurationMs = 1000.0f / profile.videoFrameRate;
+ float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
if (VERBOSE) {
Log.v(TAG, "video frame rate: " + profile.videoFrameRate +
@@ -691,7 +695,7 @@
}
// Validation.
- validateRecording(videoSz, durationMs);
+ validateRecording(videoSz, durationMs, frameDurationMs, FRMDRP_RATE_TOLERANCE);
}
if (maxVideoFrameRate != -1) {
// At least one CamcorderProfile is present, check FPS
@@ -737,11 +741,11 @@
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
// Convert number of frames camera produced into the duration in unit of ms.
- int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
- VIDEO_FRAME_RATE);
+ float frameDurationMs = 1000.0f / VIDEO_FRAME_RATE;
+ float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
// Validation.
- validateRecording(sz, durationMs);
+ validateRecording(sz, durationMs, frameDurationMs, FRMDRP_RATE_TOLERANCE);
}
}
@@ -992,17 +996,19 @@
SystemClock.sleep(RECORDING_DURATION_MS / 2);
// Stop recording and preview
- int durationMs = stopRecording(/* useMediaRecorder */true);
+ float durationMs = (float) stopRecording(/* useMediaRecorder */true);
// For non-burst test, use number of frames to also double check video frame rate.
// Burst video snapshot is allowed to cause frame rate drop, so do not use number
// of frames to estimate duration
if (!burstTest) {
- durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
- profile.videoFrameRate);
+ durationMs = resultListener.getTotalNumFrames() * 1000.0f /
+ profile.videoFrameRate;
}
+ float frameDurationMs = 1000.0f / profile.videoFrameRate;
// Validation recorded video
- validateRecording(videoSz, durationMs);
+ validateRecording(videoSz, durationMs,
+ frameDurationMs, VID_SNPSHT_FRMDRP_RATE_TOLERANCE);
if (burstTest) {
for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
@@ -1235,16 +1241,19 @@
}
}
- private void validateRecording(Size sz, int expectedDurationMs) throws Exception {
+ private void validateRecording(
+ Size sz, float expectedDurationMs, float expectedFrameDurationMs,
+ float frameDropTolerance) throws Exception {
File outFile = new File(mOutMediaFileName);
assertTrue("No video is recorded", outFile.exists());
-
+ float maxFrameDuration = expectedFrameDurationMs * (1.0f + FRAMEDURATION_MARGIN);
MediaExtractor extractor = new MediaExtractor();
try {
extractor.setDataSource(mOutMediaFileName);
long durationUs = 0;
int width = -1, height = -1;
int numTracks = extractor.getTrackCount();
+ int selectedTrack = -1;
final String VIDEO_MIME_TYPE = "video";
for (int i = 0; i < numTracks; i++) {
MediaFormat format = extractor.getTrackFormat(i);
@@ -1254,26 +1263,65 @@
durationUs = format.getLong(MediaFormat.KEY_DURATION);
width = format.getInteger(MediaFormat.KEY_WIDTH);
height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ selectedTrack = i;
+ extractor.selectTrack(i);
break;
}
}
+ if (selectedTrack < 0) {
+ throw new AssertionFailedError(
+ "Cannot find video track!");
+ }
+
Size videoSz = new Size(width, height);
assertTrue("Video size doesn't match, expected " + sz.toString() +
" got " + videoSz.toString(), videoSz.equals(sz));
- int duration = (int) (durationUs / 1000);
+ float duration = (float) (durationUs / 1000);
if (VERBOSE) {
- Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
+ Log.v(TAG, String.format("Video duration: recorded %fms, expected %fms",
duration, expectedDurationMs));
}
// 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.",
+ "Camera %s: Video duration doesn't match: recorded %fms, expected %fms.",
mCamera.getId(), duration, expectedDurationMs),
Math.abs(duration - expectedDurationMs) <
DURATION_MARGIN * expectedDurationMs);
}
+
+ // Check for framedrop
+ long lastSampleUs = 0;
+ int frameDropCount = 0;
+ int expectedFrameCount = (int) (expectedDurationMs / expectedFrameDurationMs);
+ ArrayList<Long> timestamps = new ArrayList<Long>(expectedFrameCount);
+ while (true) {
+ timestamps.add(extractor.getSampleTime());
+ if (!extractor.advance()) {
+ break;
+ }
+ }
+ Collections.sort(timestamps);
+ long prevSampleUs = timestamps.get(0);
+ for (int i = 1; i < timestamps.size(); i++) {
+ long currentSampleUs = timestamps.get(i);
+ float frameDurationMs = (float) (currentSampleUs - prevSampleUs) / 1000;
+ if (frameDurationMs > maxFrameDuration) {
+ Log.w(TAG, String.format(
+ "Frame drop at %d: expectation %f, observed %f",
+ i, expectedFrameDurationMs, frameDurationMs));
+ frameDropCount++;
+ }
+ prevSampleUs = currentSampleUs;
+ }
+ float frameDropRate = 100.f * frameDropCount / expectedFrameCount;
+ Log.i(TAG, String.format("Frame drop rate %d/%d (%f%%)",
+ frameDropCount, expectedFrameCount, frameDropRate));
+ assertTrue(String.format(
+ "Camera %s: Video frame drop rate too high: %f%%, tolerance %f%%.",
+ mCamera.getId(), frameDropRate, frameDropTolerance),
+ frameDropRate < frameDropTolerance);
} finally {
extractor.release();
if (!DEBUG_DUMP) {