16d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/* 26d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Copyright 2016 The Android Open Source Project 36d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 46d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Licensed under the Apache License, Version 2.0 (the "License"); 56d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * you may not use this file except in compliance with the License. 66d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * You may obtain a copy of the License at 76d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 86d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * http://www.apache.org/licenses/LICENSE-2.0 96d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Unless required by applicable law or agreed to in writing, software 116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * distributed under the License is distributed on an "AS IS" BASIS, 126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * See the License for the specific language governing permissions and 146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * limitations under the License. 156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimpackage com.android.mediaframeworktest.helpers; 186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingCameraManager; 206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; 216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingSessionCallback; 226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.blocking.BlockingStateCallback; 236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport com.android.ex.camera2.exceptions.TimeoutRuntimeException; 246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport junit.framework.Assert; 266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport org.mockito.Mockito; 286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.Bitmap; 306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.BitmapFactory; 316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.ImageFormat; 326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.PointF; 336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.graphics.Rect; 346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraAccessException; 356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraCaptureSession; 366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraCharacteristics; 376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; 386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraDevice; 396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CameraManager; 406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureFailure; 416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureRequest; 426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.CaptureResult; 436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.TotalCaptureResult; 446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.InputConfiguration; 456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.MeteringRectangle; 466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.hardware.camera2.params.StreamConfigurationMap; 476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.location.Location; 486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.location.LocationManager; 496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ExifInterface; 506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.Image; 516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.Image.Plane; 526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ImageReader; 536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.media.ImageWriter; 546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Build; 556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Environment; 566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.os.Handler; 576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Log; 586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Pair; 596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.util.Size; 606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.Display; 616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.Surface; 626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport android.view.WindowManager; 636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.io.FileOutputStream; 656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.io.IOException; 666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.lang.reflect.Array; 676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.nio.ByteBuffer; 686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.text.ParseException; 696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.text.SimpleDateFormat; 706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.ArrayList; 716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Arrays; 726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Collections; 736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Comparator; 746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.Date; 756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.HashMap; 766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.List; 776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.LinkedBlockingQueue; 786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.Semaphore; 796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.TimeUnit; 806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimimport java.util.concurrent.atomic.AtomicLong; 816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/** 836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * A package private utility class for wrapping up the camera2 framework test common utility 846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * functions 856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim/** 876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * (non-Javadoc) 886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @see android.hardware.camera2.cts.CameraTestUtils 896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kimpublic class CameraTestUtils extends Assert { 916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final String TAG = "CameraTestUtils"; 926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final Size SIZE_BOUND_1080P = new Size(1920, 1088); 956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final Size SIZE_BOUND_2160P = new Size(3840, 2160); 966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Only test the preview size that is no larger than 1080p. 976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final Size PREVIEW_SIZE_BOUND = SIZE_BOUND_1080P; 986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Default timeouts for reaching various states 996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_OPEN_TIMEOUT_MS = 3000; 1006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_CLOSE_TIMEOUT_MS = 3000; 1016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_IDLE_TIMEOUT_MS = 3000; 1026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_ACTIVE_TIMEOUT_MS = 1000; 1036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_BUSY_TIMEOUT_MS = 1000; 1046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_UNCONFIGURED_TIMEOUT_MS = 1000; 1056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAMERA_CONFIGURE_TIMEOUT_MS = 3000; 1066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAPTURE_RESULT_TIMEOUT_MS = 3000; 1076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int CAPTURE_IMAGE_TIMEOUT_MS = 3000; 1086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int SESSION_CONFIGURE_TIMEOUT_MS = 3000; 1106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int SESSION_CLOSE_TIMEOUT_MS = 3000; 1116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int SESSION_READY_TIMEOUT_MS = 3000; 1126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int SESSION_ACTIVE_TIMEOUT_MS = 1000; 1136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final int MAX_READER_IMAGES = 5; 1156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final int EXIF_DATETIME_LENGTH = 19; 1176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60; 1186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f; 1196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final float EXIF_EXPOSURE_TIME_ERROR_MARGIN_RATIO = 0.05f; 1206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final float EXIF_EXPOSURE_TIME_MIN_ERROR_MARGIN_SEC = 0.002f; 1216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final float EXIF_APERTURE_ERROR_MARGIN = 0.001f; 1226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER); 1246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER); 1256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER); 1266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim protected static final String DEBUG_FILE_NAME_BASE = 1286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Environment.getExternalStorageDirectory().getPath(); 1296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static { 1316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation0.setTime(1199145600L); 1326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation0.setLatitude(37.736071); 1336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation0.setLongitude(-122.441983); 1346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation0.setAltitude(21.0); 1356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation1.setTime(1199145601L); 1376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation1.setLatitude(0.736071); 1386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation1.setLongitude(0.441983); 1396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation1.setAltitude(1.0); 1406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation2.setTime(1199145602L); 1426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation2.setLatitude(-89.736071); 1436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation2.setLongitude(-179.441983); 1446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sTestLocation2.setAltitude(100000.0); 1456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 1466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Exif test data vectors. 1486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static final ExifTestData[] EXIF_TEST_DATA = { 1496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new ExifTestData( 1506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*gpsLocation*/ sTestLocation0, 1516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* orientation */90, 1526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* jpgQuality */(byte) 80, 1536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* thumbQuality */(byte) 75), 1546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new ExifTestData( 1556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*gpsLocation*/ sTestLocation1, 1566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* orientation */180, 1576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* jpgQuality */(byte) 90, 1586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* thumbQuality */(byte) 85), 1596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new ExifTestData( 1606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*gpsLocation*/ sTestLocation2, 1616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* orientation */270, 1626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* jpgQuality */(byte) 100, 1636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /* thumbQuality */(byte) 100) 1646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim }; 1656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 1676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Create an {@link ImageReader} object and get the surface. 1686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 1696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param size The size of this ImageReader to be created. 1706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param format The format of this ImageReader to be created 1716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param maxNumImages The max number of images that can be acquired simultaneously. 1726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param listener The listener used by this ImageReader to notify callbacks. 1736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param handler The handler to use for any listener callbacks. 1746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 1756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static ImageReader makeImageReader(Size size, int format, int maxNumImages, 1766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ImageReader.OnImageAvailableListener listener, Handler handler) { 1776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ImageReader reader; 1786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), format, 1796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim maxNumImages); 1806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim reader.setOnImageAvailableListener(listener, handler); 1816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size); 1826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return reader; 1836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 1846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 1856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 1866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Create an ImageWriter and hook up the ImageListener. 1876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 1886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param inputSurface The input surface of the ImageWriter. 1896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param maxImages The max number of Images that can be dequeued simultaneously. 1906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param listener The listener used by this ImageWriter to notify callbacks 1916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param handler The handler to post listener callbacks. 1926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return ImageWriter object created. 1936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 1946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static ImageWriter makeImageWriter( 1956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Surface inputSurface, int maxImages, 1966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ImageWriter.OnImageReleasedListener listener, Handler handler) { 1976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ImageWriter writer = ImageWriter.newInstance(inputSurface, maxImages); 1986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim writer.setOnImageReleasedListener(listener, handler); 1996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return writer; 2006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Close pending images and clean up an {@link ImageReader} object. 2046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param reader an {@link ImageReader} to close. 2056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 2066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void closeImageReader(ImageReader reader) { 2076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (reader != null) { 2086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim reader.close(); 2096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Close pending images and clean up an {@link ImageWriter} object. 2146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param writer an {@link ImageWriter} to close. 2156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 2166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void closeImageWriter(ImageWriter writer) { 2176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (writer != null) { 2186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim writer.close(); 2196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Dummy listener that release the image immediately once it is available. 2246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 2256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 2266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * It can be used for the case where we don't care the image data at all. 2276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 2286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 2296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class ImageDropperListener implements ImageReader.OnImageAvailableListener { 2306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 2316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onImageAvailable(ImageReader reader) { 2326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Image image = null; 2336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 2346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim image = reader.acquireNextImage(); 2356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } finally { 2366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (image != null) { 2376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim image.close(); 2386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Image listener that release the image immediately after validating the image 2456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 2466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class ImageVerifierListener implements ImageReader.OnImageAvailableListener { 2476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private Size mSize; 2486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private int mFormat; 2496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public ImageVerifierListener(Size sz, int format) { 2516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mSize = sz; 2526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mFormat = format; 2536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 2566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onImageAvailable(ImageReader reader) { 2576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Image image = null; 2586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 2596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim image = reader.acquireNextImage(); 2606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } finally { 2616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (image != null) { 2626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateImage(image, mSize.getWidth(), mSize.getHeight(), mFormat, null); 2636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim image.close(); 2646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class SimpleImageReaderListener 2706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim implements ImageReader.OnImageAvailableListener { 2716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final LinkedBlockingQueue<Image> mQueue = 2726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new LinkedBlockingQueue<Image>(); 2736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Indicate whether this listener will drop images or not, 2746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // when the queued images reaches the reader maxImages 2756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final boolean mAsyncMode; 2766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // maxImages held by the queue in async mode. 2776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final int mMaxImages; 2786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Create a synchronous SimpleImageReaderListener that queues the images 2816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * automatically when they are available, no image will be dropped. If 2826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the caller doesn't call getImage(), the producer will eventually run 2836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * into buffer starvation. 2846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 2856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public SimpleImageReaderListener() { 2866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mAsyncMode = false; 2876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mMaxImages = 0; 2886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 2896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 2906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 2916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Create a synchronous/asynchronous SimpleImageReaderListener that 2926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * queues the images automatically when they are available. For 2936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * asynchronous listener, image will be dropped if the queued images 2946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * reach to maxImages queued. If the caller doesn't call getImage(), the 2956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * producer will not be blocked. For synchronous listener, no image will 2966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * be dropped. If the caller doesn't call getImage(), the producer will 2976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * eventually run into buffer starvation. 2986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 2996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param asyncMode If the listener is operating at asynchronous mode. 3006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param maxImages The max number of images held by this listener. 3016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 3026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 3036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 3046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param asyncMode 3056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 3066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public SimpleImageReaderListener(boolean asyncMode, int maxImages) { 3076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mAsyncMode = asyncMode; 3086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mMaxImages = maxImages; 3096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 3126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onImageAvailable(ImageReader reader) { 3136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 3146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mQueue.put(reader.acquireNextImage()); 3156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (mAsyncMode && mQueue.size() >= mMaxImages) { 3166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Image img = mQueue.poll(); 3176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim img.close(); 3186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 3206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException( 3216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Can't handle InterruptedException in onImageAvailable"); 3226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 3266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get an image from the image reader. 3276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 3286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param timeout Timeout value for the wait. 3296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return The image from the image reader. 3306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 3316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public Image getImage(long timeout) throws InterruptedException { 3326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS); 3336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Wait for an image timed out in " + timeout + "ms", image); 3346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return image; 3356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 3386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Drain the pending images held by this listener currently. 3396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 3406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 3416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void drain() { 3426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim while (!mQueue.isEmpty()) { 3436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Image image = mQueue.poll(); 3446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Unable to get an image", image); 3456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim image.close(); 3466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class SimpleImageWriterListener implements ImageWriter.OnImageReleasedListener { 3516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final Semaphore mImageReleasedSema = new Semaphore(0); 3526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final ImageWriter mWriter; 3536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 3546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onImageReleased(ImageWriter writer) { 3556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (writer != mWriter) { 3566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 3576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 3606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "Input image is released"); 3616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mImageReleasedSema.release(); 3636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public SimpleImageWriterListener(ImageWriter writer) { 3666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (writer == null) { 3676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("writer cannot be null"); 3686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mWriter = writer; 3706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void waitForImageReleased(long timeoutMs) throws InterruptedException { 3736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (!mImageReleasedSema.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) { 3746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim fail("wait for image available timed out after " + timeoutMs + "ms"); 3756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback { 3806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final LinkedBlockingQueue<TotalCaptureResult> mQueue = 3816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new LinkedBlockingQueue<TotalCaptureResult>(); 3826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final LinkedBlockingQueue<CaptureFailure> mFailureQueue = 3836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new LinkedBlockingQueue<>(); 3846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Pair<CaptureRequest, Long> is a pair of capture request and timestamp. 3856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private final LinkedBlockingQueue<Pair<CaptureRequest, Long>> mCaptureStartQueue = 3866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new LinkedBlockingQueue<>(); 3876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private AtomicLong mNumFramesArrived = new AtomicLong(0); 3896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 3906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 3916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 3926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long timestamp, long frameNumber) { 3936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 3946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mCaptureStartQueue.put(new Pair(request, timestamp)); 3956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 3966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException( 3976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Can't handle InterruptedException in onCaptureStarted"); 3986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 3996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 4026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, 4036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TotalCaptureResult result) { 4046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 4056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mNumFramesArrived.incrementAndGet(); 4066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mQueue.put(result); 4076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 4086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException( 4096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Can't handle InterruptedException in onCaptureCompleted"); 4106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 4146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 4156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CaptureFailure failure) { 4166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 4176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mFailureQueue.put(failure); 4186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 4196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException( 4206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Can't handle InterruptedException in onCaptureFailed"); 4216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 4256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId, 4266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long frameNumber) { 4276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public long getTotalNumFrames() { 4306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return mNumFramesArrived.get(); 4316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public CaptureResult getCaptureResult(long timeout) { 4346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getTotalCaptureResult(timeout); 4356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public TotalCaptureResult getCaptureResult(long timeout, long timestamp) { 4386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 4396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long currentTs = -1L; 4406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TotalCaptureResult result; 4416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim while (true) { 4426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim result = mQueue.poll(timeout, TimeUnit.MILLISECONDS); 4436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (result == null) { 4446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new RuntimeException( 4456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Wait for a capture result timed out in " + timeout + "ms"); 4466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim currentTs = result.get(CaptureResult.SENSOR_TIMESTAMP); 4486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (currentTs == timestamp) { 4496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return result; 4506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 4546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException("Unhandled interrupted exception", e); 4556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public TotalCaptureResult getTotalCaptureResult(long timeout) { 4596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 4606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TotalCaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS); 4616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result); 4626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return result; 4636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 4646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException("Unhandled interrupted exception", e); 4656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 4696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the {@link #CaptureResult capture result} for a given 4706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * {@link #CaptureRequest capture request}. 4716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 4726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param myRequest The {@link #CaptureRequest capture request} whose 4736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * corresponding {@link #CaptureResult capture result} was 4746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * being waited for 4756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param numResultsWait Number of frames to wait for the capture result 4766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * before timeout. 4776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws TimeoutRuntimeException If more than numResultsWait results are 4786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * seen before the result matching myRequest arrives, or each 4796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * individual wait for result times out after 4806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * {@value #CAPTURE_RESULT_TIMEOUT_MS}ms. 4816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 4826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public CaptureResult getCaptureResultForRequest(CaptureRequest myRequest, 4836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int numResultsWait) { 4846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getTotalCaptureResultForRequest(myRequest, numResultsWait); 4856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 4866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 4876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 4886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the {@link #TotalCaptureResult total capture result} for a given 4896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * {@link #CaptureRequest capture request}. 4906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 4916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param myRequest The {@link #CaptureRequest capture request} whose 4926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * corresponding {@link #TotalCaptureResult capture result} was 4936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * being waited for 4946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param numResultsWait Number of frames to wait for the capture result 4956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * before timeout. 4966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws TimeoutRuntimeException If more than numResultsWait results are 4976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * seen before the result matching myRequest arrives, or each 4986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * individual wait for result times out after 4996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * {@value #CAPTURE_RESULT_TIMEOUT_MS}ms. 5006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 5016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public TotalCaptureResult getTotalCaptureResultForRequest(CaptureRequest myRequest, 5026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int numResultsWait) { 5036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ArrayList<CaptureRequest> captureRequests = new ArrayList<>(1); 5046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim captureRequests.add(myRequest); 5056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getTotalCaptureResultsForRequests(captureRequests, numResultsWait)[0]; 5066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 5096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get an array of {@link #TotalCaptureResult total capture results} for a given list of 5106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * {@link #CaptureRequest capture requests}. This can be used when the order of results 5116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * may not the same as the order of requests. 5126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 5136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param captureRequests The list of {@link #CaptureRequest capture requests} whose 5146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * corresponding {@link #TotalCaptureResult capture results} are 5156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * being waited for. 5166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param numResultsWait Number of frames to wait for the capture results 5176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * before timeout. 5186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws TimeoutRuntimeException If more than numResultsWait results are 5196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * seen before all the results matching captureRequests arrives. 5206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 5216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public TotalCaptureResult[] getTotalCaptureResultsForRequests( 5226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<CaptureRequest> captureRequests, int numResultsWait) { 5236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (numResultsWait < 0) { 5246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("numResultsWait must be no less than 0"); 5256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (captureRequests == null || captureRequests.size() == 0) { 5276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("captureRequests must have at least 1 request."); 5286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Create a request -> a list of result indices map that it will wait for. 5316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim HashMap<CaptureRequest, ArrayList<Integer>> remainingResultIndicesMap = new HashMap<>(); 5326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < captureRequests.size(); i++) { 5336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CaptureRequest request = captureRequests.get(i); 5346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ArrayList<Integer> indices = remainingResultIndicesMap.get(request); 5356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (indices == null) { 5366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim indices = new ArrayList<>(); 5376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim remainingResultIndicesMap.put(request, indices); 5386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim indices.add(i); 5406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TotalCaptureResult[] results = new TotalCaptureResult[captureRequests.size()]; 5436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int i = 0; 5446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim do { 5456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TotalCaptureResult result = getTotalCaptureResult(CAPTURE_RESULT_TIMEOUT_MS); 5466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CaptureRequest request = result.getRequest(); 5476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ArrayList<Integer> indices = remainingResultIndicesMap.get(request); 5486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (indices != null) { 5496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim results[indices.get(0)] = result; 5506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim indices.remove(0); 5516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Remove the entry if all results for this request has been fulfilled. 5536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (indices.isEmpty()) { 5546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim remainingResultIndicesMap.remove(request); 5556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (remainingResultIndicesMap.isEmpty()) { 5596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return results; 5606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } while (i++ < numResultsWait); 5626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new TimeoutRuntimeException("Unable to get the expected capture result after " 5646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + "waiting for " + numResultsWait + " results"); 5656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 5686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get an array list of {@link #CaptureFailure capture failure} with maxNumFailures entries 5696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * at most. If it times out before maxNumFailures failures are received, return the failures 5706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * received so far. 5716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 5726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param maxNumFailures The maximal number of failures to return. If it times out before 5736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the maximal number of failures are received, return the received 5746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * failures so far. 5756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws UnsupportedOperationException If an error happens while waiting on the failure. 5766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 5776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public ArrayList<CaptureFailure> getCaptureFailures(long maxNumFailures) { 5786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ArrayList<CaptureFailure> failures = new ArrayList<>(); 5796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 5806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < maxNumFailures; i++) { 5816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CaptureFailure failure = mFailureQueue.poll(CAPTURE_RESULT_TIMEOUT_MS, 5826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim TimeUnit.MILLISECONDS); 5836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (failure == null) { 5846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // If waiting on a failure times out, return the failures so far. 5856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 5866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim failures.add(failure); 5886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (InterruptedException e) { 5906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException("Unhandled interrupted exception", e); 5916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return failures; 5946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 5956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 5966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 5976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Wait until the capture start of a request and expected timestamp arrives or it times 5986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * out after a number of capture starts. 5996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param request The request for the capture start to wait for. 6016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param timestamp The timestamp for the capture start to wait for. 6026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param numCaptureStartsWait The number of capture start events to wait for before timing 6036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * out. 6046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 6056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void waitForCaptureStart(CaptureRequest request, Long timestamp, 6066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int numCaptureStartsWait) throws Exception { 6076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Pair<CaptureRequest, Long> expectedShutter = new Pair<>(request, timestamp); 6086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int i = 0; 6106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim do { 6116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Pair<CaptureRequest, Long> shutter = mCaptureStartQueue.poll( 6126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CAPTURE_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 6136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (shutter == null) { 6156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new TimeoutRuntimeException("Unable to get any more capture start " + 6166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "event after waiting for " + CAPTURE_RESULT_TIMEOUT_MS + " ms."); 6176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else if (expectedShutter.equals(shutter)) { 6186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 6196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } while (i++ < numCaptureStartsWait); 6226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new TimeoutRuntimeException("Unable to get the expected capture start " + 6246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "event after waiting for " + numCaptureStartsWait + " capture starts"); 6256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public boolean hasMoreResults() 6286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim { 6296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return mQueue.isEmpty(); 6306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void drain() { 6336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mQueue.clear(); 6346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mNumFramesArrived.getAndSet(0); 6356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mFailureQueue.clear(); 6366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim mCaptureStartQueue.clear(); 6376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 6416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Block until the camera is opened. 6426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Don't use this to test #onDisconnected/#onError since this will throw 6446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * an AssertionError if it fails to open the camera device.</p> 6456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return CameraDevice opened camera device 6476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws IllegalArgumentException 6496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If the handler is null, or if the handler's looper is current. 6506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws CameraAccessException 6516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If open fails immediately. 6526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws BlockingOpenException 6536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If open fails after blocking for some amount of time. 6546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws TimeoutRuntimeException 6556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If opening times out. Typically unrecoverable. 6566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 6576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static CameraDevice openCamera(CameraManager manager, String cameraId, 6586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraDevice.StateCallback listener, Handler handler) throws CameraAccessException, 6596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BlockingOpenException { 6606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 6626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Although camera2 API allows 'null' Handler (it will just use the current 6636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * thread's Looper), this is not what we want for CTS. 6646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * In Camera framework test the default looper is used only to process events 6666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * in between test runs, 6676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * so anything sent there would not be executed inside a test and the test would fail. 6686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * In this case, BlockingCameraManager#openCamera performs the check for us. 6706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 6716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return (new BlockingCameraManager(manager)).openCamera(cameraId, listener, handler); 6726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 6766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Block until the camera is opened. 6776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Don't use this to test #onDisconnected/#onError since this will throw 6796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * an AssertionError if it fails to open the camera device.</p> 6806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws IllegalArgumentException 6826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If the handler is null, or if the handler's looper is current. 6836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws CameraAccessException 6846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If open fails immediately. 6856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws BlockingOpenException 6866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If open fails after blocking for some amount of time. 6876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws TimeoutRuntimeException 6886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * If opening times out. Typically unrecoverable. 6896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 6906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static CameraDevice openCamera(CameraManager manager, String cameraId, Handler handler) 6916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException, 6926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BlockingOpenException { 6936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return openCamera(manager, cameraId, /*listener*/null, handler); 6946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 6956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 6966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 6976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Configure a new camera session with output surfaces and type. 6986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 6996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param camera The CameraDevice to be configured. 7006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param outputSurfaces The surface list that used for camera output. 7016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param listener The callback CameraDevice will notify when capture results are available. 7026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 7036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static CameraCaptureSession configureCameraSession(CameraDevice camera, 7046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Surface> outputSurfaces, boolean isHighSpeed, 7056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCaptureSession.StateCallback listener, Handler handler) 7066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 7076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener); 7086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (isHighSpeed) { 7096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim camera.createConstrainedHighSpeedCaptureSession(outputSurfaces, 7106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sessionListener, handler); 7116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else { 7126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim camera.createCaptureSession(outputSurfaces, sessionListener, handler); 7136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCaptureSession session = 7156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS); 7166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertFalse("Camera session should not be a reprocessable session", 7176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim session.isReprocessable()); 7186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String sessionType = isHighSpeed ? "High Speed" : "Normal"; 7196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Capture session type must be " + sessionType, 7206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim isHighSpeed == 7216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraConstrainedHighSpeedCaptureSession.class.isAssignableFrom(session.getClass())); 7226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return session; 7246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 7276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Configure a new camera session with output surfaces. 7286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 7296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param camera The CameraDevice to be configured. 7306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param outputSurfaces The surface list that used for camera output. 7316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param listener The callback CameraDevice will notify when capture results are available. 7326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 7336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static CameraCaptureSession configureCameraSession(CameraDevice camera, 7346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Surface> outputSurfaces, 7356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCaptureSession.StateCallback listener, Handler handler) 7366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 7376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return configureCameraSession(camera, outputSurfaces, /*isHighSpeed*/false, 7396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim listener, handler); 7406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static CameraCaptureSession configureReprocessableCameraSession(CameraDevice camera, 7436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim InputConfiguration inputConfiguration, List<Surface> outputSurfaces, 7446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCaptureSession.StateCallback listener, Handler handler) 7456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 7466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener); 7476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim camera.createReprocessableCaptureSession(inputConfiguration, outputSurfaces, 7486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sessionListener, handler); 7496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY, 7516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BlockingSessionCallback.SESSION_CONFIGURE_FAILED}; 7526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int state = sessionListener.getStateWaiter().waitForAnyOfStates( 7536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Arrays.asList(sessionStates), SESSION_CONFIGURE_TIMEOUT_MS); 7546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Creating a reprocessable session failed.", 7566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim state == BlockingSessionCallback.SESSION_READY); 7576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCaptureSession session = 7596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS); 7606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Camera session should be a reprocessable session", session.isReprocessable()); 7616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return session; 7636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static <T> void assertArrayNotEmpty(T arr, String message) { 7666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue(message, arr != null && Array.getLength(arr) > 0); 7676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 7706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Check if the format is a legal YUV format camera supported. 7716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 7726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void checkYuvFormat(int format) { 7736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if ((format != ImageFormat.YUV_420_888) && 7746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim (format != ImageFormat.NV21) && 7756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim (format != ImageFormat.YV12)) { 7766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim fail("Wrong formats: " + format); 7776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 7816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Check if image size and format match given size and format. 7826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 7836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void checkImage(Image image, int width, int height, int format) { 7846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Image reader will wrap YV12/NV21 image by YUV_420_888 7856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (format == ImageFormat.NV21 || format == ImageFormat.YV12) { 7866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim format = ImageFormat.YUV_420_888; 7876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Input image is invalid", image); 7896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Format doesn't match", format, image.getFormat()); 7906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Width doesn't match", width, image.getWidth()); 7916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Height doesn't match", height, image.getHeight()); 7926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 7936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 7946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 7956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Read data from all planes of an Image into a contiguous unpadded, unpacked 7966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 1-D linear byte array, such that it can be write into disk, or accessed by 7976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * software conveniently. It supports YUV_420_888/NV21/YV12 and JPEG input 7986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Image format.</p> 7996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 8006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains 8016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the Y plane data first, followed by U(Cb), V(Cr) planes if there is any 8026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * (xstride = width, ystride = height for chroma and luma components).</p> 8036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 8046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>For JPEG, it returns a 1-D byte array contains a complete JPEG image.</p> 8056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 8066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static byte[] getDataFromImage(Image image) { 8076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Invalid image:", image); 8086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int format = image.getFormat(); 8096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int width = image.getWidth(); 8106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int height = image.getHeight(); 8116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int rowStride, pixelStride; 8126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim byte[] data = null; 8136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 8146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Read image data 8156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] planes = image.getPlanes(); 8166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Fail to get image planes", planes != null && planes.length > 0); 8176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 8186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Check image validity 8196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim checkAndroidImageFormat(image); 8206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 8216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ByteBuffer buffer = null; 8226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer. 8236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Same goes for DEPTH_POINT_CLOUD 8246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD || 8256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim format == ImageFormat.RAW_PRIVATE) { 8266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer = planes[0].getBuffer(); 8276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer); 8286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim data = new byte[buffer.remaining()]; 8296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.get(data); 8306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.rewind(); 8316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return data; 8326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 8346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int offset = 0; 8356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8]; 8366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int maxRowSize = planes[0].getRowStride(); 8376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < planes.length; i++) { 8386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (maxRowSize < planes[i].getRowStride()) { 8396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim maxRowSize = planes[i].getRowStride(); 8406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim byte[] rowData = new byte[maxRowSize]; 8436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes"); 8446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < planes.length; i++) { 8456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer = planes[i].getBuffer(); 8466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Fail to get bytebuffer from plane", buffer); 8476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim rowStride = planes[i].getRowStride(); 8486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim pixelStride = planes[i].getPixelStride(); 8496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0); 8506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 8516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "pixelStride " + pixelStride); 8526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "rowStride " + rowStride); 8536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "width " + width); 8546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "height " + height); 8556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling. 8576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int w = (i == 0) ? width : width / 2; 8586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int h = (i == 0) ? height : height / 2; 8596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w); 8606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int row = 0; row < h; row++) { 8616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8; 8626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int length; 8636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (pixelStride == bytesPerPixel) { 8646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Special case: optimized read of the entire row 8656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim length = w * bytesPerPixel; 8666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.get(data, offset, length); 8676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim offset += length; 8686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else { 8696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Generic case: should work for any pixelStride but slower. 8706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Use intermediate buffer to avoid read byte-by-byte from 8716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // DirectByteBuffer, which is very bad for performance 8726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim length = (w - 1) * pixelStride + bytesPerPixel; 8736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.get(rowData, 0, length); 8746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int col = 0; col < w; col++) { 8756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim data[offset++] = rowData[col * pixelStride]; 8766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Advance buffer the remainder of the row stride 8796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (row < h - 1) { 8806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.position(buffer.position() + rowStride - length); 8816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i); 8846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim buffer.rewind(); 8856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return data; 8876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 8886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 8896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 8906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Check android image format validity for an image, only support below formats:</p> 8916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 8926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>YUV_420_888/NV21/YV12, can add more for future</p> 8936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 8946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void checkAndroidImageFormat(Image image) { 8956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int format = image.getFormat(); 8966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] planes = image.getPlanes(); 8976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim switch (format) { 8986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.YUV_420_888: 8996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.NV21: 9006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.YV12: 9016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("YUV420 format Images should have 3 planes", 3, planes.length); 9026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 9036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.JPEG: 9046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.RAW_SENSOR: 9056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.RAW_PRIVATE: 9066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.DEPTH16: 9076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.DEPTH_POINT_CLOUD: 9086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("JPEG/RAW/depth Images should have one plane", 1, planes.length); 9096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 9106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim default: 9116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim fail("Unsupported Image Format: " + format); 9126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void dumpFile(String fileName, Bitmap data) { 9166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim FileOutputStream outStream; 9176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 9186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "output will be saved as " + fileName); 9196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim outStream = new FileOutputStream(fileName); 9206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (IOException ioe) { 9216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new RuntimeException("Unable to create debug output file " + fileName, ioe); 9226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 9256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim data.compress(Bitmap.CompressFormat.JPEG, /*quality*/90, outStream); 9266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim outStream.close(); 9276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (IOException ioe) { 9286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new RuntimeException("failed writing data to file " + fileName, ioe); 9296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void dumpFile(String fileName, byte[] data) { 9336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim FileOutputStream outStream; 9346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 9356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "output will be saved as " + fileName); 9366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim outStream = new FileOutputStream(fileName); 9376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (IOException ioe) { 9386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new RuntimeException("Unable to create debug output file " + fileName, ioe); 9396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim try { 9426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim outStream.write(data); 9436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim outStream.close(); 9446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } catch (IOException ioe) { 9456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new RuntimeException("failed writing data to file " + fileName, ioe); 9466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 9506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the available output sizes for the user-defined {@code format}. 9516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 9526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Note that implementation-defined/hidden formats are not supported.</p> 9536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 9546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Size[] getSupportedSizeForFormat(int format, String cameraId, 9556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager) throws CameraAccessException { 9566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId); 9576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Can't get camera characteristics!", properties); 9586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 9596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "get camera characteristics for camera: " + cameraId); 9606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim StreamConfigurationMap configMap = 9626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 9636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] availableSizes = configMap.getOutputSizes(format); 9646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for format: " 9656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + format); 9666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(format); 9676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (highResAvailableSizes != null && highResAvailableSizes.length > 0) { 9686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length]; 9696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim System.arraycopy(availableSizes, 0, allSizes, 0, 9706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim availableSizes.length); 9716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length, 9726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim highResAvailableSizes.length); 9736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim availableSizes = allSizes; 9746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes)); 9766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return availableSizes; 9776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 9796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 9806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the available output sizes for the given class. 9816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 9826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 9836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Size[] getSupportedSizeForClass(Class klass, String cameraId, 9846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager) throws CameraAccessException { 9856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId); 9866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Can't get camera characteristics!", properties); 9876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 9886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "get camera characteristics for camera: " + cameraId); 9896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 9906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim StreamConfigurationMap configMap = 9916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 9926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] availableSizes = configMap.getOutputSizes(klass); 9936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertArrayNotEmpty(availableSizes, "availableSizes should not be empty for class: " 9946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + klass); 9956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] highResAvailableSizes = configMap.getHighResolutionOutputSizes(ImageFormat.PRIVATE); 9966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (highResAvailableSizes != null && highResAvailableSizes.length > 0) { 9976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] allSizes = new Size[availableSizes.length + highResAvailableSizes.length]; 9986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim System.arraycopy(availableSizes, 0, allSizes, 0, 9996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim availableSizes.length); 10006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim System.arraycopy(highResAvailableSizes, 0, allSizes, availableSizes.length, 10016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim highResAvailableSizes.length); 10026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim availableSizes = allSizes; 10036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes)); 10056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return availableSizes; 10066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 10096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Size comparator that compares the number of pixels it covers. 10106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 10116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>If two the areas of two sizes are same, compare the widths.</p> 10126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 10136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class SizeComparator implements Comparator<Size> { 10146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 10156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public int compare(Size lhs, Size rhs) { 10166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return CameraUtils 10176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim .compareSizes(lhs.getWidth(), lhs.getHeight(), rhs.getWidth(), rhs.getHeight()); 10186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 10226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get sorted size list in descending order. Remove the sizes larger than 10236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the bound. If the bound is null, don't do the size bound filtering. 10246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 10256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public List<Size> getSupportedPreviewSizes(String cameraId, 10266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager, Size bound) throws CameraAccessException { 10276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] rawSizes = getSupportedSizeForClass(android.view.SurfaceHolder.class, cameraId, 10296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim cameraManager); 10306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertArrayNotEmpty(rawSizes, 10316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Available sizes for SurfaceHolder class should not be empty"); 10326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 10336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes)); 10346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (bound == null) { 10376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false); 10386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sizes = new ArrayList<Size>(); 10416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (Size sz: rawSizes) { 10426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) { 10436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sizes.add(sz); 10446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getAscendingOrderSizes(sizes, /*ascending*/false); 10476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 10506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get a sorted list of sizes from a given size list. 10516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 10526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 10536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * The size is compare by area it covers, if the areas are same, then 10546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * compare the widths. 10556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 10566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 10576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param sizeList The input size list to be sorted 10586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param ascending True if the order is ascending, otherwise descending order 10596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return The ordered list of sizes 10606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 10616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public List<Size> getAscendingOrderSizes(final List<Size> sizeList, boolean ascending) { 10626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (sizeList == null) { 10636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("sizeList shouldn't be null"); 10646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Comparator<Size> comparator = new SizeComparator(); 10676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sortedSizes = new ArrayList<Size>(); 10686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sortedSizes.addAll(sizeList); 10696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Collections.sort(sortedSizes, comparator); 10706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (!ascending) { 10716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Collections.reverse(sortedSizes); 10726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sortedSizes; 10756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 10786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get sorted (descending order) size list for given format. Remove the sizes larger than 10796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the bound. If the bound is null, don't do the size bound filtering. 10806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 10816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public List<Size> getSortedSizesForFormat(String cameraId, 10826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager, int format, Size bound) throws CameraAccessException { 10836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Comparator<Size> comparator = new SizeComparator(); 10846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager); 10856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sortedSizes = null; 10866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (bound != null) { 10876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sortedSizes = new ArrayList<Size>(/*capacity*/1); 10886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (Size sz : sizes) { 10896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (comparator.compare(sz, bound) <= 0) { 10906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sortedSizes.add(sz); 10916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else { 10946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sortedSizes = Arrays.asList(sizes); 10956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 10966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Supported size list should have at least one element", 10976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sortedSizes.size() > 0); 10986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 10996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Collections.sort(sortedSizes, comparator); 11006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Make it in descending order. 11016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Collections.reverse(sortedSizes); 11026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sortedSizes; 11036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get supported video size list for a given camera device. 11076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 11086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 11096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Filter out the sizes that are larger than the bound. If the bound is 11106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * null, don't do the size bound filtering. 11116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 11126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public List<Size> getSupportedVideoSizes(String cameraId, 11146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager, Size bound) throws CameraAccessException { 11156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size[] rawSizes = getSupportedSizeForClass(android.media.MediaRecorder.class, 11176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim cameraId, cameraManager); 11186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertArrayNotEmpty(rawSizes, 11196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Available sizes for MediaRecorder class should not be empty"); 11206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) { 11216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes)); 11226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (bound == null) { 11256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false); 11266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sizes = new ArrayList<Size>(); 11296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (Size sz: rawSizes) { 11306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) { 11316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sizes.add(sz); 11326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getAscendingOrderSizes(sizes, /*ascending*/false); 11356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get supported video size list (descending order) for a given camera device. 11396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 11406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 11416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Filter out the sizes that are larger than the bound. If the bound is 11426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * null, don't do the size bound filtering. 11436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 11446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public List<Size> getSupportedStillSizes(String cameraId, 11466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraManager cameraManager, Size bound) throws CameraAccessException { 11476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.JPEG, bound); 11486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public Size getMinPreviewSize(String cameraId, CameraManager cameraManager) 11516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 11526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, null); 11536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sizes.get(sizes.size() - 1); 11546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get max supported preview size for a camera device. 11586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager) 11606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 11616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return getMaxPreviewSize(cameraId, cameraManager, /*bound*/null); 11626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get max preview size for a camera device in the supported sizes that are no larger 11666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * than the bound. 11676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager, Size bound) 11696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 11706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, bound); 11716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sizes.get(0); 11726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get max depth size for a camera device. 11766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim static public Size getMaxDepthSize(String cameraId, CameraManager cameraManager) 11786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws CameraAccessException { 11796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Size> sizes = getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.DEPTH16, 11806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*bound*/ null); 11816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sizes.get(0); 11826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 11856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the largest size by area. 11866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 11876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param sizes an array of sizes, must have at least 1 element 11886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 11896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Largest Size 11906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 11916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws IllegalArgumentException if sizes was null or had 0 elements 11926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 11936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Size getMaxSize(Size... sizes) { 11946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (sizes == null || sizes.length == 0) { 11956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("sizes was empty"); 11966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 11976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 11986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size sz = sizes[0]; 11996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (Size size : sizes) { 12006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) { 12016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim sz = size; 12026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return sz; 12066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Returns true if the given {@code array} contains the given element. 12106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array {@code array} to check for {@code elem} 12126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param elem {@code elem} to test for 12136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return {@code true} if the given element is contained 12146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static boolean contains(int[] array, int elem) { 12166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (array == null) return false; 12176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < array.length; i++) { 12186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (elem == array[i]) return true; 12196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 12216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get object array from byte array. 12256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array Input byte array to be converted 12276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Byte object array converted from input byte array 12286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Byte[] toObject(byte[] array) { 12306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return convertPrimitiveArrayToObjectArray(array, Byte.class); 12316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get object array from int array. 12356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array Input int array to be converted 12376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Integer object array converted from input int array 12386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Integer[] toObject(int[] array) { 12406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return convertPrimitiveArrayToObjectArray(array, Integer.class); 12416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get object array from float array. 12456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array Input float array to be converted 12476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Float object array converted from input float array 12486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Float[] toObject(float[] array) { 12506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return convertPrimitiveArrayToObjectArray(array, Float.class); 12516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get object array from double array. 12556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array Input double array to be converted 12576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Double object array converted from input double array 12586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Double[] toObject(double[] array) { 12606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return convertPrimitiveArrayToObjectArray(array, Double.class); 12616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Convert a primitive input array into its object array version (e.g. from int[] to Integer[]). 12656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param array Input array object 12676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param wrapperClass The boxed class it converts to 12686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Boxed version of primitive array 12696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static <T> T[] convertPrimitiveArrayToObjectArray(final Object array, 12716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim final Class<T> wrapperClass) { 12726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // getLength does the null check and isArray check already. 12736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int arrayLength = Array.getLength(array); 12746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (arrayLength == 0) { 12756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Input array shouldn't be empty"); 12766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @SuppressWarnings("unchecked") 12796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim final T[] result = (T[]) Array.newInstance(wrapperClass, arrayLength); 12806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < arrayLength; i++) { 12816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Array.set(result, i, Array.get(array, i)); 12826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return result; 12846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 12856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 12866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 12876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Validate image based on format and size. 12886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 12896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param image The image to be validated. 12906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param width The image width. 12916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param height The image height. 12926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param format The image format. 12936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param filePath The debug dump file path, null if don't want to dump to 12946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * file. 12956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws UnsupportedOperationException if calling with an unknown format 12966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 12976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void validateImage(Image image, int width, int height, int format, 12986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String filePath) { 12996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim checkImage(image, width, height, format); 13006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 13026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * TODO: validate timestamp: 13036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 1. capture result timestamp against the image timestamp (need 13046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * consider frame drops) 13056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 2. timestamps should be monotonically increasing for different requests 13066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 13076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if(VERBOSE) Log.v(TAG, "validating Image"); 13086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim byte[] data = getDataFromImage(image); 13096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Invalid image data", data != null && data.length > 0); 13106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim switch (format) { 13126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.JPEG: 13136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateJpegData(data, width, height, filePath); 13146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.YUV_420_888: 13166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.YV12: 13176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateYuvData(data, width, height, format, image.getTimestamp(), filePath); 13186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.RAW_SENSOR: 13206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateRaw16Data(data, width, height, format, image.getTimestamp(), filePath); 13216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.DEPTH16: 13236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateDepth16Data(data, width, height, format, image.getTimestamp(), filePath); 13246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.DEPTH_POINT_CLOUD: 13266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateDepthPointCloudData(data, width, height, format, image.getTimestamp(), filePath); 13276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ImageFormat.RAW_PRIVATE: 13296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateRawPrivateData(data, width, height, image.getTimestamp(), filePath); 13306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim break; 13316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim default: 13326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new UnsupportedOperationException("Unsupported format for validation: " 13336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + format); 13346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 13386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Provide a mock for {@link CameraDevice.StateCallback}. 13396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 13406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p>Only useful because mockito can't mock {@link CameraDevice.StateCallback} which is an 13416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * abstract class.</p> 13426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 13436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 13446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Use this instead of other classes when needing to verify interactions, since 13456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * trying to spy on {@link BlockingStateCallback} (or others) will cause unnecessary extra 13466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * interactions which will cause false test failures. 13476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 13486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 13496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 13506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class MockStateCallback extends CameraDevice.StateCallback { 13516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 13536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onOpened(CameraDevice camera) { 13546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 13576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onDisconnected(CameraDevice camera) { 13586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim @Override 13616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public void onError(CameraDevice camera, int error) { 13626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private MockStateCallback() {} 13656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 13676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Create a Mockito-ready mocked StateCallback. 13686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 13696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static MockStateCallback mock() { 13706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return Mockito.spy(new MockStateCallback()); 13716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateJpegData(byte[] jpegData, int width, int height, String filePath) { 13756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BitmapFactory.Options bmpOptions = new BitmapFactory.Options(); 13766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // DecodeBound mode: only parse the frame header to get width/height. 13776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // it doesn't decode the pixel. 13786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim bmpOptions.inJustDecodeBounds = true; 13796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions); 13806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals(width, bmpOptions.outWidth); 13816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals(height, bmpOptions.outHeight); 13826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Pixel decoding mode: decode whole image. check if the image data 13846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // is decodable here. 13856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Decoding jpeg failed", 13866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length)); 13876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 13886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 13896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + ".jpeg"; 13906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, jpegData); 13916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 13936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 13946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateYuvData(byte[] yuvData, int width, int height, int format, 13956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long ts, String filePath) { 13966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim checkYuvFormat(format); 13976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Validating YUV data"); 13986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8; 13996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Yuv data doesn't match", expectedSize, yuvData.length); 14006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TODO: Can add data validation for test pattern. 14026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 14046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 14056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv"; 14066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, yuvData); 14076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateRaw16Data(byte[] rawData, int width, int height, int format, 14116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long ts, String filePath) { 14126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Validating raw data"); 14136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8; 14146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Raw data doesn't match", expectedSize, rawData.length); 14156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TODO: Can add data validation for test pattern. 14176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 14196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 14206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".raw16"; 14216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, rawData); 14226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 14256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateRawPrivateData(byte[] rawData, int width, int height, 14286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long ts, String filePath) { 14296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Validating private raw data"); 14306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Expect each RAW pixel should occupy at least one byte and no more than 2.5 bytes 14316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedSizeMin = width * height; 14326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedSizeMax = width * height * 5 / 2; 14336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Opaque RAW size " + rawData.length + "out of normal bound [" + 14356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedSizeMin + "," + expectedSizeMax + "]", 14366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedSizeMin <= rawData.length && rawData.length <= expectedSizeMax); 14376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 14396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 14406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".rawPriv"; 14416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, rawData); 14426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 14456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateDepth16Data(byte[] depthData, int width, int height, int format, 14486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long ts, String filePath) { 14496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Validating depth16 data"); 14516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8; 14526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Depth data doesn't match", expectedSize, depthData.length); 14536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 14566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 14576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".depth16"; 14586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, depthData); 14596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 14626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void validateDepthPointCloudData(byte[] depthData, int width, int height, int format, 14666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long ts, String filePath) { 14676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (VERBOSE) Log.v(TAG, "Validating depth point cloud data"); 14696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Can't validate size since it is variable 14716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (DEBUG && filePath != null) { 14736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String fileName = 14746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".depth_point_cloud"; 14756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(fileName, depthData); 14766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return; 14796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) { 14836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (result == null) { 14846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Result must not be null"); 14856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim T value = result.get(key); 14886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value); 14896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return value; 14906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static <T> T getValueNotNull(CameraCharacteristics characteristics, 14936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraCharacteristics.Key<T> key) { 14946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (characteristics == null) { 14956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Camera characteristics must not be null"); 14966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 14976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 14986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim T value = characteristics.get(key); 14996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value); 15006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return value; 15016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 15046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get a crop region for a given zoom factor and center position. 15056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 15066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * The center position is normalized position in range of [0, 1.0], where 15076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * (0, 0) represents top left corner, (1.0. 1.0) represents bottom right 15086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * corner. The center position could limit the effective minimal zoom 15096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * factor, for example, if the center position is (0.75, 0.75), the 15106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * effective minimal zoom position becomes 2.0. If the requested zoom factor 15116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * is smaller than 2.0, a crop region with 2.0 zoom factor will be returned. 15126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 15136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 15146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * The aspect ratio of the crop region is maintained the same as the aspect 15156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * ratio of active array. 15166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 15176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 15186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param zoomFactor The zoom factor to generate the crop region, it must be 15196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * >= 1.0 15206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param center The normalized zoom center point that is in the range of [0, 1]. 15216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param maxZoom The max zoom factor supported by this device. 15226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param activeArray The active array size of this device. 15236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return crop region for the given normalized center and zoom factor. 15246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 15256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Rect getCropRegionForZoom(float zoomFactor, final PointF center, 15266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim final float maxZoom, final Rect activeArray) { 15276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (zoomFactor < 1.0) { 15286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("zoom factor " + zoomFactor + " should be >= 1.0"); 15296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (center.x > 1.0 || center.x < 0) { 15316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("center.x " + center.x 15326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + " should be in range of [0, 1.0]"); 15336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (center.y > 1.0 || center.y < 0) { 15356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("center.y " + center.y 15366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + " should be in range of [0, 1.0]"); 15376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (maxZoom < 1.0) { 15396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("max zoom factor " + maxZoom + " should be >= 1.0"); 15406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (activeArray == null) { 15426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("activeArray must not be null"); 15436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float minCenterLength = Math.min(Math.min(center.x, 1.0f - center.x), 15466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Math.min(center.y, 1.0f - center.y)); 15476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float minEffectiveZoom = 0.5f / minCenterLength; 15486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (minEffectiveZoom > maxZoom) { 15496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Requested center " + center.toString() + 15506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim " has minimal zoomable factor " + minEffectiveZoom + ", which exceeds max" 15516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + " zoom factor " + maxZoom); 15526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (zoomFactor < minEffectiveZoom) { 15556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.w(TAG, "Requested zoomFactor " + zoomFactor + " > minimal zoomable factor " 15566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + minEffectiveZoom + ". It will be overwritten by " + minEffectiveZoom); 15576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim zoomFactor = minEffectiveZoom; 15586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int cropCenterX = (int)(activeArray.width() * center.x); 15616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int cropCenterY = (int)(activeArray.height() * center.y); 15626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int cropWidth = (int) (activeArray.width() / zoomFactor); 15636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int cropHeight = (int) (activeArray.height() / zoomFactor); 15646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return new Rect( 15666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*left*/cropCenterX - cropWidth / 2, 15676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*top*/cropCenterY - cropHeight / 2, 15686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*right*/ cropCenterX + cropWidth / 2 - 1, 15696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*bottom*/cropCenterY + cropHeight / 2 - 1); 15706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 15736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Calculate output 3A region from the intersection of input 3A region and cropped region. 15746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 15756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param requestRegions The input 3A regions 15766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param cropRect The cropped region 15776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return expected 3A regions output in capture result 15786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 15796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static MeteringRectangle[] getExpectedOutputRegion( 15806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim MeteringRectangle[] requestRegions, Rect cropRect){ 15816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim MeteringRectangle[] resultRegions = new MeteringRectangle[requestRegions.length]; 15826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < requestRegions.length; i++) { 15836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Rect requestRect = requestRegions[i].getRect(); 15846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Rect resultRect = new Rect(); 15856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue("Input 3A region must intersect cropped region", 15866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultRect.setIntersect(requestRect, cropRect)); 15876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultRegions[i] = new MeteringRectangle( 15886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultRect, 15896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim requestRegions[i].getMeteringWeight()); 15906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return resultRegions; 15926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 15936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 15946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 15956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Copy source image data to destination image. 15966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 15976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param src The source image to be copied from. 15986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param dst The destination image to be copied to. 15996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws IllegalArgumentException If the source and destination images have 16006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * different format, or one of the images is not copyable. 16016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 16026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void imageCopy(Image src, Image dst) { 16036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (src == null || dst == null) { 16046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Images should be non-null"); 16056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (src.getFormat() != dst.getFormat()) { 16076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Src and dst images should have the same format"); 16086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (src.getFormat() == ImageFormat.PRIVATE || 16106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dst.getFormat() == ImageFormat.PRIVATE) { 16116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("PRIVATE format images are not copyable"); 16126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TODO: check the owner of the dst image, it must be from ImageWriter, other source may 16156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // not be writable. Maybe we should add an isWritable() method in image class. 16166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] srcPlanes = src.getPlanes(); 16186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] dstPlanes = dst.getPlanes(); 16196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ByteBuffer srcBuffer = null; 16206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ByteBuffer dstBuffer = null; 16216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < srcPlanes.length; i++) { 16226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim srcBuffer = srcPlanes[i].getBuffer(); 16236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int srcPos = srcBuffer.position(); 16246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim srcBuffer.rewind(); 16256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dstBuffer = dstPlanes[i].getBuffer(); 16266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dstBuffer.rewind(); 16276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dstBuffer.put(srcBuffer); 16286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim srcBuffer.position(srcPos); 16296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dstBuffer.rewind(); 16306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 16346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 16356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Checks whether the two images are strongly equal. 16366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 16376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 16386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Two images are strongly equal if and only if the data, formats, sizes, 16396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format 16406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * images, the image data is not not accessible thus the data comparison is 16416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * effectively skipped as the number of planes is zero. 16426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 16436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 16446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Note that this method compares the pixel data even outside of the crop 16456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * region, which may not be necessary for general use case. 16466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 16476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 16486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param lhsImg First image to be compared with. 16496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param rhsImg Second image to be compared with. 16506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return true if the two images are equal, false otherwise. 16516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @throws IllegalArgumentException If either of image is null. 16526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 16536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static boolean isImageStronglyEqual(Image lhsImg, Image rhsImg) { 16546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (lhsImg == null || rhsImg == null) { 16556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throw new IllegalArgumentException("Images should be non-null"); 16566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (lhsImg.getFormat() != rhsImg.getFormat()) { 16596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "lhsImg format " + lhsImg.getFormat() + " is different with rhsImg format " 16606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + rhsImg.getFormat()); 16616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (lhsImg.getWidth() != rhsImg.getWidth()) { 16656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "lhsImg width " + lhsImg.getWidth() + " is different with rhsImg width " 16666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + rhsImg.getWidth()); 16676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (lhsImg.getHeight() != rhsImg.getHeight()) { 16716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "lhsImg height " + lhsImg.getHeight() + " is different with rhsImg height " 16726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + rhsImg.getHeight()); 16736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (lhsImg.getTimestamp() != rhsImg.getTimestamp()) { 16776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "lhsImg timestamp " + lhsImg.getTimestamp() 16786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + " is different with rhsImg timestamp " + rhsImg.getTimestamp()); 16796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (!lhsImg.getCropRect().equals(rhsImg.getCropRect())) { 16836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "lhsImg crop rect " + lhsImg.getCropRect() 16846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim + " is different with rhsImg crop rect " + rhsImg.getCropRect()); 16856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 16876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 16886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Compare data inside of the image. 16896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] lhsPlanes = lhsImg.getPlanes(); 16906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Plane[] rhsPlanes = rhsImg.getPlanes(); 16916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ByteBuffer lhsBuffer = null; 16926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ByteBuffer rhsBuffer = null; 16936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for (int i = 0; i < lhsPlanes.length; i++) { 16946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim lhsBuffer = lhsPlanes[i].getBuffer(); 16956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim rhsBuffer = rhsPlanes[i].getBuffer(); 16966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (!lhsBuffer.equals(rhsBuffer)) { 16976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Log.i(TAG, "byte buffers for plane " + i + " don't matach."); 16986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 16996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return true; 17036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 17066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Set jpeg related keys in a capture request builder. 17076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 17086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param builder The capture request builder to set the keys inl 17096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param exifData The exif data to set. 17106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param thumbnailSize The thumbnail size to set. 17116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param collector The camera error collector to collect errors. 17126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 17136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void setJpegKeys(CaptureRequest.Builder builder, ExifTestData exifData, 17146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size thumbnailSize, CameraErrorCollector collector) { 17156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, thumbnailSize); 17166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.set(CaptureRequest.JPEG_GPS_LOCATION, exifData.gpsLocation); 17176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.set(CaptureRequest.JPEG_ORIENTATION, exifData.jpegOrientation); 17186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.set(CaptureRequest.JPEG_QUALITY, exifData.jpegQuality); 17196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY, 17206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifData.thumbnailQuality); 17216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Validate request set and get. 17236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG thumbnail size request set and get should match", 17246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim thumbnailSize, builder.get(CaptureRequest.JPEG_THUMBNAIL_SIZE)); 17256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("GPS locations request set and get should match.", 17266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim areGpsFieldsEqual(exifData.gpsLocation, 17276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.get(CaptureRequest.JPEG_GPS_LOCATION))); 17286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG orientation request set and get should match", 17296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifData.jpegOrientation, 17306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.get(CaptureRequest.JPEG_ORIENTATION)); 17316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG quality request set and get should match", 17326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifData.jpegQuality, builder.get(CaptureRequest.JPEG_QUALITY)); 17336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG thumbnail quality request set and get should match", 17346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifData.thumbnailQuality, 17356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim builder.get(CaptureRequest.JPEG_THUMBNAIL_QUALITY)); 17366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 17396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Simple validation of JPEG image size and format. 17406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * <p> 17416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Only validate the image object sanity. It is fast, but doesn't actually 17426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * check the buffer data. Assert is used here as it make no sense to 17436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * continue the test if the jpeg image captured has some serious failures. 17446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * </p> 17456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 17466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param image The captured jpeg image 17476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param expectedSize Expected capture jpeg size 17486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 17496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void basicValidateJpegImage(Image image, Size expectedSize) { 17506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size imageSz = new Size(image.getWidth(), image.getHeight()); 17516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertTrue( 17526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String.format("Image size doesn't match (expected %s, actual %s) ", 17536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedSize.toString(), imageSz.toString()), expectedSize.equals(imageSz)); 17546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Image format should be JPEG", ImageFormat.JPEG, image.getFormat()); 17556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertNotNull("Image plane shouldn't be null", image.getPlanes()); 17566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim assertEquals("Image plane number should be 1", 1, image.getPlanes().length); 17576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Jpeg decoding validate was done in ImageReaderTest, no need to duplicate the test here. 17596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 17626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Verify the JPEG EXIF and JPEG related keys in a capture result are expected. 17636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * - Capture request get values are same as were set. 17646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * - capture result's exif data is the same as was set by 17656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the capture request. 17666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * - new tags in the result set by the camera service are 17676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * present and semantically correct. 17686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 17696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param image The output JPEG image to verify. 17706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param captureResult The capture result to verify. 17716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param expectedSize The expected JPEG size. 17726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param expectedThumbnailSize The expected thumbnail size. 17736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param expectedExifData The expected EXIF data 17746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param staticInfo The static metadata for the camera device. 17756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param jpegFilename The filename to dump the jpeg to. 17766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param collector The camera error collector to collect errors. 17776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 17786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static void verifyJpegKeys(Image image, CaptureResult captureResult, Size expectedSize, 17796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size expectedThumbnailSize, ExifTestData expectedExifData, StaticMetadata staticInfo, 17806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraErrorCollector collector) throws Exception { 17816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim basicValidateJpegImage(image, expectedSize); 17836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim byte[] jpegBuffer = getDataFromImage(image); 17856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Have to dump into a file to be able to use ExifInterface 17866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String jpegFilename = DEBUG_FILE_NAME_BASE + "/verifyJpegKeys.jpeg"; 17876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dumpFile(jpegFilename, jpegBuffer); 17886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ExifInterface exif = new ExifInterface(jpegFilename); 17896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (expectedThumbnailSize.equals(new Size(0,0))) { 17916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Jpeg shouldn't have thumbnail when thumbnail size is (0, 0)", 17926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim !exif.hasThumbnail()); 17936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else { 17946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Jpeg must have thumbnail for thumbnail size " + 17956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedThumbnailSize, exif.hasThumbnail()); 17966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 17976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 17986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Validate capture result vs. request 17996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size resultThumbnailSize = captureResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE); 18006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int orientationTested = expectedExifData.jpegOrientation; 18016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Legacy shim always doesn't rotate thumbnail size 18026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if ((orientationTested == 90 || orientationTested == 270) && 18036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim staticInfo.isHardwareLevelLimitedOrBetter()) { 18046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 18056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*defaultValue*/-1); 18066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (exifOrientation == ExifInterface.ORIENTATION_UNDEFINED) { 18076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Device physically rotated image+thumbnail data 18086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Expect thumbnail size to be also rotated 18096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultThumbnailSize = new Size(resultThumbnailSize.getHeight(), 18106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultThumbnailSize.getWidth()); 18116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 18146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG thumbnail size result and request should match", 18156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedThumbnailSize, resultThumbnailSize); 18166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (collector.expectKeyValueNotNull(captureResult, CaptureResult.JPEG_GPS_LOCATION) != 18176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim null) { 18186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("GPS location result and request should match.", 18196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim areGpsFieldsEqual(expectedExifData.gpsLocation, 18206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim captureResult.get(CaptureResult.JPEG_GPS_LOCATION))); 18216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG orientation result and request should match", 18236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedExifData.jpegOrientation, 18246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim captureResult.get(CaptureResult.JPEG_ORIENTATION)); 18256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG quality result and request should match", 18266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedExifData.jpegQuality, captureResult.get(CaptureResult.JPEG_QUALITY)); 18276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("JPEG thumbnail quality result and request should match", 18286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedExifData.thumbnailQuality, 18296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim captureResult.get(CaptureResult.JPEG_THUMBNAIL_QUALITY)); 18306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 18316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Validate other exif tags for all non-legacy devices 18326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (!staticInfo.isHardwareLevelLegacy()) { 18336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim verifyJpegExifExtraTags(exif, expectedSize, captureResult, staticInfo, collector); 18346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 18376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 18386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Get the degree of an EXIF orientation. 18396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 18406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static int getExifOrientationInDegree(int exifOrientation, 18416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraErrorCollector collector) { 18426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim switch (exifOrientation) { 18436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ExifInterface.ORIENTATION_NORMAL: 18446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return 0; 18456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ExifInterface.ORIENTATION_ROTATE_90: 18466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return 90; 18476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ExifInterface.ORIENTATION_ROTATE_180: 18486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return 180; 18496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim case ExifInterface.ORIENTATION_ROTATE_270: 18506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return 270; 18516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim default: 18526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.addMessage("It is impossible to get non 0, 90, 180, 270 degress exif" + 18536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "info based on the request orientation range"); 18546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return 0; 18556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 18586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 18596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Validate and return the focal length. 18606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 18616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param result Capture result to get the focal length 18626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Focal length from capture result or -1 if focal length is not available. 18636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 18646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static float validateFocalLength(CaptureResult result, StaticMetadata staticInfo, 18656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraErrorCollector collector) { 18666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked(); 18676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Float resultFocalLength = result.get(CaptureResult.LENS_FOCAL_LENGTH); 18686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (collector.expectTrue("Focal length is invalid", 18696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultFocalLength != null && resultFocalLength > 0)) { 18706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Float> focalLengthList = 18716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Arrays.asList(CameraTestUtils.toObject(focalLengths)); 18726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Focal length should be one of the available focal length", 18736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim focalLengthList.contains(resultFocalLength)); 18746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return resultFocalLength; 18756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return -1; 18776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 18796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 18806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Validate and return the aperture. 18816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 18826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @param result Capture result to get the aperture 18836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * @return Aperture from capture result or -1 if aperture is not available. 18846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 18856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static float validateAperture(CaptureResult result, StaticMetadata staticInfo, 18866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CameraErrorCollector collector) { 18876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float[] apertures = staticInfo.getAvailableAperturesChecked(); 18886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Float resultAperture = result.get(CaptureResult.LENS_APERTURE); 18896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (collector.expectTrue("Capture result aperture is invalid", 18906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim resultAperture != null && resultAperture > 0)) { 18916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim List<Float> apertureList = 18926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Arrays.asList(CameraTestUtils.toObject(apertures)); 18936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Aperture should be one of the available apertures", 18946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim apertureList.contains(resultAperture)); 18956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return resultAperture; 18966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return -1; 18986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 18996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Return the closest value in an array of floats. 19026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static float getClosestValueInArray(float[] values, float target) { 19046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int minIdx = 0; 19056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float minDistance = Math.abs(values[0] - target); 19066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim for(int i = 0; i < values.length; i++) { 19076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float distance = Math.abs(values[i] - target); 19086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (minDistance > distance) { 19096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim minDistance = distance; 19106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim minIdx = i; 19116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return values[minIdx]; 19156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Return if two Location's GPS field are the same. 19196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static boolean areGpsFieldsEqual(Location a, Location b) { 19216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (a == null || b == null) { 19226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return false; 19236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return a.getTime() == b.getTime() && a.getLatitude() == b.getLatitude() && 19266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim a.getLongitude() == b.getLongitude() && a.getAltitude() == b.getAltitude() && 19276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim a.getProvider() == b.getProvider(); 19286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Verify extra tags in JPEG EXIF 19326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim private static void verifyJpegExifExtraTags(ExifInterface exif, Size jpegSize, 19346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim CaptureResult result, StaticMetadata staticInfo, CameraErrorCollector collector) 19356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim throws ParseException { 19366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION. 19386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Orientation and exif width/height need to be tested carefully, two cases: 19396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 19406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 1. Device rotate the image buffer physically, then exif width/height may not match 19416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the requested still capture size, we need swap them to check. 19426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 19436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 2. Device use the exif tag to record the image orientation, it doesn't rotate 19446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the jpeg image buffer itself. In this case, the exif width/height should always match 19456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * the requested still capture size, and the exif orientation should always match the 19466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * requested orientation. 19476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * 19486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0); 19506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0); 19516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Size exifSize = new Size(exifWidth, exifHeight); 19526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Orientation could be missing, which is ok, default to 0. 19536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 19546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*defaultValue*/-1); 19556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Get requested orientation from result, because they should be same. 19566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (collector.expectKeyValueNotNull(result, CaptureResult.JPEG_ORIENTATION) != null) { 19576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int requestedOrientation = result.get(CaptureResult.JPEG_ORIENTATION); 19586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED; 19596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270; 19606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim boolean orientationValid = collector.expectTrue(String.format( 19616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Exif orientation must be in range of [%d, %d]", 19626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim ORIENTATION_MIN, ORIENTATION_MAX), 19636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX); 19646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (orientationValid) { 19656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Device captured image doesn't respect the requested orientation, 19676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * which means it rotates the image buffer physically. Then we 19686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * should swap the exif width/height accordingly to compare. 19696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED; 19716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (deviceRotatedImage) { 19736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Case 1. 19746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim boolean needSwap = (requestedOrientation % 180 == 90); 19756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (needSwap) { 19766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifSize = new Size(exifHeight, exifWidth); 19776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } else { 19796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // Case 2. 19806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif orientaiton should match requested orientation", 19816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim requestedOrientation, getExifOrientationInDegree(exifOrientation, 19826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector)); 19836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 19866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 19886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Ideally, need check exifSize == jpegSize == actual buffer size. But 19896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * jpegSize == jpeg decode bounds size(from jpeg jpeg frame 19906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * header, not exif) was validated in ImageReaderTest, no need to 19916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * validate again here. 19926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 19936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif size should match jpeg capture size", jpegSize, exifSize); 19946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 19956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_DATETIME, it should be local time 19966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long currentTimeInMs = System.currentTimeMillis(); 19976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long currentTimeInSecond = currentTimeInMs / 1000; 19986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Date date = new Date(currentTimeInMs); 19996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date); 20006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME); 20016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (collector.expectTrue("Exif TAG_DATETIME shouldn't be null", dateTime != null)) { 20026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Exif TAG_DATETIME is wrong", 20036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dateTime.length() == EXIF_DATETIME_LENGTH); 20046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long exifTimeInSecond = 20056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(dateTime).getTime() / 1000; 20066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long delta = currentTimeInSecond - exifTimeInSecond; 20076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Capture time deviates too much from the current time", 20086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Math.abs(delta) < EXIF_DATETIME_ERROR_MARGIN_SEC); 20096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // It should be local time. 20106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Exif date time should be local time", 20116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim dateTime.startsWith(localDatetime)); 20126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_FOCAL_LENGTH. 20156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float[] focalLengths = staticInfo.getAvailableFocalLengthsChecked(); 20166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float exifFocalLength = (float)exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1); 20176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Focal length should match", 20186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim getClosestValueInArray(focalLengths, exifFocalLength), 20196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN); 20206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // More checks for focal length. 20216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif focal length should match capture result", 20226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateFocalLength(result, staticInfo, collector), exifFocalLength); 20236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_EXPOSURE_TIME 20256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // ExifInterface API gives exposure time value in the form of float instead of rational 20266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME); 20276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_EXPOSURE_TIME shouldn't be null", exposureTime); 20286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (staticInfo.areKeysAvailable(CaptureResult.SENSOR_EXPOSURE_TIME)) { 20296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (exposureTime != null) { 20306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim double exposureTimeValue = Double.parseDouble(exposureTime); 20316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim long expTimeResult = result.get(CaptureResult.SENSOR_EXPOSURE_TIME); 20326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim double expected = expTimeResult / 1e9; 20336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim double tolerance = expected * EXIF_EXPOSURE_TIME_ERROR_MARGIN_RATIO; 20346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim tolerance = Math.max(tolerance, EXIF_EXPOSURE_TIME_MIN_ERROR_MARGIN_SEC); 20356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif exposure time doesn't match", expected, 20366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim exposureTimeValue, tolerance); 20376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_APERTURE 20416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // ExifInterface API gives aperture value in the form of float instead of rational 20426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String exifAperture = exif.getAttribute(ExifInterface.TAG_APERTURE); 20436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_APERTURE shouldn't be null", exifAperture); 20446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (staticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)) { 20456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float[] apertures = staticInfo.getAvailableAperturesChecked(); 20466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (exifAperture != null) { 20476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim float apertureValue = Float.parseFloat(exifAperture); 20486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Aperture value should match", 20496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim getClosestValueInArray(apertures, apertureValue), 20506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim apertureValue, EXIF_APERTURE_ERROR_MARGIN); 20516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // More checks for aperture. 20526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif aperture length should match capture result", 20536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim validateAperture(result, staticInfo, collector), apertureValue); 20546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 20586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * TAG_FLASH. TODO: For full devices, can check a lot more info 20596d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * (http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html#Flash) 20606d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 20616d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String flash = exif.getAttribute(ExifInterface.TAG_FLASH); 20626d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_FLASH shouldn't be null", flash); 20636d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20646d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 20656d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * TAG_WHITE_BALANCE. TODO: For full devices, with the DNG tags, we 20666d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * should be able to cross-check android.sensor.referenceIlluminant. 20676d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 20686d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE); 20696d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_WHITE_BALANCE shouldn't be null", whiteBalance); 20706d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20716d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_MAKE 20726d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String make = exif.getAttribute(ExifInterface.TAG_MAKE); 20736d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif TAG_MAKE is incorrect", Build.MANUFACTURER, make); 20746d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20756d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_MODEL 20766d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String model = exif.getAttribute(ExifInterface.TAG_MODEL); 20776d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif TAG_MODEL is incorrect", Build.MODEL, model); 20786d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20796d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20806d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_ISO 20816d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, /*defaultValue*/-1); 20826d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (staticInfo.areKeysAvailable(CaptureResult.SENSOR_SENSITIVITY)) { 20836d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int expectedIso = result.get(CaptureResult.SENSOR_SENSITIVITY); 20846d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("Exif TAG_ISO is incorrect", expectedIso, iso); 20856d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20866d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20876d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras). 20886d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String digitizedTime = exif.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED); 20896d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_DATETIME_DIGITIZED shouldn't be null", digitizedTime); 20906d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (digitizedTime != null) { 20916d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim String expectedDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME); 20926d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectNotNull("Exif TAG_DATETIME shouldn't be null", expectedDateTime); 20936d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (expectedDateTime != null) { 20946d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectEquals("dataTime should match digitizedTime", 20956d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim expectedDateTime, digitizedTime); 20966d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20976d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 20986d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 20996d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 21006d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at 21016d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * most 9 digits in ExifInterface implementation, use getAttributeInt to 21026d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * sanitize it. When the default value -1 is returned, it means that 21036d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * this exif tag either doesn't exist or is a non-numerical invalid 21046d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * string. Same rule applies to the rest of sub second tags. 21056d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 21066d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int subSecTime = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME, /*defaultValue*/-1); 21076d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Exif TAG_SUBSEC_TIME value is null or invalid!", subSecTime > 0); 21086d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21096d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_SUBSEC_TIME_ORIG 21106d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int subSecTimeOrig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_ORIG, 21116d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*defaultValue*/-1); 21126d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue("Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!", 21136d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim subSecTimeOrig > 0); 21146d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21156d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim // TAG_SUBSEC_TIME_DIG 21166d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int subSecTimeDig = exif.getAttributeInt(ExifInterface.TAG_SUBSEC_TIME_DIG, 21176d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /*defaultValue*/-1); 21186d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim collector.expectTrue( 21196d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", subSecTimeDig > 0); 21206d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 21216d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21226d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21236d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim /** 21246d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim * Immutable class wrapping the exif test data. 21256d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim */ 21266d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static class ExifTestData { 21276d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public final Location gpsLocation; 21286d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public final int jpegOrientation; 21296d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public final byte jpegQuality; 21306d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public final byte thumbnailQuality; 21316d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21326d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public ExifTestData(Location location, int orientation, 21336d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim byte jpgQuality, byte thumbQuality) { 21346d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim gpsLocation = location; 21356d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim jpegOrientation = orientation; 21366d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim jpegQuality = jpgQuality; 21376d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim thumbnailQuality = thumbQuality; 21386d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 21396d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 21406d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21416d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) { 21426d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim Display display = windowManager.getDefaultDisplay(); 21436d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21446d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int width = display.getWidth(); 21456d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim int height = display.getHeight(); 21466d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21476d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (height > width) { 21486d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim height = width; 21496d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim width = display.getHeight(); 21506d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 21516d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim 21526d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim if (bound.getWidth() <= width && 21536d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim bound.getHeight() <= height) 21546d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return bound; 21556d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim else 21566d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim return new Size(width, height); 21576d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim } 21586d8fd0a23fdc01b9414202c3de9bba41222583e6Hyungtae Tim Kim} 2159