Merge "Camera: Detect session parameter updates and re-configure camera"

gugelfrei
TreeHugger Robot 7 years ago committed by Android (Google) Code Review
commit b83eb465ce

@ -194,8 +194,14 @@ status_t Camera3Device::initializeCommonLocked() {
mTagMonitor.initialize(mVendorTagId);
Vector<int32_t> sessionParamKeys;
camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
if (sessionKeysEntry.count > 0) {
sessionParamKeys.insertArrayAt(sessionKeysEntry.data.i32, 0, sessionKeysEntry.count);
}
/** Start up request queue thread */
mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
mRequestThread = new RequestThread(this, mStatusTracker, mInterface, sessionParamKeys);
res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@ -1104,7 +1110,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
// This point should only be reached via API1 (API2 must explicitly call configureStreams)
// so unilaterally select normal operating mode.
res = configureStreamsLocked(CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE, mSessionParams);
res = filterParamsAndConfigureLocked(request, CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE);
// Stream configuration failed. Client might try other configuraitons.
if (res != OK) {
CLOGE("Can't set up streams: %s (%d)", strerror(-res), res);
@ -1508,11 +1514,20 @@ status_t Camera3Device::configureStreams(const CameraMetadata& sessionParams, in
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
return filterParamsAndConfigureLocked(sessionParams, operatingMode);
}
status_t Camera3Device::filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
int operatingMode) {
//Filter out any incoming session parameters
const CameraMetadata params(sessionParams);
CameraMetadata filteredParams;
camera_metadata_entry_t availableSessionKeys = mDeviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
CameraMetadata filteredParams(availableSessionKeys.count);
camera_metadata_t *meta = const_cast<camera_metadata_t *>(
filteredParams.getAndLock());
set_camera_metadata_vendor_id(meta, mVendorTagId);
filteredParams.unlock(meta);
if (availableSessionKeys.count > 0) {
for (size_t i = 0; i < availableSessionKeys.count; i++) {
camera_metadata_ro_entry entry = params.find(
@ -2203,10 +2218,47 @@ void Camera3Device::cancelStreamsConfigurationLocked() {
// properly clean things up
internalUpdateStatusLocked(STATUS_UNCONFIGURED);
mNeedConfig = true;
res = mPreparerThread->resume();
if (res != OK) {
ALOGE("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
}
}
bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
ATRACE_CALL();
bool ret = false;
Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
if (rc == NO_ERROR) {
mNeedConfig = true;
rc = configureStreamsLocked(mOperatingMode, sessionParams, /*notifyRequestThread*/ false);
if (rc == NO_ERROR) {
ret = true;
mPauseStateNotify = false;
//Moving to active state while holding 'mLock' is important.
//There could be pending calls to 'create-/deleteStream' which
//will trigger another stream configuration while the already
//present streams end up with outstanding buffers that will
//not get drained.
internalUpdateStatusLocked(STATUS_ACTIVE);
} else {
setErrorStateLocked("%s: Failed to re-configure camera: %d",
__FUNCTION__, rc);
}
} else {
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
return ret;
}
status_t Camera3Device::configureStreamsLocked(int operatingMode,
const CameraMetadata& sessionParams) {
const CameraMetadata& sessionParams, bool notifyRequestThread) {
ATRACE_CALL();
status_t res;
@ -2247,6 +2299,8 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
// Start configuring the streams
ALOGV("%s: Camera %s: Starting stream configuration", __FUNCTION__, mId.string());
mPreparerThread->pause();
camera3_stream_configuration config;
config.operation_mode = mOperatingMode;
config.num_streams = (mInputStream != NULL) + mOutputStreams.size();
@ -2338,7 +2392,9 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
// Request thread needs to know to avoid using repeat-last-settings protocol
// across configure_streams() calls
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration);
if (notifyRequestThread) {
mRequestThread->configurationComplete(mIsConstrainedHighSpeedConfiguration, sessionParams);
}
char value[PROPERTY_VALUE_MAX];
property_get("camera.fifo.disable", value, "0");
@ -2376,6 +2432,12 @@ status_t Camera3Device::configureStreamsLocked(int operatingMode,
// tear down the deleted streams after configure streams.
mDeletedStreams.clear();
auto rc = mPreparerThread->resume();
if (rc != OK) {
SET_ERR_L("%s: Camera %s: Preparer thread failed to resume!", __FUNCTION__, mId.string());
return rc;
}
return OK;
}
@ -3747,7 +3809,7 @@ void Camera3Device::HalInterface::onBufferFreed(
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
sp<HalInterface> interface) :
sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys) :
Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
@ -3764,7 +3826,9 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
mRepeatingLastFrameNumber(
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
mPrepareVideoStream(false),
mRequestLatency(kRequestLatencyBinSize) {
mRequestLatency(kRequestLatencyBinSize),
mSessionParamKeys(sessionParamKeys),
mLatestSessionParams(sessionParamKeys.size()) {
mStatusId = statusTracker->addComponent();
}
@ -3777,10 +3841,12 @@ void Camera3Device::RequestThread::setNotificationListener(
mListener = listener;
}
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed) {
void Camera3Device::RequestThread::configurationComplete(bool isConstrainedHighSpeed,
const CameraMetadata& sessionParams) {
ATRACE_CALL();
Mutex::Autolock l(mRequestLock);
mReconfigured = true;
mLatestSessionParams = sessionParams;
// Prepare video stream for high speed recording.
mPrepareVideoStream = isConstrainedHighSpeed;
}
@ -4191,6 +4257,52 @@ nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_
return maxExpectedDuration;
}
bool Camera3Device::RequestThread::updateSessionParameters(const CameraMetadata& settings) {
ATRACE_CALL();
bool updatesDetected = false;
for (auto tag : mSessionParamKeys) {
camera_metadata_ro_entry entry = settings.find(tag);
camera_metadata_entry lastEntry = mLatestSessionParams.find(tag);
if (entry.count > 0) {
bool isDifferent = false;
if (lastEntry.count > 0) {
// Have a last value, compare to see if changed
if (lastEntry.type == entry.type &&
lastEntry.count == entry.count) {
// Same type and count, compare values
size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
size_t entryBytes = bytesPerValue * lastEntry.count;
int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
if (cmp != 0) {
isDifferent = true;
}
} else {
// Count or type has changed
isDifferent = true;
}
} else {
// No last entry, so always consider to be different
isDifferent = true;
}
if (isDifferent) {
ALOGV("%s: Session parameter tag id %d changed", __FUNCTION__, tag);
mLatestSessionParams.update(entry);
updatesDetected = true;
}
} else if (lastEntry.count > 0) {
// Value has been removed
ALOGV("%s: Session parameter tag id %d removed", __FUNCTION__, tag);
mLatestSessionParams.erase(tag);
updatesDetected = true;
}
}
return updatesDetected;
}
bool Camera3Device::RequestThread::threadLoop() {
ATRACE_CALL();
status_t res;
@ -4217,6 +4329,49 @@ bool Camera3Device::RequestThread::threadLoop() {
latestRequestId = NAME_NOT_FOUND;
}
// 'mNextRequests' will at this point contain either a set of HFR batched requests
// or a single request from streaming or burst. In either case the first element
// should contain the latest camera settings that we need to check for any session
// parameter updates.
if (updateSessionParameters(mNextRequests[0].captureRequest->mSettings)) {
res = OK;
//Input stream buffers are already acquired at this point so an input stream
//will not be able to move to idle state unless we force it.
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
res = mNextRequests[0].captureRequest->mInputStream->forceToIdle();
if (res != OK) {
ALOGE("%s: Failed to force idle input stream: %d", __FUNCTION__, res);
cleanUpFailedRequests(/*sendRequestError*/ false);
return false;
}
}
if (res == OK) {
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != 0) {
statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
sp<Camera3Device> parent = mParent.promote();
if (parent != nullptr) {
mReconfigured |= parent->reconfigureCamera(mLatestSessionParams);
}
statusTracker->markComponentActive(mStatusId);
setPaused(false);
}
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
mNextRequests[0].captureRequest->mInputStream->restoreConfiguredState();
if (res != OK) {
ALOGE("%s: Failed to restore configured input stream: %d", __FUNCTION__, res);
cleanUpFailedRequests(/*sendRequestError*/ false);
return false;
}
}
}
}
// Prepare a batch of HAL requests and output buffers.
res = prepareHalRequests();
if (res == TIMED_OUT) {
@ -4980,7 +5135,7 @@ status_t Camera3Device::RequestThread::addDummyTriggerIds(
Camera3Device::PreparerThread::PreparerThread() :
Thread(/*canCallJava*/false), mListener(nullptr),
mActive(false), mCancelNow(false) {
mActive(false), mCancelNow(false), mCurrentMaxCount(0), mCurrentPrepareComplete(false) {
}
Camera3Device::PreparerThread::~PreparerThread() {
@ -5031,18 +5186,101 @@ status_t Camera3Device::PreparerThread::prepare(int maxCount, sp<Camera3StreamIn
}
// queue up the work
mPendingStreams.push_back(stream);
mPendingStreams.emplace(maxCount, stream);
ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId());
return OK;
}
void Camera3Device::PreparerThread::pause() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
std::unordered_map<int, sp<camera3::Camera3StreamInterface> > pendingStreams;
pendingStreams.insert(mPendingStreams.begin(), mPendingStreams.end());
sp<camera3::Camera3StreamInterface> currentStream = mCurrentStream;
int currentMaxCount = mCurrentMaxCount;
mPendingStreams.clear();
mCancelNow = true;
while (mActive) {
auto res = mThreadActiveSignal.waitRelative(mLock, kActiveTimeout);
if (res == TIMED_OUT) {
ALOGE("%s: Timed out waiting on prepare thread!", __FUNCTION__);
return;
} else if (res != OK) {
ALOGE("%s: Encountered an error: %d waiting on prepare thread!", __FUNCTION__, res);
return;
}
}
//Check whether the prepare thread was able to complete the current
//stream. In case work is still pending emplace it along with the rest
//of the streams in the pending list.
if (currentStream != nullptr) {
if (!mCurrentPrepareComplete) {
pendingStreams.emplace(currentMaxCount, currentStream);
}
}
mPendingStreams.insert(pendingStreams.begin(), pendingStreams.end());
for (const auto& it : mPendingStreams) {
it.second->cancelPrepare();
}
}
status_t Camera3Device::PreparerThread::resume() {
ATRACE_CALL();
status_t res;
Mutex::Autolock l(mLock);
sp<NotificationListener> listener = mListener.promote();
if (mActive) {
ALOGE("%s: Trying to resume an already active prepare thread!", __FUNCTION__);
return NO_INIT;
}
auto it = mPendingStreams.begin();
for (; it != mPendingStreams.end();) {
res = it->second->startPrepare(it->first);
if (res == OK) {
if (listener != NULL) {
listener->notifyPrepared(it->second->getId());
}
it = mPendingStreams.erase(it);
} else if (res != NOT_ENOUGH_DATA) {
ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__,
res, strerror(-res));
it = mPendingStreams.erase(it);
} else {
it++;
}
}
if (mPendingStreams.empty()) {
return OK;
}
res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND);
if (res != OK) {
ALOGE("%s: Unable to start preparer stream: %d (%s)",
__FUNCTION__, res, strerror(-res));
return res;
}
mCancelNow = false;
mActive = true;
ALOGV("%s: Preparer stream started", __FUNCTION__);
return OK;
}
status_t Camera3Device::PreparerThread::clear() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
for (const auto& stream : mPendingStreams) {
stream->cancelPrepare();
for (const auto& it : mPendingStreams) {
it.second->cancelPrepare();
}
mPendingStreams.clear();
mCancelNow = true;
@ -5067,12 +5305,15 @@ bool Camera3Device::PreparerThread::threadLoop() {
// threadLoop _must not_ re-acquire mLock after it sets mActive to false; would
// cause deadlock with prepare()'s requestExitAndWait triggered by !mActive.
mActive = false;
mThreadActiveSignal.signal();
return false;
}
// Get next stream to prepare
auto it = mPendingStreams.begin();
mCurrentStream = *it;
mCurrentStream = it->second;
mCurrentMaxCount = it->first;
mCurrentPrepareComplete = false;
mPendingStreams.erase(it);
ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId());
ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId());
@ -5107,6 +5348,7 @@ bool Camera3Device::PreparerThread::threadLoop() {
ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId());
mCurrentStream.clear();
mCurrentPrepareComplete = true;
return true;
}

@ -551,12 +551,24 @@ class Camera3Device :
sp<CaptureRequest> createCaptureRequest(const CameraMetadata &request,
const SurfaceMap &surfaceMap);
/**
* Internally re-configure camera device using new session parameters.
* This will get triggered by the request thread.
*/
bool reconfigureCamera(const CameraMetadata& sessionParams);
/**
* Filter stream session parameters and configure camera HAL.
*/
status_t filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
int operatingMode);
/**
* Take the currently-defined set of streams and configure the HAL to use
* them. This is a long-running operation (may be several hundered ms).
*/
status_t configureStreamsLocked(int operatingMode,
const CameraMetadata& sessionParams);
const CameraMetadata& sessionParams, bool notifyRequestThread = true);
/**
* Cancel stream configuration that did not finish successfully.
@ -655,7 +667,7 @@ class Camera3Device :
RequestThread(wp<Camera3Device> parent,
sp<camera3::StatusTracker> statusTracker,
sp<HalInterface> interface);
sp<HalInterface> interface, const Vector<int32_t>& sessionParamKeys);
~RequestThread();
void setNotificationListener(wp<NotificationListener> listener);
@ -663,7 +675,8 @@ class Camera3Device :
/**
* Call after stream (re)-configuration is completed.
*/
void configurationComplete(bool isConstrainedHighSpeed);
void configurationComplete(bool isConstrainedHighSpeed,
const CameraMetadata& sessionParams);
/**
* Set or clear the list of repeating requests. Does not block
@ -812,6 +825,12 @@ class Camera3Device :
// Calculate the expected maximum duration for a request
nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
// Check and update latest session parameters based on the current request settings.
bool updateSessionParameters(const CameraMetadata& settings);
// Re-configure camera using the latest session parameters.
bool reconfigureCamera();
wp<Camera3Device> mParent;
wp<camera3::StatusTracker> mStatusTracker;
sp<HalInterface> mInterface;
@ -869,6 +888,9 @@ class Camera3Device :
static const int32_t kRequestLatencyBinSize = 40; // in ms
CameraLatencyHistogram mRequestLatency;
Vector<int32_t> mSessionParamKeys;
CameraMetadata mLatestSessionParams;
};
sp<RequestThread> mRequestThread;
@ -1006,21 +1028,34 @@ class Camera3Device :
*/
status_t clear();
/**
* Pause all preparation activities
*/
void pause();
/**
* Resume preparation activities
*/
status_t resume();
private:
Mutex mLock;
Condition mThreadActiveSignal;
virtual bool threadLoop();
// Guarded by mLock
wp<NotificationListener> mListener;
List<sp<camera3::Camera3StreamInterface> > mPendingStreams;
std::unordered_map<int, sp<camera3::Camera3StreamInterface> > mPendingStreams;
bool mActive;
bool mCancelNow;
// Only accessed by threadLoop and the destructor
sp<camera3::Camera3StreamInterface> mCurrentStream;
int mCurrentMaxCount;
bool mCurrentPrepareComplete;
};
sp<PreparerThread> mPreparerThread;

@ -140,6 +140,75 @@ android_dataspace Camera3Stream::getOriginalDataSpace() const {
return mOriginalDataSpace;
}
status_t Camera3Stream::forceToIdle() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res;
switch (mState) {
case STATE_ERROR:
case STATE_CONSTRUCTED:
case STATE_IN_CONFIG:
case STATE_PREPARING:
case STATE_IN_RECONFIG:
ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
res = NO_INIT;
break;
case STATE_CONFIGURED:
if (hasOutstandingBuffersLocked()) {
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != 0) {
statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
}
}
mState = STATE_IN_IDLE;
res = OK;
break;
default:
ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
res = NO_INIT;
}
return res;
}
status_t Camera3Stream::restoreConfiguredState() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
status_t res;
switch (mState) {
case STATE_ERROR:
case STATE_CONSTRUCTED:
case STATE_IN_CONFIG:
case STATE_PREPARING:
case STATE_IN_RECONFIG:
case STATE_CONFIGURED:
ALOGE("%s: Invalid state: %d", __FUNCTION__, mState);
res = NO_INIT;
break;
case STATE_IN_IDLE:
if (hasOutstandingBuffersLocked()) {
sp<StatusTracker> statusTracker = mStatusTracker.promote();
if (statusTracker != 0) {
statusTracker->markComponentActive(mStatusId);
}
}
mState = STATE_CONFIGURED;
res = OK;
break;
default:
ALOGE("%s: Unknown state %d", __FUNCTION__, mState);
res = NO_INIT;
}
return res;
}
camera3_stream* Camera3Stream::startConfiguration() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@ -150,6 +219,7 @@ camera3_stream* Camera3Stream::startConfiguration() {
ALOGE("%s: In error state", __FUNCTION__);
return NULL;
case STATE_CONSTRUCTED:
case STATE_IN_IDLE:
// OK
break;
case STATE_IN_CONFIG:
@ -179,6 +249,11 @@ camera3_stream* Camera3Stream::startConfiguration() {
return NULL;
}
if (mState == STATE_IN_IDLE) {
// Skip configuration.
return this;
}
// Stop tracking if currently doing so
if (mStatusId != StatusTracker::NO_STATUS_ID) {
sp<StatusTracker> statusTracker = mStatusTracker.promote();
@ -219,6 +294,9 @@ status_t Camera3Stream::finishConfiguration() {
ALOGE("%s: Cannot finish configuration that hasn't been started",
__FUNCTION__);
return INVALID_OPERATION;
case STATE_IN_IDLE:
//Skip configuration in this state
return OK;
default:
ALOGE("%s: Unknown state", __FUNCTION__);
return INVALID_OPERATION;
@ -267,6 +345,7 @@ status_t Camera3Stream::cancelConfiguration() {
return INVALID_OPERATION;
case STATE_IN_CONFIG:
case STATE_IN_RECONFIG:
case STATE_IN_IDLE:
// OK
break;
case STATE_CONSTRUCTED:
@ -282,7 +361,9 @@ status_t Camera3Stream::cancelConfiguration() {
mUsage = mOldUsage;
camera3_stream::max_buffers = mOldMaxBuffers;
mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED;
mState = ((mState == STATE_IN_RECONFIG) || (mState == STATE_IN_IDLE)) ? STATE_CONFIGURED :
STATE_CONSTRUCTED;
return OK;
}

@ -68,6 +68,12 @@ namespace camera3 {
* duration. In this state, only prepareNextBuffer() and cancelPrepare()
* may be called.
*
* STATE_IN_IDLE: This is a temporary state only intended to be used for input
* streams and only for the case where we need to re-configure the camera device
* while the input stream has an outstanding buffer. All other streams should not
* be able to switch to this state. For them this is invalid and should be handled
* as an unknown state.
*
* Transition table:
*
* <none> => STATE_CONSTRUCTED:
@ -98,6 +104,11 @@ namespace camera3 {
* all stream buffers, or cancelPrepare is called.
* STATE_CONFIGURED => STATE_ABANDONED:
* When the buffer queue of the stream is abandoned.
* STATE_CONFIGURED => STATE_IN_IDLE:
* Only for an input stream which has an outstanding buffer.
* STATE_IN_IDLE => STATE_CONFIGURED:
* After the internal re-configuration, the input should revert back to
* the configured state.
*
* Status Tracking:
* Each stream is tracked by StatusTracker as a separate component,
@ -108,7 +119,9 @@ namespace camera3 {
*
* - ACTIVE: One or more buffers have been handed out (with #getBuffer).
* - IDLE: All buffers have been returned (with #returnBuffer), and their
* respective release_fence(s) have been signaled.
* respective release_fence(s) have been signaled. The only exception to this
* rule is an input stream that moves to "STATE_IN_IDLE" during internal
* re-configuration.
*
* A typical use case is output streams. When the HAL has any buffers
* dequeued, the stream is marked ACTIVE. When the HAL returns all buffers
@ -386,6 +399,19 @@ class Camera3Stream :
*/
bool isAbandoned() const;
/**
* Switch a configured stream with possibly outstanding buffers in idle
* state. Configuration for such streams will be skipped assuming there
* are no changes to the stream parameters.
*/
status_t forceToIdle();
/**
* Restore a forced idle stream to configured state, marking it active
* in case it contains outstanding buffers.
*/
status_t restoreConfiguredState();
protected:
const int mId;
/**
@ -414,7 +440,8 @@ class Camera3Stream :
STATE_IN_RECONFIG,
STATE_CONFIGURED,
STATE_PREPARING,
STATE_ABANDONED
STATE_ABANDONED,
STATE_IN_IDLE
} mState;
mutable Mutex mLock;

Loading…
Cancel
Save