Merge changes Ia6300c23,Idd2ae8ad into klp-dev
* changes: Camera2: Immutable metadata Camera2: Listener rework and other API updates
This commit is contained in:
committed by
Android (Google) Code Review
commit
ede3eeb72c
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.camera2;
|
||||
package android.hardware.camera2.impl;
|
||||
|
||||
/** @hide */
|
||||
parcelable CameraMetadata;
|
||||
parcelable CameraMetadataNative;
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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[] {
|
||||
|
||||
Reference in New Issue
Block a user