blob: 2ddcb14ec1e1796c1f4edc7ce0b59bb3f3585bde [file] [log] [blame]
Eino-Ville Talvala70c22072013-08-27 12:09:04 -07001/*
2 * Copyright (C) 2013 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 android.hardware.camera2.impl;
18
Zhijun Heb7bfdc72013-10-02 11:39:43 -070019import android.graphics.ImageFormat;
20import android.graphics.Point;
21import android.graphics.Rect;
22import android.hardware.camera2.CameraCharacteristics;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070023import android.hardware.camera2.CameraMetadata;
Zhijun Heb7bfdc72013-10-02 11:39:43 -070024import android.hardware.camera2.CaptureResult;
25import android.hardware.camera2.Face;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070026import android.hardware.camera2.Rational;
27import android.os.Parcelable;
28import android.os.Parcel;
29import android.util.Log;
30
31import java.lang.reflect.Array;
32import java.nio.ByteBuffer;
33import java.nio.ByteOrder;
34import java.util.ArrayList;
35import java.util.HashMap;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070036
37/**
38 * Implementation of camera metadata marshal/unmarshal across Binder to
39 * the camera service
40 */
41public class CameraMetadataNative extends CameraMetadata implements Parcelable {
42
43 private static final String TAG = "CameraMetadataJV";
Eino-Ville Talvala227c09f2013-10-04 16:14:38 -070044 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
Zhijun Heb7bfdc72013-10-02 11:39:43 -070045 // this should be in sync with HAL_PIXEL_FORMAT_BLOB defined in graphics.h
46 private static final int NATIVE_JPEG_FORMAT = 0x21;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070047
48 public CameraMetadataNative() {
49 super();
50 mMetadataPtr = nativeAllocate();
51 if (mMetadataPtr == 0) {
52 throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
53 }
54 }
55
56 /**
57 * Copy constructor - clone metadata
58 */
59 public CameraMetadataNative(CameraMetadataNative other) {
60 super();
61 mMetadataPtr = nativeAllocateCopy(other);
62 if (mMetadataPtr == 0) {
63 throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
64 }
65 }
66
67 public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
68 new Parcelable.Creator<CameraMetadataNative>() {
69 @Override
70 public CameraMetadataNative createFromParcel(Parcel in) {
71 CameraMetadataNative metadata = new CameraMetadataNative();
72 metadata.readFromParcel(in);
73 return metadata;
74 }
75
76 @Override
77 public CameraMetadataNative[] newArray(int size) {
78 return new CameraMetadataNative[size];
79 }
80 };
81
82 @Override
83 public int describeContents() {
84 return 0;
85 }
86
87 @Override
88 public void writeToParcel(Parcel dest, int flags) {
89 nativeWriteToParcel(dest);
90 }
91
92 @SuppressWarnings("unchecked")
93 @Override
94 public <T> T get(Key<T> key) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -070095 T value = getOverride(key);
96 if (value != null) {
97 return value;
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070098 }
99
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700100 return getBase(key);
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700101 }
102
103 public void readFromParcel(Parcel in) {
104 nativeReadFromParcel(in);
105 }
106
107 /**
108 * Set a camera metadata field to a value. The field definitions can be
Igor Murashkin68f40062013-09-10 12:15:54 -0700109 * found in {@link CameraCharacteristics}, {@link CaptureResult}, and
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700110 * {@link CaptureRequest}.
111 *
112 * @param key The metadata field to write.
113 * @param value The value to set the field to, which must be of a matching
114 * type to the key.
115 */
116 public <T> void set(Key<T> key, T value) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700117 if (setOverride(key, value)) {
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700118 return;
119 }
120
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700121 setBase(key, value);
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700122 }
123
124 // Keep up-to-date with camera_metadata.h
125 /**
126 * @hide
127 */
128 public static final int TYPE_BYTE = 0;
129 /**
130 * @hide
131 */
132 public static final int TYPE_INT32 = 1;
133 /**
134 * @hide
135 */
136 public static final int TYPE_FLOAT = 2;
137 /**
138 * @hide
139 */
140 public static final int TYPE_INT64 = 3;
141 /**
142 * @hide
143 */
144 public static final int TYPE_DOUBLE = 4;
145 /**
146 * @hide
147 */
148 public static final int TYPE_RATIONAL = 5;
149 /**
150 * @hide
151 */
152 public static final int NUM_TYPES = 6;
153
154 private void close() {
155 // this sets mMetadataPtr to 0
156 nativeClose();
157 mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
158 }
159
160 private static int getTypeSize(int nativeType) {
161 switch(nativeType) {
162 case TYPE_BYTE:
163 return 1;
164 case TYPE_INT32:
165 case TYPE_FLOAT:
166 return 4;
167 case TYPE_INT64:
168 case TYPE_DOUBLE:
169 case TYPE_RATIONAL:
170 return 8;
171 }
172
173 throw new UnsupportedOperationException("Unknown type, can't get size "
174 + nativeType);
175 }
176
177 private static Class<?> getExpectedType(int nativeType) {
178 switch(nativeType) {
179 case TYPE_BYTE:
180 return Byte.TYPE;
181 case TYPE_INT32:
182 return Integer.TYPE;
183 case TYPE_FLOAT:
184 return Float.TYPE;
185 case TYPE_INT64:
186 return Long.TYPE;
187 case TYPE_DOUBLE:
188 return Double.TYPE;
189 case TYPE_RATIONAL:
190 return Rational.class;
191 }
192
193 throw new UnsupportedOperationException("Unknown type, can't map to Java type "
194 + nativeType);
195 }
196
197 @SuppressWarnings("unchecked")
198 private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
199 int nativeType, boolean sizeOnly) {
200
201 if (!sizeOnly) {
202 /**
203 * Rewrite types when the native type doesn't match the managed type
204 * - Boolean -> Byte
205 * - Integer -> Byte
206 */
207
208 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
209 // Since a boolean can't be cast to byte, and we don't want to use putBoolean
210 boolean asBool = (Boolean) value;
211 byte asByte = (byte) (asBool ? 1 : 0);
212 value = (T) (Byte) asByte;
213 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
214 int asInt = (Integer) value;
215 byte asByte = (byte) asInt;
216 value = (T) (Byte) asByte;
217 } else if (type != getExpectedType(nativeType)) {
218 throw new UnsupportedOperationException("Tried to pack a type of " + type +
219 " but we expected the type to be " + getExpectedType(nativeType));
220 }
221
222 if (nativeType == TYPE_BYTE) {
223 buffer.put((Byte) value);
224 } else if (nativeType == TYPE_INT32) {
225 buffer.putInt((Integer) value);
226 } else if (nativeType == TYPE_FLOAT) {
227 buffer.putFloat((Float) value);
228 } else if (nativeType == TYPE_INT64) {
229 buffer.putLong((Long) value);
230 } else if (nativeType == TYPE_DOUBLE) {
231 buffer.putDouble((Double) value);
232 } else if (nativeType == TYPE_RATIONAL) {
233 Rational r = (Rational) value;
234 buffer.putInt(r.getNumerator());
235 buffer.putInt(r.getDenominator());
236 }
237
238 }
239
240 return getTypeSize(nativeType);
241 }
242
243 @SuppressWarnings({"unchecked", "rawtypes"})
244 private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
245 boolean sizeOnly) {
246
247 int size = 0;
248
249 if (type.isPrimitive() || type == Rational.class) {
250 size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
251 } else if (type.isEnum()) {
252 size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
253 } else if (type.isArray()) {
254 size = packArray(value, buffer, type, nativeType, sizeOnly);
255 } else {
256 size = packClass(value, buffer, type, nativeType, sizeOnly);
257 }
258
259 return size;
260 }
261
262 private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
263 int nativeType, boolean sizeOnly) {
264
265 // TODO: add support for enums with their own values.
266 return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
267 }
268
269 @SuppressWarnings("unchecked")
270 private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
271 boolean sizeOnly) {
272
273 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
274 if (marshaler == null) {
275 throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
276 }
277
278 return marshaler.marshal(value, buffer, nativeType, sizeOnly);
279 }
280
281 private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
282 boolean sizeOnly) {
283
284 int size = 0;
285 int arrayLength = Array.getLength(value);
286
287 @SuppressWarnings("unchecked")
288 Class<Object> componentType = (Class<Object>)type.getComponentType();
289
290 for (int i = 0; i < arrayLength; ++i) {
291 size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
292 }
293
294 return size;
295 }
296
297 @SuppressWarnings("unchecked")
298 private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
299
300 T val;
301
302 if (nativeType == TYPE_BYTE) {
303 val = (T) (Byte) buffer.get();
304 } else if (nativeType == TYPE_INT32) {
305 val = (T) (Integer) buffer.getInt();
306 } else if (nativeType == TYPE_FLOAT) {
307 val = (T) (Float) buffer.getFloat();
308 } else if (nativeType == TYPE_INT64) {
309 val = (T) (Long) buffer.getLong();
310 } else if (nativeType == TYPE_DOUBLE) {
311 val = (T) (Double) buffer.getDouble();
312 } else if (nativeType == TYPE_RATIONAL) {
313 val = (T) new Rational(buffer.getInt(), buffer.getInt());
314 } else {
315 throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
316 + nativeType);
317 }
318
319 /**
320 * Rewrite types when the native type doesn't match the managed type
321 * - Byte -> Boolean
322 * - Byte -> Integer
323 */
324
325 if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
326 // Since a boolean can't be cast to byte, and we don't want to use getBoolean
327 byte asByte = (Byte) val;
328 boolean asBool = asByte != 0;
329 val = (T) (Boolean) asBool;
330 } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
331 byte asByte = (Byte) val;
332 int asInt = asByte;
333 val = (T) (Integer) asInt;
334 } else if (type != getExpectedType(nativeType)) {
335 throw new UnsupportedOperationException("Tried to unpack a type of " + type +
336 " but we expected the type to be " + getExpectedType(nativeType));
337 }
338
339 return val;
340 }
341
342 @SuppressWarnings({"unchecked", "rawtypes"})
343 private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
344
345 if (type.isPrimitive() || type == Rational.class) {
346 return unpackSingleNative(buffer, type, nativeType);
347 }
348
349 if (type.isEnum()) {
350 return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
351 }
352
353 if (type.isArray()) {
354 return unpackArray(buffer, type, nativeType);
355 }
356
357 T instance = unpackClass(buffer, type, nativeType);
358
359 return instance;
360 }
361
362 private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
363 int nativeType) {
364 int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
365 return getEnumFromValue(type, ordinal);
366 }
367
368 private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
369
370 MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
371 if (marshaler == null) {
372 throw new IllegalArgumentException("Unknown class type: " + type);
373 }
374
375 return marshaler.unmarshal(buffer, nativeType);
376 }
377
378 @SuppressWarnings("unchecked")
379 private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
380
381 Class<?> componentType = type.getComponentType();
382 Object array;
383
384 int elementSize = getTypeSize(nativeType);
385
386 MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
387 if (marshaler != null) {
388 elementSize = marshaler.getNativeSize(nativeType);
389 }
390
391 if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
392 int remaining = buffer.remaining();
393 int arraySize = remaining / elementSize;
394
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700395 if (VERBOSE) {
396 Log.v(TAG,
397 String.format(
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700398 "Attempting to unpack array (count = %d, element size = %d, bytes " +
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700399 "remaining = %d) for type %s",
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700400 arraySize, elementSize, remaining, type));
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700401 }
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700402
403 array = Array.newInstance(componentType, arraySize);
404 for (int i = 0; i < arraySize; ++i) {
405 Object elem = unpackSingle(buffer, componentType, nativeType);
406 Array.set(array, i, elem);
407 }
408 } else {
409 // Dynamic size, use an array list.
410 ArrayList<Object> arrayList = new ArrayList<Object>();
411
412 int primitiveSize = getTypeSize(nativeType);
413 while (buffer.remaining() >= primitiveSize) {
414 Object elem = unpackSingle(buffer, componentType, nativeType);
415 arrayList.add(elem);
416 }
417
418 array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
419 }
420
421 if (buffer.remaining() != 0) {
422 Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
423 + type);
424 }
425
426 return (T) array;
427 }
428
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700429 private <T> T getBase(Key<T> key) {
430 int tag = key.getTag();
431 byte[] values = readValues(tag);
432 if (values == null) {
433 return null;
434 }
435
436 int nativeType = getNativeType(tag);
437
438 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
439 return unpackSingle(buffer, key.getType(), nativeType);
440 }
441
442 // Need overwrite some metadata that has different definitions between native
443 // and managed sides.
444 @SuppressWarnings("unchecked")
445 private <T> T getOverride(Key<T> key) {
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700446 if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700447 return (T) getAvailableFormats();
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700448 } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700449 return (T) getFaces();
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700450 } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
Zhijun He2f1680b2013-11-13 13:16:56 -0800451 return (T) getFaceRectangles();
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700452 }
453
454 // For other keys, get() falls back to getBase()
455 return null;
456 }
457
458 private int[] getAvailableFormats() {
459 int[] availableFormats = getBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
Zhijun He2f1680b2013-11-13 13:16:56 -0800460 if (availableFormats != null) {
461 for (int i = 0; i < availableFormats.length; i++) {
462 // JPEG has different value between native and managed side, need override.
463 if (availableFormats[i] == NATIVE_JPEG_FORMAT) {
464 availableFormats[i] = ImageFormat.JPEG;
465 }
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700466 }
467 }
Zhijun He2f1680b2013-11-13 13:16:56 -0800468
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700469 return availableFormats;
470 }
471
472 private Face[] getFaces() {
473 final int FACE_LANDMARK_SIZE = 6;
474
Zhijun He58216c22013-10-14 15:29:37 -0700475 Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700476 if (faceDetectMode == null) {
Zhijun He58216c22013-10-14 15:29:37 -0700477 Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
478 faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
479 } else {
480 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
481 return new Face[0];
482 }
483 if (faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE &&
484 faceDetectMode != CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
485 Log.w(TAG, "Unknown face detect mode: " + faceDetectMode);
486 return new Face[0];
487 }
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700488 }
489
490 // Face scores and rectangles are required by SIMPLE and FULL mode.
Zhijun He58216c22013-10-14 15:29:37 -0700491 byte[] faceScores = get(CaptureResult.STATISTICS_FACE_SCORES);
492 Rect[] faceRectangles = get(CaptureResult.STATISTICS_FACE_RECTANGLES);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700493 if (faceScores == null || faceRectangles == null) {
Zhijun He58216c22013-10-14 15:29:37 -0700494 Log.w(TAG, "Expect face scores and rectangles to be non-null");
495 return new Face[0];
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700496 } else if (faceScores.length != faceRectangles.length) {
Zhijun He58216c22013-10-14 15:29:37 -0700497 Log.w(TAG, String.format("Face score size(%d) doesn match face rectangle size(%d)!",
498 faceScores.length, faceRectangles.length));
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700499 }
500
Zhijun He58216c22013-10-14 15:29:37 -0700501 // To be safe, make number of faces is the minimal of all face info metadata length.
502 int numFaces = Math.min(faceScores.length, faceRectangles.length);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700503 // Face id and landmarks are only required by FULL mode.
Zhijun He58216c22013-10-14 15:29:37 -0700504 int[] faceIds = get(CaptureResult.STATISTICS_FACE_IDS);
505 int[] faceLandmarks = get(CaptureResult.STATISTICS_FACE_LANDMARKS);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700506 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
507 if (faceIds == null || faceLandmarks == null) {
Zhijun He58216c22013-10-14 15:29:37 -0700508 Log.w(TAG, "Expect face ids and landmarks to be non-null for FULL mode," +
509 "fallback to SIMPLE mode");
510 faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
511 } else {
512 if (faceIds.length != numFaces ||
513 faceLandmarks.length != numFaces * FACE_LANDMARK_SIZE) {
514 Log.w(TAG, String.format("Face id size(%d), or face landmark size(%d) don't" +
515 "match face number(%d)!",
516 faceIds.length, faceLandmarks.length * FACE_LANDMARK_SIZE, numFaces));
517 }
518 // To be safe, make number of faces is the minimal of all face info metadata length.
519 numFaces = Math.min(numFaces, faceIds.length);
520 numFaces = Math.min(numFaces, faceLandmarks.length / FACE_LANDMARK_SIZE);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700521 }
522 }
523
Zhijun He844b3522013-10-16 16:13:50 -0700524 ArrayList<Face> faceList = new ArrayList<Face>();
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700525 if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
526 for (int i = 0; i < numFaces; i++) {
Zhijun He844b3522013-10-16 16:13:50 -0700527 if (faceScores[i] <= Face.SCORE_MAX &&
528 faceScores[i] >= Face.SCORE_MIN) {
529 faceList.add(new Face(faceRectangles[i], faceScores[i]));
530 }
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700531 }
532 } else {
533 // CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL
534 for (int i = 0; i < numFaces; i++) {
Zhijun He844b3522013-10-16 16:13:50 -0700535 if (faceScores[i] <= Face.SCORE_MAX &&
536 faceScores[i] >= Face.SCORE_MIN &&
537 faceIds[i] >= 0) {
538 Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]);
539 Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]);
540 Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]);
541 Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i],
542 leftEye, rightEye, mouth);
543 faceList.add(face);
544 }
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700545 }
546 }
Zhijun He844b3522013-10-16 16:13:50 -0700547 Face[] faces = new Face[faceList.size()];
548 faceList.toArray(faces);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700549 return faces;
550 }
551
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700552 // Face rectangles are defined as (left, top, right, bottom) instead of
553 // (left, top, width, height) at the native level, so the normal Rect
554 // conversion that does (l, t, w, h) -> (l, t, r, b) is unnecessary. Undo
555 // that conversion here for just the faces.
Zhijun He2f1680b2013-11-13 13:16:56 -0800556 private Rect[] getFaceRectangles() {
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700557 Rect[] faceRectangles = getBase(CaptureResult.STATISTICS_FACE_RECTANGLES);
558 if (faceRectangles == null) return null;
559
560 Rect[] fixedFaceRectangles = new Rect[faceRectangles.length];
561 for (int i = 0; i < faceRectangles.length; i++) {
562 fixedFaceRectangles[i] = new Rect(
563 faceRectangles[i].left,
564 faceRectangles[i].top,
565 faceRectangles[i].right - faceRectangles[i].left,
566 faceRectangles[i].bottom - faceRectangles[i].top);
567 }
568 return fixedFaceRectangles;
569 }
570
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700571 private <T> void setBase(Key<T> key, T value) {
572 int tag = key.getTag();
573
574 if (value == null) {
575 writeValues(tag, null);
576 return;
577 }
578
579 int nativeType = getNativeType(tag);
580
581 int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);
582
583 // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
584 byte[] values = new byte[size];
585
586 ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
587 packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);
588
589 writeValues(tag, values);
590 }
591
592 // Set the camera metadata override.
593 private <T> boolean setOverride(Key<T> key, T value) {
Eino-Ville Talvala615b75f2013-10-14 18:20:43 -0700594 if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700595 return setAvailableFormats((int[]) value);
Zhijun He2f1680b2013-11-13 13:16:56 -0800596 } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
597 return setFaceRectangles((Rect[]) value);
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700598 }
599
600 // For other keys, set() falls back to setBase().
601 return false;
602 }
603
604 private boolean setAvailableFormats(int[] value) {
605 int[] availableFormat = value;
606 if (value == null) {
607 // Let setBase() to handle the null value case.
608 return false;
609 }
610
611 int[] newValues = new int[availableFormat.length];
612 for (int i = 0; i < availableFormat.length; i++) {
613 newValues[i] = availableFormat[i];
614 if (availableFormat[i] == ImageFormat.JPEG) {
615 newValues[i] = NATIVE_JPEG_FORMAT;
616 }
617 }
618
619 setBase(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, newValues);
620 return true;
621 }
622
Zhijun He2f1680b2013-11-13 13:16:56 -0800623 /**
624 * Convert Face Rectangles from managed side to native side as they have different definitions.
625 * <p>
626 * Managed side face rectangles are defined as: left, top, width, height.
627 * Native side face rectangles are defined as: left, top, right, bottom.
628 * The input face rectangle need to be converted to native side definition when set is called.
629 * </p>
630 *
631 * @param faceRects Input face rectangles.
632 * @return true if face rectangles can be set successfully. Otherwise, Let the caller
633 * (setBase) to handle it appropriately.
634 */
635 private boolean setFaceRectangles(Rect[] faceRects) {
636 if (faceRects == null) {
637 return false;
638 }
639
640 Rect[] newFaceRects = new Rect[faceRects.length];
641 for (int i = 0; i < newFaceRects.length; i++) {
642 newFaceRects[i] = new Rect(
643 faceRects[i].left,
644 faceRects[i].top,
645 faceRects[i].right + faceRects[i].left,
646 faceRects[i].bottom + faceRects[i].top);
647 }
648
649 setBase(CaptureResult.STATISTICS_FACE_RECTANGLES, newFaceRects);
650 return true;
651 }
652
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700653 private long mMetadataPtr; // native CameraMetadata*
654
655 private native long nativeAllocate();
656 private native long nativeAllocateCopy(CameraMetadataNative other)
657 throws NullPointerException;
658
659 private native synchronized void nativeWriteToParcel(Parcel dest);
660 private native synchronized void nativeReadFromParcel(Parcel source);
661 private native synchronized void nativeSwap(CameraMetadataNative other)
662 throws NullPointerException;
663 private native synchronized void nativeClose();
664 private native synchronized boolean nativeIsEmpty();
665 private native synchronized int nativeGetEntryCount();
666
667 private native synchronized byte[] nativeReadValues(int tag);
668 private native synchronized void nativeWriteValues(int tag, byte[] src);
669
670 private static native int nativeGetTagFromKey(String keyName)
671 throws IllegalArgumentException;
672 private static native int nativeGetTypeFromTag(int tag)
673 throws IllegalArgumentException;
674 private static native void nativeClassInit();
675
676 /**
677 * <p>Perform a 0-copy swap of the internal metadata with another object.</p>
678 *
679 * <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
680 *
681 * @param other Metadata to swap with
682 * @throws NullPointerException if other was null
683 * @hide
684 */
685 public void swap(CameraMetadataNative other) {
686 nativeSwap(other);
687 }
688
689 /**
690 * @hide
691 */
692 public int getEntryCount() {
693 return nativeGetEntryCount();
694 }
695
696 /**
697 * Does this metadata contain at least 1 entry?
698 *
699 * @hide
700 */
701 public boolean isEmpty() {
702 return nativeIsEmpty();
703 }
704
705 /**
706 * Convert a key string into the equivalent native tag.
707 *
708 * @throws IllegalArgumentException if the key was not recognized
709 * @throws NullPointerException if the key was null
710 *
711 * @hide
712 */
713 public static int getTag(String key) {
714 return nativeGetTagFromKey(key);
715 }
716
717 /**
718 * Get the underlying native type for a tag.
719 *
720 * @param tag An integer tag, see e.g. {@link #getTag}
721 * @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
722 *
723 * @hide
724 */
725 public static int getNativeType(int tag) {
726 return nativeGetTypeFromTag(tag);
727 }
728
729 /**
730 * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
731 * the entry if src was null.</p>
732 *
733 * <p>An empty array can be passed in to update the entry to 0 elements.</p>
734 *
735 * @param tag An integer tag, see e.g. {@link #getTag}
736 * @param src An array of bytes, or null to erase the entry
737 *
738 * @hide
739 */
740 public void writeValues(int tag, byte[] src) {
741 nativeWriteValues(tag, src);
742 }
743
744 /**
745 * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
746 * the data properly.</p>
747 *
748 * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
749 *
750 * @param tag An integer tag, see e.g. {@link #getTag}
751 *
752 * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
753 * @hide
754 */
755 public byte[] readValues(int tag) {
Zhijun Heb7bfdc72013-10-02 11:39:43 -0700756 // TODO: Optimization. Native code returns a ByteBuffer instead.
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700757 return nativeReadValues(tag);
758 }
759
760 @Override
761 protected void finalize() throws Throwable {
762 try {
763 close();
764 } finally {
765 super.finalize();
766 }
767 }
768
769 private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
770 new HashMap<Class<? extends Enum>, int[]>();
771 /**
772 * Register a non-sequential set of values to be used with the pack/unpack functions.
773 * This enables get/set to correctly marshal the enum into a value that is C-compatible.
774 *
775 * @param enumType The class for an enum
776 * @param values A list of values mapping to the ordinals of the enum
777 *
778 * @hide
779 */
780 public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
781 if (enumType.getEnumConstants().length != values.length) {
782 throw new IllegalArgumentException(
783 "Expected values array to be the same size as the enumTypes values "
784 + values.length + " for type " + enumType);
785 }
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700786 if (VERBOSE) {
787 Log.v(TAG, "Registered enum values for type " + enumType + " values");
788 }
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700789
790 sEnumValues.put(enumType, values);
791 }
792
793 /**
794 * Get the numeric value from an enum. This is usually the same as the ordinal value for
795 * enums that have fully sequential values, although for C-style enums the range of values
796 * may not map 1:1.
797 *
798 * @param enumValue Enum instance
799 * @return Int guaranteed to be ABI-compatible with the C enum equivalent
800 */
801 private static <T extends Enum<T>> int getEnumValue(T enumValue) {
802 int[] values;
803 values = sEnumValues.get(enumValue.getClass());
804
805 int ordinal = enumValue.ordinal();
806 if (values != null) {
807 return values[ordinal];
808 }
809
810 return ordinal;
811 }
812
813 /**
814 * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
815 *
816 * @param enumType Class of the enum we want to find
817 * @param value The numeric value of the enum
818 * @return An instance of the enum
819 */
820 private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
821 int ordinal;
822
823 int[] registeredValues = sEnumValues.get(enumType);
824 if (registeredValues != null) {
825 ordinal = -1;
826
827 for (int i = 0; i < registeredValues.length; ++i) {
828 if (registeredValues[i] == value) {
829 ordinal = i;
830 break;
831 }
832 }
833 } else {
834 ordinal = value;
835 }
836
837 T[] values = enumType.getEnumConstants();
838
839 if (ordinal < 0 || ordinal >= values.length) {
840 throw new IllegalArgumentException(
841 String.format(
842 "Argument 'value' (%d) was not a valid enum value for type %s "
843 + "(registered? %b)",
844 value,
845 enumType, (registeredValues != null)));
846 }
847
848 return values[ordinal];
849 }
850
851 static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
852 HashMap<Class<?>, MetadataMarshalClass<?>>();
853
854 private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
855 sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
856 }
857
858 @SuppressWarnings("unchecked")
859 private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
860 MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
861
862 if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
863 throw new UnsupportedOperationException("Unsupported type " + nativeType +
864 " to be marshalled to/from a " + type);
865 }
866
867 return marshaler;
868 }
869
870 /**
871 * We use a class initializer to allow the native code to cache some field offsets
872 */
873 static {
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700874 nativeClassInit();
875
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700876 if (VERBOSE) {
877 Log.v(TAG, "Shall register metadata marshalers");
878 }
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700879
880 // load built-in marshallers
881 registerMarshaler(new MetadataMarshalRect());
882 registerMarshaler(new MetadataMarshalSize());
883 registerMarshaler(new MetadataMarshalString());
884
Eino-Ville Talvala51ca8d62013-10-04 14:39:58 -0700885 if (VERBOSE) {
886 Log.v(TAG, "Registered metadata marshalers");
887 }
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700888 }
889
890}