Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions modules/v4l2/v4l2_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,20 @@ int V4L2DevicePoller::Poll(int timeout_ms, int events, std::vector<V4L2Device*>*
}
int ret = ::poll(poll_fds_.data(), poll_fds_.size(), timeout_ms);
if (ret <= 0) {
for (size_t i = 0; i < devices_.size(); i++) {
LOGE("%s: Device node fd %d poll timeout.", __func__, devices_[i]->fd_);
if (ret < 0) {
LOGE("%s: poll error: %s", __func__, strerror(errno));
} else {
/* ret == 0: normal 1-second timeout — not an error, just no frames yet.
* Log at debug level to avoid flooding the error log every second. */
for (size_t i = 0; i < devices_.size(); i++) {
LOG2("%s: Device node fd %d poll timeout.", __func__, devices_[i]->fd_);
}
/* Defensive: if flush_fd fired in the same tick as timeout (race),
* treat it as a flush so the caller exits promptly. */
if (flush_fd_ != -1 && (poll_fds_.back().revents & (POLLIN | POLLPRI))) {
LOG1("%s: flush detected on timeout path, fd %d.", __func__, poll_fds_.back().fd);
return 1;
}
}
return ret;
}
Expand Down
7 changes: 6 additions & 1 deletion src/core/CameraDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,12 @@ int CameraDevice::stop() {
LOG1("<id%d>@%s, mState:%d", mCameraId, __func__, mState);
AutoMutex m(mDeviceLock);

mRequestThread->clearRequests();
/* Stop the RequestThread first (sets mState=EXIT + notifies mFrameAvailableSignal).
* This unblocks any icamerasrc streaming thread blocked inside camera_stream_dqbuf →
* waitFrame() immediately, instead of waiting for the 30-second dead-camera timeout.
* requestStop() includes clearRequests() so no separate call needed.
* The second requestStop() call in deinit() is safe — it is a no-op when already stopped. */
mRequestThread->requestStop();

m3AControl->stop();
mLensCtrl->stop();
Expand Down
19 changes: 17 additions & 2 deletions src/core/CaptureUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,27 @@ int CaptureUnit::poll() {
// Exiting, no error
return -1;
}
CheckAndLogError(ret < 0, UNKNOWN_ERROR, "%s: Poll error, ret:%d", __func__, ret);
if (ret < 0) {
LOGE("<id%d>%s: Unexpected poll error (POLLERR) while streaming — "
"camera may have been disrupted by another camera's STREAMOFF. "
"Poll thread will exit; other cameras should continue normally.",
mCameraId, __func__);
return ret;
}
if (ret == 0) {
LOG1("<id%d>%s, timeout happens, wait recovery", mCameraId, __func__);
LOG2("<id%d>%s, timeout happens, wait recovery", mCameraId, __func__);
return OK;
}

/* ret > 0: one or more fds are ready. If the flush pipe triggered this
* wake-up (stop() wrote to mFlushFd[1]) there will be no camera device in
* readyDevices, so the dequeue loop below is a no-op. Check mExitPending
* first to avoid a spurious dequeue attempt after stop(). */
if (mExitPending) {
LOG2("%s: mExitPending true after poll wakeup, exit", __func__);
return -1;
}

for (const auto& readyDevice : readyDevices) {
for (auto device : mDevices) {
if (device->getV4l2Device() == readyDevice) {
Expand Down
26 changes: 24 additions & 2 deletions src/core/RequestThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ int RequestThread::waitFrame(int streamId, camera_buffer_t **ubuffer) {
if (mState == EXIT) {
return NO_INIT;
}
static const int kMaxTimeouts = 2; // ~10s before declaring camera dead and setting EXIT
int timeoutCount = 0;
while (frameQueue.mFrameQueue.empty()) {
std::cv_status ret = frameQueue.mFrameAvailableSignal.wait_for(
lock,
Expand All @@ -229,8 +231,28 @@ int RequestThread::waitFrame(int streamId, camera_buffer_t **ubuffer) {
return NO_INIT;
}

CheckWarning(ret == std::cv_status::timeout, TIMED_OUT,
"<id%d>@%s, time out happens, wait recovery", mCameraId, __func__);
if (ret == std::cv_status::timeout) {
++timeoutCount;
if (timeoutCount < kMaxTimeouts) {
LOGW("<id%d>@%s, no frame for ~%ds — waiting (attempt %d/%d)",
mCameraId, __func__,
(int)(kWaitFrameDuration * SLOWLY_MULTIPLIER / 1000000000LL) * timeoutCount,
timeoutCount, kMaxTimeouts);
} else {
LOGE("<id%d>@%s, camera appears dead (no frame in ~%ds). "
"Poll thread may have exited due to POLLERR from another camera's STREAMOFF. "
"Marking EXIT so dqbuf() loop breaks and pipeline can shut down.",
mCameraId, __func__,
(int)(kWaitFrameDuration * SLOWLY_MULTIPLIER / 1000000000LL) * timeoutCount);
/* Set EXIT before releasing the lock so the next waitFrame() call (or a
* concurrent one on another stream) returns NO_INIT immediately instead of
* blocking for another kMaxTimeouts × kWaitFrameDuration seconds.
* CameraDevice::dqbuf() loops while ret==TIMED_OUT — returning NO_INIT here
* breaks that loop and lets icamerasrc post GST_FLOW_ERROR to start teardown. */
mState = EXIT;
return NO_INIT;
}
}
}

shared_ptr<CameraBuffer> camBuffer = frameQueue.mFrameQueue.front();
Expand Down
1 change: 1 addition & 0 deletions src/hal/CameraHal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ int CameraHal::deviceStart(int cameraId) {
cameraId, mConfigTimes);
}
}
lock.unlock(); // allow concurent STREAMON for all VC Cameras
// VIRTUAL_CHANNEL_E

return device->start();
Expand Down
9 changes: 8 additions & 1 deletion src/iutils/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,16 @@ void Thread::wait() {
return;
}

const int64_t kWaitTimeout = 5000000000LL; // 5 seconds
while (mState != EXITED) {
mState = EXITING;
mExitedCondition.wait(lock);
int waitRet = mExitedCondition.waitRelative(lock, kWaitTimeout);
if (waitRet == TIMED_OUT) {
LOGE("Thread::wait() timed out after 5s waiting for thread '%s' to exit — "
"poll thread may be stuck in kernel; forcing exit",
mName.empty() ? "NO_NAME" : mName.c_str());
break;
}
}

return;
Expand Down
4 changes: 2 additions & 2 deletions src/platformdata/CameraSensorsParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,8 +777,8 @@ void CameraSensorsParser::parseSensorSection(const Json::Value& node) {
if (node.isMember("vcId")) {
mCurCam->mVCId = node["vcId"].asInt();
}
if (node.isMember("vcGoupId")) {
mCurCam->mVCGroupId = node["vcGoupId"].asInt();
if (node.isMember("vcGroupId")) {
mCurCam->mVCGroupId = node["vcGroupId"].asInt();
}
// VIRTUAL_CHANNEL_E
resolveCsiPortAndI2CBus();
Expand Down
13 changes: 8 additions & 5 deletions src/v4l2/MediaControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1022,10 +1022,12 @@ void MediaControl::mediaCtlClear(int cameraId, MediaCtlConf* mc) {
* clearing routes here would disrupt other processes or camera instances that share the
* same subdev and are still streaming. Routes are left active on the hardware; the next
* open() will skip SetRouting if the routes already match (see setRouting).
* Remove the receiver from the in-process tracking set so that a subsequent open() in
* this process re-evaluates the full setup (formats, links), even though SetRouting itself
* will be skipped when routes are still correctly configured.
* Reset mIsMediaCtlSetup so that a subsequent open() in this process re-evaluates the
* full setup (formats, links), even though SetRouting itself will be skipped when routes
* are still correctly configured.
*/
AutoMutex lock(sLock);
mIsMediaCtlSetup = false;
// VIRTUAL_CHANNEL_E
}

Expand Down Expand Up @@ -1125,8 +1127,9 @@ int MediaControl::getI2CBusAddress(const string& sensorEntityName, const string&
char* entityName = nullptr;
size_t sensorEntityNameLen = sensorEntityName.length();
for (int i = 0; i < linksCount; i++) {
if (strcmp(links[i].sink->entity->info.name, sinkEntityName.c_str()) == 0) {
entityName = entity.info.name;
if (strcmp(links[i].sink->entity->info.name, sinkEntityName.c_str()) == 0 &&
strncmp(entity.info.name, sensorEntityName.c_str(), sensorEntityNameLen) == 0) {
entityName = entity.info.name;
break;
}
}
Expand Down
Loading