1 /* 2 * Copyright (C) 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 com.android.camera.util; 18 19 import android.hardware.camera2.CameraMetadata; 20 import android.hardware.camera2.CaptureRequest; 21 import android.hardware.camera2.CaptureResult; 22 import android.hardware.camera2.params.ColorSpaceTransform; 23 import android.hardware.camera2.params.RggbChannelVector; 24 import android.hardware.camera2.params.TonemapCurve; 25 import android.util.Pair; 26 import android.util.Rational; 27 28 import com.android.camera.debug.Log; 29 import com.android.camera.debug.Log.Tag; 30 31 import java.io.BufferedWriter; 32 import java.io.File; 33 import java.io.FileWriter; 34 import java.io.IOException; 35 import java.io.StringWriter; 36 import java.io.Writer; 37 import java.lang.reflect.Array; 38 import java.util.Arrays; 39 import java.util.List; 40 41 /** 42 * Can be used for debugging to output details about Camera2 capture request and 43 * responses. 44 */ 45 public class CaptureDataSerializer { 46 private static interface Writeable { 47 public void write(Writer writer) throws IOException; 48 } 49 50 private static final Tag TAG = new Tag("CaptureDataSerilzr"); 51 52 /** 53 * Generate a human-readable string of the given capture request and return 54 * it. 55 */ 56 public static String toString(String title, CaptureRequest metadata) { 57 StringWriter writer = new StringWriter(); 58 dumpMetadata(title, metadata, writer); 59 return writer.toString(); 60 } 61 62 /** 63 * Generate a human-readable string of the given capture request and write 64 * it to the given file. 65 */ 66 public static void toFile(String title, CameraMetadata<?> metadata, File file) { 67 try { 68 // Will append if the file already exists. 69 FileWriter writer = new FileWriter(file, true); 70 if (metadata instanceof CaptureRequest) { 71 dumpMetadata(title, (CaptureRequest) metadata, writer); 72 } else if (metadata instanceof CaptureResult) { 73 dumpMetadata(title, (CaptureResult) metadata, writer); 74 } else { 75 writer.close(); 76 throw new IllegalArgumentException("Cannot generate debug data from type " 77 + metadata.getClass().getName()); 78 } 79 writer.close(); 80 } catch (IOException ex) { 81 Log.e(TAG, "Could not write capture data to file.", ex); 82 } 83 } 84 85 /** 86 * Writes the data about the marker and requests to the given folder for 87 * offline debugging. 88 */ 89 private static void dumpMetadata(final String title, final CaptureRequest metadata, 90 Writer writer) { 91 Writeable writeable = new Writeable() { 92 @Override 93 public void write(Writer writer) throws IOException { 94 List<CaptureRequest.Key<?>> keys = metadata.getKeys(); 95 writer.write(title + '\n'); 96 97 // TODO: move to CameraMetadata#toString ? 98 for (CaptureRequest.Key<?> key : keys) { 99 writer.write(String.format(" %s\n", key.getName())); 100 writer.write(String.format(" %s\n", 101 metadataValueToString(metadata.get(key)))); 102 } 103 } 104 }; 105 dumpMetadata(writeable, new BufferedWriter(writer)); 106 } 107 108 /** 109 * Writes the data about the marker and requests to the given folder for 110 * offline debugging. 111 */ 112 private static void dumpMetadata(final String title, final CaptureResult metadata, 113 Writer writer) { 114 Writeable writeable = new Writeable() { 115 @Override 116 public void write(Writer writer) throws IOException { 117 List<CaptureResult.Key<?>> keys = metadata.getKeys(); 118 writer.write(String.format(title)); 119 120 // TODO: move to CameraMetadata#toString ? 121 for (CaptureResult.Key<?> key : keys) { 122 writer.write(String.format(" %s\n", key.getName())); 123 writer.write(String.format(" %s\n", 124 metadataValueToString(metadata.get(key)))); 125 } 126 } 127 }; 128 dumpMetadata(writeable, new BufferedWriter(writer)); 129 } 130 131 private static String metadataValueToString(Object object) { 132 if (object == null) { 133 return "<null>"; 134 } 135 if (object.getClass().isArray()) { 136 StringBuilder builder = new StringBuilder(); 137 builder.append("["); 138 139 int length = Array.getLength(object); 140 for (int i = 0; i < length; ++i) { 141 Object item = Array.get(object, i); 142 builder.append(metadataValueToString(item)); 143 144 if (i != length - 1) { 145 builder.append(", "); 146 } 147 } 148 builder.append(']'); 149 150 return builder.toString(); 151 } else { 152 // These classes don't have a toString() method yet 153 // See: http://b/16899576 154 if (object instanceof RggbChannelVector) { 155 return toString((RggbChannelVector) object); 156 } else if (object instanceof ColorSpaceTransform) { 157 return toString((ColorSpaceTransform) object); 158 } else if (object instanceof TonemapCurve) { 159 return toString((TonemapCurve) object); 160 } else if (object instanceof Pair) { 161 return toString((Pair<?, ?>) object); 162 } 163 return object.toString(); 164 } 165 } 166 167 private static void dumpMetadata(Writeable metadata, Writer writer) { 168 /** 169 * Save metadata to file, appending if another metadata is already in 170 * that file. 171 */ 172 try { 173 metadata.write(writer); 174 } catch (IOException e) { 175 Log.e(TAG, "dumpMetadata - Failed to dump metadata", e); 176 } finally { 177 try { 178 if (writer != null) { 179 writer.close(); 180 } 181 } catch (IOException e) { 182 Log.e(TAG, "dumpMetadata - Failed to close writer.", e); 183 } 184 } 185 } 186 187 private static String toString(RggbChannelVector vector) { 188 StringBuilder str = new StringBuilder(); 189 str.append("RggbChannelVector:"); 190 str.append(" R:"); 191 str.append(vector.getRed()); 192 str.append(" G(even):"); 193 str.append(vector.getGreenEven()); 194 str.append(" G(odd):"); 195 str.append(vector.getGreenOdd()); 196 str.append(" B:"); 197 str.append(vector.getBlue()); 198 199 return str.toString(); 200 } 201 202 private static String toString(ColorSpaceTransform transform) { 203 StringBuilder str = new StringBuilder(); 204 Rational[] rationals = new Rational[9]; 205 transform.copyElements(rationals, 0); 206 str.append("ColorSpaceTransform: "); 207 str.append(Arrays.toString(rationals)); 208 return str.toString(); 209 } 210 211 private static String toString(TonemapCurve curve) { 212 StringBuilder str = new StringBuilder(); 213 str.append("TonemapCurve:"); 214 215 float[] reds = new float[curve.getPointCount(TonemapCurve.CHANNEL_RED) 216 * TonemapCurve.POINT_SIZE]; 217 curve.copyColorCurve(TonemapCurve.CHANNEL_RED, reds, 0); 218 float[] greens = new float[curve.getPointCount(TonemapCurve.CHANNEL_GREEN) 219 * TonemapCurve.POINT_SIZE]; 220 curve.copyColorCurve(TonemapCurve.CHANNEL_GREEN, greens, 0); 221 float[] blues = new float[curve.getPointCount(TonemapCurve.CHANNEL_BLUE) 222 * TonemapCurve.POINT_SIZE]; 223 curve.copyColorCurve(TonemapCurve.CHANNEL_BLUE, blues, 0); 224 225 str.append("\n\nReds: "); 226 str.append(Arrays.toString(reds)); 227 str.append("\n\nGreens: "); 228 str.append(Arrays.toString(greens)); 229 str.append("\n\nBlues: "); 230 str.append(Arrays.toString(blues)); 231 232 return str.toString(); 233 } 234 235 private static String toString(Pair<?, ?> pair) { 236 return "Pair: " + metadataValueToString(pair.first) + " / " 237 + metadataValueToString(pair.second); 238 } 239 } 240