Camera3Device: Don't time out on long exposures
- Calculate expected duration based on request settings instead
of using a fixed timeout for shutdown
- Ensure the timeouts are always at least 5 seconds anyway
Test: Camera CTS passes with a long-exposure device
Bug: 38212789
Change-Id: I6b154710314cdc84b223f1a9f39fead9262ce27b
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 0807c0a..16fcfb1 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -251,9 +251,11 @@
SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
} else {
- res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+ res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain");
+ SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
+ maxExpectedDuration);
// Continue to close device even in case of error
}
}
@@ -309,6 +311,7 @@
{
Mutex::Autolock l(mLock);
+ mExpectedInflightDuration = 0;
mInterface->clear();
mOutputStreams.clear();
mInputStream.clear();
@@ -1555,8 +1558,11 @@
return INVALID_OPERATION;
}
- ALOGV("%s: Camera %s: Waiting until idle", __FUNCTION__, mId.string());
- status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+
+ ALOGV("%s: Camera %s: Waiting until idle (%" PRIi64 "ns)", __FUNCTION__, mId.string(),
+ maxExpectedDuration);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
res);
@@ -1576,11 +1582,13 @@
mRequestThread->setPaused(true);
mPauseStateNotify = true;
- ALOGV("%s: Camera %s: Internal wait until idle", __FUNCTION__, mId.string());
- status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+ ALOGV("%s: Camera %s: Internal wait until idle (% " PRIi64 " ns)", __FUNCTION__, mId.string(),
+ maxExpectedDuration);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
if (res != OK) {
SET_ERR_L("Can't idle device in %f seconds!",
- kShutdownTimeout/1e9);
+ maxExpectedDuration/1e9);
}
return res;
@@ -2347,13 +2355,13 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool hasAppCallback) {
+ bool hasAppCallback, nsecs_t maxExpectedDuration) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- hasAppCallback));
+ hasAppCallback, maxExpectedDuration));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2364,6 +2372,9 @@
}
}
+ Mutex::Autolock ml(mLock);
+ mExpectedInflightDuration += maxExpectedDuration;
+
return OK;
}
@@ -2384,6 +2395,7 @@
}
void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+ nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
mInFlightMap.removeItemsAt(idx, 1);
// Indicate idle inFlightMap to the status tracker
@@ -2394,6 +2406,8 @@
mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
}
}
+ Mutex::Autolock l(mLock);
+ mExpectedInflightDuration -= duration;
}
void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) {
@@ -3913,6 +3927,42 @@
return true;
}
+nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_metadata_t *request) {
+ nsecs_t maxExpectedDuration = kDefaultExpectedDuration;
+ camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+ find_camera_metadata_ro_entry(request,
+ ANDROID_CONTROL_AE_MODE,
+ &e);
+ if (e.count == 0) return maxExpectedDuration;
+
+ switch (e.data.u8[0]) {
+ case ANDROID_CONTROL_AE_MODE_OFF:
+ find_camera_metadata_ro_entry(request,
+ ANDROID_SENSOR_EXPOSURE_TIME,
+ &e);
+ if (e.count > 0) {
+ maxExpectedDuration = e.data.i64[0];
+ }
+ find_camera_metadata_ro_entry(request,
+ ANDROID_SENSOR_FRAME_DURATION,
+ &e);
+ if (e.count > 0) {
+ maxExpectedDuration = std::max(e.data.i64[0], maxExpectedDuration);
+ }
+ break;
+ default:
+ find_camera_metadata_ro_entry(request,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ &e);
+ if (e.count > 1) {
+ maxExpectedDuration = 1e9 / e.data.u8[0];
+ }
+ break;
+ }
+
+ return maxExpectedDuration;
+}
+
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
@@ -4133,7 +4183,8 @@
res = parent->registerInFlight(halRequest->frame_number,
totalNumBuffers, captureRequest->mResultExtras,
/*hasInput*/halRequest->input_buffer != NULL,
- hasCallback);
+ hasCallback,
+ calculateMaxExpectedDuration(halRequest->settings));
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
__FUNCTION__,
@@ -4187,6 +4238,11 @@
return false;
}
+nsecs_t Camera3Device::getExpectedInFlightDurationLocked() {
+ return mExpectedInflightDuration > kMinInflightDuration ?
+ mExpectedInflightDuration : kMinInflightDuration;
+}
+
void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
if (mNextRequests.empty()) {
return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 0251d62..686a28b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -187,10 +187,11 @@
static const size_t kDumpLockAttempts = 10;
static const size_t kDumpSleepDuration = 100000; // 0.10 sec
- static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
static const nsecs_t kActiveTimeout = 500000000; // 500 ms
static const size_t kInFlightWarnLimit = 30;
static const size_t kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
+ static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
+ static const nsecs_t kMinInflightDuration = 5000000000; // 5 s
// SCHED_FIFO priority for request submission thread in HFR mode
static const int kRequestThreadPriority = 1;
@@ -776,6 +777,9 @@
// send request in mNextRequests to HAL in a batch. Return true = sucssess
bool sendRequestsBatch();
+ // Calculate the expected maximum duration for a request
+ nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
+
wp<Camera3Device> mParent;
wp<camera3::StatusTracker> mStatusTracker;
sp<HalInterface> mInterface;
@@ -877,6 +881,11 @@
// is not for constrained high speed recording, this flag will also be true.
bool hasCallback;
+ // Maximum expected frame duration for this request.
+ // For manual captures, equal to the max of requested exposure time and frame duration
+ // For auto-exposure modes, equal to 1/(lower end of target FPS range)
+ nsecs_t maxExpectedDuration;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
shutterTimestamp(0),
@@ -885,11 +894,12 @@
haveResultMetadata(false),
numBuffersLeft(0),
hasInputBuffer(false),
- hasCallback(true) {
+ hasCallback(true),
+ maxExpectedDuration(kDefaultExpectedDuration) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
- bool hasAppCallback) :
+ bool hasAppCallback, nsecs_t maxDuration) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -897,20 +907,28 @@
numBuffersLeft(numBuffers),
resultExtras(extras),
hasInputBuffer(hasInput),
- hasCallback(hasAppCallback) {
+ hasCallback(hasAppCallback),
+ maxExpectedDuration(maxDuration) {
}
};
// Map from frame number to the in-flight request state
typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
+ nsecs_t mExpectedInflightDuration = 0;
Mutex mInFlightLock; // Protects mInFlightMap
InFlightMap mInFlightMap;
int mInFlightStatusId;
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool callback);
+ bool callback, nsecs_t maxExpectedDuration);
+
+ /**
+ * Returns the maximum expected time it'll take for all currently in-flight
+ * requests to complete, based on their settings
+ */
+ nsecs_t getExpectedInFlightDurationLocked();
/**
* Tracking for idle detection