Merge changes Ia6300c23,Idd2ae8ad into klp-dev

* changes:
  Camera2: Immutable metadata
  Camera2: Listener rework and other API updates
This commit is contained in:
Eino-Ville Talvala
2013-09-12 00:08:04 +00:00
committed by Android (Google) Code Review
20 changed files with 1457 additions and 1061 deletions

View File

@@ -10815,16 +10815,17 @@ package android.hardware.camera2 {
}
public abstract interface CameraDevice implements java.lang.AutoCloseable {
method public abstract void capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
method public abstract void capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void close() throws java.lang.Exception;
method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
method public abstract android.hardware.camera2.CameraProperties getProperties() throws android.hardware.camera2.CameraAccessException;
method public abstract void setErrorListener(android.hardware.camera2.CameraDevice.ErrorListener);
method public abstract void setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
method public abstract void setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener) throws android.hardware.camera2.CameraAccessException;
method public abstract void setDeviceListener(android.hardware.camera2.CameraDevice.CameraDeviceListener, android.os.Handler);
method public abstract void setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
method public abstract void waitUntilIdle() throws android.hardware.camera2.CameraAccessException;
field public static final int TEMPLATE_MANUAL = 5; // 0x5
@@ -10834,39 +10835,38 @@ package android.hardware.camera2 {
field public static final int TEMPLATE_VIDEO_SNAPSHOT = 4; // 0x4
}
public static abstract interface CameraDevice.CaptureListener {
method public abstract void onCaptureComplete(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
method public abstract void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest);
public static abstract class CameraDevice.CameraDeviceListener {
ctor public CameraDevice.CameraDeviceListener();
method public void onCameraDisconnected(android.hardware.camera2.CameraDevice);
method public void onCameraError(android.hardware.camera2.CameraDevice, int);
method public void onCameraIdle(android.hardware.camera2.CameraDevice);
field public static final int ERROR_CAMERA_DEVICE = 1; // 0x1
field public static final int ERROR_CAMERA_SERVICE = 2; // 0x2
}
public static abstract interface CameraDevice.ErrorListener {
method public abstract void onCameraDeviceError(android.hardware.camera2.CameraDevice, int);
field public static final int DEVICE_DISCONNECTED = 1; // 0x1
field public static final int DEVICE_ERROR = 2; // 0x2
field public static final int SERVICE_ERROR = 3; // 0x3
public static abstract class CameraDevice.CaptureListener {
ctor public CameraDevice.CaptureListener();
method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest);
method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
}
public final class CameraManager {
method public void addAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener, android.os.Handler);
method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method public android.hardware.camera2.CameraProperties getCameraProperties(java.lang.String) throws android.hardware.camera2.CameraAccessException;
method public java.lang.String[] getDeviceIdList() throws android.hardware.camera2.CameraAccessException;
method public android.hardware.camera2.CameraDevice openCamera(java.lang.String) throws android.hardware.camera2.CameraAccessException;
method public void registerCameraListener(android.hardware.camera2.CameraManager.CameraListener);
method public void unregisterCameraListener(android.hardware.camera2.CameraManager.CameraListener);
method public void removeAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener);
}
public static abstract interface CameraManager.CameraListener {
method public abstract void onCameraAvailable(java.lang.String);
method public abstract void onCameraUnavailable(java.lang.String);
public static abstract class CameraManager.AvailabilityListener {
ctor public CameraManager.AvailabilityListener();
method public void onCameraAvailable(java.lang.String);
method public void onCameraUnavailable(java.lang.String);
}
public class CameraMetadata implements java.lang.AutoCloseable android.os.Parcelable {
ctor public CameraMetadata();
method public void close() throws java.lang.Exception;
method public int describeContents();
method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
method public void readFromParcel(android.os.Parcel);
method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
method public void writeToParcel(android.os.Parcel, int);
public abstract class CameraMetadata {
method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>);
field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -10950,7 +10950,6 @@ package android.hardware.camera2 {
field public static final int CONTROL_SCENE_MODE_SUNSET = 10; // 0xa
field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7
field public static final int CONTROL_SCENE_MODE_UNSUPPORTED = 0; // 0x0
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int EDGE_MODE_FAST = 1; // 0x1
field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int EDGE_MODE_OFF = 0; // 0x0
@@ -10983,14 +10982,13 @@ package android.hardware.camera2 {
}
public static class CameraMetadata.Key {
ctor public CameraMetadata.Key(java.lang.String, java.lang.Class<T>);
method public final boolean equals(java.lang.Object);
method public final java.lang.String getName();
method public final int hashCode();
}
public final class CameraProperties extends android.hardware.camera2.CameraMetadata {
ctor public CameraProperties();
method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE;
@@ -11033,10 +11031,10 @@ package android.hardware.camera2 {
}
public final class CaptureRequest extends android.hardware.camera2.CameraMetadata implements android.os.Parcelable {
method public void addTarget(android.view.Surface);
method public int describeContents();
method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
method public java.lang.Object getTag();
method public void removeTarget(android.view.Surface);
method public void setTag(java.lang.Object);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE;
@@ -11086,7 +11084,17 @@ package android.hardware.camera2 {
field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE;
}
public static final class CaptureRequest.Builder {
method public void addTarget(android.view.Surface);
method public android.hardware.camera2.CaptureRequest build();
method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
method public void removeTarget(android.view.Surface);
method public void set(android.hardware.camera2.CameraMetadata.Key<T>, T);
method public void setTag(java.lang.Object);
}
public final class CaptureResult extends android.hardware.camera2.CameraMetadata {
method public T get(android.hardware.camera2.CameraMetadata.Key<T>);
field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK;
field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS;
field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM;

View File

@@ -17,6 +17,8 @@
package android.hardware.camera2;
import android.view.Surface;
import android.os.Handler;
import android.util.Log;
import java.lang.AutoCloseable;
import java.util.List;
@@ -98,6 +100,26 @@ public interface CameraDevice extends AutoCloseable {
*/
public static final int TEMPLATE_MANUAL = 5;
/**
* Get the ID of this camera device.
*
* <p>This matches the ID given to {@link CameraManager#openCamera} to instantiate this
* this camera device.</p>
*
* <p>This ID can be used to query the camera device's {@link
* CameraProperties fixed properties} with {@link
* CameraManager#getCameraProperties}.</p>
*
* <p>This method can be called even if the device has been closed or has encountered
* a serious error.</p>
*
* @return the ID for this camera device
*
* @see CameraManager#getCameraProperties
* @see CameraManager#getDeviceIdList
*/
public String getId();
/**
* Get the static properties for this camera. These are identical to the
* properties returned by {@link CameraManager#getCameraProperties}.
@@ -109,6 +131,7 @@ public interface CameraDevice extends AutoCloseable {
* @see CameraManager#getCameraProperties
*/
public CameraProperties getProperties() throws CameraAccessException;
/**
* <p>Set up a new output set of Surfaces for the camera device.</p>
*
@@ -177,10 +200,16 @@ public interface CameraDevice extends AutoCloseable {
* if the format is user-visible, it must be one of android.scaler.availableFormats;
* and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p>
*
* <p>To change the configuration after requests have been submitted to the
* device, the camera device must be idle. To idle the device, stop any
* repeating requests with {@link #stopRepeating stopRepeating}, and then
* call {@link #waitUntilIdle waitUntilIdle}.</p>
* <p>To change the output, the camera device must be idle. The device is considered
* to be idle once all in-flight and pending capture requests have been processed,
* and all output image buffers from the captures have been sent to their destination
* Surfaces.</p>
*
* <p>To reach an idle state without cancelling any submitted captures, first
* stop any repeating request/burst with {@link #stopRepeating}, and then
* wait for the {@link CameraDeviceListener#onCameraIdle} callback to be
* called. To idle as fast as possible, use {@link #flush} and wait for the
* idle callback.</p>
*
* <p>Using larger resolution outputs, or more outputs, can result in slower
* output rate from the device.</p>
@@ -193,19 +222,25 @@ public interface CameraDevice extends AutoCloseable {
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device is not idle, or has
* encountered a fatal error
*
* @see CameraDeviceListener#onCameraIdle
* @see #stopRepeating
* @see #flush
*/
public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
/**
* <p>Create a {@link CaptureRequest} initialized with template for a target
* use case. The settings are chosen to be the best options for the specific
* camera device, so it is not recommended to reuse the same request for a
* different camera device; create a request for that device and override
* the settings as desired, instead.</p>
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. The settings are chosen
* to be the best options for the specific camera device, so it is not
* recommended to reuse the same request for a different camera device;
* create a builder specific for that device and template and override the
* settings as desired, instead.</p>
*
* @param templateType An enumeration selecting the use case for this
* request; one of the CameraDevice.TEMPLATE_ values.
* @return a filled-in CaptureRequest, except for output streams
* @return a builder for a capture request, initialized with default
* settings for that template, and no output streams
*
* @throws IllegalArgumentException if the templateType is not in the list
* of supported templates.
@@ -219,7 +254,7 @@ public interface CameraDevice extends AutoCloseable {
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
*/
public CaptureRequest createCaptureRequest(int templateType)
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException;
/**
@@ -229,7 +264,10 @@ public interface CameraDevice extends AutoCloseable {
* including sensor, lens, flash, and post-processing settings.</p>
*
* <p>Each request will produce one {@link CaptureResult} and produce new
* frames for one or more target Surfaces, as defined by the request's .</p>
* frames for one or more target Surfaces, set with the CaptureRequest
* builder's {@link CaptureRequest.Builder#addTarget} method. The target
* surfaces must be configured as active outputs with
* {@link #configureOutputs} before calling this method.</p>
*
* <p>Multiple requests can be in progress at once. They are processed in
* first-in, first-out order, with minimal delays between each
@@ -242,27 +280,36 @@ public interface CameraDevice extends AutoCloseable {
* @param listener The callback object to notify once this request has been
* processed. If null, no metadata will be produced for this capture,
* although image data will still be produced.
* @param handler the handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device has been closed or the
* device has encountered a fatal error.
* @throws IllegalArgumentException If the request targets Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
*
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
public void capture(CaptureRequest request, CaptureListener listener)
public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException;
/**
* <p>Submit a list of requests to be captured in sequence as a burst. The
* Submit a list of requests to be captured in sequence as a burst. The
* burst will be captured in the minimum amount of time possible, and will
* not be interleaved with requests submitted by other capture or repeat
* calls.</p>
* calls.
*
* <p>The requests will be captured in order, each capture producing one
* {@link CaptureResult} and frames for one or more
* target {@link android.view.Surface surfaces}.</p>
* {@link CaptureResult} and image buffers for one or more target
* {@link android.view.Surface surfaces}. The target surfaces for each
* request (set with {@link CaptureRequest.Builder#addTarget}) must be
* configured as active outputs with {@link #configureOutputs} before
* calling this method.</p>
*
* <p>The main difference between this method and simply calling
* {@link #capture} repeatedly is that this method guarantees that no
@@ -273,29 +320,38 @@ public interface CameraDevice extends AutoCloseable {
* requests in the burst has been processed. If null, no metadata will be
* produced for any requests in this burst, although image data will still
* be produced.
* @param handler the handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device has been closed or the
* device has encountered a fatal error.
* @throws IllegalArgumentException If the requests target Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
*
* @see #capture
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
public void captureBurst(List<CaptureRequest> requests,
CaptureListener listener) throws CameraAccessException;
public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* <p>Request endlessly repeating capture of images by this
* CameraDevice.</p>
* Request endlessly repeating capture of images by this CameraDevice.
*
* <p>With this method, the CameraDevice will continually capture
* images using the settings in the provided {@link
* CaptureRequest}, at the maximum rate possible.</p>
* <p>With this method, the CameraDevice will continually capture images
* using the settings in the provided {@link CaptureRequest}, at the maximum
* rate possible.</p>
*
* <p>Repeating requests are a simple way for an application to maintain a
* preview or other continuous stream of frames, without having to
* continually submit identical requests through {@link #capture}.</p>
*
* <p>Repeat requests have lower priority than those submitted
* through {@link #capture} or {@link #captureBurst}, so if
* capture() is called when a repeating request is active, the
* {@link #capture} is called when a repeating request is active, the
* capture request will be processed before any further repeating
* requests are processed.<p>
*
@@ -306,20 +362,27 @@ public interface CameraDevice extends AutoCloseable {
* <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
* {@link #flush} will also clear the request.</p>
*
* <p>Calling repeat will replace a burst set up by {@link
* #setRepeatingBurst}, although any in-progress burst will be
* completed before the new repeat request will be used.</p>
* <p>Calling this method will replace any earlier repeating request or
* burst set up by this method or {@link #setRepeatingBurst}, although any
* in-progress burst will be completed before the new repeat request will be
* used.</p>
*
* @param request the request to repeat indefinitely
* @param listener The callback object to notify every time the
* request finishes processing. If null, no metadata will be
* produced for this stream of requests, although image data will
* still be produced.
* @param handler the handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
* @throws CameraAccessException if the camera device is no longer
* connected
* @throws IllegalStateException if the camera device has been closed or the
* device has encountered a fatal error.
* @throws IllegalArgumentException If the requests reference Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
*
* @see #capture
* @see #captureBurst
@@ -327,8 +390,8 @@ public interface CameraDevice extends AutoCloseable {
* @see #stopRepeating
* @see #flush
*/
public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
throws CameraAccessException;
public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* <p>Request endlessly repeating capture of a sequence of images by this
@@ -347,26 +410,33 @@ public interface CameraDevice extends AutoCloseable {
*
* <p>Repeating burst requests are a simple way for an application to
* maintain a preview or other continuous stream of frames where each
* request is different in a predicatable way, without having to submit
* requests through {@link #capture} at video rates.</p>
* request is different in a predicatable way, without having to continually
* submit requests through {@link #captureBurst} .</p>
*
* <p>To stop the repeating capture, call {@link #stopRepeating}. Any
* ongoing burst will still be completed, however. Calling
* {@link #flush} will also clear the request.</p>
*
* <p>Calling repeatBurst will replace a repeating request set up by
* {@link #setRepeatingRequest}, although any in-progress capture will be completed
* before the new repeat burst will be used.</p>
* <p>Calling this method will replace a previously-set repeating request or
* burst set up by this method or {@link #setRepeatingRequest}, although any
* in-progress burst will be completed before the new repeat burst will be
* used.</p>
*
* @param requests the list of requests to cycle through indefinitely
* @param listener The callback object to notify each time one of the
* requests in the repeating bursts has finished processing. If null, no
* metadata will be produced for this stream of requests, although image
* data will still be produced.
* @param handler the handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device has been closed or the
* device has encountered a fatal error.
* @throws IllegalArgumentException If the requests reference Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
*
* @see #capture
* @see #captureBurst
@@ -374,8 +444,8 @@ public interface CameraDevice extends AutoCloseable {
* @see #stopRepeating
* @see #flush
*/
public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
throws CameraAccessException;
public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* <p>Cancel any ongoing repeating capture set by either
@@ -385,8 +455,9 @@ public interface CameraDevice extends AutoCloseable {
*
* <p>Any currently in-flight captures will still complete, as will any
* burst that is mid-capture. To ensure that the device has finished
* processing all of its capture requests and is in idle state, use the
* {@link #waitUntilIdle waitUntilIdle} method.</p>
* processing all of its capture requests and is in idle state, wait for the
* {@link CameraDeviceListener#onCameraIdle} callback after calling this
* method..</p>
*
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device has been closed or the
@@ -394,7 +465,7 @@ public interface CameraDevice extends AutoCloseable {
*
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #waitUntilIdle
* @see CameraDeviceListener#onCameraIdle
*
* @throws CameraAccessException if the camera device is no longer connected
* @throws IllegalStateException if the camera device has been closed, the
@@ -429,16 +500,27 @@ public interface CameraDevice extends AutoCloseable {
public void waitUntilIdle() throws CameraAccessException;
/**
* Set the error listener object to call when an asynchronous error
* occurs. The errors reported here are only device-wide errors; errors
* about individual requests or frames are reported through
* {@link CaptureListener#onCaptureFailed}.
* Set the listener object to call when an asynchronous device event occurs,
* such as errors or idle notifications.
*
* @param listener the ErrorListener to send asynchronous error
* notifications to. Setting this to null will stop notifications about
* asynchronous errors.
* <p>The events reported here are device-wide; notifications about
* individual capture requests or capture results are reported through
* {@link CaptureListener}.</p>
*
* <p>If the camera device is idle when the listener is set, then the
* {@link CameraDeviceListener#onCameraIdle} method will be immediately called,
* even if the device has never been active before.
* </p>
*
* @param listener the CameraDeviceListener to send device-level event
* notifications to. Setting this to null will stop notifications.
* @param handler the handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper looper}.
*
* @throws IllegalArgumentException if handler is null, the listener is
* not null, and the calling thread has no looper
*/
public void setErrorListener(ErrorListener listener);
public void setDeviceListener(CameraDeviceListener listener, Handler handler);
/**
* Flush all captures currently pending and in-progress as fast as
@@ -446,7 +528,7 @@ public interface CameraDevice extends AutoCloseable {
*
* <p>The camera device will discard all of its current work as fast as
* possible. Some in-flight captures may complete successfully and call
* {@link CaptureListener#onCaptureComplete}, while others will trigger
* {@link CaptureListener#onCaptureCompleted}, while others will trigger
* their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
* request or a repeating burst is set, it will be cleared by the flush.</p>
*
@@ -483,14 +565,60 @@ public interface CameraDevice extends AutoCloseable {
// TODO: We should decide on the behavior of in-flight requests should be on close.
/**
* A listener for receiving metadata about completed image captures. The
* metadata includes, among other things, the final capture settings and the
* state of the control algorithms.
* <p>A listener for tracking the progress of a {@link CaptureRequest}
* submitted to the camera device.</p>
*
* <p>This listener is called when a request triggers a capture to start,
* and when the capture is complete. In case on an error capturing an image,
* the error method is triggered instead of the completion method.</p>
*
* @see #capture
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*
*/
public interface CaptureListener {
public static abstract class CaptureListener {
/**
* <p>Called when a capture request has been processed by a
* {@link CameraDevice}.</p>
* This method is called when the camera device has started capturing
* the output image for the request, at the beginning of image exposure.
*
* <p>This callback is invoked right as the capture of a frame begins,
* so it is the most appropriate time for playing a shutter sound,
* or triggering UI indicators of capture.</p>
*
* <p>The request that is being used for this capture is provided, along
* with the actual timestamp for the start of exposure. This timestamp
* matches the timestamp that will be included in
* {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
* and in the buffers sent to each output Surface. These buffer
* timestamps are accessible through, for example,
* {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
* {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
*
* <p>For the simplest way to play a shutter sound camera shutter or a
* video recording start/stop sound, see the
* {@link android.media.MediaActionSound} class.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera the CameraDevice sending the callback
* @param request the request for the capture that just begun
* @param timestamp the timestamp at start of capture, in nanoseconds.
*
* @see android.media.MediaActionSound
*/
public void onCaptureStarted(CameraDevice camera,
CaptureRequest request, long timestamp) {
// default empty implementation
}
/**
* This method is called when an image capture has completed and the
* result metadata is available.
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera The CameraDevice sending the callback.
* @param request The request that was given to the CameraDevice
@@ -503,14 +631,21 @@ public interface CameraDevice extends AutoCloseable {
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
public void onCaptureComplete(CameraDevice camera,
CaptureRequest request, CaptureResult result);
public void onCaptureCompleted(CameraDevice camera,
CaptureRequest request, CaptureResult result) {
// default empty implementation
}
/**
* <p>Called instead of onCaptureComplete when the camera device failed
* to produce a CaptureResult for the request. Other requests are
* unaffected, and some or all image buffers from the capture may have
* been pushed to their respective output streams.</p>
* This method is called instead of {@link #onCaptureCompleted} when the
* camera device failed to produce a {@link CaptureResult} for the
* request.
*
* <p>Other requests are unaffected, and some or all image buffers from
* the capture may have been pushed to their respective output
* streams.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera The CameraDevice sending the callback.
* @param request The request that was given to the CameraDevice
@@ -521,62 +656,129 @@ public interface CameraDevice extends AutoCloseable {
* @see #setRepeatingBurst
*/
public void onCaptureFailed(CameraDevice camera,
CaptureRequest request);
CaptureRequest request) {
// default empty implementation
}
}
/**
* <p>A listener for asynchronous errors from the camera device. Errors
* about specific {@link CaptureRequest CaptureRequests} are sent through
* the capture {@link CaptureListener#onCaptureFailed listener}
* interface. Errors reported through this listener affect the device as a
* whole.</p>
* A listener for notifications about the state of a camera
* device.
*
* <p>These events include notifications about the device becoming idle (
* allowing for {@link #configureOutputs} to be called), about device
* disconnection, and about unexpected device errors.</p>
*
* <p>Events about the progress of specific {@link CaptureRequest
* CaptureRequests} are provided through a {@link CaptureListener} given to
* the {@link #capture}, {@link #captureBurst}, {@link
* #setRepeatingRequest}, or {@link #setRepeatingBurst} methods.
*
* @see #setDeviceListener
*/
public interface ErrorListener {
public static abstract class CameraDeviceListener {
/**
* <p>This camera device has been disconnected by the camera
* service. Any attempt to call methods on this CameraDevice will throw
* a {@link CameraAccessException}. The disconnection could be due to a
* An error code that can be reported by {@link #onCameraError}
* indicating that the camera device has encountered a fatal error.
*
* <p>The camera device needs to be re-opened to be used again.</p>
*
* @see #onCameraDeviceError
*/
public static final int ERROR_CAMERA_DEVICE = 1;
/**
* An error code that can be reported by {@link #onCameraError}
* indicating that the camera service has encountered a fatal error.
*
* <p>The Android device may need to be shut down and restarted to restore
* camera function, or there may be a persistent hardware problem.</p>
*
* @see #onCameraDeviceError
*/
public static final int ERROR_CAMERA_SERVICE = 2;
/**
* The method called when a camera device has finished processing all
* submitted capture requests and has reached an idle state.
*
* <p>An idle camera device can have its outputs changed by calling
* {@link CameraDevice#configureOutputs}.</p>
*
* <p>To idle and reconfigure outputs without cancelling any submitted
* capture requests, the application needs to clear its repeating
* request/burst, if set, with {@link CameraDevice#stopRepeating}, and
* then wait for this callback to be called before calling {@link
* CameraDevice#configureOutputs}.</p>
*
* <p>To idle and reconfigure a camera device as fast as possible, the
* {@link CameraDevice#flush} method can be used, which will discard all
* pending and in-progess capture requests. Once the {@link
* CameraDevice#flush} method is called, the application must wait for
* this callback to fire before calling {@link
* CameraDevice#configureOutputs}.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera the camera device that has become idle
*
* @see CameraDevice#configureOutputs
* @see CameraDevice#stopRepeating
* @see CameraDevice#flush
*/
public void onCameraIdle(CameraDevice camera) {
// Default empty implementation
}
/**
* The method called when a camera device is no longer available for
* use.
*
* <p>Any attempt to call methods on this CameraDevice will throw a
* {@link CameraAccessException}. The disconnection could be due to a
* change in security policy or permissions; the physical disconnection
* of a removable camera device; or the camera being needed for a
* higher-priority use case.</p>
*
* <p>There may still be capture completion or camera stream listeners
* that will be called after this error is received.</p>
* <p>There may still be capture listener callbacks that are called
* after this method is called, or new image buffers that are delivered
* to active outputs.</p>
*
* <p>The default implementation logs a notice to the system log
* about the disconnection.</p>
*
* @param camera the device that has been disconnected
*/
public static final int DEVICE_DISCONNECTED = 1;
public void onCameraDisconnected(CameraDevice camera) {
Log.i("CameraListener",
String.format("Camera device %s disconnected", camera.getId()));
}
/**
* <p>The camera device has encountered a fatal error. Any attempt to
* call methods on this CameraDevice will throw a
* {@link java.lang.IllegalStateException}.</p>
* The method called when a camera device has encountered a serious error.
*
* <p>This indicates a failure of the camera device or camera service in
* some way. Any attempt to call methods on this CameraDevice in the
* future will throw a {@link java.lang.IllegalStateException}.</p>
*
* <p>There may still be capture completion or camera stream listeners
* that will be called after this error is received.</p>
*
* <p>The application needs to re-open the camera device to use it
* again.</p>
*/
public static final int DEVICE_ERROR = 2;
/**
* <p>The camera service has encountered a fatal error. Any attempt to
* call methods on this CameraDevice in the future will throw a
* {@link java.lang.IllegalStateException}.</p>
*
* <p>There may still be capture completion or camera stream listeners
* that will be called after this error is received.</p>
*
* <p>The device may need to be shut down and restarted to restore
* camera function, or there may be a persistent hardware problem.</p>
*/
public static final int SERVICE_ERROR = 3;
/**
* The method to call when a camera device has encountered an error.
* <p>The default implementation logs an error to the system log about
* the camera failure.</p>
*
* @param camera The device reporting the error
* @param error The error code, one of the ErrorListener.ERROR_ values.
* @param error The error code, one of the
* {@code CameraDeviceListener.ERROR_*} values.
*
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
*/
public void onCameraDeviceError(CameraDevice camera, int error);
public void onCameraError(CameraDevice camera, int error) {
Log.e("CameraListener",
String.format("Camera device %s has encountered an error: %d",
camera.getId(), error));
}
}
}

View File

@@ -20,17 +20,19 @@ import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
import android.hardware.IProCameraUser;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.BinderHolder;
import android.os.IBinder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
* <p>An interface for iterating, listing, and connecting to
@@ -55,7 +57,10 @@ public final class CameraManager {
private final ICameraService mCameraService;
private ArrayList<String> mDeviceIdList;
private final HashSet<CameraListener> mListenerSet = new HashSet<CameraListener>();
private ArrayMap<AvailabilityListener, Handler> mListenerMap =
new ArrayMap<AvailabilityListener, Handler>();
private final Context mContext;
private final Object mLock = new Object();
@@ -85,14 +90,16 @@ public final class CameraManager {
}
/**
* <p>Return the list of currently connected camera devices by
* identifier. Non-removable cameras use integers starting at 0 for their
* Return the list of currently connected camera devices by
* identifier.
*
* <p>Non-removable cameras use integers starting at 0 for their
* identifiers, while removable cameras have a unique identifier for each
* individual device, even if they are the same model.</p>
*
* @return The list of currently connected camera devices.
*/
public String[] getDeviceIdList() throws CameraAccessException {
public String[] getCameraIdList() throws CameraAccessException {
synchronized (mLock) {
try {
return getOrCreateDeviceIdListLocked().toArray(new String[0]);
@@ -107,13 +114,25 @@ public final class CameraManager {
/**
* Register a listener to be notified about camera device availability.
*
* Registering a listener more than once has no effect.
* <p>Registering the same listener again will replace the handler with the
* new one provided.</p>
*
* @param listener The new listener to send camera availability notices to
* @param handler The handler on which the listener should be invoked, or
* {@code null} to use the current thread's {@link android.os.Looper looper}.
*/
public void registerCameraListener(CameraListener listener) {
public void addAvailabilityListener(AvailabilityListener listener, Handler handler) {
if (handler == null) {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException(
"No handler given, and current thread has no looper!");
}
handler = new Handler(looper);
}
synchronized (mLock) {
mListenerSet.add(listener);
mListenerMap.put(listener, handler);
}
}
@@ -121,13 +140,13 @@ public final class CameraManager {
* Remove a previously-added listener; the listener will no longer receive
* connection and disconnection callbacks.
*
* Removing a listener that isn't registered has no effect.
* <p>Removing a listener that isn't registered has no effect.</p>
*
* @param listener The listener to remove from the notification list
*/
public void unregisterCameraListener(CameraListener listener) {
public void removeAvailabilityListener(AvailabilityListener listener) {
synchronized (mLock) {
mListenerSet.remove(listener);
mListenerMap.remove(listener);
}
}
@@ -144,7 +163,7 @@ public final class CameraManager {
* @throws SecurityException if the application does not have permission to
* access the camera
*
* @see #getDeviceIdList
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
public CameraProperties getCameraProperties(String cameraId)
@@ -160,14 +179,14 @@ public final class CameraManager {
// TODO: implement and call a service function to get the capabilities on C++ side
// TODO: get properties from service
return new CameraProperties();
return new CameraProperties(new CameraMetadataNative());
}
/**
* Open a connection to a camera with the given ID. Use
* {@link #getDeviceIdList} to get the list of available camera
* {@link #getCameraIdList} to get the list of available camera
* devices. Note that even if an id is listed, open may fail if the device
* is disconnected between the calls to {@link #getDeviceIdList} and
* is disconnected between the calls to {@link #getCameraIdList} and
* {@link #openCamera}.
*
* @param cameraId The unique identifier of the camera device to open
@@ -179,7 +198,7 @@ public final class CameraManager {
* @throws SecurityException if the application does not have permission to
* access the camera
*
* @see #getDeviceIdList
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
public CameraDevice openCamera(String cameraId) throws CameraAccessException {
@@ -218,29 +237,43 @@ public final class CameraManager {
}
/**
* Interface for listening to cameras becoming available or unavailable.
* Cameras become available when they are no longer in use, or when a new
* Interface for listening to camera devices becoming available or
* unavailable.
*
* <p>Cameras become available when they are no longer in use, or when a new
* removable camera is connected. They become unavailable when some
* application or service starts using a camera, or when a removable camera
* is disconnected.
* is disconnected.</p>
*
* @see addAvailabilityListener
*/
public interface CameraListener {
public static abstract class AvailabilityListener {
/**
* A new camera has become available to use.
*
* <p>The default implementation of this method does nothing.</p>
*
* @param cameraId The unique identifier of the new camera.
*/
public void onCameraAvailable(String cameraId);
public void onCameraAvailable(String cameraId) {
// default empty implementation
}
/**
* A previously-available camera has become unavailable for use. If an
* application had an active CameraDevice instance for the
* now-disconnected camera, that application will receive a {@link
* CameraDevice.ErrorListener#DEVICE_DISCONNECTED disconnection error}.
* A previously-available camera has become unavailable for use.
*
* <p>If an application had an active CameraDevice instance for the
* now-disconnected camera, that application will receive a
* {@link CameraDevice.CameraDeviceListener#onCameraDisconnected disconnection error}.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param cameraId The unique identifier of the disconnected camera.
*/
public void onCameraUnavailable(String cameraId);
public void onCameraUnavailable(String cameraId) {
// default empty implementation
}
}
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
@@ -285,7 +318,7 @@ public final class CameraManager {
public static final int STATUS_NOT_AVAILABLE = 0x80000000;
// Camera ID -> Status map
private final HashMap<String, Integer> mDeviceStatus = new HashMap<String, Integer>();
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
private static final String TAG = "CameraServiceListener";
@@ -322,7 +355,7 @@ public final class CameraManager {
Log.v(TAG,
String.format("Camera id %d has status changed to 0x%x", cameraId, status));
String id = String.valueOf(cameraId);
final String id = String.valueOf(cameraId);
if (!validStatus(status)) {
Log.e(TAG, String.format("Ignoring invalid device %d status 0x%x", cameraId,
@@ -363,11 +396,24 @@ public final class CameraManager {
return;
}
for (CameraListener listener : mListenerSet) {
final int listenerCount = mListenerMap.size();
for (int i = 0; i < listenerCount; i++) {
Handler handler = mListenerMap.valueAt(i);
final AvailabilityListener listener = mListenerMap.keyAt(i);
if (isAvailable(status)) {
listener.onCameraAvailable(id);
handler.post(
new Runnable() {
public void run() {
listener.onCameraAvailable(id);
}
});
} else {
listener.onCameraUnavailable(id);
handler.post(
new Runnable() {
public void run() {
listener.onCameraUnavailable(id);
}
});
}
} // for
} // synchronized

View File

@@ -16,20 +16,7 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.MetadataMarshalClass;
import android.hardware.camera2.impl.MetadataMarshalRect;
import android.hardware.camera2.impl.MetadataMarshalSize;
import android.hardware.camera2.impl.MetadataMarshalString;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.hardware.camera2.impl.CameraMetadataNative;
/**
* The base class for camera controls and information.
@@ -42,62 +29,12 @@ import java.util.Map;
* @see CameraManager
* @see CameraProperties
**/
public class CameraMetadata implements Parcelable, AutoCloseable {
public CameraMetadata() {
mMetadataMap = new HashMap<Key<?>, Object>();
mMetadataPtr = nativeAllocate();
if (mMetadataPtr == 0) {
throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
}
}
public static final Parcelable.Creator<CameraMetadata> CREATOR =
new Parcelable.Creator<CameraMetadata>() {
@Override
public CameraMetadata createFromParcel(Parcel in) {
CameraMetadata metadata = new CameraMetadata();
metadata.readFromParcel(in);
return metadata;
}
@Override
public CameraMetadata[] newArray(int size) {
return new CameraMetadata[size];
}
};
private static final String TAG = "CameraMetadataJV";
public abstract class CameraMetadata {
/**
* Set a camera metadata field to a value. The field definitions can be
* found in {@link CameraProperties}, {@link CaptureResult}, and
* {@link CaptureRequest}.
*
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
* @hide
*/
public <T> void set(Key<T> key, T value) {
int tag = key.getTag();
if (value == null) {
writeValues(tag, null);
return;
}
int nativeType = getNativeType(tag);
int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true);
// TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
byte[] values = new byte[size];
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
packSingle(value, buffer, key.mType, nativeType, /*sizeOnly*/false);
writeValues(tag, values);
protected CameraMetadata() {
}
/**
@@ -110,342 +47,16 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
* @param key The metadata field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
int tag = key.getTag();
byte[] values = readValues(tag);
if (values == null) {
return null;
}
int nativeType = getNativeType(tag);
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
return unpackSingle(buffer, key.mType, nativeType);
}
// Keep up-to-date with camera_metadata.h
/**
* @hide
*/
public static final int TYPE_BYTE = 0;
/**
* @hide
*/
public static final int TYPE_INT32 = 1;
/**
* @hide
*/
public static final int TYPE_FLOAT = 2;
/**
* @hide
*/
public static final int TYPE_INT64 = 3;
/**
* @hide
*/
public static final int TYPE_DOUBLE = 4;
/**
* @hide
*/
public static final int TYPE_RATIONAL = 5;
/**
* @hide
*/
public static final int NUM_TYPES = 6;
private static int getTypeSize(int nativeType) {
switch(nativeType) {
case TYPE_BYTE:
return 1;
case TYPE_INT32:
case TYPE_FLOAT:
return 4;
case TYPE_INT64:
case TYPE_DOUBLE:
case TYPE_RATIONAL:
return 8;
}
throw new UnsupportedOperationException("Unknown type, can't get size "
+ nativeType);
}
private static Class<?> getExpectedType(int nativeType) {
switch(nativeType) {
case TYPE_BYTE:
return Byte.TYPE;
case TYPE_INT32:
return Integer.TYPE;
case TYPE_FLOAT:
return Float.TYPE;
case TYPE_INT64:
return Long.TYPE;
case TYPE_DOUBLE:
return Double.TYPE;
case TYPE_RATIONAL:
return Rational.class;
}
throw new UnsupportedOperationException("Unknown type, can't map to Java type "
+ nativeType);
}
@SuppressWarnings("unchecked")
private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
int nativeType, boolean sizeOnly) {
if (!sizeOnly) {
/**
* Rewrite types when the native type doesn't match the managed type
* - Boolean -> Byte
* - Integer -> Byte
*/
if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
// Since a boolean can't be cast to byte, and we don't want to use putBoolean
boolean asBool = (Boolean) value;
byte asByte = (byte) (asBool ? 1 : 0);
value = (T) (Byte) asByte;
} else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
int asInt = (Integer) value;
byte asByte = (byte) asInt;
value = (T) (Byte) asByte;
} else if (type != getExpectedType(nativeType)) {
throw new UnsupportedOperationException("Tried to pack a type of " + type +
" but we expected the type to be " + getExpectedType(nativeType));
}
if (nativeType == TYPE_BYTE) {
buffer.put((Byte) value);
} else if (nativeType == TYPE_INT32) {
buffer.putInt((Integer) value);
} else if (nativeType == TYPE_FLOAT) {
buffer.putFloat((Float) value);
} else if (nativeType == TYPE_INT64) {
buffer.putLong((Long) value);
} else if (nativeType == TYPE_DOUBLE) {
buffer.putDouble((Double) value);
} else if (nativeType == TYPE_RATIONAL) {
Rational r = (Rational) value;
buffer.putInt(r.getNumerator());
buffer.putInt(r.getDenominator());
}
}
return getTypeSize(nativeType);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
int size = 0;
if (type.isPrimitive() || type == Rational.class) {
size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
} else if (type.isEnum()) {
size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
} else if (type.isArray()) {
size = packArray(value, buffer, type, nativeType, sizeOnly);
} else {
size = packClass(value, buffer, type, nativeType, sizeOnly);
}
return size;
}
private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
int nativeType, boolean sizeOnly) {
// TODO: add support for enums with their own values.
return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
}
@SuppressWarnings("unchecked")
private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
if (marshaler == null) {
throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
}
return marshaler.marshal(value, buffer, nativeType, sizeOnly);
}
private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
int size = 0;
int arrayLength = Array.getLength(value);
@SuppressWarnings("unchecked")
Class<Object> componentType = (Class<Object>)type.getComponentType();
for (int i = 0; i < arrayLength; ++i) {
size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
}
return size;
}
@SuppressWarnings("unchecked")
private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
T val;
if (nativeType == TYPE_BYTE) {
val = (T) (Byte) buffer.get();
} else if (nativeType == TYPE_INT32) {
val = (T) (Integer) buffer.getInt();
} else if (nativeType == TYPE_FLOAT) {
val = (T) (Float) buffer.getFloat();
} else if (nativeType == TYPE_INT64) {
val = (T) (Long) buffer.getLong();
} else if (nativeType == TYPE_DOUBLE) {
val = (T) (Double) buffer.getDouble();
} else if (nativeType == TYPE_RATIONAL) {
val = (T) new Rational(buffer.getInt(), buffer.getInt());
} else {
throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
+ nativeType);
}
/**
* Rewrite types when the native type doesn't match the managed type
* - Byte -> Boolean
* - Byte -> Integer
*/
if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
// Since a boolean can't be cast to byte, and we don't want to use getBoolean
byte asByte = (Byte) val;
boolean asBool = asByte != 0;
val = (T) (Boolean) asBool;
} else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
byte asByte = (Byte) val;
int asInt = asByte;
val = (T) (Integer) asInt;
} else if (type != getExpectedType(nativeType)) {
throw new UnsupportedOperationException("Tried to unpack a type of " + type +
" but we expected the type to be " + getExpectedType(nativeType));
}
return val;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
if (type.isPrimitive() || type == Rational.class) {
return unpackSingleNative(buffer, type, nativeType);
}
if (type.isEnum()) {
return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
}
if (type.isArray()) {
return unpackArray(buffer, type, nativeType);
}
T instance = unpackClass(buffer, type, nativeType);
return instance;
}
private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
int nativeType) {
int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
return getEnumFromValue(type, ordinal);
}
private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
if (marshaler == null) {
throw new IllegalArgumentException("Unknown class type: " + type);
}
return marshaler.unmarshal(buffer, nativeType);
}
@SuppressWarnings("unchecked")
private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
Class<?> componentType = type.getComponentType();
Object array;
int elementSize = getTypeSize(nativeType);
MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
if (marshaler != null) {
elementSize = marshaler.getNativeSize(nativeType);
}
if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
int remaining = buffer.remaining();
int arraySize = remaining / elementSize;
Log.v(TAG,
String.format(
"Attempting to unpack array (count = %d, element size = %d, bytes " +
"remaining = %d) for type %s",
arraySize, elementSize, remaining, type));
array = Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; ++i) {
Object elem = unpackSingle(buffer, componentType, nativeType);
Array.set(array, i, elem);
}
} else {
// Dynamic size, use an array list.
ArrayList<Object> arrayList = new ArrayList<Object>();
int primitiveSize = getTypeSize(nativeType);
while (buffer.remaining() >= primitiveSize) {
Object elem = unpackSingle(buffer, componentType, nativeType);
arrayList.add(elem);
}
array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
}
if (buffer.remaining() != 0) {
Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+ type);
}
return (T) array;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
nativeWriteToParcel(dest);
}
/**
* Expand this object from a Parcel.
* @param in The Parcel from which the object should be read
*/
public void readFromParcel(Parcel in) {
nativeReadFromParcel(in);
}
public abstract <T> T get(Key<T> key);
public static class Key<T> {
private boolean mHasTag;
private int mTag;
private final Class<T> mType;
private final String mName;
/*
/**
* @hide
*/
public Key(String name, Class<T> type) {
@@ -483,8 +94,6 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
return mName.equals(lhs.mName);
}
private final String mName;
/**
* <p>
* Get the tag corresponding to this key. This enables insertion into the
@@ -499,267 +108,20 @@ public class CameraMetadata implements Parcelable, AutoCloseable {
*/
public final int getTag() {
if (!mHasTag) {
mTag = CameraMetadata.getTag(mName);
mTag = CameraMetadataNative.getTag(mName);
mHasTag = true;
}
return mTag;
}
}
private final Map<Key<?>, Object> mMetadataMap;
private long mMetadataPtr; // native CameraMetadata*
private native long nativeAllocate();
private native synchronized void nativeWriteToParcel(Parcel dest);
private native synchronized void nativeReadFromParcel(Parcel source);
private native synchronized void nativeSwap(CameraMetadata other) throws NullPointerException;
private native synchronized void nativeClose();
private native synchronized boolean nativeIsEmpty();
private native synchronized int nativeGetEntryCount();
private native synchronized byte[] nativeReadValues(int tag);
private native synchronized void nativeWriteValues(int tag, byte[] src);
private static native int nativeGetTagFromKey(String keyName)
throws IllegalArgumentException;
private static native int nativeGetTypeFromTag(int tag)
throws IllegalArgumentException;
private static native void nativeClassInit();
/**
* <p>Perform a 0-copy swap of the internal metadata with another object.</p>
*
* <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
*
* @param other Metadata to swap with
* @throws NullPointerException if other was null
* @hide
*/
public void swap(CameraMetadata other) {
nativeSwap(other);
}
/**
* @hide
*/
public int getEntryCount() {
return nativeGetEntryCount();
}
/**
* Does this metadata contain at least 1 entry?
*
* @hide
*/
public boolean isEmpty() {
return nativeIsEmpty();
}
/**
* <p>Closes this object, and releases all native resources associated with it.</p>
*
* <p>Calling any other public method after this will result in an IllegalStateException
* being thrown.</p>
*/
@Override
public void close() throws Exception {
// this sets mMetadataPtr to 0
nativeClose();
mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
}
/**
* Whether or not {@link #close} has already been called (at least once) on this object.
* @hide
*/
public boolean isClosed() {
synchronized (this) {
return mMetadataPtr == 0;
/**
* @hide
*/
public final Class<T> getType() {
return mType;
}
}
/**
* Convert a key string into the equivalent native tag.
*
* @throws IllegalArgumentException if the key was not recognized
* @throws NullPointerException if the key was null
*
* @hide
*/
public static int getTag(String key) {
return nativeGetTagFromKey(key);
}
/**
* Get the underlying native type for a tag.
*
* @param tag An integer tag, see e.g. {@link #getTag}
* @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
*
* @hide
*/
public static int getNativeType(int tag) {
return nativeGetTypeFromTag(tag);
}
/**
* <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
* the entry if src was null.</p>
*
* <p>An empty array can be passed in to update the entry to 0 elements.</p>
*
* @param tag An integer tag, see e.g. {@link #getTag}
* @param src An array of bytes, or null to erase the entry
*
* @hide
*/
public void writeValues(int tag, byte[] src) {
nativeWriteValues(tag, src);
}
/**
* <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
* the data properly.</p>
*
* <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
*
* @param tag An integer tag, see e.g. {@link #getTag}
*
* @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
* @hide
*/
public byte[] readValues(int tag) {
// TODO: Optimization. Native code returns a ByteBuffer instead.
return nativeReadValues(tag);
}
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
new HashMap<Class<? extends Enum>, int[]>();
/**
* Register a non-sequential set of values to be used with the pack/unpack functions.
* This enables get/set to correctly marshal the enum into a value that is C-compatible.
*
* @param enumType The class for an enum
* @param values A list of values mapping to the ordinals of the enum
*
* @hide
*/
public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
if (enumType.getEnumConstants().length != values.length) {
throw new IllegalArgumentException(
"Expected values array to be the same size as the enumTypes values "
+ values.length + " for type " + enumType);
}
Log.v(TAG, "Registered enum values for type " + enumType + " values");
sEnumValues.put(enumType, values);
}
/**
* Get the numeric value from an enum. This is usually the same as the ordinal value for
* enums that have fully sequential values, although for C-style enums the range of values
* may not map 1:1.
*
* @param enumValue Enum instance
* @return Int guaranteed to be ABI-compatible with the C enum equivalent
*/
private static <T extends Enum<T>> int getEnumValue(T enumValue) {
int[] values;
values = sEnumValues.get(enumValue.getClass());
int ordinal = enumValue.ordinal();
if (values != null) {
return values[ordinal];
}
return ordinal;
}
/**
* Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
*
* @param enumType Class of the enum we want to find
* @param value The numeric value of the enum
* @return An instance of the enum
*/
private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
int ordinal;
int[] registeredValues = sEnumValues.get(enumType);
if (registeredValues != null) {
ordinal = -1;
for (int i = 0; i < registeredValues.length; ++i) {
if (registeredValues[i] == value) {
ordinal = i;
break;
}
}
} else {
ordinal = value;
}
T[] values = enumType.getEnumConstants();
if (ordinal < 0 || ordinal >= values.length) {
throw new IllegalArgumentException(
String.format(
"Argument 'value' (%d) was not a valid enum value for type %s "
+ "(registered? %b)",
value,
enumType, (registeredValues != null)));
}
return values[ordinal];
}
static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
HashMap<Class<?>, MetadataMarshalClass<?>>();
private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
}
@SuppressWarnings("unchecked")
private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
throw new UnsupportedOperationException("Unsupported type " + nativeType +
" to be marshalled to/from a " + type);
}
return marshaler;
}
/**
* We use a class initializer to allow the native code to cache some field offsets
*/
static {
System.loadLibrary("media_jni");
nativeClassInit();
Log.v(TAG, "Shall register metadata marshalers");
// load built-in marshallers
registerMarshaler(new MetadataMarshalRect());
registerMarshaler(new MetadataMarshalSize());
registerMarshaler(new MetadataMarshalString());
Log.v(TAG, "Registered metadata marshalers");
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* The enum values below this point are generated from metadata
* definitions in /system/media/camera/docs. Do not modify by hand or

View File

@@ -16,6 +16,8 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
/**
* <p>The properties describing a
* {@link CameraDevice CameraDevice}.</p>
@@ -29,6 +31,21 @@ package android.hardware.camera2;
*/
public final class CameraProperties extends CameraMetadata {
private final CameraMetadataNative mProperties;
/**
* Takes ownership of the passed-in properties object
* @hide
*/
public CameraProperties(CameraMetadataNative properties) {
mProperties = properties;
}
@Override
public <T> T get(Key<T> key) {
return mProperties.get(key);
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* The key entries below this point are generated from metadata
* definitions in /system/media/camera/docs. Do not modify by hand or

View File

@@ -16,6 +16,7 @@
package android.hardware.camera2;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
@@ -24,13 +25,16 @@ import java.util.HashSet;
/**
* <p>All the settings required to capture a single image from the image sensor.</p>
* <p>An immutable package of settings and outputs needed to capture a single
* image from the camera device.</p>
*
* <p>Contains the configuration for the capture hardware (sensor, lens, flash),
* the processing pipeline, the control algorithms, and the output buffers.</p>
* the processing pipeline, the control algorithms, and the output buffers. Also
* contains the list of target Surfaces to send image data to for this
* capture.</p>
*
* <p>CaptureRequests can be created by calling
* {@link CameraDevice#createCaptureRequest}</p>
* <p>CaptureRequests can be created by using a {@link Builder} instance,
* obtained by calling {@link CameraDevice#createCaptureRequest}</p>
*
* <p>CaptureRequests are given to {@link CameraDevice#capture} or
* {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p>
@@ -38,7 +42,8 @@ import java.util.HashSet;
* <p>Each request can specify a different subset of target Surfaces for the
* camera to send the captured data to. All the surfaces used in a request must
* be part of the surface list given to the last call to
* {@link CameraDevice#configureOutputs}.</p>
* {@link CameraDevice#configureOutputs}, when the request is submitted to the
* camera device.</p>
*
* <p>For example, a request meant for repeating preview might only include the
* Surface for the preview SurfaceView or SurfaceTexture, while a
@@ -47,64 +52,43 @@ import java.util.HashSet;
*
* @see CameraDevice#capture
* @see CameraDevice#setRepeatingRequest
* @see CameraDevice#createRequest
* @see CameraDevice#createCaptureRequest
*/
public final class CaptureRequest extends CameraMetadata implements Parcelable {
private final Object mLock = new Object();
private final HashSet<Surface> mSurfaceSet = new HashSet<Surface>();
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private Object mUserTag;
/**
* Construct empty request
* @hide
*/
public CaptureRequest() {
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
}
/**
* <p>Add a surface to the list of targets for this request</p>
*
* <p>The Surface added must be one of the surfaces included in the last
* call to {@link CameraDevice#configureOutputs}.</p>
*
* <p>Adding a target more than once has no effect.</p>
*
* @param outputTarget Surface to use as an output target for this request
* Clone from source capture request
*/
public void addTarget(Surface outputTarget) {
synchronized (mLock) {
mSurfaceSet.add(outputTarget);
}
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
}
/**
* <p>Remove a surface from the list of targets for this request.</p>
*
* <p>Removing a target that is not currently added has no effect.</p>
*
* @param outputTarget Surface to use as an output target for this request
* Take ownership of passed-in settings
*/
public void removeTarget(Surface outputTarget) {
synchronized (mLock) {
mSurfaceSet.remove(outputTarget);
}
private CaptureRequest(CameraMetadataNative settings) {
mSettings = settings;
mSurfaceSet = new HashSet<Surface>();
}
/**
* Set a tag for this request.
*
* <p>This tag is not used for anything by the camera device, but can be
* used by an application to easily identify a CaptureRequest when it is
* returned by
* {@link CameraDevice.CaptureListener#onCaptureComplete CaptureListener.onCaptureComplete}
*
* @param tag an arbitrary Object to store with this request
* @see #getTag
*/
public void setTag(Object tag) {
synchronized (mLock) {
mUserTag = tag;
}
@Override
public <T> T get(Key<T> key) {
return mSettings.get(key);
}
/**
@@ -113,17 +97,15 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* <p>This tag is not used for anything by the camera device, but can be
* used by an application to easily identify a CaptureRequest when it is
* returned by
* {@link CameraDevice.CaptureListener#onCaptureComplete CaptureListener.onCaptureComplete}
* {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted}
* </p>
*
* @return the last tag Object set on this request, or {@code null} if
* no tag has been set.
* @see #setTag
* @see Builder#setTag
*/
public Object getTag() {
synchronized (mLock) {
return mUserTag;
}
return mUserTag;
}
public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -132,6 +114,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
public CaptureRequest createFromParcel(Parcel in) {
CaptureRequest request = new CaptureRequest();
request.readFromParcel(in);
return request;
}
@@ -143,35 +126,152 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
/**
* Expand this object from a Parcel.
* Hidden since this breaks the immutability of CaptureRequest, but is
* needed to receive CaptureRequests with aidl.
*
* @param in The parcel from which the object should be read
* @hide
*/
@Override
public void readFromParcel(Parcel in) {
synchronized (mLock) {
super.readFromParcel(in);
mSettings.readFromParcel(in);
mSurfaceSet.clear();
mSurfaceSet.clear();
Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
if (parcelableArray == null) {
return;
}
if (parcelableArray == null) {
return;
}
for (Parcelable p : parcelableArray) {
Surface s = (Surface) p;
mSurfaceSet.add(s);
}
for (Parcelable p : parcelableArray) {
Surface s = (Surface) p;
mSurfaceSet.add(s);
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
synchronized (mLock) {
super.writeToParcel(dest, flags);
public int describeContents() {
return 0;
}
dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
@Override
public void writeToParcel(Parcel dest, int flags) {
mSettings.writeToParcel(dest, flags);
dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
}
/**
* A builder for capture requests.
*
* <p>To obtain a builder instance, use the
* {@link CameraDevice#createCaptureRequest} method, which initializes the
* request fields to one of the templates defined in {@link CameraDevice}.
*
* @see CameraDevice#createCaptureRequest
* @see #TEMPLATE_PREVIEW
* @see #TEMPLATE_RECORD
* @see #TEMPLATE_STILL_CAPTURE
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
*/
public final static class Builder {
private CaptureRequest mRequest;
/**
* Initialize the builder using the template; the request takes
* ownership of the template.
*
* @hide
*/
public Builder(CameraMetadataNative template) {
mRequest = new CaptureRequest(template);
}
/**
* <p>Add a surface to the list of targets for this request</p>
*
* <p>The Surface added must be one of the surfaces included in the most
* recent call to {@link CameraDevice#configureOutputs}, when the
* request is given to the camera device.</p>
*
* <p>Adding a target more than once has no effect.</p>
*
* @param outputTarget Surface to use as an output target for this request
*/
public void addTarget(Surface outputTarget) {
mRequest.mSurfaceSet.add(outputTarget);
}
/**
* <p>Remove a surface from the list of targets for this request.</p>
*
* <p>Removing a target that is not currently added has no effect.</p>
*
* @param outputTarget Surface to use as an output target for this request
*/
public void removeTarget(Surface outputTarget) {
mRequest.mSurfaceSet.remove(outputTarget);
}
/**
* Set a capture request field to a value. The field definitions can be
* found in {@link CaptureRequest}.
*
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
*/
public <T> void set(Key<T> key, T value) {
mRequest.mSettings.set(key, value);
}
/**
* Get a capture request field value. The field definitions can be
* found in {@link CaptureRequest}.
*
* @throws IllegalArgumentException if the key was not valid
*
* @param key The metadata field to read.
* @return The value of that key, or {@code null} if the field is not set.
*/
public <T> T get(Key<T> key) {
return mRequest.mSettings.get(key);
}
/**
* Set a tag for this request.
*
* <p>This tag is not used for anything by the camera device, but can be
* used by an application to easily identify a CaptureRequest when it is
* returned by
* {@link CameraDevice.CaptureListener#onCaptureCompleted CaptureListener.onCaptureCompleted}
*
* @param tag an arbitrary Object to store with this request
* @see CaptureRequest#getTag
*/
public void setTag(Object tag) {
mRequest.mUserTag = tag;
}
/**
* Build a request using the current target Surfaces and settings.
*
* @return A new capture request instance, ready for submission to the
* camera device.
*/
public CaptureRequest build() {
return new CaptureRequest(mRequest);
}
/**
* @hide
*/
public boolean isEmpty() {
return mRequest.mSettings.isEmpty();
}
}
/*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~

View File

@@ -18,6 +18,7 @@ package android.hardware.camera2;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.impl.CameraMetadataNative;
/**
* <p>The results of a single image capture from the image sensor.</p>
@@ -34,10 +35,20 @@ import android.graphics.Rect;
*
*/
public final class CaptureResult extends CameraMetadata {
private final CameraMetadataNative mResults;
/**
* Takes ownership of the passed-in properties object
* @hide
*/
public CaptureResult() {
public CaptureResult(CameraMetadataNative results) {
mResults = results;
}
@Override
public <T> T get(Key<T> key) {
return mResults.get(key);
}
/**

View File

@@ -16,7 +16,7 @@
package android.hardware.camera2;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.impl.CameraMetadataNative;
/** @hide */
interface ICameraDeviceCallbacks
@@ -26,5 +26,5 @@ interface ICameraDeviceCallbacks
*/
oneway void notifyCallback(int msgType, int ext1, int ext2);
oneway void onResultReceived(int frameId, in CameraMetadata result);
oneway void onResultReceived(int frameId, in CameraMetadataNative result);
}

View File

@@ -17,7 +17,7 @@
package android.hardware.camera2;
import android.view.Surface;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.CaptureRequest;
/** @hide */
@@ -40,9 +40,9 @@ interface ICameraDeviceUser
// non-negative value is the stream ID. negative value is status_t
int createStream(int width, int height, int format, in Surface surface);
int createDefaultRequest(int templateId, out CameraMetadata request);
int createDefaultRequest(int templateId, out CameraMetadataNative request);
int getCameraInfo(out CameraMetadata info);
int getCameraInfo(out CameraMetadataNative info);
int waitUntilIdle();

View File

@@ -29,6 +29,8 @@ import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.SparseArray;
import android.view.Surface;
@@ -40,7 +42,7 @@ import java.util.List;
import java.util.Stack;
/**
* HAL2.1+ implementation of CameraDevice Use CameraManager#open to instantiate
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
*/
public class CameraDevice implements android.hardware.camera2.CameraDevice {
@@ -53,10 +55,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
private final Object mLock = new Object();
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
// XX: Make this a WeakReference<CaptureListener> ?
// TODO: Convert to SparseIntArray
private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
new HashMap<Integer, CaptureListenerHolder>();
private CameraDeviceListener mDeviceListener;
private Handler mDeviceHandler;
private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
new SparseArray<CaptureListenerHolder>();
private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
// Map stream IDs to Surfaces
@@ -79,11 +82,15 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
}
@Override
public String getId() {
return mCameraId;
}
@Override
public CameraProperties getProperties() throws CameraAccessException {
CameraProperties properties = new CameraProperties();
CameraMetadata info = new CameraMetadata();
CameraMetadataNative info = new CameraMetadataNative();
try {
mRemoteDevice.getCameraInfo(/*out*/info);
@@ -94,7 +101,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
return null;
}
properties.swap(info);
CameraProperties properties = new CameraProperties(info);
return properties;
}
@@ -149,11 +156,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
@Override
public CaptureRequest createCaptureRequest(int templateType) throws CameraAccessException {
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
synchronized (mLock) {
CameraMetadata templatedRequest = new CameraMetadata();
CameraMetadataNative templatedRequest = new CameraMetadataNative();
try {
mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
@@ -164,23 +171,22 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
return null;
}
CaptureRequest request = new CaptureRequest();
request.swap(templatedRequest);
return request;
CaptureRequest.Builder builder =
new CaptureRequest.Builder(templatedRequest);
return builder;
}
}
@Override
public void capture(CaptureRequest request, CaptureListener listener)
public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException {
submitCaptureRequest(request, listener, /*streaming*/false);
submitCaptureRequest(request, listener, handler, /*streaming*/false);
}
@Override
public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
throws CameraAccessException {
public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException {
if (requests.isEmpty()) {
Log.w(TAG, "Capture burst request list is empty, do nothing!");
return;
@@ -191,7 +197,18 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
boolean repeating) throws CameraAccessException {
Handler handler, boolean repeating) throws CameraAccessException {
// Need a valid handler, or current thread needs to have a looper, if
// listener is valid
if (handler == null && listener != null) {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException(
"No handler given, and current thread has no looper!");
}
handler = new Handler(looper);
}
synchronized (mLock) {
@@ -205,9 +222,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
// impossible
return;
}
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
repeating));
if (listener != null) {
mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
handler, repeating));
}
if (repeating) {
mRepeatingRequestIdStack.add(requestId);
@@ -217,14 +235,14 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
@Override
public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
throws CameraAccessException {
submitCaptureRequest(request, listener, /*streaming*/true);
public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException {
submitCaptureRequest(request, listener, handler, /*streaming*/true);
}
@Override
public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
throws CameraAccessException {
public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException {
if (requests.isEmpty()) {
Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
return;
@@ -274,9 +292,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
@Override
public void setErrorListener(ErrorListener listener) {
// TODO Auto-generated method stub
public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
synchronized (mLock) {
mDeviceListener = listener;
mDeviceHandler = handler;
}
}
@Override
@@ -332,9 +352,16 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
private final boolean mRepeating;
private final CaptureListener mListener;
private final CaptureRequest mRequest;
private final Handler mHandler;
CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
boolean repeating) {
if (listener == null || handler == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid listener");
}
mRepeating = repeating;
mHandler = handler;
mRequest = request;
mListener = listener;
}
@@ -350,6 +377,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
public CaptureRequest getRequest() {
return mRequest;
}
public Handler getHandler() {
return mHandler;
}
}
// TODO: unit tests
@@ -370,11 +402,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
@Override
public void onResultReceived(int requestId, CameraMetadata result) throws RemoteException {
public void onResultReceived(int requestId, CameraMetadataNative result)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "Received result for id " + requestId);
}
CaptureListenerHolder holder;
final CaptureListenerHolder holder;
synchronized (mLock) {
// TODO: move this whole map into this class to make it more testable,
@@ -393,18 +426,22 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
}
}
// Check if we have a listener for this
if (holder == null) {
Log.e(TAG, "Result had no listener holder associated with it, dropping result");
return;
}
CaptureResult resultAsCapture = new CaptureResult();
resultAsCapture.swap(result);
final CaptureResult resultAsCapture = new CaptureResult(result);
if (holder.getListener() != null) {
holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
resultAsCapture);
}
holder.getHandler().post(
new Runnable() {
public void run() {
holder.getListener().onCaptureCompleted(
CameraDevice.this,
holder.getRequest(),
resultAsCapture);
}
});
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package android.hardware.camera2;
package android.hardware.camera2.impl;
/** @hide */
parcelable CameraMetadata;
parcelable CameraMetadataNative;

View File

@@ -0,0 +1,669 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.camera2.impl;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.Rational;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of camera metadata marshal/unmarshal across Binder to
* the camera service
*/
public class CameraMetadataNative extends CameraMetadata implements Parcelable {
private static final String TAG = "CameraMetadataJV";
public CameraMetadataNative() {
super();
mMetadataPtr = nativeAllocate();
if (mMetadataPtr == 0) {
throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
}
}
/**
* Copy constructor - clone metadata
*/
public CameraMetadataNative(CameraMetadataNative other) {
super();
mMetadataPtr = nativeAllocateCopy(other);
if (mMetadataPtr == 0) {
throw new OutOfMemoryError("Failed to allocate native CameraMetadata");
}
}
public static final Parcelable.Creator<CameraMetadataNative> CREATOR =
new Parcelable.Creator<CameraMetadataNative>() {
@Override
public CameraMetadataNative createFromParcel(Parcel in) {
CameraMetadataNative metadata = new CameraMetadataNative();
metadata.readFromParcel(in);
return metadata;
}
@Override
public CameraMetadataNative[] newArray(int size) {
return new CameraMetadataNative[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
nativeWriteToParcel(dest);
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(Key<T> key) {
int tag = key.getTag();
byte[] values = readValues(tag);
if (values == null) {
return null;
}
int nativeType = getNativeType(tag);
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
return unpackSingle(buffer, key.getType(), nativeType);
}
public void readFromParcel(Parcel in) {
nativeReadFromParcel(in);
}
/**
* Set a camera metadata field to a value. The field definitions can be
* found in {@link CameraProperties}, {@link CaptureResult}, and
* {@link CaptureRequest}.
*
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
*/
public <T> void set(Key<T> key, T value) {
int tag = key.getTag();
if (value == null) {
writeValues(tag, null);
return;
}
int nativeType = getNativeType(tag);
int size = packSingle(value, null, key.getType(), nativeType, /* sizeOnly */true);
// TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
byte[] values = new byte[size];
ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
packSingle(value, buffer, key.getType(), nativeType, /*sizeOnly*/false);
writeValues(tag, values);
}
// Keep up-to-date with camera_metadata.h
/**
* @hide
*/
public static final int TYPE_BYTE = 0;
/**
* @hide
*/
public static final int TYPE_INT32 = 1;
/**
* @hide
*/
public static final int TYPE_FLOAT = 2;
/**
* @hide
*/
public static final int TYPE_INT64 = 3;
/**
* @hide
*/
public static final int TYPE_DOUBLE = 4;
/**
* @hide
*/
public static final int TYPE_RATIONAL = 5;
/**
* @hide
*/
public static final int NUM_TYPES = 6;
private void close() {
// this sets mMetadataPtr to 0
nativeClose();
mMetadataPtr = 0; // set it to 0 again to prevent eclipse from making this field final
}
private static int getTypeSize(int nativeType) {
switch(nativeType) {
case TYPE_BYTE:
return 1;
case TYPE_INT32:
case TYPE_FLOAT:
return 4;
case TYPE_INT64:
case TYPE_DOUBLE:
case TYPE_RATIONAL:
return 8;
}
throw new UnsupportedOperationException("Unknown type, can't get size "
+ nativeType);
}
private static Class<?> getExpectedType(int nativeType) {
switch(nativeType) {
case TYPE_BYTE:
return Byte.TYPE;
case TYPE_INT32:
return Integer.TYPE;
case TYPE_FLOAT:
return Float.TYPE;
case TYPE_INT64:
return Long.TYPE;
case TYPE_DOUBLE:
return Double.TYPE;
case TYPE_RATIONAL:
return Rational.class;
}
throw new UnsupportedOperationException("Unknown type, can't map to Java type "
+ nativeType);
}
@SuppressWarnings("unchecked")
private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
int nativeType, boolean sizeOnly) {
if (!sizeOnly) {
/**
* Rewrite types when the native type doesn't match the managed type
* - Boolean -> Byte
* - Integer -> Byte
*/
if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
// Since a boolean can't be cast to byte, and we don't want to use putBoolean
boolean asBool = (Boolean) value;
byte asByte = (byte) (asBool ? 1 : 0);
value = (T) (Byte) asByte;
} else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
int asInt = (Integer) value;
byte asByte = (byte) asInt;
value = (T) (Byte) asByte;
} else if (type != getExpectedType(nativeType)) {
throw new UnsupportedOperationException("Tried to pack a type of " + type +
" but we expected the type to be " + getExpectedType(nativeType));
}
if (nativeType == TYPE_BYTE) {
buffer.put((Byte) value);
} else if (nativeType == TYPE_INT32) {
buffer.putInt((Integer) value);
} else if (nativeType == TYPE_FLOAT) {
buffer.putFloat((Float) value);
} else if (nativeType == TYPE_INT64) {
buffer.putLong((Long) value);
} else if (nativeType == TYPE_DOUBLE) {
buffer.putDouble((Double) value);
} else if (nativeType == TYPE_RATIONAL) {
Rational r = (Rational) value;
buffer.putInt(r.getNumerator());
buffer.putInt(r.getDenominator());
}
}
return getTypeSize(nativeType);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
int size = 0;
if (type.isPrimitive() || type == Rational.class) {
size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
} else if (type.isEnum()) {
size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
} else if (type.isArray()) {
size = packArray(value, buffer, type, nativeType, sizeOnly);
} else {
size = packClass(value, buffer, type, nativeType, sizeOnly);
}
return size;
}
private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
int nativeType, boolean sizeOnly) {
// TODO: add support for enums with their own values.
return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
}
@SuppressWarnings("unchecked")
private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
if (marshaler == null) {
throw new IllegalArgumentException(String.format("Unknown Key type: %s", type));
}
return marshaler.marshal(value, buffer, nativeType, sizeOnly);
}
private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
boolean sizeOnly) {
int size = 0;
int arrayLength = Array.getLength(value);
@SuppressWarnings("unchecked")
Class<Object> componentType = (Class<Object>)type.getComponentType();
for (int i = 0; i < arrayLength; ++i) {
size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
}
return size;
}
@SuppressWarnings("unchecked")
private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
T val;
if (nativeType == TYPE_BYTE) {
val = (T) (Byte) buffer.get();
} else if (nativeType == TYPE_INT32) {
val = (T) (Integer) buffer.getInt();
} else if (nativeType == TYPE_FLOAT) {
val = (T) (Float) buffer.getFloat();
} else if (nativeType == TYPE_INT64) {
val = (T) (Long) buffer.getLong();
} else if (nativeType == TYPE_DOUBLE) {
val = (T) (Double) buffer.getDouble();
} else if (nativeType == TYPE_RATIONAL) {
val = (T) new Rational(buffer.getInt(), buffer.getInt());
} else {
throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
+ nativeType);
}
/**
* Rewrite types when the native type doesn't match the managed type
* - Byte -> Boolean
* - Byte -> Integer
*/
if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
// Since a boolean can't be cast to byte, and we don't want to use getBoolean
byte asByte = (Byte) val;
boolean asBool = asByte != 0;
val = (T) (Boolean) asBool;
} else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
byte asByte = (Byte) val;
int asInt = asByte;
val = (T) (Integer) asInt;
} else if (type != getExpectedType(nativeType)) {
throw new UnsupportedOperationException("Tried to unpack a type of " + type +
" but we expected the type to be " + getExpectedType(nativeType));
}
return val;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
if (type.isPrimitive() || type == Rational.class) {
return unpackSingleNative(buffer, type, nativeType);
}
if (type.isEnum()) {
return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
}
if (type.isArray()) {
return unpackArray(buffer, type, nativeType);
}
T instance = unpackClass(buffer, type, nativeType);
return instance;
}
private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
int nativeType) {
int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
return getEnumFromValue(type, ordinal);
}
private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
if (marshaler == null) {
throw new IllegalArgumentException("Unknown class type: " + type);
}
return marshaler.unmarshal(buffer, nativeType);
}
@SuppressWarnings("unchecked")
private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
Class<?> componentType = type.getComponentType();
Object array;
int elementSize = getTypeSize(nativeType);
MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
if (marshaler != null) {
elementSize = marshaler.getNativeSize(nativeType);
}
if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
int remaining = buffer.remaining();
int arraySize = remaining / elementSize;
Log.v(TAG,
String.format(
"Attempting to unpack array (count = %d, element size = %d, bytes " +
"remaining = %d) for type %s",
arraySize, elementSize, remaining, type));
array = Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; ++i) {
Object elem = unpackSingle(buffer, componentType, nativeType);
Array.set(array, i, elem);
}
} else {
// Dynamic size, use an array list.
ArrayList<Object> arrayList = new ArrayList<Object>();
int primitiveSize = getTypeSize(nativeType);
while (buffer.remaining() >= primitiveSize) {
Object elem = unpackSingle(buffer, componentType, nativeType);
arrayList.add(elem);
}
array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
}
if (buffer.remaining() != 0) {
Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+ type);
}
return (T) array;
}
private long mMetadataPtr; // native CameraMetadata*
private native long nativeAllocate();
private native long nativeAllocateCopy(CameraMetadataNative other)
throws NullPointerException;
private native synchronized void nativeWriteToParcel(Parcel dest);
private native synchronized void nativeReadFromParcel(Parcel source);
private native synchronized void nativeSwap(CameraMetadataNative other)
throws NullPointerException;
private native synchronized void nativeClose();
private native synchronized boolean nativeIsEmpty();
private native synchronized int nativeGetEntryCount();
private native synchronized byte[] nativeReadValues(int tag);
private native synchronized void nativeWriteValues(int tag, byte[] src);
private static native int nativeGetTagFromKey(String keyName)
throws IllegalArgumentException;
private static native int nativeGetTypeFromTag(int tag)
throws IllegalArgumentException;
private static native void nativeClassInit();
/**
* <p>Perform a 0-copy swap of the internal metadata with another object.</p>
*
* <p>Useful to convert a CameraMetadata into e.g. a CaptureRequest.</p>
*
* @param other Metadata to swap with
* @throws NullPointerException if other was null
* @hide
*/
public void swap(CameraMetadataNative other) {
nativeSwap(other);
}
/**
* @hide
*/
public int getEntryCount() {
return nativeGetEntryCount();
}
/**
* Does this metadata contain at least 1 entry?
*
* @hide
*/
public boolean isEmpty() {
return nativeIsEmpty();
}
/**
* Convert a key string into the equivalent native tag.
*
* @throws IllegalArgumentException if the key was not recognized
* @throws NullPointerException if the key was null
*
* @hide
*/
public static int getTag(String key) {
return nativeGetTagFromKey(key);
}
/**
* Get the underlying native type for a tag.
*
* @param tag An integer tag, see e.g. {@link #getTag}
* @return An int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
*
* @hide
*/
public static int getNativeType(int tag) {
return nativeGetTypeFromTag(tag);
}
/**
* <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
* the entry if src was null.</p>
*
* <p>An empty array can be passed in to update the entry to 0 elements.</p>
*
* @param tag An integer tag, see e.g. {@link #getTag}
* @param src An array of bytes, or null to erase the entry
*
* @hide
*/
public void writeValues(int tag, byte[] src) {
nativeWriteValues(tag, src);
}
/**
* <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
* the data properly.</p>
*
* <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
*
* @param tag An integer tag, see e.g. {@link #getTag}
*
* @return {@code null} if there were 0 entries for this tag, a byte[] otherwise.
* @hide
*/
public byte[] readValues(int tag) {
// TODO: Optimization. Native code returns a ByteBuffer instead.
return nativeReadValues(tag);
}
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
new HashMap<Class<? extends Enum>, int[]>();
/**
* Register a non-sequential set of values to be used with the pack/unpack functions.
* This enables get/set to correctly marshal the enum into a value that is C-compatible.
*
* @param enumType The class for an enum
* @param values A list of values mapping to the ordinals of the enum
*
* @hide
*/
public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
if (enumType.getEnumConstants().length != values.length) {
throw new IllegalArgumentException(
"Expected values array to be the same size as the enumTypes values "
+ values.length + " for type " + enumType);
}
Log.v(TAG, "Registered enum values for type " + enumType + " values");
sEnumValues.put(enumType, values);
}
/**
* Get the numeric value from an enum. This is usually the same as the ordinal value for
* enums that have fully sequential values, although for C-style enums the range of values
* may not map 1:1.
*
* @param enumValue Enum instance
* @return Int guaranteed to be ABI-compatible with the C enum equivalent
*/
private static <T extends Enum<T>> int getEnumValue(T enumValue) {
int[] values;
values = sEnumValues.get(enumValue.getClass());
int ordinal = enumValue.ordinal();
if (values != null) {
return values[ordinal];
}
return ordinal;
}
/**
* Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
*
* @param enumType Class of the enum we want to find
* @param value The numeric value of the enum
* @return An instance of the enum
*/
private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
int ordinal;
int[] registeredValues = sEnumValues.get(enumType);
if (registeredValues != null) {
ordinal = -1;
for (int i = 0; i < registeredValues.length; ++i) {
if (registeredValues[i] == value) {
ordinal = i;
break;
}
}
} else {
ordinal = value;
}
T[] values = enumType.getEnumConstants();
if (ordinal < 0 || ordinal >= values.length) {
throw new IllegalArgumentException(
String.format(
"Argument 'value' (%d) was not a valid enum value for type %s "
+ "(registered? %b)",
value,
enumType, (registeredValues != null)));
}
return values[ordinal];
}
static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
HashMap<Class<?>, MetadataMarshalClass<?>>();
private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
}
@SuppressWarnings("unchecked")
private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
throw new UnsupportedOperationException("Unsupported type " + nativeType +
" to be marshalled to/from a " + type);
}
return marshaler;
}
/**
* We use a class initializer to allow the native code to cache some field offsets
*/
static {
System.loadLibrary("media_jni");
nativeClassInit();
Log.v(TAG, "Shall register metadata marshalers");
// load built-in marshallers
registerMarshaler(new MetadataMarshalRect());
registerMarshaler(new MetadataMarshalSize());
registerMarshaler(new MetadataMarshalString());
Log.v(TAG, "Registered metadata marshalers");
}
}

View File

@@ -26,7 +26,7 @@ public interface MetadataMarshalClass<T> {
* @param value the value of type T that we wish to write into the byte buffer
* @param buffer the byte buffer into which the marshalled object will be written
* @param nativeType the native type, e.g.
* {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
* {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
* Guaranteed to be one for which isNativeTypeSupported returns true.
* @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
* @return the size that needs to be written to the byte buffer
@@ -37,7 +37,7 @@ public interface MetadataMarshalClass<T> {
* Unmarshal a new object instance from the byte buffer.
* @param buffer the byte buffer, from which we will read the object
* @param nativeType the native type, e.g.
* {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
* {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}.
* Guaranteed to be one for which isNativeTypeSupported returns true.
* @return a new instance of type T read from the byte buffer
*/
@@ -50,7 +50,7 @@ public interface MetadataMarshalClass<T> {
* will are likely to only support one type.
*
* @param nativeType the native type, e.g.
* {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}
* {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
* @return true if it supports, false otherwise
*/
boolean isNativeTypeSupported(int nativeType);
@@ -60,7 +60,7 @@ public interface MetadataMarshalClass<T> {
/**
* How many bytes T will take up if marshalled to/from nativeType
* @param nativeType the native type, e.g.
* {@link android.hardware.camera2.CameraMetadata#TYPE_BYTE TYPE_BYTE}
* {@link android.hardware.camera2.impl.CameraMetadataNative#TYPE_BYTE TYPE_BYTE}
* @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
*/
int getNativeSize(int nativeType);

View File

@@ -16,7 +16,6 @@
package android.hardware.camera2.impl;
import android.graphics.Rect;
import android.hardware.camera2.CameraMetadata;
import java.nio.ByteBuffer;
@@ -58,7 +57,7 @@ public class MetadataMarshalRect implements MetadataMarshalClass<Rect> {
@Override
public boolean isNativeTypeSupported(int nativeType) {
return nativeType == CameraMetadata.TYPE_INT32;
return nativeType == CameraMetadataNative.TYPE_INT32;
}
@Override

View File

@@ -15,7 +15,6 @@
*/
package android.hardware.camera2.impl;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.Size;
import java.nio.ByteBuffer;
@@ -51,7 +50,7 @@ public class MetadataMarshalSize implements MetadataMarshalClass<Size> {
@Override
public boolean isNativeTypeSupported(int nativeType) {
return nativeType == CameraMetadata.TYPE_INT32;
return nativeType == CameraMetadataNative.TYPE_INT32;
}
@Override

View File

@@ -15,8 +15,6 @@
*/
package android.hardware.camera2.impl;
import android.hardware.camera2.CameraMetadata;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
@@ -71,7 +69,7 @@ public class MetadataMarshalString implements MetadataMarshalClass<String> {
@Override
public boolean isNativeTypeSupported(int nativeType) {
return nativeType == CameraMetadata.TYPE_BYTE;
return nativeType == CameraMetadataNative.TYPE_BYTE;
}
@Override

View File

@@ -38,7 +38,7 @@
#endif
// fully-qualified class name
#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/CameraMetadata"
#define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative"
using namespace android;
@@ -152,6 +152,21 @@ static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
return reinterpret_cast<jlong>(new CameraMetadata());
}
static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
jobject other) {
ALOGV("%s", __FUNCTION__);
CameraMetadata* otherMetadata =
CameraMetadata_getPointerThrow(env, other, "other");
// In case of exception, return
if (otherMetadata == NULL) return NULL;
// Clone native metadata and return new pointer
return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
}
static jboolean CameraMetadata_isEmpty(JNIEnv *env, jobject thiz) {
ALOGV("%s", __FUNCTION__);
@@ -361,6 +376,9 @@ static JNINativeMethod gCameraMetadataMethods[] = {
{ "nativeAllocate",
"()J",
(void*)CameraMetadata_allocate },
{ "nativeAllocateCopy",
"(L" CAMERA_METADATA_CLASS_NAME ";)J",
(void *)CameraMetadata_allocateCopy },
{ "nativeIsEmpty",
"()Z",
(void*)CameraMetadata_isEmpty },

View File

@@ -25,6 +25,7 @@ import android.hardware.IProCameraUser;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.BinderHolder;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.os.Binder;
@@ -155,7 +156,7 @@ public class CameraBinderTest extends AndroidTestCase {
}
@Override
public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException {
}
}

View File

@@ -22,6 +22,7 @@ import android.hardware.camera2.CameraProperties;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.BinderHolder;
import android.os.RemoteException;
import android.test.AndroidTestCase;
@@ -62,13 +63,13 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
}
@Override
public void onResultReceived(int frameId, CameraMetadata result) throws RemoteException {
public void onResultReceived(int frameId, CameraMetadataNative result) throws RemoteException {
}
}
class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadata> {
class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
public boolean matches(Object obj) {
return !((CameraMetadata) obj).isEmpty();
return !((CameraMetadataNative) obj).isEmpty();
}
}
@@ -78,20 +79,17 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
mSurface = new Surface(mSurfaceTexture);
}
private CaptureRequest createDefaultRequest(boolean needStream) throws Exception {
CameraMetadata metadata = new CameraMetadata();
private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception {
CameraMetadataNative metadata = new CameraMetadataNative();
assertTrue(metadata.isEmpty());
CaptureRequest request = new CaptureRequest();
assertTrue(request.isEmpty());
int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
request.swap(metadata);
CaptureRequest.Builder request = new CaptureRequest.Builder(metadata);
assertFalse(request.isEmpty());
assertTrue(metadata.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {
int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20,
/* ignored */30, mSurface);
@@ -150,14 +148,13 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
@SmallTest
public void testCreateDefaultRequest() throws Exception {
CameraMetadata metadata = new CameraMetadata();
CameraMetadataNative metadata = new CameraMetadataNative();
assertTrue(metadata.isEmpty());
int status = mCameraUser.createDefaultRequest(TEMPLATE_PREVIEW, /* out */metadata);
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
metadata.close();
}
@SmallTest
@@ -208,37 +205,39 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
@SmallTest
public void testSubmitBadRequest() throws Exception {
CaptureRequest request = createDefaultRequest(/* needStream */false);
int status = mCameraUser.submitRequest(request, /* streaming */false);
CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */false);
CaptureRequest request1 = builder.build();
int status = mCameraUser.submitRequest(request1, /* streaming */false);
assertEquals("Expected submitRequest to return BAD_VALUE " +
"since we had 0 surface targets set.", CameraBinderTestUtils.BAD_VALUE, status);
request.addTarget(mSurface);
status = mCameraUser.submitRequest(request, /* streaming */false);
builder.addTarget(mSurface);
CaptureRequest request2 = builder.build();
status = mCameraUser.submitRequest(request2, /* streaming */false);
assertEquals("Expected submitRequest to return BAD_VALUE since " +
"the target surface wasn't registered with createStream.",
CameraBinderTestUtils.BAD_VALUE, status);
request.close();
}
@SmallTest
public void testSubmitGoodRequest() throws Exception {
CaptureRequest request = createDefaultRequest(/* needStream */true);
CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
CaptureRequest request = builder.build();
// Submit valid request twice.
int requestId1 = submitCameraRequest(request, /* streaming */false);
int requestId2 = submitCameraRequest(request, /* streaming */false);
assertNotSame("Request IDs should be unique for multiple requests", requestId1, requestId2);
request.close();
}
@SmallTest
public void testSubmitStreamingRequest() throws Exception {
CaptureRequest request = createDefaultRequest(/* needStream */true);
CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
CaptureRequest request = builder.build();
// Submit valid request once (non-streaming), and another time
// (streaming)
@@ -260,12 +259,11 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
assertEquals("Streaming request IDs should be cancellable", CameraBinderTestUtils.NO_ERROR,
status);
request.close();
}
@SmallTest
public void testCameraInfo() throws RemoteException {
CameraMetadata info = new CameraMetadata();
CameraMetadataNative info = new CameraMetadataNative();
int status = mCameraUser.getCameraInfo(/*out*/info);
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
@@ -276,8 +274,8 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
@SmallTest
public void testWaitUntilIdle() throws Exception {
CaptureRequest request = createDefaultRequest(/* needStream */true);
int requestIdStreaming = submitCameraRequest(request, /* streaming */true);
CaptureRequest.Builder builder = createDefaultBuilder(/* needStream */true);
int requestIdStreaming = submitCameraRequest(builder.build(), /* streaming */true);
// Test Bad case first: waitUntilIdle when there is active repeating request
int status = mCameraUser.waitUntilIdle();
@@ -294,7 +292,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
@SmallTest
public void testCaptureResultCallbacks() throws Exception {
IsMetadataNotEmpty matcher = new IsMetadataNotEmpty();
CaptureRequest request = createDefaultRequest(/* needStream */true);
CaptureRequest request = createDefaultBuilder(/* needStream */true).build();
// Test both single request and streaming request.
int requestId1 = submitCameraRequest(request, /* streaming */false);
@@ -307,7 +305,6 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
.onResultReceived(
eq(streamingId),
argThat(matcher));
request.close();
}
@SmallTest
@@ -319,7 +316,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase {
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
// Then set up a stream
CaptureRequest request = createDefaultRequest(/* needStream */true);
CaptureRequest request = createDefaultBuilder(/* needStream */true).build();
// Flush should still be a no-op, really
status = mCameraUser.flush();

View File

@@ -23,8 +23,9 @@ import android.graphics.Rect;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.Rational;
import android.hardware.camera2.Size;
import android.hardware.camera2.impl.CameraMetadataNative;
import static android.hardware.camera2.CameraMetadata.*;
import static android.hardware.camera2.impl.CameraMetadataNative.*;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
@@ -42,7 +43,7 @@ import static org.junit.Assert.assertArrayEquals;
*/
public class CameraMetadataTest extends junit.framework.TestCase {
CameraMetadata mMetadata;
CameraMetadataNative mMetadata;
Parcel mParcel;
// Sections
@@ -62,13 +63,12 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@Override
public void setUp() {
mMetadata = new CameraMetadata();
mMetadata = new CameraMetadataNative();
mParcel = Parcel.obtain();
}
@Override
public void tearDown() throws Exception {
mMetadata.close();
mMetadata = null;
mParcel.recycle();
@@ -81,116 +81,48 @@ public class CameraMetadataTest extends junit.framework.TestCase {
assertTrue(mMetadata.isEmpty());
}
@SmallTest
public void testClose() throws Exception {
mMetadata.isEmpty(); // no throw
assertFalse(mMetadata.isClosed());
mMetadata.close();
assertTrue(mMetadata.isClosed());
// OK: second close should not throw
mMetadata.close();
assertTrue(mMetadata.isClosed());
// All other calls after close should throw IllegalStateException
try {
mMetadata.isEmpty();
fail("Unreachable -- isEmpty after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.getEntryCount();
fail("Unreachable -- getEntryCount after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.swap(mMetadata);
fail("Unreachable -- swap after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.readFromParcel(mParcel);
fail("Unreachable -- readFromParcel after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.writeToParcel(mParcel, /*flags*/0);
fail("Unreachable -- writeToParcel after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.readValues(/*tag*/0);
fail("Unreachable -- readValues after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
try {
mMetadata.writeValues(/*tag*/0, /*source*/new byte[] { 1,2,3 });
fail("Unreachable -- readValues after close should throw IllegalStateException");
} catch (IllegalStateException e) {
// good: we expect calling this method after close to fail
}
}
@SmallTest
public void testGetTagFromKey() {
// Test success
assertEquals(ANDROID_COLOR_CORRECTION_MODE,
CameraMetadata.getTag("android.colorCorrection.mode"));
CameraMetadataNative.getTag("android.colorCorrection.mode"));
assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM,
CameraMetadata.getTag("android.colorCorrection.transform"));
CameraMetadataNative.getTag("android.colorCorrection.transform"));
assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
CameraMetadata.getTag("android.control.aeAntibandingMode"));
CameraMetadataNative.getTag("android.control.aeAntibandingMode"));
assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
CameraMetadata.getTag("android.control.aeExposureCompensation"));
CameraMetadataNative.getTag("android.control.aeExposureCompensation"));
// Test failures
try {
CameraMetadata.getTag(null);
CameraMetadataNative.getTag(null);
fail("A null key should throw NPE");
} catch(NullPointerException e) {
}
try {
CameraMetadata.getTag("android.control");
CameraMetadataNative.getTag("android.control");
fail("A section name only should not be a valid key");
} catch(IllegalArgumentException e) {
}
try {
CameraMetadata.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
CameraMetadataNative.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
fail("A valid section with an invalid tag name should not be a valid key");
} catch(IllegalArgumentException e) {
}
try {
CameraMetadata.getTag("android");
CameraMetadataNative.getTag("android");
fail("A namespace name only should not be a valid key");
} catch(IllegalArgumentException e) {
}
try {
CameraMetadata.getTag("this.key.is.definitely.invalid");
CameraMetadataNative.getTag("this.key.is.definitely.invalid");
fail("A completely fake key name should not be valid");
} catch(IllegalArgumentException e) {
}
@@ -198,14 +130,14 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testGetTypeFromTag() {
assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
assertEquals(TYPE_FLOAT, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
assertEquals(TYPE_FLOAT, CameraMetadataNative.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
assertEquals(TYPE_BYTE, CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
assertEquals(TYPE_INT32,
CameraMetadata.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
CameraMetadataNative.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
try {
CameraMetadata.getNativeType(0xDEADF00D);
CameraMetadataNative.getNativeType(0xDEADF00D);
fail("No type should exist for invalid tag 0xDEADF00D");
} catch(IllegalArgumentException e) {
}
@@ -454,7 +386,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
@SmallTest
public void testReadWriteEnumWithCustomValues() {
CameraMetadata.registerEnumValues(AeAntibandingMode.class, new int[] {
CameraMetadataNative.registerEnumValues(AeAntibandingMode.class, new int[] {
0,
10,
20,
@@ -475,7 +407,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
Key<AeAntibandingMode[]> aeAntibandingModeKey =
new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes",
AeAntibandingMode[].class);
byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadata
byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadataNative
.getTag("android.control.aeAvailableAntibandingModes"));
byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
assertArrayEquals(expectedValues, aeAntibandingModeValues);
@@ -485,7 +417,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
* Stranger cases that don't use byte enums
*/
// int (n)
CameraMetadata.registerEnumValues(AvailableFormat.class, new int[] {
CameraMetadataNative.registerEnumValues(AvailableFormat.class, new int[] {
0x20,
0x32315659,
0x11,
@@ -505,7 +437,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
Key<AeAntibandingMode> availableFormatsKey =
new Key<AeAntibandingMode>("android.scaler.availableFormats",
AeAntibandingMode.class);
byte[] availableFormatValues = mMetadata.readValues(CameraMetadata
byte[] availableFormatValues = mMetadata.readValues(CameraMetadataNative
.getTag(availableFormatsKey.getName()));
int[] expectedIntValues = new int[] {