Camera2: Add buffer drop error callback
Previously single buffer drop errors were not propagated to
the client application, even though the HAL generated them.
Add new error callback to handle this case.
Bug: 24168122
Change-Id: Ice0d9a3592efed222351353abd7acc35854a20bd
diff --git a/api/current.txt b/api/current.txt
index 9d4674e..4fd9217 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13775,6 +13775,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/api/system-current.txt b/api/system-current.txt
index 23b9d97..db48e1d8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -14176,6 +14176,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/api/test-current.txt b/api/test-current.txt
index cdffa46..5c1acc7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -13785,6 +13785,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 8724a96..38279a4 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -990,6 +990,30 @@
int sequenceId) {
// default empty implementation
}
+
+ /**
+ * <p>This method is called if a single buffer for a capture could not be sent to its
+ * destination surface.</p>
+ *
+ * <p>If the whole capture failed, then {@link #onCaptureFailed} will be called instead. If
+ * some but not all buffers were captured but the result metadata will not be available,
+ * then onCaptureFailed will be invoked with {@link CaptureFailure#wasImageCaptured}
+ * returning true, along with one or more calls to {@link #onCaptureBufferLost} for the
+ * failed outputs.</p>
+ *
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
+ * @param request
+ * The request that was given to the CameraDevice
+ * @param target
+ * The target Surface that the buffer will not be produced for
+ * @param frameNumber
+ * The frame number for the request
+ */
+ public void onCaptureBufferLost(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 37d2ea2..d84a6fc 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1116,6 +1116,11 @@
int sequenceId) {
// default empty implementation
}
+
+ public void onCaptureBufferLost(CameraDevice camera,
+ CaptureRequest request, Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
@@ -1887,48 +1892,66 @@
final CaptureRequest request = holder.getRequest(subsequenceId);
- // No way to report buffer errors right now
+ Runnable failureDispatch = null;
if (errorCode == ERROR_CAMERA_BUFFER) {
- Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
- return;
- }
-
- boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
-
- // This is only approximate - exact handling needs the camera service and HAL to
- // disambiguate between request failures to due abort and due to real errors.
- // For now, assume that if the session believes we're mid-abort, then the error
- // is due to abort.
- int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
- CaptureFailure.REASON_FLUSHED :
- CaptureFailure.REASON_ERROR;
-
- final CaptureFailure failure = new CaptureFailure(
- request,
- reason,
- /*dropped*/ mayHaveBuffers,
- requestId,
- frameNumber);
-
- Runnable failureDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getCallback().onCaptureFailed(
- CameraDeviceImpl.this,
- request,
- failure);
- }
+ final Surface outputSurface =
+ mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurface();
+ if (DEBUG) {
+ Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
+ frameNumber, outputSurface));
}
- };
- holder.getHandler().post(failureDispatch);
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureBufferLost(
+ CameraDeviceImpl.this,
+ request,
+ outputSurface,
+ frameNumber);
+ }
+ }
+ };
+ } else {
+ boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
- // Fire onCaptureSequenceCompleted if appropriate
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", frameNumber));
+ // This is only approximate - exact handling needs the camera service and HAL to
+ // disambiguate between request failures to due abort and due to real errors. For
+ // now, assume that if the session believes we're mid-abort, then the error is due
+ // to abort.
+ int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
+ CaptureFailure.REASON_FLUSHED :
+ CaptureFailure.REASON_ERROR;
+
+ final CaptureFailure failure = new CaptureFailure(
+ request,
+ reason,
+ /*dropped*/ mayHaveBuffers,
+ requestId,
+ frameNumber);
+
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureFailed(
+ CameraDeviceImpl.this,
+ request,
+ failure);
+ }
+ }
+ };
+
+ // Fire onCaptureSequenceCompleted if appropriate
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", frameNumber));
+ }
+ mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
+ checkAndFireSequenceComplete();
}
- mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
- checkAndFireSequenceComplete();
+
+ // Dispatch the failure callback
+ holder.getHandler().post(failureDispatch);
}
} // public class CameraDeviceCallbacks
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index d859da7..40535e2 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -28,6 +28,7 @@
private int precaptureTriggerId;
private long frameNumber;
private int partialResultCount;
+ private int errorStreamId;
public static final Parcelable.Creator<CaptureResultExtras> CREATOR =
new Parcelable.Creator<CaptureResultExtras>() {
@@ -48,13 +49,14 @@
public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
int precaptureTriggerId, long frameNumber,
- int partialResultCount) {
+ int partialResultCount, int errorStreamId) {
this.requestId = requestId;
this.subsequenceId = subsequenceId;
this.afTriggerId = afTriggerId;
this.precaptureTriggerId = precaptureTriggerId;
this.frameNumber = frameNumber;
this.partialResultCount = partialResultCount;
+ this.errorStreamId = errorStreamId;
}
@Override
@@ -70,6 +72,7 @@
dest.writeInt(precaptureTriggerId);
dest.writeLong(frameNumber);
dest.writeInt(partialResultCount);
+ dest.writeInt(errorStreamId);
}
public void readFromParcel(Parcel in) {
@@ -79,6 +82,7 @@
precaptureTriggerId = in.readInt();
frameNumber = in.readLong();
partialResultCount = in.readInt();
+ errorStreamId = in.readInt();
}
public int getRequestId() {
@@ -104,4 +108,8 @@
public int getPartialResultCount() {
return partialResultCount;
}
+
+ public int getErrorStreamId() {
+ return errorStreamId;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4c4adea..661edd7 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -91,11 +91,11 @@
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
+ ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1);
+ /*partialResultCount*/1, /*errorStreamId*/-1);
}
/**