From ba6fe8ff2e3b5eaed64c232eb8c9c283a431789d Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 31 Jan 2021 18:52:48 -0800 Subject: [PATCH] [cscore] Add USB camera change event (#3123) --- .../main/java/edu/wpi/cscore/VideoEvent.java | 5 +- cscore/src/main/native/cpp/Instance.cpp | 6 +- cscore/src/main/native/cpp/Instance.h | 2 + cscore/src/main/native/cpp/Notifier.cpp | 4 ++ cscore/src/main/native/cpp/Notifier.h | 1 + .../src/main/native/cpp/UsbCameraListener.h | 31 ++++++++ cscore/src/main/native/cpp/cscore_cpp.cpp | 7 ++ cscore/src/main/native/include/cscore_c.h | 3 +- cscore/src/main/native/include/cscore_cpp.h | 3 +- .../main/native/linux/UsbCameraListener.cpp | 55 ++++++++++++++ .../src/main/native/osx/UsbCameraListener.cpp | 17 +++++ .../main/native/windows/UsbCameraListener.cpp | 72 +++++++++++++++++++ 12 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 cscore/src/main/native/cpp/UsbCameraListener.h create mode 100644 cscore/src/main/native/linux/UsbCameraListener.cpp create mode 100644 cscore/src/main/native/osx/UsbCameraListener.cpp create mode 100644 cscore/src/main/native/windows/UsbCameraListener.cpp diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java b/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java index ee03a293ff..c2731fdc78 100644 --- a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java +++ b/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java @@ -26,7 +26,8 @@ public class VideoEvent { kTelemetryUpdated(0x8000), kSinkPropertyCreated(0x10000), kSinkPropertyValueUpdated(0x20000), - kSinkPropertyChoicesUpdated(0x40000); + kSinkPropertyChoicesUpdated(0x40000), + kUsbCamerasChanged(0x80000); private final int value; @@ -84,6 +85,8 @@ public class VideoEvent { return Kind.kSinkPropertyValueUpdated; case 0x40000: return Kind.kSinkPropertyChoicesUpdated; + case 0x80000: + return Kind.kUsbCamerasChanged; default: return Kind.kUnknown; } diff --git a/cscore/src/main/native/cpp/Instance.cpp b/cscore/src/main/native/cpp/Instance.cpp index bb2330feee..4233204c7c 100644 --- a/cscore/src/main/native/cpp/Instance.cpp +++ b/cscore/src/main/native/cpp/Instance.cpp @@ -36,7 +36,10 @@ static void def_log_func(unsigned int level, const char* file, wpi::errs() << oss.str(); } -Instance::Instance() : telemetry(notifier), networkListener(logger, notifier) { +Instance::Instance() + : telemetry(notifier), + networkListener(logger, notifier), + usbCameraListener(logger, notifier) { SetDefaultLogger(); } @@ -52,6 +55,7 @@ void Instance::Shutdown() { m_sinks.FreeAll(); m_sources.FreeAll(); networkListener.Stop(); + usbCameraListener.Stop(); telemetry.Stop(); notifier.Stop(); } diff --git a/cscore/src/main/native/cpp/Instance.h b/cscore/src/main/native/cpp/Instance.h index 66ede809ee..fba616353e 100644 --- a/cscore/src/main/native/cpp/Instance.h +++ b/cscore/src/main/native/cpp/Instance.h @@ -18,6 +18,7 @@ #include "SourceImpl.h" #include "Telemetry.h" #include "UnlimitedHandleResource.h" +#include "UsbCameraListener.h" namespace cs { @@ -54,6 +55,7 @@ class Instance { Notifier notifier; Telemetry telemetry; NetworkListener networkListener; + UsbCameraListener usbCameraListener; private: UnlimitedHandleResource m_sources; diff --git a/cscore/src/main/native/cpp/Notifier.cpp b/cscore/src/main/native/cpp/Notifier.cpp index 79e0d1c69e..318e1b25ff 100644 --- a/cscore/src/main/native/cpp/Notifier.cpp +++ b/cscore/src/main/native/cpp/Notifier.cpp @@ -94,3 +94,7 @@ void Notifier::NotifyNetworkInterfacesChanged() { void Notifier::NotifyTelemetryUpdated() { Send(UINT_MAX, RawEvent::kTelemetryUpdated); } + +void Notifier::NotifyUsbCamerasChanged() { + Send(UINT_MAX, RawEvent::kUsbCamerasChanged); +} diff --git a/cscore/src/main/native/cpp/Notifier.h b/cscore/src/main/native/cpp/Notifier.h index 5cb88e96c1..3a5533ac05 100644 --- a/cscore/src/main/native/cpp/Notifier.h +++ b/cscore/src/main/native/cpp/Notifier.h @@ -83,6 +83,7 @@ class Notifier : public wpi::CallbackManager { const wpi::Twine& valueStr); void NotifyNetworkInterfacesChanged(); void NotifyTelemetryUpdated(); + void NotifyUsbCamerasChanged(); }; } // namespace cs diff --git a/cscore/src/main/native/cpp/UsbCameraListener.h b/cscore/src/main/native/cpp/UsbCameraListener.h new file mode 100644 index 0000000000..c8e8e151c5 --- /dev/null +++ b/cscore/src/main/native/cpp/UsbCameraListener.h @@ -0,0 +1,31 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#ifndef CSCORE_USBCAMERALISTENER_H_ +#define CSCORE_USBCAMERALISTENER_H_ + +#include + +#include + +namespace cs { + +class Notifier; + +class UsbCameraListener { + public: + UsbCameraListener(wpi::Logger& logger, Notifier& notifier); + ~UsbCameraListener(); + + void Start(); + void Stop(); + + private: + class Impl; + std::unique_ptr m_impl; +}; + +} // namespace cs + +#endif // CSCORE_USBCAMERALISTENER_H_ diff --git a/cscore/src/main/native/cpp/cscore_cpp.cpp b/cscore/src/main/native/cpp/cscore_cpp.cpp index fc40ed1991..cf35cec7c0 100644 --- a/cscore/src/main/native/cpp/cscore_cpp.cpp +++ b/cscore/src/main/native/cpp/cscore_cpp.cpp @@ -718,6 +718,13 @@ static void StartBackground(int eventMask, bool immediateNotify) { inst.notifier.NotifyNetworkInterfacesChanged(); } } + if ((eventMask & CS_USB_CAMERAS_CHANGED) != 0) { + // start network interface event listener + inst.usbCameraListener.Start(); + if (immediateNotify) { + inst.notifier.NotifyUsbCamerasChanged(); + } + } } CS_Listener AddListener(std::function callback, diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h index e53924020c..76bd8360b4 100644 --- a/cscore/src/main/native/include/cscore_c.h +++ b/cscore/src/main/native/include/cscore_c.h @@ -170,7 +170,8 @@ enum CS_EventKind { CS_TELEMETRY_UPDATED = 0x8000, CS_SINK_PROPERTY_CREATED = 0x10000, CS_SINK_PROPERTY_VALUE_UPDATED = 0x20000, - CS_SINK_PROPERTY_CHOICES_UPDATED = 0x40000 + CS_SINK_PROPERTY_CHOICES_UPDATED = 0x40000, + CS_USB_CAMERAS_CHANGED = 0x80000 }; /** diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h index f7fbfec24c..52f6f5f93c 100644 --- a/cscore/src/main/native/include/cscore_cpp.h +++ b/cscore/src/main/native/include/cscore_cpp.h @@ -116,7 +116,8 @@ struct RawEvent { kTelemetryUpdated = CS_TELEMETRY_UPDATED, kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED, kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED, - kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED + kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED, + kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED }; RawEvent() = default; diff --git a/cscore/src/main/native/linux/UsbCameraListener.cpp b/cscore/src/main/native/linux/UsbCameraListener.cpp new file mode 100644 index 0000000000..7c73b79876 --- /dev/null +++ b/cscore/src/main/native/linux/UsbCameraListener.cpp @@ -0,0 +1,55 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "UsbCameraListener.h" + +#include +#include +#include + +#include "Notifier.h" + +using namespace cs; + +class UsbCameraListener::Impl { + public: + explicit Impl(Notifier& notifier) : m_notifier(notifier) {} + + Notifier& m_notifier; + + std::unique_ptr m_runner; +}; + +UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier) + : m_impl(std::make_unique(notifier)) {} + +UsbCameraListener::~UsbCameraListener() = default; + +void UsbCameraListener::Start() { + if (!m_impl->m_runner) { + m_impl->m_runner = std::make_unique(); + m_impl->m_runner->ExecAsync([impl = m_impl.get()](wpi::uv::Loop& loop) { + auto refreshTimer = wpi::uv::Timer::Create(loop); + refreshTimer->timeout.connect([notifier = &impl->m_notifier] { + notifier->NotifyUsbCamerasChanged(); + }); + refreshTimer->Unreference(); + + auto devEvents = wpi::uv::FsEvent::Create(loop); + devEvents->fsEvent.connect([refreshTimer](const char* fn, int flags) { + if (wpi::StringRef(fn).startswith("video")) { + refreshTimer->Start(wpi::uv::Timer::Time(200)); + } + }); + devEvents->Start("/dev"); + devEvents->Unreference(); + }); + } +} + +void UsbCameraListener::Stop() { + if (m_impl->m_runner) { + m_impl->m_runner.reset(); + } +} diff --git a/cscore/src/main/native/osx/UsbCameraListener.cpp b/cscore/src/main/native/osx/UsbCameraListener.cpp new file mode 100644 index 0000000000..b83663d120 --- /dev/null +++ b/cscore/src/main/native/osx/UsbCameraListener.cpp @@ -0,0 +1,17 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "UsbCameraListener.h" + +using namespace cs; + +class UsbCameraListener::Impl {}; + +UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier) {} + +UsbCameraListener::~UsbCameraListener() = default; + +void UsbCameraListener::Start() {} + +void UsbCameraListener::Stop() {} diff --git a/cscore/src/main/native/windows/UsbCameraListener.cpp b/cscore/src/main/native/windows/UsbCameraListener.cpp new file mode 100644 index 0000000000..be33771ab8 --- /dev/null +++ b/cscore/src/main/native/windows/UsbCameraListener.cpp @@ -0,0 +1,72 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "UsbCameraListener.h" + +#include "Notifier.h" +#include "WindowsMessagePump.h" + +#include // NOLINT(build/include_order) + +#define IDT_TIMER1 1001 + +using namespace cs; + +class UsbCameraListener::Impl { + public: + explicit Impl(Notifier& notifier) : m_notifier{notifier} {} + + void Start() { + m_messagePump = std::make_unique( + [this](HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) { + return this->PumpMain(hwnd, uiMsg, wParam, lParam); + }); + } + + void Stop() { m_messagePump = nullptr; } + + LRESULT PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) { + switch (uiMsg) { + case WM_CLOSE: + KillTimer(hwnd, IDT_TIMER1); + break; + case WM_TIMER: + if (wParam == IDT_TIMER1) { + KillTimer(hwnd, IDT_TIMER1); + m_notifier.NotifyUsbCamerasChanged(); + } + break; + case WM_DEVICECHANGE: + PDEV_BROADCAST_HDR parameter = + reinterpret_cast(lParam); + if (wParam == DBT_DEVICEARRIVAL && + parameter->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + SetTimer(hwnd, IDT_TIMER1, 200, nullptr); + } else if (wParam == DBT_DEVICEREMOVECOMPLETE && + parameter->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + SetTimer(hwnd, IDT_TIMER1, 200, nullptr); + } + break; + } + return 0; + } + + Notifier& m_notifier; + std::unique_ptr m_messagePump; +}; + +UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier) + : m_impl{std::make_unique(notifier)} {} + +UsbCameraListener::~UsbCameraListener() { + Stop(); +} + +void UsbCameraListener::Start() { + m_impl->Start(); +} + +void UsbCameraListener::Stop() { + m_impl->Stop(); +}