From ae10eb9f7d60ab530ad586e7c11c896ed6f3ee2d Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Wed, 1 Mar 2017 12:58:04 -0800 Subject: [PATCH] Camera: Enable batching for constrained high speed recording For each constrained high speed recording request list, only 1 shutter notify and 1 capture result are sent from cameraserver to application process as a power-saving measure. Once the shutter notify and capture result are passed into application process, derive individual shutter and result for other requests in the batch, and send them to application. Test: Manually test high speed recording with GCA and CTS. Bug: 35775704 Change-Id: Ie5fa1de6b3b87f1242d6c81770e40ae876991bc3 --- .../camera2/impl/CameraDeviceImpl.java | 133 +++++++++++++++--- 1 file changed, 116 insertions(+), 17 deletions(-) diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 8bc65af65ef12..e3b97e82f6c84 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -100,6 +100,8 @@ public class CameraDeviceImpl extends CameraDevice private final CameraCharacteristics mCharacteristics; private final int mTotalPartialCount; + private static final long NANO_PER_SECOND = 1000000000; //ns + /** * A list tracking request and its expected last regular frame number and last reprocess frame * number. Updated when calling ICameraDeviceUser methods. @@ -1239,6 +1241,14 @@ public class CameraDeviceImpl extends CameraDevice private final List mRequestList; private final Handler mHandler; private final int mSessionId; + /** + *

Determine if the callback holder is for a constrained high speed request list that + * expects batched capture results. Capture results will be batched if the request list + * is interleaved with preview and video requests. Capture results won't be batched if the + * request list only contains preview requests, or if the request doesn't belong to a + * constrained high speed list. + */ + private final boolean mHasBatchedOutputs; CaptureCallbackHolder(CaptureCallback callback, List requestList, Handler handler, boolean repeating, int sessionId) { @@ -1251,6 +1261,25 @@ public class CameraDeviceImpl extends CameraDevice mRequestList = new ArrayList(requestList); mCallback = callback; mSessionId = sessionId; + + // Check whether this callback holder is for batched outputs. + // The logic here should match createHighSpeedRequestList. + boolean hasBatchedOutputs = true; + for (int i = 0; i < requestList.size(); i++) { + CaptureRequest request = requestList.get(i); + if (!request.isPartOfCRequestList()) { + hasBatchedOutputs = false; + break; + } + if (i == 0) { + Collection targets = request.getTargets(); + if (targets.size() != 2) { + hasBatchedOutputs = false; + break; + } + } + } + mHasBatchedOutputs = hasBatchedOutputs; } public boolean isRepeating() { @@ -1288,6 +1317,14 @@ public class CameraDeviceImpl extends CameraDevice public int getSessionId() { return mSessionId; } + + public int getRequestCount() { + return mRequestList.size(); + } + + public boolean hasBatchedOutputs() { + return mHasBatchedOutputs; + } } /** @@ -1777,10 +1814,27 @@ public class CameraDeviceImpl extends CameraDevice @Override public void run() { if (!CameraDeviceImpl.this.isClosed()) { - holder.getCallback().onCaptureStarted( - CameraDeviceImpl.this, - holder.getRequest(resultExtras.getSubsequenceId()), - timestamp, frameNumber); + final int subsequenceId = resultExtras.getSubsequenceId(); + final CaptureRequest request = holder.getRequest(subsequenceId); + + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureStarted for requests within the batch + final Range fpsRange = + request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + for (int i = 0; i < holder.getRequestCount(); i++) { + holder.getCallback().onCaptureStarted( + CameraDeviceImpl.this, + holder.getRequest(i), + timestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper(), + frameNumber - (subsequenceId - i)); + } + } else { + holder.getCallback().onCaptureStarted( + CameraDeviceImpl.this, + holder.getRequest(resultExtras.getSubsequenceId()), + timestamp, frameNumber); + } } } }); @@ -1845,46 +1899,91 @@ public class CameraDeviceImpl extends CameraDevice Runnable resultDispatch = null; CaptureResult finalResult; + // Make a copy of the native metadata before it gets moved to a CaptureResult + // object. + final CameraMetadataNative resultCopy; + if (holder.hasBatchedOutputs()) { + resultCopy = new CameraMetadataNative(result); + } else { + resultCopy = null; + } // Either send a partial result or the final capture completed result if (isPartialResult) { final CaptureResult resultAsCapture = new CaptureResult(result, request, resultExtras); - // Partial result resultDispatch = new Runnable() { @Override public void run() { - if (!CameraDeviceImpl.this.isClosed()){ - holder.getCallback().onCaptureProgressed( - CameraDeviceImpl.this, - request, - resultAsCapture); + if (!CameraDeviceImpl.this.isClosed()) { + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureProgressed for requests within + // the batch. + for (int i = 0; i < holder.getRequestCount(); i++) { + CameraMetadataNative resultLocal = + new CameraMetadataNative(resultCopy); + CaptureResult resultInBatch = new CaptureResult( + resultLocal, holder.getRequest(i), resultExtras); + + holder.getCallback().onCaptureProgressed( + CameraDeviceImpl.this, + holder.getRequest(i), + resultInBatch); + } + } else { + holder.getCallback().onCaptureProgressed( + CameraDeviceImpl.this, + request, + resultAsCapture); + } } } }; - finalResult = resultAsCapture; } else { List partialResults = mFrameNumberTracker.popPartialResults(frameNumber); + final long sensorTimestamp = + result.get(CaptureResult.SENSOR_TIMESTAMP); + final Range fpsRange = + request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + final int subsequenceId = resultExtras.getSubsequenceId(); final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, request, resultExtras, partialResults, holder.getSessionId()); - // Final capture result resultDispatch = new Runnable() { @Override public void run() { if (!CameraDeviceImpl.this.isClosed()){ - holder.getCallback().onCaptureCompleted( - CameraDeviceImpl.this, - request, - resultAsCapture); + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureCompleted for requests within + // the batch. + for (int i = 0; i < holder.getRequestCount(); i++) { + resultCopy.set(CaptureResult.SENSOR_TIMESTAMP, + sensorTimestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper()); + CameraMetadataNative resultLocal = + new CameraMetadataNative(resultCopy); + TotalCaptureResult resultInBatch = new TotalCaptureResult( + resultLocal, holder.getRequest(i), resultExtras, + partialResults, holder.getSessionId()); + + holder.getCallback().onCaptureCompleted( + CameraDeviceImpl.this, + holder.getRequest(i), + resultInBatch); + } + } else { + holder.getCallback().onCaptureCompleted( + CameraDeviceImpl.this, + request, + resultAsCapture); + } } } }; - finalResult = resultAsCapture; }