diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp index 367ebd06d7..f15fc5fcf8 100644 --- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp +++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp @@ -30,6 +30,12 @@ HttpCameraImpl::HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind, HttpCameraImpl::~HttpCameraImpl() { m_active = false; + // force wakeup of monitor thread + m_monitorCond.notify_one(); + + // join monitor thread + if (m_monitorThread.joinable()) m_monitorThread.join(); + // Close file if it's open { std::lock_guard lock(m_mutex); @@ -54,6 +60,31 @@ void HttpCameraImpl::Start() { // Kick off the stream and settings threads m_streamThread = std::thread(&HttpCameraImpl::StreamThreadMain, this); m_settingsThread = std::thread(&HttpCameraImpl::SettingsThreadMain, this); + m_monitorThread = std::thread(&HttpCameraImpl::MonitorThreadMain, this); +} + +void HttpCameraImpl::MonitorThreadMain() { + while (m_active) { + std::unique_lock lock(m_mutex); + // sleep for 1 second between checks + m_monitorCond.wait_for(lock, std::chrono::seconds(1), + [=] { return !m_active; }); + + if (!m_active) break; + + // check to see if we got any frames, and close the stream if not + // (this will result in an error at the read point, and ultimately + // a reconnect attempt) + if (m_streamConn && m_frameCount == 0) { + SWARNING("Monitor detected stream hung, disconnecting"); + m_streamConn->stream->close(); + } + + // reset the frame counter + m_frameCount = 0; + } + + SDEBUG("Monitor Thread exiting"); } void HttpCameraImpl::StreamThreadMain() { @@ -86,6 +117,10 @@ void HttpCameraImpl::StreamThreadMain() { // stream DeviceStream(conn->is, boundary); + { + std::unique_lock lock(m_mutex); + m_streamConn = nullptr; + } } SDEBUG("Camera Thread exiting"); @@ -120,6 +155,7 @@ wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect( // update m_streamConn { std::lock_guard lock(m_mutex); + m_frameCount = 1; // avoid a race with monitor thread m_streamConn = std::move(connPtr); } @@ -229,6 +265,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is, } PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf, wpi::Now()); + ++m_frameCount; return true; } @@ -246,6 +283,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is, image->width = width; image->height = height; PutFrame(std::move(image), wpi::Now()); + ++m_frameCount; return true; } diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.h b/cscore/src/main/native/cpp/HttpCameraImpl.h index 497d45dccf..59670156ee 100644 --- a/cscore/src/main/native/cpp/HttpCameraImpl.h +++ b/cscore/src/main/native/cpp/HttpCameraImpl.h @@ -111,10 +111,14 @@ class HttpCameraImpl : public SourceImpl { void SettingsThreadMain(); void DeviceSendSettings(wpi::HttpRequest& req); + // The monitor thread + void MonitorThreadMain(); + std::atomic_bool m_connected{false}; std::atomic_bool m_active{true}; // set to false to terminate thread std::thread m_streamThread; std::thread m_settingsThread; + std::thread m_monitorThread; // // Variables protected by m_mutex @@ -130,6 +134,8 @@ class HttpCameraImpl : public SourceImpl { size_t m_nextLocation{0}; int m_prefLocation{-1}; // preferred location + std::atomic_int m_frameCount{0}; + wpi::condition_variable m_sinkEnabledCond; wpi::StringMap> m_settings; @@ -137,6 +143,8 @@ class HttpCameraImpl : public SourceImpl { wpi::StringMap> m_streamSettings; std::atomic_bool m_streamSettingsUpdated{false}; + + wpi::condition_variable m_monitorCond; }; class AxisCameraImpl : public HttpCameraImpl {