diff options
11 files changed, 219 insertions, 20 deletions
diff --git a/api/current.txt b/api/current.txt index 8f87163e45d4..818114d14c71 100644 --- a/api/current.txt +++ b/api/current.txt @@ -13056,6 +13056,7 @@ package android.hardware.camera2 { method public abstract android.hardware.camera2.CameraDevice getDevice(); method public abstract android.view.Surface getInputSurface(); method public abstract boolean isReprocessible(); + method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException; @@ -13078,6 +13079,7 @@ package android.hardware.camera2 { method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession); method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession); method public void onReady(android.hardware.camera2.CameraCaptureSession); + method public void onSurfacePrepared(android.hardware.camera2.CameraCaptureSession, android.view.Surface); } public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata { @@ -57345,4 +57347,3 @@ package org.xmlpull.v1.sax2 { } } - diff --git a/api/system-current.txt b/api/system-current.txt index a9a5211864a9..bef85ada74df 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -13352,6 +13352,7 @@ package android.hardware.camera2 { method public abstract android.hardware.camera2.CameraDevice getDevice(); method public abstract android.view.Surface getInputSurface(); method public abstract boolean isReprocessible(); + method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException; @@ -13374,6 +13375,7 @@ package android.hardware.camera2 { method public abstract void onConfigureFailed(android.hardware.camera2.CameraCaptureSession); method public abstract void onConfigured(android.hardware.camera2.CameraCaptureSession); method public void onReady(android.hardware.camera2.CameraCaptureSession); + method public void onSurfacePrepared(android.hardware.camera2.CameraCaptureSession, android.view.Surface); } public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata { @@ -60223,4 +60225,3 @@ package org.xmlpull.v1.sax2 { } } - diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 6b6f026db929..31e6e254a951 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -18,6 +18,7 @@ package android.hardware.camera2; import android.os.Handler; import android.view.Surface; + import java.util.List; @@ -69,6 +70,61 @@ public abstract class CameraCaptureSession implements AutoCloseable { public abstract CameraDevice getDevice(); /** + * <p>Pre-allocate all buffers for an output Surface.</p> + * + * <p>Normally, the image buffers for a given output Surface are allocated on-demand, + * to minimize startup latency and memory overhead.</p> + * + * <p>However, in some cases, it may be desirable for the buffers to be allocated before + * any requests targeting the Surface are actually submitted to the device. Large buffers + * may take some time to allocate, which can result in delays in submitting requests until + * sufficient buffers are allocated to reach steady-state behavior. Such delays can cause + * bursts to take longer than desired, or cause skips or stutters in preview output.</p> + * + * <p>The prepare() method can be used to perform this preallocation. It may only be called for + * a given output Surface before that Surface is used as a target for a request. The number of + * buffers allocated is the sum of the count needed by the consumer providing the output + * Surface, and the maximum number needed by the camera device to fill its pipeline. Since this + * may be a larger number than what is actually required for steady-state operation, using + * prepare may result in higher memory consumption than the normal on-demand behavior results + * in. Prepare() will also delay the time to first output to a given Surface, in exchange for + * smoother frame rate once the allocation is complete.</p> + * + * <p>For example, an application that creates an + * {@link android.media.ImageReader#newInstance ImageReader} with a maxImages argument of 10, + * but only uses 3 simultaneous Images at once would normally only cause those 3 images to be + * allocated (plus what is needed by the camera device for smooth operation). But using + * prepare() on the ImageReader Surface will result in all 10 Images being allocated. So + * applications using this method should take care to request only the number of buffers + * actually necessary for their application.</p> + * + * <p>If the same output Surface is used in consecutive sessions (without closing the first + * session explicitly), then its already-allocated buffers are carried over, and if it was + * used as a target of a capture request in the first session, prepare cannot be called on it + * in the second session.</p> + * + * <p>Once allocation is complete, {@link StateCallback#onSurfacePrepared} will be invoked with + * the Surface provided to this method. Between the prepare call and the onSurfacePrepared call, + * the Surface provided to prepare must not be used as a target of a CaptureRequest submitted + * to this session.</p> + * + * @param surface the output Surface for which buffers should be pre-allocated. Must be one of + * the output Surfaces used to create this session. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException if the Surface is invalid, not part of this Session, or has + * already been used as a target of a CaptureRequest in this + * session or immediately prior sessions. + * + * @see StateCallback#onSurfacePrepared + */ + public abstract void prepare(Surface surface) throws CameraAccessException; + + /** * <p>Submit a request for an image to be captured by the camera device.</p> * * <p>The request defines all the parameters for capturing the single image, @@ -110,9 +166,10 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not - * configured as outputs for this session. Or if a reprocess + * configured as outputs for this session; or a reprocess * capture request is submitted in a non-reprocessible capture - * session. Or if the handler is + * session; or the capture targets a Surface in the middle + * of being {@link #prepare prepared}; or the handler is * null, the listener is not null, and the calling thread has * no looper. * @@ -164,13 +221,15 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests target no Surfaces, or target Surfaces not - * currently configured as outputs. Or if a reprocess + * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target + * Surfaces not currently configured as outputs; or a reprocess * capture request is submitted in a non-reprocessible capture - * session. Or if the list of requests contains both requests - * to capture images from the camera and reprocess capture - * requests. Or if the handler is null, the listener is not - * null, and the calling thread has no looper. + * session; or the list of requests contains both requests to + * capture images from the camera and reprocess capture + * requests; or one of the captures targets a Surface in the + * middle of being {@link #prepare prepared}; or if the handler + * is null, the listener is not null, and the calling thread + * has no looper. * * @see #capture * @see #setRepeatingRequest @@ -230,11 +289,12 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces that are - * not currently configured as outputs. Or if the request is - * a reprocess capture request. Or if the handler is null, the - * listener is not null, and the calling thread has no looper. - * Or if no requests were passed in. + * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces + * that are not currently configured as outputs; or the request + * is a reprocess capture request; or the capture targets a + * Surface in the middle of being {@link #prepare prepared}; or + * the handler is null, the listener is not null, and the + * calling thread has no looper; or no requests were passed in. * * @see #capture * @see #captureBurst @@ -299,11 +359,13 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalStateException if this session is no longer active, either because the session * was explicitly closed, a new session has been created * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces not - * currently configured as outputs. Or if one of the requests - * is a reprocess capture request. Or if the handler is null, - * the listener is not null, and the calling thread has no - * looper. Or if no requests were passed in. + * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces + * not currently configured as outputs; or one of the requests + * is a reprocess capture request; or one of the captures + * targets a Surface in the middle of being + * {@link #prepare prepared}; or the handler is null, the + * listener is not null, and the calling thread has no looper; + * or no requests were passed in. * * @see #capture * @see #captureBurst @@ -514,6 +576,25 @@ public abstract class CameraCaptureSession implements AutoCloseable { public void onClosed(CameraCaptureSession session) { // default empty implementation } + + /** + * This method is called when the buffer pre-allocation for an output Surface is complete. + * + * <p>Buffer pre-allocation for an output Surface is started by the {@link #prepare} call. + * While allocation is underway, the Surface must not be used as a capture target. + * Once this callback fires, the output Surface provided can again be used as a target for + * a capture request.</p> + * + * <p>In case of a error during pre-allocation (such as running out of suitable memory), + * this callback is still invoked after the error is encountered, though some buffers may + * not have been successfully pre-allocated.</p> + * + * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param surface the Surface that was used with the {@link #prepare} call. + */ + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + // default empty implementation + } } /** diff --git a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl index ca0935cce54e..151c9183d731 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl @@ -31,4 +31,5 @@ interface ICameraDeviceCallbacks oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp); oneway void onResultReceived(in CameraMetadataNative result, in CaptureResultExtras resultExtras); + oneway void onPrepared(int streamId); } diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl index 23bfa66c9a88..375b310e1bfb 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl @@ -98,4 +98,6 @@ interface ICameraDeviceUser int waitUntilIdle(); int flush(out LongParcelable lastFrameNumber); + + int prepare(int streamId); } diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java index f0217acc0116..dac2ef8c6245 100644 --- a/core/java/android/hardware/camera2/impl/CallbackProxies.java +++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java @@ -23,6 +23,7 @@ import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.dispatch.Dispatchable; import android.hardware.camera2.dispatch.MethodNameInvoker; +import android.view.Surface; import static com.android.internal.util.Preconditions.*; @@ -175,6 +176,12 @@ public class CallbackProxies { public void onClosed(CameraCaptureSession session) { mProxy.invoke("onClosed", session); } + + @Override + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + mProxy.invoke("onSurfacePrepared", session, surface); + } + } private CallbackProxies() { diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index fb5b13c9f650..c74204d63c96 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -144,6 +144,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } @Override + public void prepare(Surface surface) throws CameraAccessException { + mDeviceImpl.prepare(surface); + } + + @Override public synchronized int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { if (request == null) { @@ -589,6 +594,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } } } + + @Override + public void onSurfacePrepared(Surface surface) { + if (VERBOSE) Log.v(TAG, mIdString + "onPrepared"); + mStateCallback.onSurfacePrepared(session, surface); + } + }; } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 91388c3aa73d..1e680dfd21b3 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -605,6 +605,29 @@ public class CameraDeviceImpl extends CameraDevice { } } + public void prepare(Surface surface) throws CameraAccessException { + synchronized(mInterfaceLock) { + int streamId = -1; + for (int i = 0; i < mConfiguredOutputs.size(); i++) { + if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { + streamId = mConfiguredOutputs.keyAt(i); + break; + } + } + if (streamId == -1) { + throw new IllegalArgumentException("Surface is not part of this session"); + } + try { + mRemoteDevice.prepare(streamId); + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } catch (RemoteException e) { + // impossible + return; + } + } + } + public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { if (DEBUG) { @@ -1056,6 +1079,14 @@ public class CameraDeviceImpl extends CameraDevice { public void onIdle(CameraDevice camera) { // Default empty implementation } + + /** + * The method called when the camera device has finished preparing + * an output Surface + */ + public void onSurfacePrepared(Surface surface) { + // Default empty implementation + } } static class CaptureCallbackHolder { @@ -1643,6 +1674,31 @@ public class CameraDeviceImpl extends CameraDevice { } } + @Override + public void onPrepared(int streamId) { + final OutputConfiguration output; + final StateCallbackKK sessionCallback; + + if (DEBUG) { + Log.v(TAG, "Stream " + streamId + " is prepared"); + } + + synchronized(mInterfaceLock) { + output = mConfiguredOutputs.get(streamId); + sessionCallback = mSessionStateCallback; + } + + if (sessionCallback == null) return; + + if (output == null) { + Log.w(TAG, "onPrepared invoked for unknown output Surface"); + return; + } + final Surface surface = output.getSurface(); + + sessionCallback.onSurfacePrepared(surface); + } + /** * Called by onDeviceError for handling single-capture failures. */ diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index 4cd9414c601f..abe26ead2170 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -252,6 +252,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override + public void onPrepared(int streamId) { + // TODO + } + + @Override public IBinder asBinder() { // This is solely intended to be used for in-process binding. return null; @@ -617,6 +622,19 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { return CameraBinderDecorator.NO_ERROR; } + public int prepare(int streamId) { + if (DEBUG) { + Log.d(TAG, "prepare called."); + } + if (mLegacyDevice.isClosed()) { + Log.e(TAG, "Cannot prepare stream, device has been closed."); + return CameraBinderDecorator.ENODEV; + } + + // TODO: Implement and fire callback + return CameraBinderDecorator.NO_ERROR; + } + @Override public IBinder asBinder() { // This is solely intended to be used for in-process binding. diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 3bb5f012623f..14c261904d26 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -261,6 +261,16 @@ public class CameraBinderTest extends AndroidTestCase { // TODO Auto-generated method stub } + + /* + * (non-Javadoc) + * @see android.hardware.camera2.ICameraDeviceCallbacks#onPrepared() + */ + @Override + public void onPrepared(int streamId) throws RemoteException { + // TODO Auto-generated method stub + + } } @SmallTest diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 0466540f0925..6f336725eab5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -132,6 +132,16 @@ public class CameraDeviceBinderTest extends AndroidTestCase { // TODO Auto-generated method stub } + + /* + * (non-Javadoc) + * @see android.hardware.camera2.ICameraDeviceCallbacks#onPrepared() + */ + @Override + public void onPrepared(int streamId) throws RemoteException { + // TODO Auto-generated method stub + + } } class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> { |