VideoCapture::isOpened() 僅在開啟瞬間檢查裝置是否可存取。攝影機在運行中被拔除,isOpened() 仍回傳 true,需透過幀讀取失敗來偵測斷線。
#include
#include
#include
#include
class RobustCamera {
public:
RobustCamera(int deviceId = 0) : deviceId_(deviceId), running_(false) {}
void start() {
running_ = true;
captureThread_ = std::thread(&RobustCamera::captureLoop, this);
}
void stop() {
running_ = false;
if (captureThread_.joinable()) captureThread_.join();
if (cap_.isOpened()) cap_.release();
}
// 取得最新幀 (線程安全)
bool getLatestFrame(cv::Mat& frame) {
std::lock_guard lock(frameMutex_);
if (latestFrame_.empty()) return false;
frame = latestFrame_.clone();
return true;
}
private:
void captureLoop() {
cv::Mat frame;
int consecutiveFailures = 0;
const int MAX_FAILURES = 5; // 連續 5 幀失敗判定斷線
while (running_) {
// 1. 確保裝置已開啟
if (!cap_.isOpened()) {
if (!reconnect()) {
std::this_thread::sleep_for(std::chrono::seconds(2));
continue;
}
consecutiveFailures = 0;
}
// 2. 嘗試讀取幀
bool success = cap_.read(frame);
if (!success || frame.empty()) {
consecutiveFailures++;
if (consecutiveFailures >= MAX_FAILURES) {
std::cerr << "Camera disconnected, attempting reconnect..." << std::endl;
cap_.release(); // 釋放句柄觸發重連
consecutiveFailures = 0;
}
std::this_thread::sleep_for(std::chrono::milliseconds(30));
continue;
}
// 3. 讀取成功:重置計數、更新快取
consecutiveFailures = 0;
{
std::lock_guard lock(frameMutex_);
latestFrame_ = frame.clone();
}
}
}
bool reconnect() {
std::cerr << "Reconnecting to camera " << deviceId_ << "..." << std::endl;
for (int i = 0; i < 10 && running_; ++i) {
cap_.open(deviceId_);
if (cap_.isOpened()) {
// 可選:重新套用解析度、FPS 等參數
cap_.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
cap_.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
std::cerr << "Reconnected successfully." << std::endl;
return true;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return false;
}
int deviceId_;
cv::VideoCapture cap_;
cv::Mat latestFrame_;
std::mutex frameMutex_;
std::thread captureThread_;
std::atomic running_;
};
isOpened() + read() 回傳值 + frame.empty()。cap_.release() 釋放 OS 層面句柄,cap_.open() 重新枚舉裝置。set() 解析度、編碼格式等參數。/dev/videoX 消失,重插可能分配新號碼,建議掃描 /dev/video* 找回同一裝置 (可用 v4l2-ctl 查詢序號)。