Camera tests: Add variable burst test

By default, this test runs through a range of application-set
exposure, frame duration, and sensitivity values, and verifies
that the duration of capture, and in some cases the brightness,
match what's expected.

Optionally, it can use environment variables to specify the
burst parameters, and to enable dumping the captured YUV images
to flash for later debugging.

Change-Id: I647d360344ef3684e99c86c369e96ac82e62fc96
diff --git a/tests/camera2/CameraStreamFixture.h b/tests/camera2/CameraStreamFixture.h
index a4dc4a8..3d614db 100644
--- a/tests/camera2/CameraStreamFixture.h
+++ b/tests/camera2/CameraStreamFixture.h
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest.h>
 #include <iostream>
+#include <fstream>
 
 #include <gui/CpuConsumer.h>
 #include <gui/Surface.h>
@@ -29,6 +30,8 @@
 #include "CameraModuleFixture.h"
 #include "TestExtensions.h"
 
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
 namespace android {
 namespace camera2 {
 namespace tests {
@@ -194,6 +197,80 @@
         return format;
     }
 
+    void DumpYuvToFile(const String8 &fileName, const CpuConsumer::LockedBuffer &img) {
+        uint8_t *dataCb, *dataCr;
+        uint32_t stride;
+        uint32_t chromaStride;
+        uint32_t chromaStep;
+
+        switch (img.format) {
+            case HAL_PIXEL_FORMAT_YCbCr_420_888:
+                stride = img.stride;
+                chromaStride = img.chromaStride;
+                chromaStep = img.chromaStep;
+                dataCb = img.dataCb;
+                dataCr = img.dataCr;
+                break;
+            case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+                stride = img.width;
+                chromaStride = img.width;
+                chromaStep = 2;
+                dataCr = img.data + img.width * img.height;
+                dataCb = dataCr + 1;
+                break;
+            case HAL_PIXEL_FORMAT_YV12:
+                stride = img.stride;
+                chromaStride = ALIGN(img.width / 2, 16);
+                chromaStep = 1;
+                dataCr = img.data + img.stride * img.height;
+                dataCb = dataCr + chromaStride * img.height/2;
+                break;
+            default:
+                ALOGE("Unknown format %d, not dumping", img.format);
+                return;
+        }
+
+        // Write Y
+        FILE *yuvFile = fopen(fileName.string(), "w");
+
+        size_t bytes;
+
+        for (size_t y = 0; y < img.height; ++y) {
+            bytes = fwrite(
+                reinterpret_cast<const char*>(img.data + stride * y),
+                1, img.width, yuvFile);
+            if (bytes != img.width) {
+                ALOGE("Unable to write to file %s", fileName.string());
+                fclose(yuvFile);
+                return;
+            }
+        }
+
+        // Write Cb/Cr
+        uint8_t *src = dataCb;
+        for (int c = 0; c < 2; ++c) {
+            for (size_t y = 0; y < img.height / 2; ++y) {
+                uint8_t *px = src + y * chromaStride;
+                if (chromaStep != 1) {
+                    for (size_t x = 0; x < img.width / 2; ++x) {
+                        fputc(*px, yuvFile);
+                        px += chromaStep;
+                    }
+                } else {
+                    bytes = fwrite(reinterpret_cast<const char*>(px),
+                            1, img.width / 2, yuvFile);
+                    if (bytes != img.width / 2) {
+                        ALOGE("Unable to write to file %s", fileName.string());
+                        fclose(yuvFile);
+                        return;
+                    }
+                }
+            }
+            src = dataCr;
+        }
+        fclose(yuvFile);
+    }
+
     int mWidth;
     int mHeight;