1 /*
2  * Copyright 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.camera2.cts;
18 
19 import static junit.framework.Assert.assertEquals;
20 import static junit.framework.Assert.assertNotNull;
21 import static junit.framework.Assert.assertTrue;
22 import static junit.framework.Assert.fail;
23 
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.graphics.BitmapRegionDecoder;
27 import android.graphics.ImageFormat;
28 import android.graphics.Rect;
29 import android.graphics.RectF;
30 import android.hardware.camera2.CameraCaptureSession;
31 import android.hardware.camera2.CameraCharacteristics;
32 import android.hardware.camera2.CameraDevice;
33 import android.hardware.camera2.CameraMetadata;
34 import android.hardware.camera2.CaptureRequest;
35 import android.hardware.camera2.CaptureResult;
36 import android.hardware.camera2.DngCreator;
37 import android.hardware.camera2.TotalCaptureResult;
38 import android.hardware.camera2.cts.helpers.StaticMetadata;
39 import android.hardware.camera2.cts.rs.BitmapUtils;
40 import android.hardware.camera2.cts.rs.RawConverter;
41 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
42 import android.hardware.camera2.params.InputConfiguration;
43 import android.location.Location;
44 import android.media.ExifInterface;
45 import android.media.Image;
46 import android.media.ImageReader;
47 import android.media.ImageWriter;
48 import android.os.ConditionVariable;
49 import android.os.SystemClock;
50 import android.util.Log;
51 import android.util.Pair;
52 import android.util.Size;
53 import android.view.Surface;
54 
55 import com.android.ex.camera2.blocking.BlockingSessionCallback;
56 
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.junit.runners.Parameterized;
60 
61 import java.io.ByteArrayOutputStream;
62 import java.io.File;
63 import java.io.FileOutputStream;
64 import java.nio.ByteBuffer;
65 import java.nio.channels.FileChannel;
66 import java.text.SimpleDateFormat;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Calendar;
70 import java.util.Collections;
71 import java.util.Date;
72 import java.util.List;
73 import java.util.TimeZone;
74 
75 /**
76  * Tests for the DngCreator API.
77  */
78 
79 @RunWith(Parameterized.class)
80 public class DngCreatorTest extends Camera2AndroidTestCase {
81     private static final String TAG = "DngCreatorTest";
82     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
83     private static final String DEBUG_DNG_FILE = "raw16.dng";
84     private static final String TEST_DNG_FILE = "test.dng";
85 
86     private static final double IMAGE_DIFFERENCE_TOLERANCE = 65;
87     private static final int DEFAULT_PATCH_DIMEN = 512;
88     private static final int AE_TIMEOUT_MS = 3000;
89     private static final int MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE = 4;
90     private static final int MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS =
91             CAPTURE_WAIT_TIMEOUT_MS * MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE;
92 
93     // Constants used for GPS testing.
94     private static final double GPS_DIFFERENCE_TOLERANCE = 0.0001;
95     private static final double GPS_LATITUDE = 37.420016;
96     private static final double GPS_LONGITUDE = -122.081987;
97     private static final String GPS_DATESTAMP = "2015:01:27";
98     private static final String GPS_TIMESTAMP = "02:12:01";
99     private static final Calendar GPS_CALENDAR =
100             Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
101 
102     /** Load DNG validation jni on initialization */
103     static {
104         System.loadLibrary("ctscamera2_jni");
105     }
106 
107     static {
108         GPS_CALENDAR.set(2015, 0, 27, 2, 12, 01);
109     }
110 
111     class CapturedData {
112         public Pair<List<Image>, CaptureResult> imagePair;
113         public CameraCharacteristics characteristics;
114     }
115 
116     // CapturedData for maximum resolution mode, raw and jpeg don't share the same capture result
117     // since mandatory streams for maximum resolution sensor pixel mode don't guarantee more than 1
118     // stream.
119     class CapturedDataMaximumResolution {
120         public Pair<Image, CaptureResult> raw;
121         public Pair<Image, CaptureResult> jpeg;
122         public CameraCharacteristics characteristics;
123     }
124 
125     class DngDebugParams {
126         String deviceId;
127         String intermediateStr;
128         CameraCharacteristics characteristics;
129         CaptureResult captureResult;
130         FileOutputStream fileStream;
131         FileChannel fileChannel;
132         Image jpeg;
133         Image raw;
134         Bitmap rawBitmap;
135     }
136 
137     /**
138      * Test basic raw capture and DNG saving functionality for each of the available cameras.
139      *
140      * <p>
141      * For each camera, capture a single RAW16 image at the first capture size reported for
142      * the raw format on that device, and save that image as a DNG file.  No further validation
143      * is done.
144      * </p>
145      *
146      * <p>
147      * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the
148      * raw image captured for the first reported camera device to be saved to an output file.
149      * </p>
150      */
151     @Test
testSingleImageBasic()152     public void testSingleImageBasic() throws Exception {
153         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
154         for (int i = 0; i < cameraIdsUnderTest.length; i++) {
155             String deviceId = cameraIdsUnderTest[i];
156             ImageReader captureReader = null;
157             FileOutputStream fileStream = null;
158             ByteArrayOutputStream outputStream = null;
159             try {
160                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
161                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
162                     Log.i(TAG, "RAW capability is not supported in camera " + cameraIdsUnderTest[i] +
163                             ". Skip the test.");
164                     continue;
165                 }
166 
167                 openDevice(deviceId);
168                 Size activeArraySize = mStaticInfo.getRawDimensChecked();
169 
170                 // Create capture image reader
171                 CameraTestUtils.SimpleImageReaderListener captureListener
172                         = new CameraTestUtils.SimpleImageReaderListener();
173                 captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
174                         captureListener);
175                 Pair<Image, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
176                         /*waitForAe*/false, captureReader, captureListener);
177                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
178 
179                 // Test simple writeImage, no header checks
180                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
181                 outputStream = new ByteArrayOutputStream();
182                 dngCreator.writeImage(outputStream, resultPair.first);
183 
184                 if (VERBOSE) {
185                     // Write DNG to file
186                     String dngFilePath = mDebugFileNameBase + "/camera_basic_" + deviceId + "_" +
187                             DEBUG_DNG_FILE;
188                     // Write out captured DNG file for the first camera device if setprop is enabled
189                     fileStream = new FileOutputStream(dngFilePath);
190                     fileStream.write(outputStream.toByteArray());
191                     fileStream.flush();
192                     fileStream.close();
193                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
194                 }
195                 assertTrue("Generated DNG file does not pass validation",
196                         validateDngNative(outputStream.toByteArray()));
197             } finally {
198                 closeDevice(deviceId);
199                 closeImageReader(captureReader);
200 
201                 if (outputStream != null) {
202                     outputStream.close();
203                 }
204 
205                 if (fileStream != null) {
206                     fileStream.close();
207                 }
208             }
209         }
210     }
211 
212     /**
213      * Test basic maximum resolution raw capture and DNG saving functionality for each of the
214      * available ultra high resolution cameras.
215      *
216      * <p>
217      * For ultra high resolution each camera, capture a single RAW16 image at the first capture size
218      * reported for the maximum resolution raw format on that device, and save that image as a DNG
219      * file. No further validation is done.
220      * </p>
221      *
222      * <p>
223      * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the
224      * raw image captured for the first reported camera device to be saved to an output file.
225      * </p>
226      */
227     @Test
testSingleImageBasicMaximumResolution()228     public void testSingleImageBasicMaximumResolution() throws Exception {
229         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
230         for (int i = 0; i < cameraIdsUnderTest.length; i++) {
231             String deviceId = cameraIdsUnderTest[i];
232             ImageReader captureReader = null;
233             ImageReader reprocessCaptureReader = null;
234             FileOutputStream fileStream = null;
235             ByteArrayOutputStream outputStream = null;
236             try {
237                 // All ultra high resolution sensors must necessarily support RAW
238                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
239                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) {
240                     Log.i(TAG, "ULTRA_HIGH_RESOLUTION_SENSOR capability is not supported in " +
241                             " camera " + cameraIdsUnderTest[i] + ". Skip the test.");
242                     continue;
243                 }
244 
245                 openDevice(deviceId);
246                 Size activeArraySize = mStaticInfo.getRawDimensChecked(/*maxResolution*/true);
247 
248                 // Create capture image reader
249                 CameraTestUtils.SimpleImageReaderListener captureReaderListener
250                         = new CameraTestUtils.SimpleImageReaderListener();
251                 CameraTestUtils.SimpleImageReaderListener reprocessReaderListener
252                         = new CameraTestUtils.SimpleImageReaderListener();
253 
254                 captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
255                         captureReaderListener);
256 
257                 reprocessCaptureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR,
258                         2, reprocessReaderListener);
259                 Pair<Image, CaptureResult> resultPair = null;
260                 if (mAllStaticInfo.get(deviceId).isCapabilitySupported(
261                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) {
262                     resultPair =
263                             captureReprocessedRawShot(activeArraySize, captureReader,
264                                     reprocessCaptureReader, captureReaderListener,
265                                     reprocessReaderListener, /*waitForAe*/false);
266                 } else {
267                     resultPair = captureSingleShotMaximumResolution(activeArraySize,
268                             captureReader, /*waitForAe*/false, captureReaderListener);
269                 }
270                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
271 
272                 // Test simple writeImage, no header checks
273                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
274                 outputStream = new ByteArrayOutputStream();
275                 dngCreator.writeImage(outputStream, resultPair.first);
276 
277                 if (VERBOSE) {
278                     // Write DNG to file
279                     String dngFilePath = mDebugFileNameBase + "/camera_basic_max_resolution_" +
280                             deviceId + "_" + DEBUG_DNG_FILE;
281                     // Write out captured DNG file for the first camera device if setprop is enabled
282                     fileStream = new FileOutputStream(dngFilePath);
283                     fileStream.write(outputStream.toByteArray());
284                     fileStream.flush();
285                     fileStream.close();
286                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
287                 }
288                 assertTrue("Generated DNG file does not pass validation",
289                         validateDngNative(outputStream.toByteArray()));
290             } finally {
291                 closeDevice(deviceId);
292                 closeImageReader(captureReader);
293                 closeImageReader(reprocessCaptureReader);
294 
295                 if (outputStream != null) {
296                     outputStream.close();
297                 }
298 
299                 if (fileStream != null) {
300                     fileStream.close();
301                 }
302             }
303         }
304     }
305 
306     /**
307      * Test basic raw capture and DNG saving with a thumbnail, rotation, usercomment, and GPS tags
308      * set.
309      *
310      * <p>
311      * For each camera, capture a single RAW16 image at the first capture size reported for
312      * the raw format on that device, and save that image as a DNG file. GPS information validation
313      * is done via ExifInterface.
314      * </p>
315      *
316      * <p>
317      * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the
318      * raw image captured for the first reported camera device to be saved to an output file.
319      * </p>
320      */
321     @Test
testSingleImageThumbnail()322     public void testSingleImageThumbnail() throws Exception {
323         String[] cameraIdsUnderTest = getCameraIdsUnderTest();
324         for (int i = 0; i < cameraIdsUnderTest.length; i++) {
325             String deviceId = cameraIdsUnderTest[i];
326             List<ImageReader> captureReaders = new ArrayList<ImageReader>();
327             List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
328                     new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
329             FileOutputStream fileStream = null;
330             ByteArrayOutputStream outputStream = null;
331             try {
332                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
333                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
334                     Log.i(TAG, "RAW capability is not supported in camera " + cameraIdsUnderTest[i] +
335                             ". Skip the test.");
336                     continue;
337                 }
338 
339                 openDevice(deviceId);
340                 Size activeArraySize = mStaticInfo.getRawDimensChecked();
341 
342                 Size[] targetPreviewSizes =
343                         mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
344                                 StaticMetadata.StreamDirection.Output);
345                 // Get smallest preview size
346                 Size previewSize = mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1);
347 
348                 // Create capture image reader
349                 CameraTestUtils.SimpleImageReaderListener captureListener
350                         = new CameraTestUtils.SimpleImageReaderListener();
351                 captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
352                         captureListener));
353                 captureListeners.add(captureListener);
354 
355                 CameraTestUtils.SimpleImageReaderListener previewListener
356                         = new CameraTestUtils.SimpleImageReaderListener();
357 
358                 captureReaders.add(createImageReader(previewSize, ImageFormat.YUV_420_888, 2,
359                         previewListener));
360                 captureListeners.add(previewListener);
361 
362                 Date beforeCaptureDate = new Date();
363                 Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
364                         captureReaders, /*waitForAe*/false, captureListeners);
365                 Date afterCaptureDate = new Date();
366                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
367 
368                 if (VERBOSE) {
369                     Log.v(TAG, "Sensor timestamp (ms): " +
370                             resultPair.second.get(CaptureResult.SENSOR_TIMESTAMP) / 1000000);
371                     Log.v(TAG, "SystemClock.elapsedRealtimeNanos (ms): " +
372                             SystemClock.elapsedRealtimeNanos() / 1000000);
373                     Log.v(TAG, "SystemClock.uptimeMillis(): " + SystemClock.uptimeMillis());
374                 }
375                 // Test simple writeImage, no header checks
376                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
377                 Location l = new Location("test");
378                 l.reset();
379                 l.setLatitude(GPS_LATITUDE);
380                 l.setLongitude(GPS_LONGITUDE);
381                 l.setTime(GPS_CALENDAR.getTimeInMillis());
382                 dngCreator.setLocation(l);
383 
384                 dngCreator.setDescription("helloworld");
385                 dngCreator.setOrientation(ExifInterface.ORIENTATION_FLIP_VERTICAL);
386                 dngCreator.setThumbnail(resultPair.first.get(1));
387                 outputStream = new ByteArrayOutputStream();
388                 dngCreator.writeImage(outputStream, resultPair.first.get(0));
389 
390                 String filePath = mDebugFileNameBase + "/camera_thumb_" + deviceId + "_" +
391                         DEBUG_DNG_FILE;
392                 // Write out captured DNG file for the first camera device
393                 fileStream = new FileOutputStream(filePath);
394                 fileStream.write(outputStream.toByteArray());
395                 fileStream.flush();
396                 fileStream.close();
397                 if (VERBOSE) {
398                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
399                 }
400 
401                 assertTrue("Generated DNG file does not pass validation",
402                         validateDngNative(outputStream.toByteArray()));
403 
404                 ExifInterface exifInterface = new ExifInterface(filePath);
405                 // Verify GPS data.
406                 float[] latLong = new float[2];
407                 assertTrue(exifInterface.getLatLong(latLong));
408                 assertEquals(GPS_LATITUDE, latLong[0], GPS_DIFFERENCE_TOLERANCE);
409                 assertEquals(GPS_LONGITUDE, latLong[1], GPS_DIFFERENCE_TOLERANCE);
410                 assertEquals(GPS_DATESTAMP,
411                         exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
412                 assertEquals(GPS_TIMESTAMP,
413                         exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
414 
415                 // Verify the orientation.
416                 assertEquals(ExifInterface.ORIENTATION_FLIP_VERTICAL,
417                         exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
418                                 ExifInterface.ORIENTATION_UNDEFINED));
419 
420                 // Verify the date/time
421                 final SimpleDateFormat dngDateTimeStampFormat =
422                         new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
423                 dngDateTimeStampFormat.setLenient(false);
424 
425                 String dateTimeString =
426                         exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
427                 assertTrue(dateTimeString != null);
428 
429                 Date dateTime = dngDateTimeStampFormat.parse(dateTimeString);
430                 long captureTimeMs = dateTime.getTime();
431 
432                 Log.i(TAG, "DNG DateTime tag: " + dateTimeString);
433                 Log.i(TAG, "Before capture time: " + beforeCaptureDate.getTime());
434                 Log.i(TAG, "Capture time: " + captureTimeMs);
435                 Log.i(TAG, "After capture time: " + afterCaptureDate.getTime());
436 
437                 // Offset beforeCaptureTime by 1 second to account for rounding down of
438                 // DNG tag
439                 long beforeCaptureTimeMs = beforeCaptureDate.getTime() - 1000;
440                 long afterCaptureTimeMs = afterCaptureDate.getTime();
441                 assertTrue(captureTimeMs >= beforeCaptureTimeMs);
442                 assertTrue(captureTimeMs <= afterCaptureTimeMs);
443 
444                 if (!VERBOSE) {
445                     // Delete the captured DNG file.
446                     File dngFile = new File(filePath);
447                     assertTrue(dngFile.delete());
448                 }
449             } finally {
450                 closeDevice(deviceId);
451                 for (ImageReader r : captureReaders) {
452                     closeImageReader(r);
453                 }
454 
455                 if (outputStream != null) {
456                     outputStream.close();
457                 }
458 
459                 if (fileStream != null) {
460                     fileStream.close();
461                 }
462             }
463         }
464     }
465 
466     /**
467      * Test basic maximum resolution RAW capture, and ensure that the rendered RAW output is
468      * similar to the maximum resolution JPEG created for a similar frame.
469      *
470      * Since mandatory streams for maximum resolution sensor pixel mode do not guarantee 2 maximum
471      * resolution streams we can't capture RAW + JPEG images of the same frame. Therefore, 2
472      * sessions are created, one for RAW capture and the other for JPEG capture.
473      *
474      * <p>
475      * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
476      * similar to one in the Adobe DNG validation tool.  JPEGs produced by the vendor hardware may
477      * have different tonemapping and saturation applied than the RGB bitmaps produced
478      * from this DNG rendering pipeline, and this test allows for fairly wide variations
479      * between the histograms for the RAW and JPEG buffers to avoid false positives.
480      * </p>
481      *
482      * <p>
483      * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
484      * metadata, the DNGs and JPEGs produced here should also be manually compared using external
485      * DNG rendering tools.  The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
486      * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
487      * using:
488      * adb shell setprop log.tag.DngCreatorTest VERBOSE
489      * </p>
490      */
491     @Test
testRaw16JpegMaximumResolutionConsistency()492     public void testRaw16JpegMaximumResolutionConsistency() throws Exception {
493         for (String deviceId : getCameraIdsUnderTest()) {
494             ImageReader rawImageReader = null;
495             ImageReader jpegImageReader = null;
496             FileOutputStream fileStream = null;
497             FileChannel fileChannel = null;
498             try {
499                 // All ultra high resolution sensors must necessarily support RAW
500                 if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
501                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) {
502                     Log.i(TAG, "ULTRA_HIGH_RESOLUTION_SENSOR capability is not supported in " +
503                             " camera " + deviceId + ". Skip " +
504                             "testRaw16JpegMaximumResolutionConsistency");
505                     continue;
506                 }
507 
508                 CapturedDataMaximumResolution data =
509                         captureRawJpegImagePairMaximumResolution(deviceId, rawImageReader,
510                                 jpegImageReader);
511                 if (data == null) {
512                     continue;
513                 }
514                 Image raw = data.raw.first;
515                 Image jpeg = data.jpeg.first;
516 
517                 Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
518                         Bitmap.Config.ARGB_8888);
519 
520                 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
521 
522                 // Render RAW image to a bitmap
523                 raw.getPlanes()[0].getBuffer().get(rawPlane);
524                 raw.getPlanes()[0].getBuffer().rewind();
525 
526                 RawConverter.convertToSRGB(raw.getWidth(),
527                         raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane,
528                         data.characteristics, /*captureREsult*/data.raw.second, /*offsetX*/ 0,
529                         /*offsetY*/ 0, /*out*/ rawBitmap);
530 
531                 rawPlane = null;
532                 System.gc(); // Hint to VM
533 
534                 if (VERBOSE) {
535                     DngDebugParams params = new DngDebugParams();
536                     params.deviceId = deviceId;
537                     params.characteristics = data.characteristics;
538                     params.captureResult = data.raw.second;
539                     params.fileStream = fileStream;
540                     params.raw = raw;
541                     params.jpeg = jpeg;
542                     params.fileChannel = fileChannel;
543                     params.rawBitmap = rawBitmap;
544                     params.intermediateStr = "maximum_resolution_";
545 
546                     debugDumpDng(params);
547                 }
548 
549                 validateRawJpegImagePair(rawBitmap, jpeg, deviceId);
550             } finally {
551                 closeImageReader(rawImageReader);
552                 closeImageReader(jpegImageReader);
553 
554                 if (fileChannel != null) {
555                     fileChannel.close();
556                 }
557                 if (fileStream != null) {
558                     fileStream.close();
559                 }
560             }
561         }
562     }
563 
564 
565 
566     /**
567      * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG
568      * created for the same frame.
569      *
570      * <p>
571      * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
572      * similar to one in the Adobe DNG validation tool.  JPEGs produced by the vendor hardware may
573      * have different tonemapping and saturation applied than the RGB bitmaps produced
574      * from this DNG rendering pipeline, and this test allows for fairly wide variations
575      * between the histograms for the RAW and JPEG buffers to avoid false positives.
576      * </p>
577      *
578      * <p>
579      * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
580      * metadata, the DNGs and JPEGs produced here should also be manually compared using external
581      * DNG rendering tools.  The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
582      * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
583      * using:
584      * adb shell setprop log.tag.DngCreatorTest VERBOSE
585      * </p>
586      */
587     @Test
testRaw16JpegConsistency()588     public void testRaw16JpegConsistency() throws Exception {
589         for (String deviceId : getCameraIdsUnderTest()) {
590             List<ImageReader> captureReaders = new ArrayList<>();
591             FileOutputStream fileStream = null;
592             FileChannel fileChannel = null;
593             try {
594                 CapturedData data = captureRawJpegImagePair(deviceId, captureReaders);
595                 if (data == null) {
596                     continue;
597                 }
598                 Image raw = data.imagePair.first.get(0);
599                 Image jpeg = data.imagePair.first.get(1);
600 
601                 Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
602                         Bitmap.Config.ARGB_8888);
603 
604                 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
605 
606                 // Render RAW image to a bitmap
607                 raw.getPlanes()[0].getBuffer().get(rawPlane);
608                 raw.getPlanes()[0].getBuffer().rewind();
609 
610                 RawConverter.convertToSRGB(raw.getWidth(),
611                         raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane,
612                         data.characteristics, data.imagePair.second, /*offsetX*/ 0, /*offsetY*/ 0,
613                         /*out*/ rawBitmap);
614 
615                 rawPlane = null;
616                 System.gc(); // Hint to VM
617 
618                 if (VERBOSE) {
619                     DngDebugParams params = new DngDebugParams();
620                     params.deviceId = deviceId;
621                     params.characteristics = data.characteristics;
622                     params.captureResult = data.imagePair.second;
623                     params.fileStream = fileStream;
624                     params.raw = raw;
625                     params.jpeg = jpeg;
626                     params.fileChannel = fileChannel;
627                     params.rawBitmap = rawBitmap;
628                     params.intermediateStr = "";
629 
630                     debugDumpDng(params);
631                 }
632 
633                 validateRawJpegImagePair(rawBitmap, jpeg, deviceId);
634             } finally {
635                 for (ImageReader r : captureReaders) {
636                     closeImageReader(r);
637                 }
638 
639                 if (fileChannel != null) {
640                     fileChannel.close();
641                 }
642 
643                 if (fileStream != null) {
644                     fileStream.close();
645                 }
646             }
647         }
648     }
649 
650     /**
651      * Test basic DNG creation, ensure that the DNG image can be rendered by BitmapFactory.
652      */
653     @Test
testDngRenderingByBitmapFactor()654     public void testDngRenderingByBitmapFactor() throws Exception {
655         for (String deviceId : getCameraIdsUnderTest()) {
656             List<ImageReader> captureReaders = new ArrayList<>();
657 
658             CapturedData data = captureRawJpegImagePair(deviceId, captureReaders);
659             if (data == null) {
660                 continue;
661             }
662             Image raw = data.imagePair.first.get(0);
663             Image jpeg = data.imagePair.first.get(1);
664 
665             // Generate DNG file
666             DngCreator dngCreator = new DngCreator(data.characteristics, data.imagePair.second);
667 
668             // Write DNG to file
669             String dngFilePath = mDebugFileNameBase + "/camera_" +
670                 deviceId + "_" + TEST_DNG_FILE;
671 
672             // Write out captured DNG file for the first camera device if setprop is enabled
673             try (FileOutputStream fileStream = new FileOutputStream(dngFilePath)) {
674                 dngCreator.writeImage(fileStream, raw);
675 
676                 // Render the DNG file using BitmapFactory.
677                 Bitmap rawBitmap = BitmapFactory.decodeFile(dngFilePath);
678                 assertNotNull(rawBitmap);
679 
680                 validateRawJpegImagePair(rawBitmap, jpeg, deviceId);
681             } finally {
682                 for (ImageReader r : captureReaders) {
683                     closeImageReader(r);
684                 }
685 
686                 System.gc(); // Hint to VM
687             }
688         }
689     }
690 
691     /*
692      * Create RAW + JPEG image pair with characteristics info.
693      */
captureRawJpegImagePair(String deviceId, List<ImageReader> captureReaders)694     private CapturedData captureRawJpegImagePair(String deviceId, List<ImageReader> captureReaders)
695             throws Exception {
696         CapturedData data = new CapturedData();
697         List<CameraTestUtils.SimpleImageReaderListener> captureListeners = new ArrayList<>();
698         try {
699             if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
700                     CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
701                 Log.i(TAG, "RAW capability is not supported in camera " + deviceId
702                         + ". Skip the test.");
703                 return null;
704             }
705 
706             openDevice(deviceId);
707             Size activeArraySize = mStaticInfo.getRawDimensChecked();
708 
709             // Get largest jpeg size
710             Size[] targetJpegSizes = mStaticInfo.getAvailableSizesForFormatChecked(
711                     ImageFormat.JPEG, StaticMetadata.StreamDirection.Output);
712 
713             Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
714                     new CameraTestUtils.SizeComparator());
715 
716             // Create raw image reader and capture listener
717             CameraTestUtils.SimpleImageReaderListener rawListener =
718                     new CameraTestUtils.SimpleImageReaderListener();
719             captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
720                     rawListener));
721             captureListeners.add(rawListener);
722 
723 
724             // Create jpeg image reader and capture listener
725             CameraTestUtils.SimpleImageReaderListener jpegListener =
726                     new CameraTestUtils.SimpleImageReaderListener();
727             captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
728                     jpegListener));
729             captureListeners.add(jpegListener);
730 
731             data.imagePair = captureSingleRawShot(activeArraySize,
732                     captureReaders, /*waitForAe*/ true, captureListeners);
733             data.characteristics = mStaticInfo.getCharacteristics();
734 
735             Image raw = data.imagePair.first.get(0);
736             Size rawBitmapSize = new Size(raw.getWidth(), raw.getHeight());
737             assertTrue("Raw bitmap size must be equal to either pre-correction active array" +
738                     " size or pixel array size.", rawBitmapSize.equals(activeArraySize));
739 
740             return data;
741         } finally {
742             closeDevice(deviceId);
743         }
744     }
745 
debugDumpDng(DngDebugParams params)746    private void debugDumpDng(DngDebugParams params) throws Exception {
747         // Generate DNG file
748         DngCreator dngCreator =
749                 new DngCreator(params.characteristics, params.captureResult);
750 
751         // Write DNG to file
752         String dngFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr +
753                 params.deviceId + "_" + DEBUG_DNG_FILE;
754         // Write out captured DNG file for the first camera device if setprop is enabled
755         params.fileStream = new FileOutputStream(dngFilePath);
756         dngCreator.writeImage(params.fileStream, params.raw);
757         params.fileStream.flush();
758         params.fileStream.close();
759         Log.v(TAG, "Test DNG file for camera " + params.deviceId + " saved to " + dngFilePath);
760 
761         // Write JPEG to file
762         String jpegFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr  +
763                 params.deviceId + "_jpeg.jpg";
764         // Write out captured DNG file for the first camera device if setprop is enabled
765         params.fileChannel = new FileOutputStream(jpegFilePath).getChannel();
766         ByteBuffer jPlane = params.jpeg.getPlanes()[0].getBuffer();
767         params.fileChannel.write(jPlane);
768         params.fileChannel.close();
769         jPlane.rewind();
770         Log.v(TAG, "Test JPEG file for camera " + params.deviceId + " saved to " +
771                 jpegFilePath);
772 
773         // Write jpeg generated from demosaiced RAW frame to file
774         String rawFilePath = mDebugFileNameBase + "/camera_" + params.intermediateStr +
775                 params.deviceId + "_raw.jpg";
776         // Write out captured DNG file for the first camera device if setprop is enabled
777         params.fileStream = new FileOutputStream(rawFilePath);
778         params.rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, params.fileStream);
779         params.fileStream.flush();
780         params.fileStream.close();
781         Log.v(TAG, "Test converted RAW file for camera " + params.deviceId + " saved to " +
782                 rawFilePath);
783    }
784 
785     /*
786      * Create RAW + JPEG image pair with characteristics info. Assumes the device supports the RAW
787      * capability.
788      */
captureRawJpegImagePairMaximumResolution(String deviceId, ImageReader rawCaptureReader, ImageReader jpegCaptureReader)789     private CapturedDataMaximumResolution captureRawJpegImagePairMaximumResolution(String deviceId,
790             ImageReader rawCaptureReader, ImageReader jpegCaptureReader)
791             throws Exception {
792         CapturedDataMaximumResolution data = new CapturedDataMaximumResolution();
793         try {
794 
795             openDevice(deviceId);
796             Size activeArraySize = mStaticInfo.getRawDimensChecked(/*maxResolution*/true);
797 
798             // Get largest jpeg size
799             Size[] targetJpegSizes = mStaticInfo.getAvailableSizesForFormatChecked(
800                     ImageFormat.JPEG, StaticMetadata.StreamDirection.Output, /*fastSizes*/ true,
801                     /*slowSizes*/ true, /*maxResolution*/true);
802 
803             Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
804                     new CameraTestUtils.SizeComparator());
805 
806             // Create raw image reader and capture listener
807             CameraTestUtils.SimpleImageReaderListener rawCaptureReaderListener =
808                     new CameraTestUtils.SimpleImageReaderListener();
809             rawCaptureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
810                     rawCaptureReaderListener);
811 
812             // Create jpeg image reader and capture listener
813             CameraTestUtils.SimpleImageReaderListener jpegCaptureListener =
814                     new CameraTestUtils.SimpleImageReaderListener();
815             jpegCaptureReader = createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
816                     jpegCaptureListener);
817 
818             Pair<Image, CaptureResult> jpegResultPair =
819                     captureSingleShotMaximumResolution(activeArraySize,
820                              jpegCaptureReader, /*waitForAe*/true, jpegCaptureListener);
821             data.jpeg = jpegResultPair;
822             data.characteristics = mStaticInfo.getCharacteristics();
823             // Create capture image reader
824             CameraTestUtils.SimpleImageReaderListener outputRawCaptureReaderListener
825                     = new CameraTestUtils.SimpleImageReaderListener();
826             CameraTestUtils.SimpleImageReaderListener reprocessReaderListener
827                     = new CameraTestUtils.SimpleImageReaderListener();
828 
829             ImageReader outputRawCaptureReader = createImageReader(activeArraySize,
830                     ImageFormat.RAW_SENSOR, 2, outputRawCaptureReaderListener);
831             Pair<Image, CaptureResult> rawResultPair = null;
832             if (mAllStaticInfo.get(deviceId).isCapabilitySupported(
833                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING)) {
834                 rawResultPair =
835                         captureReprocessedRawShot(activeArraySize, outputRawCaptureReader,
836                                     rawCaptureReader, outputRawCaptureReaderListener,
837                                     reprocessReaderListener, /*waitForAe*/ true);
838             } else {
839                 rawResultPair = captureSingleShotMaximumResolution(activeArraySize,
840                         rawCaptureReader, /*waitForAe*/true, rawCaptureReaderListener);
841             }
842             data.raw = rawResultPair;
843             Size rawBitmapSize =
844                     new Size(rawResultPair.first.getWidth(), rawResultPair.first.getHeight());
845             assertTrue("Raw bitmap size must be equal to either pre-correction active array" +
846                     " size or pixel array size.", rawBitmapSize.equals(activeArraySize));
847 
848             return data;
849         } finally {
850             closeDevice(deviceId);
851         }
852     }
853 
854     /*
855      * Verify the image pair by comparing the center patch.
856      */
validateRawJpegImagePair(Bitmap rawBitmap, Image jpeg, String deviceId)857     private void validateRawJpegImagePair(Bitmap rawBitmap, Image jpeg, String deviceId)
858             throws Exception {
859         // Decompress JPEG image to a bitmap
860         byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
861 
862         // Get JPEG dimensions without decoding
863         BitmapFactory.Options opt0 = new BitmapFactory.Options();
864         opt0.inJustDecodeBounds = true;
865         BitmapFactory.decodeByteArray(compressedJpegData, /*offset*/0,
866                 compressedJpegData.length, /*inout*/opt0);
867         Rect jpegDimens = new Rect(0, 0, opt0.outWidth, opt0.outHeight);
868 
869         // Find square center patch from JPEG and RAW bitmaps
870         RectF jpegRect = new RectF(jpegDimens);
871         RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
872         int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
873                 jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()),
874                 rawBitmap.getHeight());
875 
876         RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
877         jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
878                 jpegRect.centerY() - jpegIntermediate.centerY());
879 
880         RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
881         rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
882                 rawRect.centerY() - rawIntermediate.centerY());
883         Rect jpegFinal = new Rect();
884         jpegIntermediate.roundOut(jpegFinal);
885         Rect rawFinal = new Rect();
886         rawIntermediate.roundOut(rawFinal);
887 
888         // Get RAW center patch, and free up rest of RAW image
889         Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
890                 rawFinal.width(), rawFinal.height());
891         rawBitmap.recycle();
892         rawBitmap = null;
893         System.gc(); // Hint to VM
894 
895         BitmapFactory.Options opt = new BitmapFactory.Options();
896         opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
897         Bitmap jpegPatch = BitmapRegionDecoder.newInstance(compressedJpegData,
898                 /*offset*/0, compressedJpegData.length, /*isShareable*/true).
899                 decodeRegion(jpegFinal, opt);
900 
901         // Compare center patch from JPEG and rendered RAW bitmap
902         double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
903         if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
904             FileOutputStream fileStream = null;
905             try {
906                 // Write JPEG patch to file
907                 String jpegFilePath = mDebugFileNameBase + "/camera_" + deviceId +
908                         "_jpeg_patch.jpg";
909                 fileStream = new FileOutputStream(jpegFilePath);
910                 jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
911                 fileStream.flush();
912                 fileStream.close();
913                 Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " +
914                         jpegFilePath);
915 
916                 // Write RAW patch to file
917                 String rawFilePath = mDebugFileNameBase + "/camera_" + deviceId +
918                         "_raw_patch.jpg";
919                 fileStream = new FileOutputStream(rawFilePath);
920                 rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
921                 fileStream.flush();
922                 fileStream.close();
923                 Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " +
924                         rawFilePath);
925 
926                 fail("Camera " + deviceId + ": RAW and JPEG image at  for the same " +
927                         "frame are not similar, center patches have difference metric of " +
928                         difference);
929             } finally {
930                 if (fileStream != null) {
931                     fileStream.close();
932                 }
933             }
934         }
935     }
936 
captureSingleRawShot(Size s, boolean waitForAe, ImageReader captureReader, CameraTestUtils.SimpleImageReaderListener captureListener)937     private Pair<Image, CaptureResult> captureSingleRawShot(Size s, boolean waitForAe,
938             ImageReader captureReader,
939             CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
940         List<ImageReader> readers = new ArrayList<ImageReader>();
941         readers.add(captureReader);
942         List<CameraTestUtils.SimpleImageReaderListener> listeners =
943                 new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
944         listeners.add(captureListener);
945         Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, waitForAe,
946                 listeners);
947         return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
948     }
949 
captureSingleRawShot(Size s, List<ImageReader> captureReaders, boolean waitForAe, List<CameraTestUtils.SimpleImageReaderListener> captureListeners)950     private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s,
951             List<ImageReader> captureReaders, boolean waitForAe,
952             List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
953         return captureRawShots(s, captureReaders, waitForAe, captureListeners, 1,
954                 /*maxResolution*/false).get(0);
955     }
956 
captureSingleShotMaximumResolution(Size s, ImageReader captureReader, boolean waitForAe, CameraTestUtils.SimpleImageReaderListener captureListener)957     private Pair<Image, CaptureResult> captureSingleShotMaximumResolution(Size s,
958             ImageReader captureReader, boolean waitForAe,
959             CameraTestUtils.SimpleImageReaderListener captureListener)
960             throws Exception {
961         List<ImageReader> readers = new ArrayList<ImageReader>();
962         readers.add(captureReader);
963         List<CameraTestUtils.SimpleImageReaderListener> listeners =
964                 new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
965         listeners.add(captureListener);
966         Pair<List<Image>, CaptureResult> res = captureRawShots(s, readers, waitForAe,
967                 listeners, /*numShots*/ 1, /*maxResolution*/ true).get(0);
968         return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
969     }
970 
captureReprocessedRawShot(Size sz, ImageReader inputReader, ImageReader reprocessOutputReader, CameraTestUtils.SimpleImageReaderListener inputReaderListener, CameraTestUtils.SimpleImageReaderListener reprocessReaderListener, boolean waitForAe)971     private Pair<Image, CaptureResult> captureReprocessedRawShot(Size sz,
972             ImageReader inputReader,
973             ImageReader reprocessOutputReader,
974             CameraTestUtils.SimpleImageReaderListener inputReaderListener,
975             CameraTestUtils.SimpleImageReaderListener reprocessReaderListener,
976             boolean waitForAe) throws Exception {
977 
978         InputConfiguration inputConfig =
979             new InputConfiguration(sz.getWidth(), sz.getHeight(), ImageFormat.RAW_SENSOR);
980         CameraTestUtils.SimpleCaptureCallback inputCaptureListener =
981                 new CameraTestUtils.SimpleCaptureCallback();
982         CameraTestUtils.SimpleCaptureCallback reprocessOutputCaptureListener =
983                 new CameraTestUtils.SimpleCaptureCallback();
984 
985         inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
986         reprocessOutputReader.setOnImageAvailableListener(reprocessReaderListener, mHandler);
987 
988         ArrayList<Surface> outputSurfaces = new ArrayList<Surface>();
989         outputSurfaces.add(inputReader.getSurface());
990         outputSurfaces.add(reprocessOutputReader.getSurface());
991         BlockingSessionCallback sessionListener = new BlockingSessionCallback();
992         ImageReader previewReader = null;
993         if (waitForAe) {
994             // Also setup a small YUV output for AE metering if needed
995             Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null :
996                     mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1);
997             assertNotNull("Must support at least one small YUV size.", yuvSize);
998             previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888,
999                         /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener());
1000             outputSurfaces.add(previewReader.getSurface());
1001         }
1002 
1003         createReprocessableSession(inputConfig, outputSurfaces);
1004 
1005         if (waitForAe) {
1006             CaptureRequest.Builder precaptureRequest =
1007                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1008             assertNotNull("Fail to get captureRequest", precaptureRequest);
1009             precaptureRequest.addTarget(previewReader.getSurface());
1010             precaptureRequest.set(CaptureRequest.CONTROL_MODE,
1011                     CaptureRequest.CONTROL_MODE_AUTO);
1012             precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE,
1013                     CaptureRequest.CONTROL_AE_MODE_ON);
1014 
1015             final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false);
1016             CameraCaptureSession.CaptureCallback captureCallback =
1017                     new CameraCaptureSession.CaptureCallback() {
1018                 @Override
1019                 public void onCaptureProgressed(CameraCaptureSession session,
1020                         CaptureRequest request, CaptureResult partialResult) {
1021                     Integer aeState = partialResult.get(CaptureResult.CONTROL_AE_STATE);
1022                     if (aeState != null &&
1023                             (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED ||
1024                              aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED)) {
1025                         waitForAeCondition.open();
1026                     }
1027                 }
1028 
1029                 @Override
1030                 public void onCaptureCompleted(CameraCaptureSession session,
1031                         CaptureRequest request, TotalCaptureResult result) {
1032                     int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
1033                     if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED ||
1034                             aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
1035                         waitForAeCondition.open();
1036                     }
1037                 }
1038             };
1039 
1040             startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler);
1041 
1042             precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1043                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1044             startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler);
1045             assertTrue("Timeout out waiting for AE to converge",
1046                     waitForAeCondition.block(AE_TIMEOUT_MS));
1047         }
1048         ImageWriter inputWriter =
1049                 ImageWriter.newInstance(mCameraSession.getInputSurface(), 1);
1050         // Prepare a request for reprocess input
1051         CaptureRequest.Builder builder = mCamera.createCaptureRequest(
1052                 CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
1053         builder.addTarget(inputReader.getSurface());
1054         // This is a max resolution capture
1055         builder.set(CaptureRequest.SENSOR_PIXEL_MODE,
1056                 CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
1057         CaptureRequest inputRequest = builder.build();
1058         mCameraSession.capture(inputRequest, inputCaptureListener, mHandler);
1059         List<CaptureRequest> reprocessCaptureRequests = new ArrayList<>();
1060 
1061         TotalCaptureResult inputResult =
1062                 inputCaptureListener.getTotalCaptureResult(
1063                         MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS);
1064         builder = mCamera.createReprocessCaptureRequest(inputResult);
1065         inputWriter.queueInputImage(inputReaderListener.getImage(
1066                         MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS));
1067         builder.addTarget(reprocessOutputReader.getSurface());
1068         reprocessCaptureRequests.add(builder.build());
1069         mCameraSession.captureBurst(reprocessCaptureRequests, reprocessOutputCaptureListener,
1070                 mHandler);
1071         TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
1072                 CAPTURE_WAIT_TIMEOUT_MS);
1073         return new Pair<Image, CaptureResult>(reprocessReaderListener.getImage(
1074                 MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_MS), result);
1075     }
1076 
1077     /**
1078      * Capture raw images.
1079      *
1080      * <p>Capture raw images for a given size.</p>
1081      *
1082      * @param sz The size of the raw image to capture.  Must be one of the available sizes for this
1083      *          device.
1084      *
1085      * @param captureReaders The image readers which are associated with the targets for this
1086      *        capture.
1087      *
1088      * @param waitForAe Whether we should wait for AE to converge before capturing outputs for
1089      *                  the captureReaders targets
1090      *
1091      * @param captureListeners ImageReader listeners which wait on the captured images to be
1092      *                         available.
1093      *
1094      * @param numShots The number of shots to be captured
1095      *
1096      * @param maxResolution Whether the target in captureReaders are max resolution captures. If
1097      *                      this is set to true, captureReaders.size() must be == 1 ( in order to
1098      *                      satisfy mandatory streams for maximum resolution sensor pixel mode).
1099      *
1100      * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for
1101      *          each capture.
1102      */
captureRawShots(Size sz, List<ImageReader> captureReaders, boolean waitForAe, List<CameraTestUtils.SimpleImageReaderListener> captureListeners, int numShots, boolean maxResolution)1103     private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size sz,
1104             List<ImageReader> captureReaders, boolean waitForAe,
1105             List<CameraTestUtils.SimpleImageReaderListener> captureListeners,
1106             int numShots, boolean maxResolution) throws Exception {
1107         if (VERBOSE) {
1108             Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
1109         }
1110 
1111         int timeoutScale = maxResolution ? MAX_RESOLUTION_CAPTURE_WAIT_TIMEOUT_SCALE : 1;
1112         Size[] targetCaptureSizes =
1113                 mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
1114                         StaticMetadata.StreamDirection.Output, /*fastSizes*/ true,
1115                         /*slowSizes*/ true, maxResolution);
1116 
1117         if (maxResolution) {
1118             assertTrue("Maximum number of maximum resolution targets for a session should be 1 as" +
1119                 " per the mandatory streams guarantee", captureReaders.size() == 1);
1120         }
1121 
1122         // Validate size
1123         boolean validSize = false;
1124         for (int i = 0; i < targetCaptureSizes.length; ++i) {
1125             if (targetCaptureSizes[i].equals(sz)) {
1126                 validSize = true;
1127                 break;
1128             }
1129         }
1130         assertTrue("Capture size is supported.", validSize);
1131 
1132         // Capture images.
1133         final List<Surface> outputSurfaces = new ArrayList<Surface>();
1134         for (ImageReader captureReader : captureReaders) {
1135             Surface captureSurface = captureReader.getSurface();
1136             outputSurfaces.add(captureSurface);
1137         }
1138 
1139         // Set up still capture template targeting JPEG/RAW outputs
1140         CaptureRequest.Builder request =
1141                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
1142         request.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
1143                 CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF);
1144         assertNotNull("Fail to get captureRequest", request);
1145         for (Surface surface : outputSurfaces) {
1146             request.addTarget(surface);
1147         }
1148 
1149         ImageReader previewReader = null;
1150         if (waitForAe) {
1151             // Also setup a small YUV output for AE metering if needed
1152             Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null :
1153                     mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1);
1154             assertNotNull("Must support at least one small YUV size.", yuvSize);
1155             previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888,
1156                         /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener());
1157             outputSurfaces.add(previewReader.getSurface());
1158         }
1159 
1160         createSession(outputSurfaces);
1161 
1162         if (waitForAe) {
1163             CaptureRequest.Builder precaptureRequest =
1164                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1165             assertNotNull("Fail to get captureRequest", precaptureRequest);
1166             precaptureRequest.addTarget(previewReader.getSurface());
1167             precaptureRequest.set(CaptureRequest.CONTROL_MODE,
1168                     CaptureRequest.CONTROL_MODE_AUTO);
1169             precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE,
1170                     CaptureRequest.CONTROL_AE_MODE_ON);
1171 
1172             final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false);
1173             CameraCaptureSession.CaptureCallback captureCallback =
1174                     new CameraCaptureSession.CaptureCallback() {
1175                 @Override
1176                 public void onCaptureProgressed(CameraCaptureSession session,
1177                         CaptureRequest request, CaptureResult partialResult) {
1178                     Integer aeState = partialResult.get(CaptureResult.CONTROL_AE_STATE);
1179                     if (aeState != null &&
1180                             (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED ||
1181                              aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED)) {
1182                         waitForAeCondition.open();
1183                     }
1184                 }
1185 
1186                 @Override
1187                 public void onCaptureCompleted(CameraCaptureSession session,
1188                         CaptureRequest request, TotalCaptureResult result) {
1189                     int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
1190                     if (aeState == CaptureRequest.CONTROL_AE_STATE_CONVERGED ||
1191                             aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
1192                         waitForAeCondition.open();
1193                     }
1194                 }
1195             };
1196             startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler);
1197 
1198             precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1199                     CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1200             startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler);
1201             assertTrue("Timeout out waiting for AE to converge",
1202                     waitForAeCondition.block(AE_TIMEOUT_MS));
1203         }
1204 
1205         request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
1206                 CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
1207         if (maxResolution) {
1208             request.set(CaptureRequest.SENSOR_PIXEL_MODE,
1209                     CaptureRequest.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
1210         }
1211         CameraTestUtils.SimpleCaptureCallback resultListener =
1212                 new CameraTestUtils.SimpleCaptureCallback();
1213 
1214         CaptureRequest request1 = request.build();
1215         for (int i = 0; i < numShots; i++) {
1216             startCapture(request1, /*repeating*/false, resultListener, mHandler);
1217         }
1218         List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>();
1219         for (int i = 0; i < numShots; i++) {
1220             // Verify capture result and images
1221             CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
1222 
1223             List<Image> resultImages = new ArrayList<Image>();
1224             for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
1225                 Image captureImage =
1226                         captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS * timeoutScale);
1227 
1228             /*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
1229                     ImageFormat.RAW_SENSOR, null);*/
1230                 resultImages.add(captureImage);
1231             }
1232             ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result));
1233         }
1234         // Stop capture, delete the streams.
1235         stopCapture(/*fast*/false);
1236 
1237         return ret;
1238     }
1239 
1240     /**
1241      * Use the DNG SDK to validate a DNG file stored in the buffer.
1242      *
1243      * Returns false if the DNG has validation errors. Validation warnings/errors
1244      * will be printed to logcat.
1245      */
validateDngNative(byte[] dngBuffer)1246     private static native boolean validateDngNative(byte[] dngBuffer);
1247 }
1248