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