use of com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback in project android_frameworks_base by crdroidandroid.
the class Camera2RecordingTest method videoSnapshotTestByCamera.
/**
* Test video snapshot for each available CamcorderProfile for a given camera.
*
* <p>
* Preview size is set to the video size. For the burst test, frame drop and jittering
* is not checked.
* </p>
*
* @param burstTest Perform burst capture or single capture. For burst capture
* {@value #BURST_VIDEO_SNAPSHOT_NUM} capture requests will be sent.
*/
private void videoSnapshotTestByCamera(boolean burstTest) throws Exception {
final int NUM_SINGLE_SHOT_TEST = 5;
final int FRAMEDROP_TOLERANCE = 8;
final int FRAME_SIZE_15M = 15000000;
final float FRAME_DROP_TOLERENCE_FACTOR = 1.5f;
int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
for (int profileId : mCamcorderProfileList) {
int cameraId = Integer.parseInt(mCamera.getId());
if (!CamcorderProfile.hasProfile(cameraId, profileId) || allowedUnsupported(cameraId, profileId)) {
continue;
}
CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
if (mStaticInfo.isHardwareLevelLegacy() && (videoSz.getWidth() > maxPreviewSize.getWidth() || videoSz.getHeight() > maxPreviewSize.getHeight())) {
// Skip. Legacy mode can only do recording up to max preview size
continue;
}
if (!mSupportedVideoSizes.contains(videoSz)) {
mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " + profileId + " must be one of the camera device supported video size!");
continue;
}
// For LEGACY, find closest supported smaller or equal JPEG size to the current video
// size; if no size is smaller than the video, pick the smallest JPEG size. The assert
// for video size above guarantees that for LIMITED or FULL, we select videoSz here.
// Also check for minFrameDuration here to make sure jpeg stream won't slow down
// video capture
Size videoSnapshotSz = mOrderedStillSizes.get(mOrderedStillSizes.size() - 1);
// Allow a bit tolerance so we don't fail for a few nano seconds of difference
final float FRAME_DURATION_TOLERANCE = 0.01f;
long videoFrameDuration = (long) (1e9 / profile.videoFrameRate * (1.0 + FRAME_DURATION_TOLERANCE));
HashMap<Size, Long> minFrameDurationMap = mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG);
for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
Size candidateSize = mOrderedStillSizes.get(i);
if (mStaticInfo.isHardwareLevelLegacy()) {
// Legacy level doesn't report min frame duration
if (candidateSize.getWidth() <= videoSz.getWidth() && candidateSize.getHeight() <= videoSz.getHeight()) {
videoSnapshotSz = candidateSize;
}
} else {
Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize, jpegFrameDuration != null);
if (candidateSize.getWidth() <= videoSz.getWidth() && candidateSize.getHeight() <= videoSz.getHeight() && jpegFrameDuration <= videoFrameDuration) {
videoSnapshotSz = candidateSize;
}
}
}
/**
* Only test full res snapshot when below conditions are all true.
* 1. Camera is a FULL device
* 2. video size is up to max preview size, which will be bounded by 1080p.
* 3. Full resolution jpeg stream can keep up to video stream speed.
* When full res jpeg stream cannot keep up to video stream speed, search
* the largest jpeg size that can susptain video speed instead.
*/
if (mStaticInfo.isHardwareLevelFull() && videoSz.getWidth() <= maxPreviewSize.getWidth() && videoSz.getHeight() <= maxPreviewSize.getHeight()) {
for (Size jpegSize : mOrderedStillSizes) {
Long jpegFrameDuration = minFrameDurationMap.get(jpegSize);
assertTrue("Cannot find minimum frame duration for jpeg size " + jpegSize, jpegFrameDuration != null);
if (jpegFrameDuration <= videoFrameDuration) {
videoSnapshotSz = jpegSize;
break;
}
if (jpegSize.equals(videoSz)) {
throw new AssertionFailedError("Cannot find adequate video snapshot size for video size" + videoSz);
}
}
}
Log.i(TAG, "Testing video snapshot size " + videoSnapshotSz + " for video size " + videoSz);
if (videoSnapshotSz.getWidth() * videoSnapshotSz.getHeight() > FRAME_SIZE_15M)
kFrameDrop_Tolerence = (int) (FRAMEDROP_TOLERANCE * FRAME_DROP_TOLERENCE_FACTOR);
createImageReader(videoSnapshotSz, ImageFormat.JPEG, MAX_VIDEO_SNAPSHOT_IMAGES, /*listener*/
null);
if (VERBOSE) {
Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
}
// Configure preview and recording surfaces.
mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
if (DEBUG_DUMP) {
mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_" + videoSz.toString() + ".mp4";
}
int numTestIterations = burstTest ? 1 : NUM_SINGLE_SHOT_TEST;
int totalDroppedFrames = 0;
for (int numTested = 0; numTested < numTestIterations; numTested++) {
prepareRecordingWithProfile(profile);
// prepare video snapshot
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
CaptureRequest.Builder videoSnapshotRequestBuilder = mCamera.createCaptureRequest((mStaticInfo.isHardwareLevelLegacy()) ? CameraDevice.TEMPLATE_RECORD : CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
// prepare preview surface by using video size.
updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
CaptureRequest request = videoSnapshotRequestBuilder.build();
// Start recording
startRecording(/* useMediaRecorder */
true, resultListener, /*useVideoStab*/
false);
long startTime = SystemClock.elapsedRealtime();
// Record certain duration.
SystemClock.sleep(RECORDING_DURATION_MS / 2);
// take video snapshot
if (burstTest) {
List<CaptureRequest> requests = new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
requests.add(request);
}
mSession.captureBurst(requests, resultListener, mHandler);
} else {
mSession.capture(request, resultListener, mHandler);
}
// make sure recording is still going after video snapshot
SystemClock.sleep(RECORDING_DURATION_MS / 2);
// Stop recording and preview
int durationMs = stopRecording(/* useMediaRecorder */
true);
// of frames to estimate duration
if (!burstTest) {
durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f / profile.videoFrameRate);
}
// Validation recorded video
validateRecording(videoSz, durationMs);
if (burstTest) {
for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
validateVideoSnapshotCapture(image, videoSnapshotSz);
image.close();
}
} else {
// validate video snapshot image
Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
validateVideoSnapshotCapture(image, videoSnapshotSz);
// validate if there is framedrop around video snapshot
totalDroppedFrames += validateFrameDropAroundVideoSnapshot(resultListener, image.getTimestamp());
//TODO: validate jittering. Should move to PTS
//validateJittering(resultListener);
image.close();
}
}
if (!burstTest) {
Log.w(TAG, String.format("Camera %d Video size %s: Number of dropped frames " + "detected in %d trials is %d frames.", cameraId, videoSz.toString(), numTestIterations, totalDroppedFrames));
mCollector.expectLessOrEqual(String.format("Camera %d Video size %s: Number of dropped frames %d must not" + " be larger than %d", cameraId, videoSz.toString(), totalDroppedFrames, kFrameDrop_Tolerence), kFrameDrop_Tolerence, totalDroppedFrames);
}
closeImageReader();
}
}
use of com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback in project android_frameworks_base by crdroidandroid.
the class Camera2ReprocessCaptureTest method submitMixedCaptureBurstRequest.
/**
* Submit a burst request mixed with regular and reprocess requests.
*
* @param outputs An array of output surfaces. One output surface will be used in one request
* so the length of the array is the number of requests in a burst request.
* @param inputResults An array of input results. If it's null, all requests are regular
* requests. If an element is null, that element represents a regular
* request. If an element if not null, that element represents a reprocess
* request.
*
*/
private TotalCaptureResult[] submitMixedCaptureBurstRequest(Surface[] outputs, TotalCaptureResult[] inputResults) throws Exception {
if (outputs == null || outputs.length <= 0) {
throw new IllegalArgumentException("outputs must have at least 1 surface");
} else if (inputResults != null && inputResults.length != outputs.length) {
throw new IllegalArgumentException("The lengths of outputs and inputResults " + "don't match");
}
int numReprocessCaptures = 0;
SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
ArrayList<CaptureRequest> captureRequests = new ArrayList<>(outputs.length);
// is based on inputResults array.
for (int i = 0; i < outputs.length; i++) {
CaptureRequest.Builder builder;
boolean isReprocess = (inputResults != null && inputResults[i] != null);
if (isReprocess) {
builder = mCamera.createReprocessCaptureRequest(inputResults[i]);
numReprocessCaptures++;
} else {
builder = mCamera.createCaptureRequest(CAPTURE_TEMPLATE);
}
builder.addTarget(outputs[i]);
CaptureRequest request = builder.build();
assertTrue("Capture request reprocess type " + request.isReprocess() + " is wrong.", request.isReprocess() == isReprocess);
captureRequests.add(request);
}
if (captureRequests.size() == 1) {
mSession.capture(captureRequests.get(0), captureCallback, mHandler);
} else {
mSession.captureBurst(captureRequests, captureCallback, mHandler);
}
TotalCaptureResult[] results;
if (numReprocessCaptures == 0 || numReprocessCaptures == outputs.length) {
results = new TotalCaptureResult[outputs.length];
// If the requests are not mixed, they should come in order.
for (int i = 0; i < results.length; i++) {
results[i] = captureCallback.getTotalCaptureResultForRequest(captureRequests.get(i), CAPTURE_TIMEOUT_FRAMES);
}
} else {
// If the requests are mixed, they may not come in order.
results = captureCallback.getTotalCaptureResultsForRequests(captureRequests, CAPTURE_TIMEOUT_FRAMES * captureRequests.size());
}
// make sure all input surfaces are released.
for (int i = 0; i < numReprocessCaptures; i++) {
mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS);
}
return results;
}
use of com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback in project android_frameworks_base by crdroidandroid.
the class Camera2ReprocessCaptureTest method testReprocessTimestamps.
/**
* Test timestamps for reprocess requests. Reprocess request's shutter timestamp, result's
* sensor timestamp, and output image's timestamp should match the reprocess input's timestamp.
*/
private void testReprocessTimestamps(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat) throws Exception {
if (VERBOSE) {
Log.v(TAG, "testReprocessTimestamps: cameraId: " + cameraId + " inputSize: " + inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat);
}
try {
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, NUM_REPROCESS_CAPTURES);
setupReprocessableSession(/*previewSurface*/
null, NUM_REPROCESS_CAPTURES);
// Prepare reprocess capture requests.
ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(NUM_REPROCESS_CAPTURES);
ArrayList<Long> expectedTimestamps = new ArrayList<>(NUM_REPROCESS_CAPTURES);
for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), /*inputResult*/
null);
mImageWriter.queueInputImage(mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result);
builder.addTarget(getReprocessOutputImageReader().getSurface());
reprocessRequests.add(builder.build());
// Reprocess result's timestamp should match input image's timestamp.
expectedTimestamps.add(result.get(CaptureResult.SENSOR_TIMESTAMP));
}
// Submit reprocess requests.
SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
mSession.captureBurst(reprocessRequests, captureCallback, mHandler);
// Verify we get the expected timestamps.
for (int i = 0; i < reprocessRequests.size(); i++) {
captureCallback.waitForCaptureStart(reprocessRequests.get(i), expectedTimestamps.get(i), CAPTURE_TIMEOUT_FRAMES);
}
TotalCaptureResult[] reprocessResults = captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, CAPTURE_TIMEOUT_FRAMES);
for (int i = 0; i < expectedTimestamps.size(); i++) {
// Verify the result timestamps match the input image's timestamps.
long expected = expectedTimestamps.get(i);
long timestamp = reprocessResults[i].get(CaptureResult.SENSOR_TIMESTAMP);
assertEquals("Reprocess result timestamp (" + timestamp + ") doesn't match input " + "image's timestamp (" + expected + ")", expected, timestamp);
// Verify the reprocess output image timestamps match the input image's timestamps.
Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
timestamp = image.getTimestamp();
image.close();
assertEquals("Reprocess output timestamp (" + timestamp + ") doesn't match input " + "image's timestamp (" + expected + ")", expected, timestamp);
}
// Make sure all input surfaces are released.
for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS);
}
} finally {
closeReprossibleSession();
closeImageReaders();
}
}
use of com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback in project android_frameworks_base by crdroidandroid.
the class Camera2ReprocessCaptureTest method testReprocessJpegExif.
/**
* Test JPEG tags for reprocess requests. Reprocess result's JPEG tags and JPEG image's tags
* match reprocess request's JPEG tags.
*/
private void testReprocessJpegExif(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize) throws Exception {
if (VERBOSE) {
Log.v(TAG, "testReprocessJpegExif: cameraId: " + cameraId + " inputSize: " + inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + reprocessOutputSize);
}
Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked();
Size[] testThumbnailSizes = new Size[EXIF_TEST_DATA.length];
Arrays.fill(testThumbnailSizes, thumbnailSizes[thumbnailSizes.length - 1]);
// Make sure thumbnail size (0, 0) is covered.
testThumbnailSizes[0] = new Size(0, 0);
try {
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, ImageFormat.JPEG, EXIF_TEST_DATA.length);
setupReprocessableSession(/*previewSurface*/
null, EXIF_TEST_DATA.length);
// Prepare reprocess capture requests.
ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(EXIF_TEST_DATA.length);
for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), /*inputResult*/
null);
mImageWriter.queueInputImage(mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result);
builder.addTarget(getReprocessOutputImageReader().getSurface());
// set jpeg keys
setJpegKeys(builder, EXIF_TEST_DATA[i], testThumbnailSizes[i], mCollector);
reprocessRequests.add(builder.build());
}
// Submit reprocess requests.
SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
mSession.captureBurst(reprocessRequests, captureCallback, mHandler);
TotalCaptureResult[] reprocessResults = captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, CAPTURE_TIMEOUT_FRAMES);
for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
// Verify output image's and result's JPEG EXIF data.
Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
verifyJpegKeys(image, reprocessResults[i], reprocessOutputSize, testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector);
image.close();
}
} finally {
closeReprossibleSession();
closeImageReaders();
}
}
use of com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback in project android_frameworks_base by crdroidandroid.
the class Camera2ReprocessCaptureTest method testReprocessRequestKeys.
/**
* Test the following keys in reprocess results match the keys in reprocess requests:
* 1. EDGE_MODE
* 2. NOISE_REDUCTION_MODE
* 3. REPROCESS_EFFECTIVE_EXPOSURE_FACTOR (only for YUV reprocess)
*/
private void testReprocessRequestKeys(String cameraId, Size inputSize, int inputFormat, Size reprocessOutputSize, int reprocessOutputFormat) throws Exception {
if (VERBOSE) {
Log.v(TAG, "testReprocessRequestKeys: cameraId: " + cameraId + " inputSize: " + inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " + reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat);
}
final Integer[] EDGE_MODES = { CaptureRequest.EDGE_MODE_FAST, CaptureRequest.EDGE_MODE_HIGH_QUALITY, CaptureRequest.EDGE_MODE_OFF, CaptureRequest.EDGE_MODE_ZERO_SHUTTER_LAG };
final Integer[] NR_MODES = { CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY, CaptureRequest.NOISE_REDUCTION_MODE_OFF, CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG, CaptureRequest.NOISE_REDUCTION_MODE_FAST };
final Float[] EFFECTIVE_EXP_FACTORS = { null, 1.0f, 2.5f, 4.0f };
int numFrames = EDGE_MODES.length;
try {
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat, numFrames);
setupReprocessableSession(/*previewSurface*/
null, numFrames);
// Prepare reprocess capture requests.
ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(numFrames);
for (int i = 0; i < numFrames; i++) {
TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(), /*inputResult*/
null);
mImageWriter.queueInputImage(mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result);
builder.addTarget(getReprocessOutputImageReader().getSurface());
// Set reprocess request keys
builder.set(CaptureRequest.EDGE_MODE, EDGE_MODES[i]);
builder.set(CaptureRequest.NOISE_REDUCTION_MODE, NR_MODES[i]);
if (inputFormat == ImageFormat.YUV_420_888) {
builder.set(CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR, EFFECTIVE_EXP_FACTORS[i]);
}
reprocessRequests.add(builder.build());
}
// Submit reprocess requests.
SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
mSession.captureBurst(reprocessRequests, captureCallback, mHandler);
TotalCaptureResult[] reprocessResults = captureCallback.getTotalCaptureResultsForRequests(reprocessRequests, CAPTURE_TIMEOUT_FRAMES);
for (int i = 0; i < numFrames; i++) {
// Verify result's keys
Integer resultEdgeMode = reprocessResults[i].get(CaptureResult.EDGE_MODE);
Integer resultNoiseReductionMode = reprocessResults[i].get(CaptureResult.NOISE_REDUCTION_MODE);
assertEquals("Reprocess result edge mode (" + resultEdgeMode + ") doesn't match requested edge mode (" + EDGE_MODES[i] + ")", resultEdgeMode, EDGE_MODES[i]);
assertEquals("Reprocess result noise reduction mode (" + resultNoiseReductionMode + ") doesn't match requested noise reduction mode (" + NR_MODES[i] + ")", resultNoiseReductionMode, NR_MODES[i]);
if (inputFormat == ImageFormat.YUV_420_888) {
Float resultEffectiveExposureFactor = reprocessResults[i].get(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
assertEquals("Reprocess effective exposure factor (" + resultEffectiveExposureFactor + ") doesn't match requested " + "effective exposure factor (" + EFFECTIVE_EXP_FACTORS[i] + ")", resultEffectiveExposureFactor, EFFECTIVE_EXP_FACTORS[i]);
}
}
} finally {
closeReprossibleSession();
closeImageReaders();
}
}
Aggregations