Merge "Camera: Add proper buffer drop errors to LEGACY mode" into nyc-dev

This commit is contained in:
Eino-Ville Talvala
2016-04-04 19:38:09 +00:00
committed by Android (Google) Code Review
6 changed files with 101 additions and 43 deletions

View File

@@ -70,7 +70,7 @@ public class CameraDeviceState {
* CameraDeviceStateListener callbacks to be called after state transitions. * CameraDeviceStateListener callbacks to be called after state transitions.
*/ */
public interface CameraDeviceStateListener { public interface CameraDeviceStateListener {
void onError(int errorCode, RequestHolder holder); void onError(int errorCode, Object errorArg, RequestHolder holder);
void onConfiguring(); void onConfiguring();
void onIdle(); void onIdle();
void onBusy(); void onBusy();
@@ -162,11 +162,12 @@ public class CameraDeviceState {
* @param captureError Report a recoverable error for a single buffer or result using a valid * @param captureError Report a recoverable error for a single buffer or result using a valid
* error code for {@code ICameraDeviceCallbacks}, or * error code for {@code ICameraDeviceCallbacks}, or
* {@link #NO_CAPTURE_ERROR}. * {@link #NO_CAPTURE_ERROR}.
* @param captureErrorArg An argument for some error captureError codes.
* @return {@code false} if an error has occurred. * @return {@code false} if an error has occurred.
*/ */
public synchronized boolean setCaptureResult(final RequestHolder request, public synchronized boolean setCaptureResult(final RequestHolder request,
final CameraMetadataNative result, final CameraMetadataNative result,
final int captureError) { final int captureError, final Object captureErrorArg) {
if (mCurrentState != STATE_CAPTURING) { if (mCurrentState != STATE_CAPTURING) {
Log.e(TAG, "Cannot receive result while in state: " + mCurrentState); Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE; mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
@@ -179,7 +180,7 @@ public class CameraDeviceState {
mCurrentHandler.post(new Runnable() { mCurrentHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
mCurrentListener.onError(captureError, request); mCurrentListener.onError(captureError, captureErrorArg, request);
} }
}); });
} else { } else {
@@ -194,6 +195,11 @@ public class CameraDeviceState {
return mCurrentError == NO_CAPTURE_ERROR; return mCurrentError == NO_CAPTURE_ERROR;
} }
public synchronized boolean setCaptureResult(final RequestHolder request,
final CameraMetadataNative result) {
return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
}
/** /**
* Set the listener for state transition callbacks. * Set the listener for state transition callbacks.
* *
@@ -239,7 +245,7 @@ public class CameraDeviceState {
mCurrentHandler.post(new Runnable() { mCurrentHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
mCurrentListener.onError(mCurrentError, mCurrentRequest); mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
} }
}); });
} }
@@ -299,7 +305,7 @@ public class CameraDeviceState {
mCurrentHandler.post(new Runnable() { mCurrentHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
mCurrentListener.onError(error, mCurrentRequest); mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
} }
}); });
} else { } else {

View File

@@ -480,19 +480,15 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err); throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
} }
ArrayList<Surface> surfaces = null; SparseArray<Surface> surfaces = null;
synchronized(mConfigureLock) { synchronized(mConfigureLock) {
if (!mConfiguring) { if (!mConfiguring) {
String err = "Cannot end configure, no configuration change in progress."; String err = "Cannot end configure, no configuration change in progress.";
Log.e(TAG, err); Log.e(TAG, err);
throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err); throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
} }
int numSurfaces = mSurfaces.size(); if (mSurfaces != null) {
if (numSurfaces > 0) { surfaces = mSurfaces.clone();
surfaces = new ArrayList<>();
for (int i = 0; i < numSurfaces; ++i) {
surfaces.add(mSurfaces.valueAt(i));
}
} }
mConfiguring = false; mConfiguring = false;
} }

View File

@@ -19,7 +19,7 @@ import android.hardware.camera2.impl.CameraDeviceImpl;
import android.util.Log; import android.util.Log;
import android.util.MutableLong; import android.util.MutableLong;
import android.util.Pair; import android.util.Pair;
import android.view.Surface;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.TreeSet; import java.util.TreeSet;
@@ -95,22 +95,28 @@ public class CaptureCollector {
} else { } else {
// Send buffer dropped errors for each pending buffer if the request has // Send buffer dropped errors for each pending buffer if the request has
// started. // started.
if (mFailedPreview) { for (Surface targetSurface : mRequest.getRequest().getTargets() ) {
Log.w(TAG, "Preview buffers dropped for request: " + try {
mRequest.getRequestId()); if (mRequest.jpegType(targetSurface)) {
for (int i = 0; i < mRequest.numPreviewTargets(); i++) { if (mFailedJpeg) {
CaptureCollector.this.mDeviceState.setCaptureResult(mRequest, CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
/*result*/null, /*result*/null,
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER); CameraDeviceImpl.CameraDeviceCallbacks.
} ERROR_CAMERA_BUFFER,
} targetSurface);
if (mFailedJpeg) { }
Log.w(TAG, "Jpeg buffers dropped for request: " + } else {
mRequest.getRequestId()); // preview buffer
for (int i = 0; i < mRequest.numJpegTargets(); i++) { if (mFailedPreview) {
CaptureCollector.this.mDeviceState.setCaptureResult(mRequest, CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
/*result*/null, /*result*/null,
CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER); CameraDeviceImpl.CameraDeviceCallbacks.
ERROR_CAMERA_BUFFER,
targetSurface);
}
}
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
Log.e(TAG, "Unexpected exception when querying Surface: " + e);
} }
} }
} }

View File

@@ -36,6 +36,7 @@ import android.os.ServiceSpecificException;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.Size; import android.util.Size;
import android.util.SparseArray;
import android.view.Surface; import android.view.Surface;
import java.util.ArrayList; import java.util.ArrayList;
@@ -64,7 +65,7 @@ public class LegacyCameraDevice implements AutoCloseable {
private final CameraCharacteristics mStaticCharacteristics; private final CameraCharacteristics mStaticCharacteristics;
private final ICameraDeviceCallbacks mDeviceCallbacks; private final ICameraDeviceCallbacks mDeviceCallbacks;
private final CameraDeviceState mDeviceState = new CameraDeviceState(); private final CameraDeviceState mDeviceState = new CameraDeviceState();
private List<Surface> mConfiguredSurfaces; private SparseArray<Surface> mConfiguredSurfaces;
private boolean mClosed = false; private boolean mClosed = false;
private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
@@ -89,13 +90,29 @@ public class LegacyCameraDevice implements AutoCloseable {
public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1; public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
return getExtrasFromRequest(holder,
/*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
}
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder,
int errorCode, Object errorArg) {
int errorStreamId = -1;
if (errorCode == CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) {
Surface errorTarget = (Surface) errorArg;
int indexOfTarget = mConfiguredSurfaces.indexOfValue(errorTarget);
if (indexOfTarget < 0) {
Log.e(TAG, "Buffer drop error reported for unknown Surface");
} else {
errorStreamId = mConfiguredSurfaces.keyAt(indexOfTarget);
}
}
if (holder == null) { if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, return new CaptureResultExtras(ILLEGAL_VALUE, 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(), return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(), /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
/*partialResultCount*/1, /*errorStreamId*/-1); /*partialResultCount*/1, errorStreamId);
} }
/** /**
@@ -105,9 +122,9 @@ public class LegacyCameraDevice implements AutoCloseable {
private final CameraDeviceState.CameraDeviceStateListener mStateListener = private final CameraDeviceState.CameraDeviceStateListener mStateListener =
new CameraDeviceState.CameraDeviceStateListener() { new CameraDeviceState.CameraDeviceStateListener() {
@Override @Override
public void onError(final int errorCode, final RequestHolder holder) { public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "onError called, errorCode = " + errorCode); Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg);
} }
switch (errorCode) { switch (errorCode) {
/* /*
@@ -125,7 +142,7 @@ public class LegacyCameraDevice implements AutoCloseable {
} }
} }
final CaptureResultExtras extras = getExtrasFromRequest(holder); final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg);
mResultHandler.post(new Runnable() { mResultHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -281,14 +298,17 @@ public class LegacyCameraDevice implements AutoCloseable {
* *
* <p>Every surface in {@code outputs} must be non-{@code null}.</p> * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
* *
* @param outputs a list of surfaces to set. * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
* list; it must not be modified by the caller once it's passed in.
* @return an error code for this binder operation, or {@link NO_ERROR} * @return an error code for this binder operation, or {@link NO_ERROR}
* on success. * on success.
*/ */
public int configureOutputs(List<Surface> outputs) { public int configureOutputs(SparseArray<Surface> outputs) {
List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>(); List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
if (outputs != null) { if (outputs != null) {
for (Surface output : outputs) { int count = outputs.size();
for (int i = 0; i < count; i++) {
Surface output = outputs.valueAt(i);
if (output == null) { if (output == null) {
Log.e(TAG, "configureOutputs - null outputs are not allowed"); Log.e(TAG, "configureOutputs - null outputs are not allowed");
return BAD_VALUE; return BAD_VALUE;
@@ -353,7 +373,7 @@ public class LegacyCameraDevice implements AutoCloseable {
} }
if (success) { if (success) {
mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null; mConfiguredSurfaces = outputs;
} else { } else {
return LegacyExceptionUtils.INVALID_OPERATION; return LegacyExceptionUtils.INVALID_OPERATION;
} }
@@ -659,6 +679,23 @@ public class LegacyCameraDevice implements AutoCloseable {
return nativeGetSurfaceId(surface); return nativeGetSurfaceId(surface);
} }
static List<Long> getSurfaceIds(SparseArray<Surface> surfaces) {
if (surfaces == null) {
throw new NullPointerException("Null argument surfaces");
}
List<Long> surfaceIds = new ArrayList<>();
int count = surfaces.size();
for (int i = 0; i < count; i++) {
long id = getSurfaceId(surfaces.valueAt(i));
if (id == 0) {
throw new IllegalStateException(
"Configured surface had null native GraphicBufferProducer pointer!");
}
surfaceIds.add(id);
}
return surfaceIds;
}
static List<Long> getSurfaceIds(Collection<Surface> surfaces) { static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
if (surfaces == null) { if (surfaces == null) {
throw new NullPointerException("Null argument surfaces"); throw new NullPointerException("Null argument surfaces");

View File

@@ -41,6 +41,8 @@ public class RequestHolder {
private final int mNumPreviewTargets; private final int mNumPreviewTargets;
private volatile boolean mFailed = false; private volatile boolean mFailed = false;
private final Collection<Long> mJpegSurfaceIds;
/** /**
* A builder class for {@link RequestHolder} objects. * A builder class for {@link RequestHolder} objects.
* *
@@ -150,13 +152,13 @@ public class RequestHolder {
*/ */
public RequestHolder build(long frameNumber) { public RequestHolder build(long frameNumber) {
return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber, return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber,
mNumJpegTargets, mNumPreviewTargets); mNumJpegTargets, mNumPreviewTargets, mJpegSurfaceIds);
} }
} }
private RequestHolder(int requestId, int subsequenceId, CaptureRequest request, private RequestHolder(int requestId, int subsequenceId, CaptureRequest request,
boolean repeating, long frameNumber, int numJpegTargets, boolean repeating, long frameNumber, int numJpegTargets,
int numPreviewTargets) { int numPreviewTargets, Collection<Long> jpegSurfaceIds) {
mRepeating = repeating; mRepeating = repeating;
mRequest = request; mRequest = request;
mRequestId = requestId; mRequestId = requestId;
@@ -164,6 +166,7 @@ public class RequestHolder {
mFrameNumber = frameNumber; mFrameNumber = frameNumber;
mNumJpegTargets = numJpegTargets; mNumJpegTargets = numJpegTargets;
mNumPreviewTargets = numPreviewTargets; mNumPreviewTargets = numPreviewTargets;
mJpegSurfaceIds = jpegSurfaceIds;
} }
/** /**
@@ -237,6 +240,17 @@ public class RequestHolder {
return mNumPreviewTargets; return mNumPreviewTargets;
} }
/**
* Returns true if the given surface requires jpeg buffers.
*
* @param s a {@link android.view.Surface} to check.
* @return true if the surface requires a jpeg buffer.
*/
public boolean jpegType(Surface s)
throws LegacyExceptionUtils.BufferQueueAbandonedException {
return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
}
/** /**
* Mark this request as failed. * Mark this request as failed.
*/ */

View File

@@ -908,8 +908,7 @@ public class RequestThreadManager {
mFaceDetectMapper.mapResultFaces(result, mLastRequest); mFaceDetectMapper.mapResultFaces(result, mLastRequest);
if (!holder.requestFailed()) { if (!holder.requestFailed()) {
mDeviceState.setCaptureResult(holder, result, mDeviceState.setCaptureResult(holder, result);
CameraDeviceState.NO_CAPTURE_ERROR);
} }
} }
if (DEBUG) { if (DEBUG) {