camera2 (v)ndk: Add APIs for pre-allocation of surface buffers.
Clients may need to pre-allocate stream buffers in order to reduce
affects of buffer-allocation time such as frame jank till steady state
capture is reached (eg: for a repeating request).
The SDK already has an api which does this :
CameraCaptureSession.prepare(Surface). We're exposing a similar API to
the ndk/ vndk here.
Bug: 259735869
Test: NdkCameraDeviceTest.java
Change-Id: Iee8559fcebd61a6886f23779a05351b52633f851
Signed-off-by: Jayant Chowdhary <jchowdhary@google.com>
diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp
index 65b8837..8a3acf3 100644
--- a/camera/ndk/NdkCameraCaptureSession.cpp
+++ b/camera/ndk/NdkCameraCaptureSession.cpp
@@ -40,6 +40,7 @@
if (session != nullptr) {
session->closeByApp();
}
+
return;
}
@@ -191,3 +192,42 @@
}
return session->updateOutputConfiguration(output);
}
+EXPORT
+camera_status_t ACameraCaptureSession_setWindowPreparedCallback(
+ ACameraCaptureSession* session, ACameraCaptureSession_prepareCallbacks *cb) {
+ ATRACE_CALL();
+ if (session == nullptr || cb == nullptr) {
+ ALOGE("%s: Error: session %p / callback %p is null", __FUNCTION__, session, cb);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cb->reserved0 != nullptr || cb->reserved1 != nullptr) {
+ ALOGE("%s: Setting reserved 0 and reserved 1 fields of "
+ "ACameraCaptureSession_prepareCallbacks is currently not supported "
+ " .They must be set to null", __FUNCTION__);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ if (session->isClosed()) {
+ ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+ return ACAMERA_ERROR_SESSION_CLOSED;
+ }
+ session->setWindowPreparedCallback(cb);
+ return ACAMERA_OK;
+}
+
+EXPORT
+camera_status_t ACameraCaptureSession_prepareWindow(
+ ACameraCaptureSession* session,
+ ACameraWindowType *window) {
+ ATRACE_CALL();
+ if (session == nullptr || window == nullptr) {
+ ALOGE("%s: Error: session %p / window %p is null", __FUNCTION__, session, window);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (session->isClosed()) {
+ ALOGE("%s: session %p is already closed", __FUNCTION__, session);
+ return ACAMERA_ERROR_SESSION_CLOSED;
+ }
+ return session->prepare(window);
+}
\ No newline at end of file
diff --git a/camera/ndk/impl/ACameraCaptureSession.cpp b/camera/ndk/impl/ACameraCaptureSession.cpp
index 110d47a..73439c7 100644
--- a/camera/ndk/impl/ACameraCaptureSession.cpp
+++ b/camera/ndk/impl/ACameraCaptureSession.cpp
@@ -146,6 +146,27 @@
return ret;
}
+camera_status_t ACameraCaptureSession::prepare(ACameraWindowType* window) {
+#ifdef __ANDROID_VNDK__
+ std::shared_ptr<acam::CameraDevice> dev = getDevicePtr();
+#else
+ sp<acam::CameraDevice> dev = getDeviceSp();
+#endif
+ if (dev == nullptr) {
+ ALOGE("Error: Device associated with session %p has been closed!", this);
+ return ACAMERA_ERROR_SESSION_CLOSED;
+ }
+
+ camera_status_t ret;
+ dev->lockDeviceForSessionOps();
+ {
+ Mutex::Autolock _l(mSessionLock);
+ ret = dev->prepareLocked(window);
+ }
+ dev->unlockDevice();
+ return ret;
+}
+
ACameraDevice*
ACameraCaptureSession::getDevice() {
Mutex::Autolock _l(mSessionLock);
diff --git a/camera/ndk/impl/ACameraCaptureSession.h b/camera/ndk/impl/ACameraCaptureSession.h
index cd65e8c..145473b 100644
--- a/camera/ndk/impl/ACameraCaptureSession.h
+++ b/camera/ndk/impl/ACameraCaptureSession.h
@@ -130,6 +130,12 @@
camera_status_t updateOutputConfiguration(ACaptureSessionOutput *output);
+ void setWindowPreparedCallback(ACameraCaptureSession_prepareCallbacks *cb) {
+ Mutex::Autolock _l(mSessionLock);
+ mPreparedCb = *cb;
+ }
+ camera_status_t prepare(ACameraWindowType *window);
+
ACameraDevice* getDevice();
private:
@@ -156,6 +162,7 @@
bool mIsClosed = false;
bool mClosedByApp = false;
+ ACameraCaptureSession_prepareCallbacks mPreparedCb;
Mutex mSessionLock;
};
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 536055b..b90f276 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -342,6 +342,58 @@
return ACAMERA_OK;
}
+camera_status_t CameraDevice::prepareLocked(ACameraWindowType *window) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ if (window == nullptr) {
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ int32_t streamId = -1;
+ for (auto& kvPair : mConfiguredOutputs) {
+ if (window == kvPair.second.first) {
+ streamId = kvPair.first;
+ break;
+ }
+ }
+ if (streamId < 0) {
+ ALOGE("Error: Invalid output configuration");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ auto remoteRet = mRemote->prepare(streamId);
+ if (!remoteRet.isOk()) {
+ // TODO:(b/259735869) Do this check for all other binder calls in the
+ // ndk as well.
+ if (remoteRet.exceptionCode() != EX_SERVICE_SPECIFIC) {
+ ALOGE("Camera device %s failed to prepare output window %p: %s", getId(), window,
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_UNKNOWN;
+
+ }
+ switch (remoteRet.serviceSpecificErrorCode()) {
+ case hardware::ICameraService::ERROR_INVALID_OPERATION:
+ ALOGE("Camera device %s invalid operation: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_INVALID_OPERATION;
+ break;
+ case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+ ALOGE("Camera device %s invalid input argument: %s", getId(),
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ break;
+ default:
+ ALOGE("Camera device %s failed to prepare output window %p: %s", getId(), window,
+ remoteRet.toString8().string());
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ }
+
+ return ACAMERA_OK;
+}
+
camera_status_t
CameraDevice::allocateCaptureRequest(
const ACaptureRequest* request, /*out*/sp<CaptureRequest>& outReq) {
@@ -919,6 +971,7 @@
case kWhatCaptureSeqEnd:
case kWhatCaptureSeqAbort:
case kWhatCaptureBufferLost:
+ case kWhatPreparedCb:
ALOGV("%s: Received msg %d", __FUNCTION__, msg->what());
break;
case kWhatCleanUpSessions:
@@ -992,6 +1045,7 @@
case kWhatCaptureSeqEnd:
case kWhatCaptureSeqAbort:
case kWhatCaptureBufferLost:
+ case kWhatPreparedCb:
{
sp<RefBase> obj;
found = msg->findObject(kSessionSpKey, &obj);
@@ -1034,6 +1088,26 @@
(*onState)(context, session.get());
break;
}
+ case kWhatPreparedCb:
+ {
+ ACameraCaptureSession_prepareCallback onWindowPrepared;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onWindowPrepared);
+ if (!found) {
+ ALOGE("%s: Cannot find state callback!", __FUNCTION__);
+ return;
+ }
+ if (onWindowPrepared == nullptr) {
+ return;
+ }
+ ACameraWindowType* anw;
+ found = msg->findPointer(kAnwKey, (void**) &anw);
+ if (!found) {
+ ALOGE("%s: Cannot find ANativeWindow: %d!", __FUNCTION__, __LINE__);
+ return;
+ }
+ (*onWindowPrepared)(context, anw, session.get());
+ break;
+ }
case kWhatCaptureStart:
{
ACameraCaptureSession_captureCallback_start onStart;
@@ -1729,8 +1803,36 @@
}
binder::Status
-CameraDevice::ServiceCallback::onPrepared(int) {
- // Prepare not yet implemented in NDK
+CameraDevice::ServiceCallback::onPrepared(int streamId) {
+ ALOGV("%s: callback for stream id %d", __FUNCTION__, streamId);
+ binder::Status ret = binder::Status::ok();
+ sp<CameraDevice> dev = mDevice.promote();
+ if (dev == nullptr) {
+ return ret; // device has been closed
+ }
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->isClosed() || dev->mRemote == nullptr) {
+ return ret;
+ }
+ auto it = dev->mConfiguredOutputs.find(streamId);
+ if (it == dev->mConfiguredOutputs.end()) {
+ ALOGE("%s: stream id %d does not exist", __FUNCTION__ , streamId);
+ return ret;
+ }
+ sp<ACameraCaptureSession> session = dev->mCurrentSession.promote();
+ if (session == nullptr) {
+ ALOGE("%s: Session is dead already", __FUNCTION__ );
+ return ret;
+ }
+ // We've found the window corresponding to the surface id.
+ ACameraWindowType *window = it->second.first;
+ sp<AMessage> msg = new AMessage(kWhatPreparedCb, dev->mHandler);
+ msg->setPointer(kContextKey, session->mPreparedCb.context);
+ msg->setPointer(kAnwKey, window);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void *)session->mPreparedCb.onWindowPrepared);
+ dev->postSessionMsgAndCleanup(msg);
+
return binder::Status::ok();
}
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 17988fe..382d6ce 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -151,6 +151,8 @@
camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
+ camera_status_t prepareLocked(ACameraWindowType *window);
+
camera_status_t allocateCaptureRequest(
const ACaptureRequest* request, sp<CaptureRequest>& outReq);
@@ -222,7 +224,8 @@
kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed
kWhatCaptureSeqEnd, // onCaptureSequenceCompleted
kWhatCaptureSeqAbort, // onCaptureSequenceAborted
- kWhatCaptureBufferLost,// onCaptureBufferLost
+ kWhatCaptureBufferLost, // onCaptureBufferLost
+ kWhatPreparedCb, // onWindowPrepared
// Internal cleanup
kWhatCleanUpSessions // Cleanup cached sp<ACameraCaptureSession>
};
diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h
index b0fd00c..0211d83 100644
--- a/camera/ndk/include/camera/NdkCameraCaptureSession.h
+++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h
@@ -99,6 +99,45 @@
ACameraCaptureSession_stateCallback onActive;
} ACameraCaptureSession_stateCallbacks;
+/**
+ * The definition of camera capture session onWindowPrepared callback.
+ * @param context The optional app-provided context pointer that was included in
+ * the {@link ACameraCaptureSession_prepareCallbacks} struct.
+ * @param window The window that {@link ACameraCaptureSession_prepare} was called on.
+ * @param session The camera capture session on which {@link ACameraCaptureSession_prepare} was
+ * called on.
+ */
+typedef void (*ACameraCaptureSession_prepareCallback)(
+ void *context,
+ ACameraWindowType *window,
+ ACameraCaptureSession *session);
+
+/**
+ * Capture session state callbacks used in {@link ACameraDevice_setPrepareCallbacks}
+ */
+typedef struct ACameraCaptureSession_prepareCallbacks {
+ /// optional application context. This will be passed in the context
+ /// parameter of the {@link onWindowPrepared} callback.
+ void* context;
+
+ /**
+ * This callback is called when the buffer pre-allocation for an output window Surface is
+ * complete.
+ * <p>Buffer pre-allocation for an output window is started by
+ * {@link ACameraCaptureSession_prepare}
+ * call. While allocation is underway, the output must not be used in a capture request.
+ * Once this callback is called, the output provided can be used as a target for a
+ * capture request. In case of an 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>
+ */
+ ACameraCaptureSession_prepareCallback onWindowPrepared;
+
+ // Reserved for future callback additions, these must be set to nullptr by the client.
+ ACameraCaptureSession_prepareCallback reserved0;
+ ACameraCaptureSession_prepareCallback reserved1;
+} ACameraCaptureSession_prepareCallbacks;
+
/// Enum for describing error reason in {@link ACameraCaptureFailure}
enum {
/**
@@ -989,6 +1028,89 @@
int numRequests, ACaptureRequest** requests,
/*optional*/int* captureSequenceId) __INTRODUCED_IN(33);
+/**
+ * Set the callback that is called when the output window for which the client has requested
+ * pre-allocation of buffers through the {@link ACameraCaptureSession_prepareWindow} call has
+ * completed the pre-allocation of buffers.
+ * @param session the ACameraCaptureSession on which ACameraCaptureSession_prepareWindow was called.
+ * @param callbacks the callback to be called when the output window's buffer pre-allocation is
+ * complete.
+ * @return <ul><li> {@link ACAMERA_OK} if the method succeeds</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if session or callbacks is
+ * NULL. Or if the session has not been configured with the window</li>
+ * <li>{@link ACAMERA_ERROR_SESSION_CLOSED} if the capture session has been closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DEVICE} if the camera device encounters fatal error</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} the camera service encounters fatal error</li>
+ * <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
+ */
+camera_status_t ACameraCaptureSession_setWindowPreparedCallback(
+ ACameraCaptureSession* session,
+ ACameraCaptureSession_prepareCallbacks* callbacks) __INTRODUCED_IN(34);
+
+/**
+ *
+ * <p>Pre-allocate all buffers for an output window.</p>
+ *
+ * <p>Normally, the image buffers for a given output window 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 window 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 ACameraCaptureSession_prepare() call can be used to perform this pre-allocation.
+ * It may only be called for a given output window before that window 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 window, 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 this call may result in higher memory consumption than the normal on-demand behavior
+ * results in. This method 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 AImageReader} with a maxImages argument of 10,
+ * but only uses 3 simultaneous {@link AImage}s 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
+ * ACameraCaptureSession_prepare() on the {@link AImageReader}'s window will result in all 10
+ * {@link AImage}s 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 window 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. If it is, {@link ACAMERA_ERROR_INVALID_PARAMETER} will
+ * be returned by the method</p>
+ *
+ * <p>Once allocation is complete, {@link ACameraCaptureSession_prepareCallback#onWindowPrepared}
+ * will be invoked with the output provided to this method. Between the prepare call and the
+ * {@link ACameraCaptureSession_prepareCallback#onWindowPrepared} call,
+ * the output provided to prepare must not be used as a target of a capture qequest submitted
+ * to this session.</p>
+ *
+ * <p>{@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}
+ * devices cannot pre-allocate output buffers; for those devices,
+ * {@link ACameraCaptureSession_prepareCallback#onWindowPrepared} will be immediately called,
+ * and no pre-allocation is done.</p>
+ *
+ * @param session the {@link ACameraCaptureSession} that needs to prepare output buffers.
+ * @param window the {@link ACameraWindowType} for which the output buffers need to be prepared.
+ *
+ * @return <ul><li>
+ * {@link ACAMERA_OK} if the method succeeds</li>
+ * <li>{@link ACAMERA_ERROR_INVALID_PARAMETER} if session/ window or prepareCallbacks is
+ * NULL. Or if the session has not been configured with the window</li>
+ * <li>{@link ACAMERA_ERROR_SESSION_CLOSED} if the capture session has been closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DISCONNECTED} if the camera device is closed</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_DEVICE} if the camera device encounters fatal error</li>
+ * <li>{@link ACAMERA_ERROR_CAMERA_SERVICE} if the camera service encounters fatal error</li>
+ * <li>{@link ACAMERA_ERROR_UNKNOWN} if the method fails for some other reasons</li></ul>
+ */
+camera_status_t ACameraCaptureSession_prepareWindow(
+ ACameraCaptureSession* session,
+ ACameraWindowType *window) __INTRODUCED_IN(34);
__END_DECLS
#endif /* _NDK_CAMERA_CAPTURE_SESSION_H */
diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt
index b3977ff..4c54658 100644
--- a/camera/ndk/libcamera2ndk.map.txt
+++ b/camera/ndk/libcamera2ndk.map.txt
@@ -12,6 +12,8 @@
ACameraCaptureSession_logicalCamera_setRepeatingRequest; # introduced=29
ACameraCaptureSession_logicalCamera_setRepeatingRequestV2; # introduced=33
ACameraCaptureSession_stopRepeating;
+ ACameraCaptureSession_setWindowPreparedCallback; # introduced=34
+ ACameraCaptureSession_prepareWindow; # introduced=34
ACameraCaptureSession_updateSharedOutput; # introduced=28
ACameraDevice_close;
ACameraDevice_createCaptureRequest;
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index 96df7db..87102e4 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -340,6 +340,33 @@
return ACAMERA_OK;
}
+camera_status_t CameraDevice::prepareLocked(ACameraWindowType *window) {
+ camera_status_t ret = checkCameraClosedOrErrorLocked();
+ if (ret != ACAMERA_OK) {
+ return ret;
+ }
+
+ if (window == nullptr) {
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ int32_t streamId = -1;
+ for (auto& kvPair : mConfiguredOutputs) {
+ if (window == kvPair.second.first) {
+ streamId = kvPair.first;
+ break;
+ }
+ }
+ if (streamId < 0) {
+ ALOGE("Error: Invalid output configuration");
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+
+ auto remoteRet = mRemote->prepare(streamId);
+ CHECK_TRANSACTION_AND_RET(remoteRet, "prepare()")
+ return ACAMERA_OK;
+}
+
camera_status_t CameraDevice::allocateCaptureRequestLocked(
const ACaptureRequest* request, /*out*/sp<CaptureRequest> &outReq) {
sp<CaptureRequest> req(new CaptureRequest());
@@ -929,6 +956,7 @@
case kWhatCaptureSeqEnd:
case kWhatCaptureSeqAbort:
case kWhatCaptureBufferLost:
+ case kWhatPreparedCb:
ALOGV("%s: Received msg %d", __FUNCTION__, msg->what());
break;
case kWhatCleanUpSessions:
@@ -1002,6 +1030,7 @@
case kWhatCaptureSeqEnd:
case kWhatCaptureSeqAbort:
case kWhatCaptureBufferLost:
+ case kWhatPreparedCb:
{
sp<RefBase> obj;
found = msg->findObject(kSessionSpKey, &obj);
@@ -1045,6 +1074,26 @@
(*onState)(context, session.get());
break;
}
+ case kWhatPreparedCb:
+ {
+ ACameraCaptureSession_prepareCallback onWindowPrepared;
+ found = msg->findPointer(kCallbackFpKey, (void**) &onWindowPrepared);
+ if (!found) {
+ ALOGE("%s: Cannot find state callback!", __FUNCTION__);
+ return;
+ }
+ if (onWindowPrepared == nullptr) {
+ return;
+ }
+ native_handle_t* anw;
+ found = msg->findPointer(kAnwKey, (void**) &anw);
+ if (!found) {
+ ALOGE("%s: Cannot find ANativeWindow: %d!", __FUNCTION__, __LINE__);
+ return;
+ }
+ (*onWindowPrepared)(context, anw, session.get());
+ break;
+ }
case kWhatCaptureStart:
{
ACameraCaptureSession_captureCallback_start onStart;
@@ -1772,6 +1821,37 @@
return ScopedAStatus::ok();
}
+ScopedAStatus CameraDevice::ServiceCallback::onPrepared(int32_t streamId) {
+ ALOGV("%s: callback for stream id %d", __FUNCTION__, streamId);
+ std::shared_ptr<CameraDevice> dev = mDevice.lock();
+ if (dev == nullptr) {
+ return ScopedAStatus::ok();
+ }
+ Mutex::Autolock _l(dev->mDeviceLock);
+ if (dev->isClosed() || dev->mRemote == nullptr) {
+ return ScopedAStatus::ok();
+ }
+ auto it = dev->mConfiguredOutputs.find(streamId);
+ if (it == dev->mConfiguredOutputs.end()) {
+ ALOGE("%s: stream id %d does not exist", __FUNCTION__ , streamId);
+ return ScopedAStatus::ok();
+ }
+ sp<ACameraCaptureSession> session = dev->mCurrentSession.promote();
+ if (session == nullptr) {
+ ALOGE("%s: Session is dead already", __FUNCTION__ );
+ return ScopedAStatus::ok();
+ }
+ // We've found the window corresponding to the surface id.
+ const native_handle_t *anw = it->second.first.mWindow;
+ sp<AMessage> msg = new AMessage(kWhatPreparedCb, dev->mHandler);
+ msg->setPointer(kContextKey, session->mPreparedCb.context);
+ msg->setPointer(kAnwKey, (void *)anw);
+ msg->setObject(kSessionSpKey, session);
+ msg->setPointer(kCallbackFpKey, (void *)session->mPreparedCb.onWindowPrepared);
+ dev->postSessionMsgAndCleanup(msg);
+ return ScopedAStatus::ok();
+}
+
camera_status_t CameraDevice::ServiceCallback::readOneResultMetadata(
const CaptureMetadataInfo& captureMetadataInfo, ResultMetadataQueue* metadataQueue,
CameraMetadata* metadata) {
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
index cad23aa..6e0c772 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h
@@ -130,6 +130,7 @@
ndk::ScopedAStatus onCaptureStarted(const CaptureResultExtras& in_resultExtras,
int64_t in_timestamp) override;
+ ndk::ScopedAStatus onPrepared(int32_t in_streamId) override;
ndk::ScopedAStatus onRepeatingRequestError(int64_t in_lastFrameNumber,
int32_t in_repeatingRequestId) override;
ndk::ScopedAStatus onResultReceived(const CaptureMetadataInfo& in_result,
@@ -196,6 +197,8 @@
camera_status_t updateOutputConfigurationLocked(ACaptureSessionOutput *output);
+ camera_status_t prepareLocked(ACameraWindowType *window);
+
// Since this writes to ICameraDeviceUser's fmq, clients must take care that:
// a) This function is called serially.
// b) This function is called in accordance with ICameraDeviceUser.submitRequestList,
@@ -269,7 +272,8 @@
kWhatLogicalCaptureFail, // onLogicalCameraCaptureFailed
kWhatCaptureSeqEnd, // onCaptureSequenceCompleted
kWhatCaptureSeqAbort, // onCaptureSequenceAborted
- kWhatCaptureBufferLost,// onCaptureBufferLost
+ kWhatCaptureBufferLost, // onCaptureBufferLost
+ kWhatPreparedCb, // onPrepared
// Internal cleanup
kWhatCleanUpSessions // Cleanup cached sp<ACameraCaptureSession>
};
diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
index 3a13a73..e62f7c3 100644
--- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
+++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
@@ -66,7 +66,7 @@
// Retaining the error code in case the caller needs to analyze it.
std::variant<int, ConfiguredWindows> initCamera(const native_handle_t* imgReaderAnw,
const std::vector<PhysicalImgReaderInfo>& physicalImgReaders,
- bool usePhysicalSettings) {
+ bool usePhysicalSettings, bool prepareWindows = false) {
ConfiguredWindows configuredWindows;
if (imgReaderAnw == nullptr) {
ALOGE("Cannot initialize camera before image reader get initialized.");
@@ -142,7 +142,25 @@
ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret);
return ret;
}
-
+ if (prepareWindows) {
+ // Set window prepared callback
+ ACameraCaptureSession_setWindowPreparedCallback(mSession, &mPreparedCb);
+ // Prepare windows
+ for (auto &window : configuredWindows) {
+ ret = ACameraCaptureSession_prepareWindow(mSession, window);
+ if (ret != ACAMERA_OK) {
+ ALOGE("%s: ACameraCaptureSession_prepareWindow failed", __FUNCTION__);
+ return ret;
+ }
+ incPendingPrepared(window);
+ }
+ // Some time for the on-PreparedCallbacks
+ usleep(configuredWindows.size() * 100000);
+ // Check that callbacks were received
+ if (!gotAllPreparedCallbacks()) {
+ return -1;
+ }
+ }
// Create capture request
if (usePhysicalSettings) {
ret = ACameraDevice_createCaptureRequest_withPhysicalIds(mDevice,
@@ -277,9 +295,56 @@
private:
+ static void onPreparedCb(void* obj, ACameraWindowType *anw, ACameraCaptureSession *session) {
+ CameraHelper* thiz = reinterpret_cast<CameraHelper*>(obj);
+ thiz->handlePrepared(anw, session);
+ }
+ bool gotAllPreparedCallbacks() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ bool ret = (mPendingPreparedCbs.size() == 0);
+ if (!ret) {
+ ALOGE("%s: mPendingPreparedCbs has the following expected callbacks", __FUNCTION__);
+ for (auto pair : mPendingPreparedCbs) {
+ ALOGE("%s: ANW: %p : pending callbacks %d", __FUNCTION__, pair.first, pair.second);
+ }
+ }
+ return ret;
+ }
+
+ void handlePrepared(ACameraWindowType *anw, ACameraCaptureSession *session) {
+ // Reduce the pending prepared count of anw by 1. If count is 0, remove the key.
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (session != mSession) {
+ ALOGE("%s: Received callback for incorrect session ? mSession %p, session %p",
+ __FUNCTION__, mSession, session);
+ return;
+ }
+ if(mPendingPreparedCbs.find(anw) == mPendingPreparedCbs.end()) {
+ ALOGE("%s: ANW %p was not being prepared at all ?", __FUNCTION__, anw);
+ return;
+ }
+ mPendingPreparedCbs[anw]--;
+ if (mPendingPreparedCbs[anw] == 0) {
+ mPendingPreparedCbs.erase(anw);
+ }
+ }
+ void incPendingPrepared(ACameraWindowType *anw) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!(mPendingPreparedCbs.find(anw) == mPendingPreparedCbs.end())) {
+ mPendingPreparedCbs[anw] = 1;
+ return;
+ }
+ mPendingPreparedCbs[anw]++;
+ }
+
+ // ANW -> pending prepared callbacks
+ std::unordered_map<ACameraWindowType *, int> mPendingPreparedCbs;
ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr};
ACameraCaptureSession_stateCallbacks mSessionCb{ this, nullptr, nullptr, nullptr};
+ ACameraCaptureSession_prepareCallbacks mPreparedCb{
+ this, onPreparedCb, /*reserved0*/nullptr, /*reserved1*/nullptr};
+
const native_handle_t* mImgReaderAnw = nullptr; // not owned by us.
// Camera device
@@ -628,7 +693,7 @@
}
bool takePictures(const char* id, uint64_t readerUsage, int readerMaxImages,
- bool readerAsync, int pictureCount, bool v2 = false) {
+ bool readerAsync, int pictureCount, bool v2 = false, bool prepareSurfaces = false) {
int ret = 0;
ImageReaderTestCase testCase(
@@ -643,7 +708,7 @@
CameraHelper cameraHelper(id, mCameraManager);
std::variant<int, ConfiguredWindows> retInit =
cameraHelper.initCamera(testCase.getNativeWindow(), {}/*physicalImageReaders*/,
- false/*usePhysicalSettings*/);
+ false/*usePhysicalSettings*/, prepareSurfaces);
int *retp = std::get_if<int>(&retInit);
if (retp) {
ALOGE("Unable to initialize camera helper");
@@ -690,7 +755,7 @@
cameraHelper.checkCallbacks(pictureCount, /* printLog= */true);
}
- bool testTakePicturesNative(const char* id) {
+ bool testTakePicturesNative(const char* id, bool prepareSurfaces) {
for (auto& readerUsage :
{AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}) {
for (auto& readerMaxImages : {1, 4, 8}) {
@@ -698,7 +763,7 @@
for (auto& pictureCount : {1, 4, 8}) {
for ( auto & v2 : {true, false}) {
if (!takePictures(id, readerUsage, readerMaxImages,
- readerAsync, pictureCount, v2)) {
+ readerAsync, pictureCount, v2, prepareSurfaces)) {
ALOGE("Test takePictures failed for test case usage=%" PRIu64
", maxImages=%d, async=%d, pictureCount=%d",
readerUsage, readerMaxImages, readerAsync, pictureCount);
@@ -878,39 +943,46 @@
ACameraMetadata_free(staticMetadata);
}
+
+ void testBasicTakePictures(bool prepareSurfaces) {
+ // We always use the first camera.
+ const char* cameraId = mCameraIdList->cameraIds[0];
+ ASSERT_TRUE(cameraId != nullptr);
+
+ ACameraMetadata* staticMetadata = nullptr;
+ camera_status_t ret = ACameraManager_getCameraCharacteristics(
+ mCameraManager, cameraId, &staticMetadata);
+ ASSERT_EQ(ret, ACAMERA_OK);
+ ASSERT_NE(staticMetadata, nullptr);
+
+ bool isBC = isCapabilitySupported(staticMetadata,
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+
+ uint32_t namedTag = 0;
+ // Test that ACameraMetadata_getTagFromName works as expected for public tag
+ // names
+ camera_status_t status = ACameraManager_getTagFromName(mCameraManager, cameraId,
+ "android.control.aeMode", &namedTag);
+
+ ASSERT_EQ(status, ACAMERA_OK);
+ ASSERT_EQ(namedTag, ACAMERA_CONTROL_AE_MODE);
+
+ ACameraMetadata_free(staticMetadata);
+
+ if (!isBC) {
+ ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
+ return;
+ }
+
+ EXPECT_TRUE(testTakePicturesNative(cameraId, prepareSurfaces));
+ }
};
+
+
TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) {
- // We always use the first camera.
- const char* cameraId = mCameraIdList->cameraIds[0];
- ASSERT_TRUE(cameraId != nullptr);
-
- ACameraMetadata* staticMetadata = nullptr;
- camera_status_t ret = ACameraManager_getCameraCharacteristics(
- mCameraManager, cameraId, &staticMetadata);
- ASSERT_EQ(ret, ACAMERA_OK);
- ASSERT_NE(staticMetadata, nullptr);
-
- bool isBC = isCapabilitySupported(staticMetadata,
- ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
-
- uint32_t namedTag = 0;
- // Test that ACameraMetadata_getTagFromName works as expected for public tag
- // names
- camera_status_t status = ACameraManager_getTagFromName(mCameraManager, cameraId,
- "android.control.aeMode", &namedTag);
-
- ASSERT_EQ(status, ACAMERA_OK);
- ASSERT_EQ(namedTag, ACAMERA_CONTROL_AE_MODE);
-
- ACameraMetadata_free(staticMetadata);
-
- if (!isBC) {
- ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
- return;
- }
-
- EXPECT_TRUE(testTakePicturesNative(cameraId));
+ testBasicTakePictures(/*prepareSurfaces*/ false);
+ testBasicTakePictures(/*prepareSurfaces*/ true);
}
TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) {
diff --git a/services/camera/libcameraservice/aidl/AidlCameraDeviceCallbacks.cpp b/services/camera/libcameraservice/aidl/AidlCameraDeviceCallbacks.cpp
index 4c6d6f6..e648a36 100644
--- a/services/camera/libcameraservice/aidl/AidlCameraDeviceCallbacks.cpp
+++ b/services/camera/libcameraservice/aidl/AidlCameraDeviceCallbacks.cpp
@@ -178,8 +178,9 @@
return binder::Status::ok();
}
-binder::Status AidlCameraDeviceCallbacks::onPrepared(int32_t) {
- // not implemented
+binder::Status AidlCameraDeviceCallbacks::onPrepared(int32_t streamId) {
+ auto ret = mBase->onPrepared(streamId);
+ LOG_STATUS_ERROR_IF_NOT_OK(ret, "onPrepared")
return binder::Status::ok();
}
@@ -191,6 +192,7 @@
LOG_STATUS_ERROR_IF_NOT_OK(ret, "onRepeatingRequestError")
return binder::Status::ok();
}
+
binder::Status AidlCameraDeviceCallbacks::onRequestQueueEmpty() {
// not implemented
return binder::Status::ok();
diff --git a/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.cpp b/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.cpp
index d9ec0b6..b9f1224 100644
--- a/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.cpp
+++ b/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.cpp
@@ -56,6 +56,7 @@
mDeviceRemote(deviceRemote) {
mInitSuccess = initDevice();
}
+
bool AidlCameraDeviceUser::initDevice() {
// TODO: Get request and result metadata queue size from a system property.
int32_t reqFMQSize = CAMERA_REQUEST_METADATA_QUEUE_SIZE;
@@ -95,6 +96,11 @@
return ScopedAStatus::ok();
}
+ndk::ScopedAStatus AidlCameraDeviceUser::prepare(int32_t in_streamId) {
+ UStatus ret = mDeviceRemote->prepare(in_streamId);
+ return fromUStatus(ret);
+}
+
ndk::ScopedAStatus AidlCameraDeviceUser::submitRequestList(
const std::vector<SCaptureRequest>& in_requestList, bool in_isRepeating,
SSubmitInfo* _aidl_return) {
diff --git a/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.h b/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.h
index 41a3f43..afff197 100644
--- a/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.h
+++ b/services/camera/libcameraservice/aidl/AidlCameraDeviceUser.h
@@ -84,6 +84,7 @@
MQDescriptor<int8_t, SynchronizedReadWrite>* _aidl_return) override;
ndk::ScopedAStatus isSessionConfigurationSupported(
const SSessionConfiguration& in_sessionConfiguration, bool* _aidl_return) override;
+ ndk::ScopedAStatus prepare(int32_t in_streamId) override;
ndk::ScopedAStatus submitRequestList(const std::vector<SCaptureRequest>& in_requestList,
bool in_isRepeating, SSubmitInfo* _aidl_return) override;
ndk::ScopedAStatus updateOutputConfiguration(