From e9d226491cb7d1370f4d4eb8dbd1fbd1ac0df2d6 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 4 Jan 2026 16:59:02 -0800 Subject: [PATCH] [cscore] Split cscore classes into separate headers Fixes #3713. --- .../native/cpp/cameraserver/CameraServer.cpp | 22 +- .../include/wpi/cameraserver/CameraServer.hpp | 13 +- .../include/wpi/vision/VisionRunner.hpp | 3 +- cscore/examples/enum_usb/enum_usb.cpp | 8 +- cscore/examples/httpcvstream/httpcvstream.cpp | 12 +- cscore/examples/settings/settings.cpp | 2 +- cscore/examples/usbcvstream/usbcvstream.cpp | 11 +- cscore/examples/usbstream/usbstream.cpp | 7 +- cscore/examples/usbviewer/usbviewer.cpp | 6 +- cscore/src/dev/native/cpp/main.cpp | 2 +- .../native/cpp/ConfigurableSourceImpl.hpp | 2 - cscore/src/main/native/cpp/Frame.cpp | 268 +-- cscore/src/main/native/cpp/Frame.hpp | 37 +- cscore/src/main/native/cpp/HttpCameraImpl.cpp | 16 +- cscore/src/main/native/cpp/HttpCameraImpl.hpp | 4 +- cscore/src/main/native/cpp/Image.hpp | 43 +- .../src/main/native/cpp/MjpegServerImpl.cpp | 40 +- .../src/main/native/cpp/MjpegServerImpl.hpp | 5 - cscore/src/main/native/cpp/Notifier.hpp | 3 +- .../src/main/native/cpp/PropertyContainer.hpp | 2 +- cscore/src/main/native/cpp/RawSinkImpl.cpp | 4 +- cscore/src/main/native/cpp/RawSinkImpl.hpp | 3 +- cscore/src/main/native/cpp/RawSourceImpl.cpp | 2 +- cscore/src/main/native/cpp/RawSourceImpl.hpp | 5 - cscore/src/main/native/cpp/SinkImpl.hpp | 1 - cscore/src/main/native/cpp/SourceImpl.cpp | 54 +- cscore/src/main/native/cpp/SourceImpl.hpp | 11 +- cscore/src/main/native/cpp/Telemetry.cpp | 1 - cscore/src/main/native/cpp/Telemetry.hpp | 2 +- cscore/src/main/native/cpp/VideoSink.cpp | 44 + .../cpp/{cscore_oo.cpp => VideoSource.cpp} | 35 +- cscore/src/main/native/cpp/cscore_c.cpp | 10 +- cscore/src/main/native/cpp/cscore_cpp.cpp | 3 +- .../main/native/cpp/jni/CameraServerJNI.cpp | 20 +- .../src/main/native/include/wpi/cs/CvSink.hpp | 229 +++ .../main/native/include/wpi/cs/CvSource.hpp | 188 +++ .../main/native/include/wpi/cs/HttpCamera.hpp | 151 ++ .../main/native/include/wpi/cs/ImageSink.hpp | 55 + .../native/include/wpi/cs/ImageSource.hpp | 170 ++ .../native/include/wpi/cs/MjpegServer.hpp | 120 ++ .../main/native/include/wpi/cs/RawEvent.hpp | 91 + .../main/native/include/wpi/cs/RawSink.hpp | 107 ++ .../main/native/include/wpi/cs/RawSource.hpp | 62 + .../main/native/include/wpi/cs/UsbCamera.hpp | 89 + .../native/include/wpi/cs/UsbCameraInfo.hpp | 30 + .../native/include/wpi/cs/VideoCamera.hpp | 103 ++ .../main/native/include/wpi/cs/VideoEvent.hpp | 51 + .../native/include/wpi/cs/VideoListener.hpp | 69 + .../main/native/include/wpi/cs/VideoMode.hpp | 55 + .../native/include/wpi/cs/VideoProperty.hpp | 243 +++ .../main/native/include/wpi/cs/VideoSink.hpp | 232 +++ .../native/include/wpi/cs/VideoSource.hpp | 378 +++++ .../src/main/native/include/wpi/cs/cscore.h | 14 - .../src/main/native/include/wpi/cs/cscore_c.h | 9 +- .../main/native/include/wpi/cs/cscore_cpp.hpp | 145 +- .../main/native/include/wpi/cs/cscore_cv.hpp | 431 ----- .../main/native/include/wpi/cs/cscore_oo.hpp | 1500 ----------------- .../main/native/include/wpi/cs/cscore_raw.h | 198 --- .../main/native/include/wpi/cs/cscore_raw.hpp | 42 + .../src/main/native/linux/UsbCameraImpl.cpp | 87 +- .../src/main/native/linux/UsbCameraImpl.hpp | 6 +- .../src/main/native/objcpp/UsbCameraImpl.hpp | 3 +- .../src/main/native/objcpp/UsbCameraImpl.mm | 3 +- .../main/native/objcpp/UsbCameraImplObjc.hpp | 3 +- .../main/native/objcpp/UsbCameraImplObjc.mm | 16 +- .../src/main/native/windows/UsbCameraImpl.cpp | 35 +- .../src/main/native/windows/UsbCameraImpl.hpp | 3 +- .../src/test/native/cpp/CameraSourceTest.cpp | 3 +- wpilibc/src/test/native/c/test.c | 2 +- .../cpp/examples/HttpCamera/cpp/Robot.cpp | 1 + wpiutil/robotpy_pybind_build_info.bzl | 8 + .../native/include/wpi/util/PixelFormat.h | 20 + .../native/include/wpi/util/PixelFormat.hpp | 26 + .../main/native/include/wpi/util/RawFrame.h | 15 - .../main/native/include/wpi/util/RawFrame.hpp | 1 + wpiutil/src/main/python/pyproject.toml | 1 + .../src/main/python/semiwrap/PixelFormat.yml | 2 + 77 files changed, 2977 insertions(+), 2721 deletions(-) create mode 100644 cscore/src/main/native/cpp/VideoSink.cpp rename cscore/src/main/native/cpp/{cscore_oo.cpp => VideoSource.cpp} (62%) create mode 100644 cscore/src/main/native/include/wpi/cs/CvSink.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/CvSource.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/HttpCamera.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/ImageSink.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/ImageSource.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/MjpegServer.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/RawEvent.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/RawSink.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/RawSource.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/UsbCamera.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoCamera.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoEvent.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoListener.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoMode.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoProperty.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoSink.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/VideoSource.hpp delete mode 100644 cscore/src/main/native/include/wpi/cs/cscore.h delete mode 100644 cscore/src/main/native/include/wpi/cs/cscore_cv.hpp delete mode 100644 cscore/src/main/native/include/wpi/cs/cscore_oo.hpp create mode 100644 cscore/src/main/native/include/wpi/cs/cscore_raw.hpp create mode 100644 wpiutil/src/main/native/include/wpi/util/PixelFormat.h create mode 100644 wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp create mode 100644 wpiutil/src/main/python/semiwrap/PixelFormat.yml diff --git a/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp b/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp index 161d2d07b5..08cea8fa4d 100644 --- a/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp +++ b/cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp @@ -13,6 +13,8 @@ #include #include "wpi/cameraserver/CameraServerShared.hpp" +#include "wpi/cs/VideoEvent.hpp" +#include "wpi/cs/VideoListener.hpp" #include "wpi/nt/BooleanTopic.hpp" #include "wpi/nt/IntegerTopic.hpp" #include "wpi/nt/NetworkTable.hpp" @@ -247,17 +249,17 @@ void Instance::UpdateStreamValues() { } } -static std::string PixelFormatToString(int pixelFormat) { +static std::string PixelFormatToString(wpi::util::PixelFormat pixelFormat) { switch (pixelFormat) { - case cs::VideoMode::PixelFormat::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: return "MJPEG"; - case cs::VideoMode::PixelFormat::kYUYV: + case wpi::util::PixelFormat::kYUYV: return "YUYV"; - case cs::VideoMode::PixelFormat::kRGB565: + case wpi::util::PixelFormat::kRGB565: return "RGB565"; - case cs::VideoMode::PixelFormat::kBGR: + case wpi::util::PixelFormat::kBGR: return "BGR"; - case cs::VideoMode::PixelFormat::kGray: + case wpi::util::PixelFormat::kGray: return "Gray"; default: return "Unknown"; @@ -506,7 +508,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name, cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) { auto& inst = ::GetInstance(); // create a dummy CvSource - cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30}; + cs::CvSource source{name, wpi::util::PixelFormat::kMJPEG, 160, 120, 30}; cs::MjpegServer server = StartAutomaticCapture(source); inst.m_fixedSources[server.GetHandle()] = source.GetHandle(); @@ -568,7 +570,7 @@ cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) { } cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera, - cs::VideoMode::PixelFormat pixelFormat) { + wpi::util::PixelFormat pixelFormat) { auto& inst = ::GetInstance(); wpi::util::SmallString<64> name{"opencv_"}; name += camera.GetName(); @@ -611,7 +613,7 @@ cs::CvSink CameraServer::GetVideo(std::string_view name) { } cs::CvSink CameraServer::GetVideo(std::string_view name, - cs::VideoMode::PixelFormat pixelFormat) { + wpi::util::PixelFormat pixelFormat) { auto& inst = ::GetInstance(); cs::VideoSource source; { @@ -630,7 +632,7 @@ cs::CvSink CameraServer::GetVideo(std::string_view name, cs::CvSource CameraServer::PutVideo(std::string_view name, int width, int height) { ::GetInstance(); - cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30}; + cs::CvSource source{name, wpi::util::PixelFormat::kMJPEG, width, height, 30}; StartAutomaticCapture(source); return source; } diff --git a/cameraserver/src/main/native/include/wpi/cameraserver/CameraServer.hpp b/cameraserver/src/main/native/include/wpi/cameraserver/CameraServer.hpp index 1f2cf59ed1..fd47b5bd37 100644 --- a/cameraserver/src/main/native/include/wpi/cameraserver/CameraServer.hpp +++ b/cameraserver/src/main/native/include/wpi/cameraserver/CameraServer.hpp @@ -6,12 +6,13 @@ #include -#include -#include #include -#include -#include "wpi/cs/cscore_cv.hpp" +#include "wpi/cs/CvSink.hpp" +#include "wpi/cs/CvSource.hpp" +#include "wpi/cs/MjpegServer.hpp" +#include "wpi/cs/UsbCamera.hpp" +#include "wpi/util/PixelFormat.hpp" namespace wpi { @@ -107,7 +108,7 @@ class CameraServer { * camera */ static cs::CvSink GetVideo(const cs::VideoSource& camera, - cs::VideoMode::PixelFormat pixelFormat); + wpi::util::PixelFormat pixelFormat); /** * Get OpenCV access to the specified camera. This allows you to get @@ -126,7 +127,7 @@ class CameraServer { * camera */ static cs::CvSink GetVideo(std::string_view name, - cs::VideoMode::PixelFormat pixelFormat); + wpi::util::PixelFormat pixelFormat); /** * Create a MJPEG stream with OpenCV input. This can be called to pass custom diff --git a/cameraserver/src/main/native/include/wpi/vision/VisionRunner.hpp b/cameraserver/src/main/native/include/wpi/vision/VisionRunner.hpp index d565ef3c56..a91eae7817 100644 --- a/cameraserver/src/main/native/include/wpi/vision/VisionRunner.hpp +++ b/cameraserver/src/main/native/include/wpi/vision/VisionRunner.hpp @@ -8,7 +8,8 @@ #include #include -#include "wpi/cs/cscore_cv.hpp" +#include "wpi/cs/CvSink.hpp" +#include "wpi/cs/VideoSource.hpp" #include "wpi/vision/VisionPipeline.hpp" namespace wpi::vision { diff --git a/cscore/examples/enum_usb/enum_usb.cpp b/cscore/examples/enum_usb/enum_usb.cpp index 837e87d45c..76fac4863a 100644 --- a/cscore/examples/enum_usb/enum_usb.cpp +++ b/cscore/examples/enum_usb/enum_usb.cpp @@ -4,7 +4,7 @@ #include -#include "wpi/cs/cscore.h" +#include "wpi/cs/UsbCamera.hpp" #include "wpi/util/print.hpp" int main() { @@ -57,13 +57,13 @@ int main() { for (const auto& mode : camera.EnumerateVideoModes()) { const char* pixelFormat; switch (mode.pixelFormat) { - case wpi::cs::VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: pixelFormat = "MJPEG"; break; - case wpi::cs::VideoMode::kYUYV: + case wpi::util::PixelFormat::kYUYV: pixelFormat = "YUYV"; break; - case wpi::cs::VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: pixelFormat = "RGB565"; break; default: diff --git a/cscore/examples/httpcvstream/httpcvstream.cpp b/cscore/examples/httpcvstream/httpcvstream.cpp index 2008bac3e1..de98b42a38 100644 --- a/cscore/examples/httpcvstream/httpcvstream.cpp +++ b/cscore/examples/httpcvstream/httpcvstream.cpp @@ -6,17 +6,19 @@ #include -#include "wpi/cs/cscore.h" -#include "wpi/cs/cscore_cv.hpp" +#include "wpi/cs/CvSink.hpp" +#include "wpi/cs/CvSource.hpp" +#include "wpi/cs/HttpCamera.hpp" +#include "wpi/cs/MjpegServer.hpp" #include "wpi/util/print.hpp" int main() { wpi::cs::HttpCamera camera{"httpcam", "http://localhost:8081/?action=stream"}; - camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30); + camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30); wpi::cs::CvSink cvsink{"cvsink"}; cvsink.SetSource(camera); - wpi::cs::CvSource cvsource{"cvsource", wpi::cs::VideoMode::kMJPEG, 320, 240, - 30}; + wpi::cs::CvSource cvsource{"cvsource", wpi::util::PixelFormat::kMJPEG, 320, + 240, 30}; wpi::cs::MjpegServer cvMjpegServer{"cvhttpserver", 8083}; cvMjpegServer.SetSource(cvsource); diff --git a/cscore/examples/settings/settings.cpp b/cscore/examples/settings/settings.cpp index 77a89da6e7..9cb8b6dc32 100644 --- a/cscore/examples/settings/settings.cpp +++ b/cscore/examples/settings/settings.cpp @@ -6,7 +6,7 @@ #include #include -#include "wpi/cs/cscore.h" +#include "wpi/cs/UsbCamera.hpp" #include "wpi/util/StringExtras.hpp" #include "wpi/util/print.hpp" diff --git a/cscore/examples/usbcvstream/usbcvstream.cpp b/cscore/examples/usbcvstream/usbcvstream.cpp index 945ea25315..6657087315 100644 --- a/cscore/examples/usbcvstream/usbcvstream.cpp +++ b/cscore/examples/usbcvstream/usbcvstream.cpp @@ -4,18 +4,21 @@ #include -#include "wpi/cs/cscore_cv.hpp" +#include "wpi/cs/CvSink.hpp" +#include "wpi/cs/CvSource.hpp" +#include "wpi/cs/MjpegServer.hpp" +#include "wpi/cs/UsbCamera.hpp" #include "wpi/util/print.hpp" int main() { wpi::cs::UsbCamera camera{"usbcam", 0}; - camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30); + camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30); wpi::cs::MjpegServer mjpegServer{"httpserver", 8081}; mjpegServer.SetSource(camera); wpi::cs::CvSink cvsink{"cvsink"}; cvsink.SetSource(camera); - wpi::cs::CvSource cvsource{"cvsource", wpi::cs::VideoMode::kMJPEG, 320, 240, - 30}; + wpi::cs::CvSource cvsource{"cvsource", wpi::util::PixelFormat::kMJPEG, 320, + 240, 30}; wpi::cs::MjpegServer cvMjpegServer{"cvhttpserver", 8082}; cvMjpegServer.SetSource(cvsource); diff --git a/cscore/examples/usbstream/usbstream.cpp b/cscore/examples/usbstream/usbstream.cpp index 72f543e0cc..ca09e2c68c 100644 --- a/cscore/examples/usbstream/usbstream.cpp +++ b/cscore/examples/usbstream/usbstream.cpp @@ -4,7 +4,10 @@ #include -#include "wpi/cs/cscore.h" +#include "wpi/cs/MjpegServer.hpp" +#include "wpi/cs/RawEvent.hpp" +#include "wpi/cs/UsbCamera.hpp" +#include "wpi/cs/cscore_cpp.hpp" #include "wpi/util/print.hpp" int main() { @@ -14,7 +17,7 @@ int main() { wpi::util::print(" {}\n", addr); } wpi::cs::UsbCamera camera{"usbcam", 0}; - camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 320, 240, 30); + camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 320, 240, 30); wpi::cs::MjpegServer mjpegServer{"httpserver", 8081}; mjpegServer.SetSource(camera); diff --git a/cscore/examples/usbviewer/usbviewer.cpp b/cscore/examples/usbviewer/usbviewer.cpp index 42adf8e487..a43fa750e9 100644 --- a/cscore/examples/usbviewer/usbviewer.cpp +++ b/cscore/examples/usbviewer/usbviewer.cpp @@ -14,8 +14,8 @@ #include #include -#include "wpi/cs/cscore.h" -#include "wpi/cs/cscore_cv.hpp" +#include "wpi/cs/CvSink.hpp" +#include "wpi/cs/UsbCamera.hpp" #include "wpi/gui/wpigui.hpp" #include "wpi/util/mutex.hpp" #include "wpi/util/print.hpp" @@ -31,7 +31,7 @@ int main() { std::atomic stopCamera{false}; wpi::cs::UsbCamera camera{"usbcam", 0}; - camera.SetVideoMode(wpi::cs::VideoMode::kMJPEG, 640, 480, 30); + camera.SetVideoMode(wpi::util::PixelFormat::kMJPEG, 640, 480, 30); wpi::cs::CvSink cvsink{"cvsink"}; cvsink.SetSource(camera); diff --git a/cscore/src/dev/native/cpp/main.cpp b/cscore/src/dev/native/cpp/main.cpp index 270b145024..4f233fa8bd 100644 --- a/cscore/src/dev/native/cpp/main.cpp +++ b/cscore/src/dev/native/cpp/main.cpp @@ -2,7 +2,7 @@ // 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 "wpi/cs/cscore.h" +#include "wpi/cs/cscore_cpp.hpp" #include "wpi/util/print.hpp" int main() { diff --git a/cscore/src/main/native/cpp/ConfigurableSourceImpl.hpp b/cscore/src/main/native/cpp/ConfigurableSourceImpl.hpp index 7e7954a351..ee1b5fd07d 100644 --- a/cscore/src/main/native/cpp/ConfigurableSourceImpl.hpp +++ b/cscore/src/main/native/cpp/ConfigurableSourceImpl.hpp @@ -6,11 +6,9 @@ #include #include -#include #include #include #include -#include #include "SourceImpl.hpp" diff --git a/cscore/src/main/native/cpp/Frame.cpp b/cscore/src/main/native/cpp/Frame.cpp index 61e2c42f59..09df134c2e 100644 --- a/cscore/src/main/native/cpp/Frame.cpp +++ b/cscore/src/main/native/cpp/Frame.cpp @@ -13,6 +13,7 @@ #include "Instance.hpp" #include "SourceImpl.hpp" +#include "wpi/util/PixelFormat.hpp" using namespace wpi::cs; @@ -67,7 +68,7 @@ Image* Frame::GetNearestImage(int width, int height) const { } Image* Frame::GetNearestImage(int width, int height, - VideoMode::PixelFormat pixelFormat, + wpi::util::PixelFormat pixelFormat, int jpegQuality) const { if (!m_impl) { return nullptr; @@ -93,22 +94,23 @@ Image* Frame::GetNearestImage(int width, int height, // 2) Same width, height, different (but non-JPEG) pixelFormat (color conv) // 2a) If we want JPEG output, prefer BGR over other pixel formats - if (pixelFormat == VideoMode::kMJPEG) { + if (pixelFormat == wpi::util::PixelFormat::kMJPEG) { for (auto i : m_impl->images) { - if (i->Is(width, height, VideoMode::kBGR)) { + if (i->Is(width, height, wpi::util::PixelFormat::kBGR)) { return i; } } } for (auto i : m_impl->images) { - if (i->Is(width, height) && i->pixelFormat != VideoMode::kMJPEG) { + if (i->Is(width, height) && + i->pixelFormat != wpi::util::PixelFormat::kMJPEG) { return i; } } // 3) Different width, height, same pixelFormat (only if non-JPEG) (resample) - if (pixelFormat != VideoMode::kMJPEG) { + if (pixelFormat != wpi::util::PixelFormat::kMJPEG) { // 3a) Smallest image at least width/height in size for (auto i : m_impl->images) { if (i->IsLarger(width, height) && i->pixelFormat == pixelFormat && @@ -135,7 +137,8 @@ Image* Frame::GetNearestImage(int width, int height, // (color conversion + resample) // 4a) Smallest image at least width/height in size for (auto i : m_impl->images) { - if (i->IsLarger(width, height) && i->pixelFormat != VideoMode::kMJPEG && + if (i->IsLarger(width, height) && + i->pixelFormat != wpi::util::PixelFormat::kMJPEG && (!found || (i->IsSmaller(*found)))) { found = i; } @@ -146,7 +149,7 @@ Image* Frame::GetNearestImage(int width, int height, // 4b) Largest image (less than width/height) for (auto i : m_impl->images) { - if (i->pixelFormat != VideoMode::kMJPEG && + if (i->pixelFormat != wpi::util::PixelFormat::kMJPEG && (!found || (i->IsLarger(*found)))) { found = i; } @@ -158,7 +161,7 @@ Image* Frame::GetNearestImage(int width, int height, // 5) Same width, height, JPEG pixelFormat (decompression). As there may be // multiple JPEG images, find the highest quality one. for (auto i : m_impl->images) { - if (i->Is(width, height, VideoMode::kMJPEG) && + if (i->Is(width, height, wpi::util::PixelFormat::kMJPEG) && (!found || i->jpegQuality > found->jpegQuality)) { found = i; // consider one without a quality setting to be the highest quality @@ -175,7 +178,8 @@ Image* Frame::GetNearestImage(int width, int height, // 6) Different width, height, JPEG pixelFormat (decompression) // 6a) Smallest image at least width/height in size for (auto i : m_impl->images) { - if (i->IsLarger(width, height) && i->pixelFormat == VideoMode::kMJPEG && + if (i->IsLarger(width, height) && + i->pixelFormat == wpi::util::PixelFormat::kMJPEG && (!found || (i->IsSmaller(*found)))) { found = i; } @@ -186,7 +190,7 @@ Image* Frame::GetNearestImage(int width, int height, // 6b) Largest image (less than width/height) for (auto i : m_impl->images) { - if (i->pixelFormat != VideoMode::kMJPEG && + if (i->pixelFormat != wpi::util::PixelFormat::kMJPEG && (!found || (i->IsLarger(*found)))) { found = i; } @@ -199,7 +203,7 @@ Image* Frame::GetNearestImage(int width, int height, return m_impl->images.empty() ? nullptr : m_impl->images[0]; } -Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, +Image* Frame::ConvertImpl(Image* image, wpi::util::PixelFormat pixelFormat, int requiredJpegQuality, int defaultJpegQuality) { if (!image || image->Is(image->width, image->height, pixelFormat, requiredJpegQuality)) { @@ -211,169 +215,171 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, // anything else with it. Note that if the destination format is JPEG, we // still need to do this (unless it was already a JPEG, in which case we // would have returned above). - if (cur->pixelFormat == VideoMode::kMJPEG) { + if (cur->pixelFormat == wpi::util::PixelFormat::kMJPEG) { cur = ConvertMJPEGToBGR(cur); - if (pixelFormat == VideoMode::kBGR) { + if (pixelFormat == wpi::util::PixelFormat::kBGR) { return cur; } } // Color convert switch (pixelFormat) { - case VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: // If source is YUYV, UYVY, Gray, or Y16, need to convert to BGR first - if (cur->pixelFormat == VideoMode::kYUYV) { + if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertYUYVToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kUYVY) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertUYVYToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kGray) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertGrayToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kY16) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; - } else if (Image* newImage = GetExistingImage(cur->width, cur->height, - VideoMode::kGray)) { + } else if (Image* newImage = + GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kGray)) { cur = ConvertGrayToBGR(newImage); } else { cur = ConvertGrayToBGR(ConvertY16ToGray(cur)); } } return ConvertBGRToRGB565(cur); - case VideoMode::kGray: - case VideoMode::kY16: + case wpi::util::PixelFormat::kGray: + case wpi::util::PixelFormat::kY16: // If source is also grayscale, convert directly - if (pixelFormat == VideoMode::kGray && - cur->pixelFormat == VideoMode::kY16) { + if (pixelFormat == wpi::util::PixelFormat::kGray && + cur->pixelFormat == wpi::util::PixelFormat::kY16) { return ConvertY16ToGray(cur); - } else if (pixelFormat == VideoMode::kY16 && - cur->pixelFormat == VideoMode::kGray) { + } else if (pixelFormat == wpi::util::PixelFormat::kY16 && + cur->pixelFormat == wpi::util::PixelFormat::kGray) { return ConvertGrayToY16(cur); } // If source is YUYV, UYVY, convert directly to Gray // If RGB565, need to convert to BGR first - if (cur->pixelFormat == VideoMode::kYUYV) { + if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) { cur = ConvertYUYVToGray(cur); - } else if (cur->pixelFormat == VideoMode::kUYVY) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) { cur = ConvertUYVYToGray(cur); - } else if (cur->pixelFormat == VideoMode::kRGB565) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertRGB565ToBGR(cur); } cur = ConvertBGRToGray(cur); } - if (pixelFormat == VideoMode::kY16) { + if (pixelFormat == wpi::util::PixelFormat::kY16) { cur = ConvertGrayToY16(cur); } return cur; - case VideoMode::kBGR: - case VideoMode::kMJPEG: - if (cur->pixelFormat == VideoMode::kYUYV) { + case wpi::util::PixelFormat::kBGR: + case wpi::util::PixelFormat::kMJPEG: + if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) { cur = ConvertYUYVToBGR(cur); - } else if (cur->pixelFormat == VideoMode::kUYVY) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) { cur = ConvertUYVYToBGR(cur); - } else if (cur->pixelFormat == VideoMode::kRGB565) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) { cur = ConvertRGB565ToBGR(cur); - } else if (cur->pixelFormat == VideoMode::kGray) { - if (pixelFormat == VideoMode::kBGR) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) { + if (pixelFormat == wpi::util::PixelFormat::kBGR) { return ConvertGrayToBGR(cur); } else { return ConvertGrayToMJPEG(cur, defaultJpegQuality); } - } else if (cur->pixelFormat == VideoMode::kY16) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) { // Check to see if Gray version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kGray)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kGray)) { cur = newImage; } else { cur = ConvertY16ToGray(cur); } - if (pixelFormat == VideoMode::kBGR) { + if (pixelFormat == wpi::util::PixelFormat::kBGR) { return ConvertGrayToBGR(cur); } else { return ConvertGrayToMJPEG(cur, defaultJpegQuality); } } break; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: // If source is RGB565, YUYV, UYVY, Gray or Y16, need to convert to BGR // first - if (cur->pixelFormat == VideoMode::kRGB565) { + if (cur->pixelFormat == wpi::util::PixelFormat::kRGB565) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertRGB565ToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kYUYV) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kYUYV) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertYUYVToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kUYVY) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kUYVY) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertUYVYToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kGray) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kGray) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; } else { cur = ConvertGrayToBGR(cur); } - } else if (cur->pixelFormat == VideoMode::kY16) { + } else if (cur->pixelFormat == wpi::util::PixelFormat::kY16) { // Check to see if BGR version already exists... - if (Image* newImage = - GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) { + if (Image* newImage = GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kBGR)) { cur = newImage; - } else if (Image* newImage = GetExistingImage(cur->width, cur->height, - VideoMode::kGray)) { + } else if (Image* newImage = + GetExistingImage(cur->width, cur->height, + wpi::util::PixelFormat::kGray)) { cur = ConvertGrayToBGR(newImage); } else { cur = ConvertGrayToBGR(ConvertY16ToGray(cur)); } } return ConvertBGRToBGRA(cur); - case VideoMode::kYUYV: - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kYUYV: + case wpi::util::PixelFormat::kUYVY: default: return nullptr; // Unsupported } // Compress if destination is JPEG - if (pixelFormat == VideoMode::kMJPEG) { + if (pixelFormat == wpi::util::PixelFormat::kMJPEG) { cur = ConvertBGRToMJPEG(cur, defaultJpegQuality); } @@ -381,14 +387,14 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, } Image* Frame::ConvertMJPEGToBGR(Image* image) { - if (!image || image->pixelFormat != VideoMode::kMJPEG) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kMJPEG) { return nullptr; } // Allocate an BGR image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, - image->width * image->height * 3); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR, + image->width, image->height, + image->width * image->height * 3); // Decode cv::Mat newMat = newImage->AsMat(); @@ -404,14 +410,14 @@ Image* Frame::ConvertMJPEGToBGR(Image* image) { } Image* Frame::ConvertMJPEGToGray(Image* image) { - if (!image || image->pixelFormat != VideoMode::kMJPEG) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kMJPEG) { return nullptr; } // Allocate an grayscale image auto newImage = - m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height, - image->width * image->height); + m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width, + image->height, image->width * image->height); // Decode cv::Mat newMat = newImage->AsMat(); @@ -427,14 +433,14 @@ Image* Frame::ConvertMJPEGToGray(Image* image) { } Image* Frame::ConvertYUYVToBGR(Image* image) { - if (!image || image->pixelFormat != VideoMode::kYUYV) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kYUYV) { return nullptr; } // Allocate a BGR image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, - image->width * image->height * 3); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR, + image->width, image->height, + image->width * image->height * 3); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_YUYV); @@ -449,14 +455,14 @@ Image* Frame::ConvertYUYVToBGR(Image* image) { } Image* Frame::ConvertYUYVToGray(Image* image) { - if (!image || image->pixelFormat != VideoMode::kYUYV) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kYUYV) { return nullptr; } // Allocate a grayscale image auto newImage = - m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height, - image->width * image->height); + m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width, + image->height, image->width * image->height); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2GRAY_YUYV); @@ -471,14 +477,14 @@ Image* Frame::ConvertYUYVToGray(Image* image) { } Image* Frame::ConvertUYVYToBGR(Image* image) { - if (!image || image->pixelFormat != VideoMode::kUYVY) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kUYVY) { return nullptr; } // Allocate a BGR image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, - image->width * image->height * 3); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR, + image->width, image->height, + image->width * image->height * 3); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_UYVY); @@ -493,14 +499,14 @@ Image* Frame::ConvertUYVYToBGR(Image* image) { } Image* Frame::ConvertUYVYToGray(Image* image) { - if (!image || image->pixelFormat != VideoMode::kUYVY) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kUYVY) { return nullptr; } // Allocate a grayscale image auto newImage = - m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height, - image->width * image->height); + m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width, + image->height, image->width * image->height); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2GRAY_UYVY); @@ -515,14 +521,14 @@ Image* Frame::ConvertUYVYToGray(Image* image) { } Image* Frame::ConvertBGRToRGB565(Image* image) { - if (!image || image->pixelFormat != VideoMode::kBGR) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) { return nullptr; } // Allocate a RGB565 image - auto newImage = - m_impl->source.AllocImage(VideoMode::kRGB565, image->width, image->height, - image->width * image->height * 2); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kRGB565, + image->width, image->height, + image->width * image->height * 2); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_RGB2BGR565); @@ -537,14 +543,14 @@ Image* Frame::ConvertBGRToRGB565(Image* image) { } Image* Frame::ConvertRGB565ToBGR(Image* image) { - if (!image || image->pixelFormat != VideoMode::kRGB565) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kRGB565) { return nullptr; } // Allocate a BGR image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, - image->width * image->height * 3); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR, + image->width, image->height, + image->width * image->height * 3); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR5652RGB); @@ -559,14 +565,14 @@ Image* Frame::ConvertRGB565ToBGR(Image* image) { } Image* Frame::ConvertBGRToGray(Image* image) { - if (!image || image->pixelFormat != VideoMode::kBGR) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) { return nullptr; } // Allocate a Grayscale image auto newImage = - m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height, - image->width * image->height); + m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width, + image->height, image->width * image->height); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2GRAY); @@ -581,14 +587,14 @@ Image* Frame::ConvertBGRToGray(Image* image) { } Image* Frame::ConvertGrayToBGR(Image* image) { - if (!image || image->pixelFormat != VideoMode::kGray) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) { return nullptr; } // Allocate a BGR image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height, - image->width * image->height * 3); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGR, + image->width, image->height, + image->width * image->height * 3); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_GRAY2BGR); @@ -603,7 +609,7 @@ Image* Frame::ConvertGrayToBGR(Image* image) { } Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) { - if (!image || image->pixelFormat != VideoMode::kBGR) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) { return nullptr; } if (!m_impl) { @@ -617,9 +623,9 @@ Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) { // Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel, // this is a little bit more conservative in assuming 50% space savings over // the equivalent BGR image. - auto newImage = - m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height, - image->width * image->height * 1.5); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kMJPEG, + image->width, image->height, + image->width * image->height * 1.5); // Compress if (m_impl->compressionParams.empty()) { @@ -638,7 +644,7 @@ Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) { } Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) { - if (!image || image->pixelFormat != VideoMode::kGray) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) { return nullptr; } if (!m_impl) { @@ -652,9 +658,9 @@ Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) { // Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel, // this is a little bit more conservative in assuming 25% space savings over // the equivalent grayscale image. - auto newImage = - m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height, - image->width * image->height * 0.75); + auto newImage = m_impl->source.AllocImage( + wpi::util::PixelFormat::kMJPEG, image->width, image->height, + image->width * image->height * 0.75); // Compress if (m_impl->compressionParams.empty()) { @@ -673,14 +679,14 @@ Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) { } Image* Frame::ConvertGrayToY16(Image* image) { - if (!image || image->pixelFormat != VideoMode::kGray) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kGray) { return nullptr; } // Allocate a Y16 image - auto newImage = - m_impl->source.AllocImage(VideoMode::kY16, image->width, image->height, - image->width * image->height * 2); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kY16, + image->width, image->height, + image->width * image->height * 2); // Convert with linear scaling image->AsMat().convertTo(newImage->AsMat(), CV_16U, 256); @@ -695,14 +701,14 @@ Image* Frame::ConvertGrayToY16(Image* image) { } Image* Frame::ConvertY16ToGray(Image* image) { - if (!image || image->pixelFormat != VideoMode::kY16) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kY16) { return nullptr; } // Allocate a Grayscale image auto newImage = - m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height, - image->width * image->height); + m_impl->source.AllocImage(wpi::util::PixelFormat::kGray, image->width, + image->height, image->width * image->height); // Scale min to 0 and max to 255 cv::normalize(image->AsMat(), newImage->AsMat(), 255, 0, cv::NORM_MINMAX); @@ -717,14 +723,14 @@ Image* Frame::ConvertY16ToGray(Image* image) { } Image* Frame::ConvertBGRToBGRA(Image* image) { - if (!image || image->pixelFormat != VideoMode::kBGR) { + if (!image || image->pixelFormat != wpi::util::PixelFormat::kBGR) { return nullptr; } // Allocate a RGB565 image - auto newImage = - m_impl->source.AllocImage(VideoMode::kBGRA, image->width, image->height, - image->width * image->height * 4); + auto newImage = m_impl->source.AllocImage(wpi::util::PixelFormat::kBGRA, + image->width, image->height, + image->width * image->height * 4); // Convert cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2BGRA); @@ -739,7 +745,7 @@ Image* Frame::ConvertBGRToBGRA(Image* image) { } Image* Frame::GetImageImpl(int width, int height, - VideoMode::PixelFormat pixelFormat, + wpi::util::PixelFormat pixelFormat, int requiredJpegQuality, int defaultJpegQuality) { if (!m_impl) { return nullptr; @@ -759,7 +765,7 @@ Image* Frame::GetImageImpl(int width, int height, // anything else with it. Note that if the destination format is JPEG, we // still need to do this (unless the width/height/compression were the same, // in which case we already returned the existing JPEG above). - if (cur->pixelFormat == VideoMode::kMJPEG) { + if (cur->pixelFormat == wpi::util::PixelFormat::kMJPEG) { cur = ConvertMJPEGToBGR(cur); } @@ -784,7 +790,7 @@ Image* Frame::GetImageImpl(int width, int height, } bool Frame::GetCv(cv::Mat& image, int width, int height, - VideoMode::PixelFormat pixelFormat) { + wpi::util::PixelFormat pixelFormat) { Image* rawImage = GetImage(width, height, pixelFormat); if (!rawImage) { return false; @@ -809,7 +815,7 @@ std::unique_ptr CreateImageFromBGRA(wpi::cs::SourceImpl* source, cv::Mat finalImage{static_cast(height), static_cast(width), CV_8UC4, const_cast(data), stride}; std::unique_ptr dest = source->AllocImage( - VideoMode::PixelFormat::kBGR, width, height, width * height * 3); + wpi::util::PixelFormat::kBGR, width, height, width * height * 3); cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR); return dest; } diff --git a/cscore/src/main/native/cpp/Frame.hpp b/cscore/src/main/native/cpp/Frame.hpp index c8673ebe32..631eeac4f5 100644 --- a/cscore/src/main/native/cpp/Frame.hpp +++ b/cscore/src/main/native/cpp/Frame.hpp @@ -12,7 +12,8 @@ #include #include "Image.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/PixelFormat.hpp" +#include "wpi/util/RawFrame.h" #include "wpi/util/SmallVector.hpp" #include "wpi/util/mutex.hpp" @@ -109,13 +110,13 @@ class Frame { return m_impl->images[0]->height; } - int GetOriginalPixelFormat() const { + wpi::util::PixelFormat GetOriginalPixelFormat() const { if (!m_impl) { - return 0; + return wpi::util::PixelFormat::kUnknown; } std::scoped_lock lock(m_impl->mutex); if (m_impl->images.empty()) { - return 0; + return wpi::util::PixelFormat::kUnknown; } return m_impl->images[0]->pixelFormat; } @@ -156,7 +157,7 @@ class Frame { } Image* GetExistingImage(int width, int height, - VideoMode::PixelFormat pixelFormat) const { + wpi::util::PixelFormat pixelFormat) const { if (!m_impl) { return nullptr; } @@ -170,7 +171,7 @@ class Frame { } Image* GetExistingImage(int width, int height, - VideoMode::PixelFormat pixelFormat, + wpi::util::PixelFormat pixelFormat, int jpegQuality) const { if (!m_impl) { return nullptr; @@ -186,18 +187,18 @@ class Frame { Image* GetNearestImage(int width, int height) const; Image* GetNearestImage(int width, int height, - VideoMode::PixelFormat pixelFormat, + wpi::util::PixelFormat pixelFormat, int jpegQuality = -1) const; - Image* Convert(Image* image, VideoMode::PixelFormat pixelFormat) { - if (pixelFormat == VideoMode::kMJPEG) { + Image* Convert(Image* image, wpi::util::PixelFormat pixelFormat) { + if (pixelFormat == wpi::util::PixelFormat::kMJPEG) { return nullptr; } return ConvertImpl(image, pixelFormat, -1, 80); } Image* ConvertToMJPEG(Image* image, int requiredQuality, int defaultQuality = 80) { - return ConvertImpl(image, VideoMode::kMJPEG, requiredQuality, + return ConvertImpl(image, wpi::util::PixelFormat::kMJPEG, requiredQuality, defaultQuality); } Image* ConvertMJPEGToBGR(Image* image); @@ -216,28 +217,28 @@ class Frame { Image* ConvertY16ToGray(Image* image); Image* ConvertBGRToBGRA(Image* image); - Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) { - if (pixelFormat == VideoMode::kMJPEG) { + Image* GetImage(int width, int height, wpi::util::PixelFormat pixelFormat) { + if (pixelFormat == wpi::util::PixelFormat::kMJPEG) { return nullptr; } return GetImageImpl(width, height, pixelFormat, -1, 80); } Image* GetImageMJPEG(int width, int height, int requiredQuality, int defaultQuality = 80) { - return GetImageImpl(width, height, VideoMode::kMJPEG, requiredQuality, - defaultQuality); + return GetImageImpl(width, height, wpi::util::PixelFormat::kMJPEG, + requiredQuality, defaultQuality); } - bool GetCv(cv::Mat& image, VideoMode::PixelFormat pixelFormat) { + bool GetCv(cv::Mat& image, wpi::util::PixelFormat pixelFormat) { return GetCv(image, GetOriginalWidth(), GetOriginalHeight(), pixelFormat); } bool GetCv(cv::Mat& image, int width, int height, - VideoMode::PixelFormat pixelFormat); + wpi::util::PixelFormat pixelFormat); private: - Image* ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat, + Image* ConvertImpl(Image* image, wpi::util::PixelFormat pixelFormat, int requiredJpegQuality, int defaultJpegQuality); - Image* GetImageImpl(int width, int height, VideoMode::PixelFormat pixelFormat, + Image* GetImageImpl(int width, int height, wpi::util::PixelFormat pixelFormat, int requiredJpegQuality, int defaultJpegQuality); void DecRef() { if (m_impl && --(m_impl->refcount) == 0) { diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp index fb325141f2..aeafc0a32b 100644 --- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp +++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp @@ -305,7 +305,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is, // the data directly into it. unsigned int contentLength = v.value(); auto image = - AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength); + AllocImage(wpi::util::PixelFormat::kMJPEG, 0, 0, contentLength); is.read(image->data(), contentLength); if (!m_active || is.has_error()) { return false; @@ -325,7 +325,7 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is, PutError("did not receive a JPEG image", wpi::util::Now()); return false; } - PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf, + PutFrame(wpi::util::PixelFormat::kMJPEG, width, height, imageBuf, wpi::util::Now()); } @@ -333,9 +333,9 @@ bool HttpCameraImpl::DeviceStreamFrame(wpi::util::raw_istream& is, // update video mode if not set std::scoped_lock lock(m_mutex); - if (m_mode.pixelFormat != VideoMode::PixelFormat::kMJPEG || + if (m_mode.pixelFormat != wpi::util::PixelFormat::kMJPEG || m_mode.width == 0 || m_mode.height == 0) { - m_mode.pixelFormat = VideoMode::PixelFormat::kMJPEG; + m_mode.pixelFormat = wpi::util::PixelFormat::kMJPEG; m_mode.width = width; m_mode.height = height; } @@ -474,9 +474,9 @@ bool HttpCameraImpl::CacheProperties(CS_Status* status) const { // Pretty typical set of video modes m_videoModes.clear(); - m_videoModes.emplace_back(VideoMode::kMJPEG, 640, 480, 30); - m_videoModes.emplace_back(VideoMode::kMJPEG, 320, 240, 30); - m_videoModes.emplace_back(VideoMode::kMJPEG, 160, 120, 30); + m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 640, 480, 30); + m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 320, 240, 30); + m_videoModes.emplace_back(wpi::util::PixelFormat::kMJPEG, 160, 120, 30); m_properties_cached = true; return true; @@ -525,7 +525,7 @@ void HttpCameraImpl::SetExposureManual(int value, CS_Status* status) { } bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { - if (mode.pixelFormat != VideoMode::kMJPEG) { + if (mode.pixelFormat != wpi::util::PixelFormat::kMJPEG) { return false; } std::scoped_lock lock(m_mutex); diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.hpp b/cscore/src/main/native/cpp/HttpCameraImpl.hpp index d55a8ee8f7..2489cc4d66 100644 --- a/cscore/src/main/native/cpp/HttpCameraImpl.hpp +++ b/cscore/src/main/native/cpp/HttpCameraImpl.hpp @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include #include @@ -15,7 +14,8 @@ #include #include "SourceImpl.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_c.h" #include "wpi/net/HttpUtil.hpp" #include "wpi/util/StringMap.hpp" #include "wpi/util/condition_variable.hpp" diff --git a/cscore/src/main/native/cpp/Image.hpp b/cscore/src/main/native/cpp/Image.hpp index 28a49d2f3f..37b3e3a83c 100644 --- a/cscore/src/main/native/cpp/Image.hpp +++ b/cscore/src/main/native/cpp/Image.hpp @@ -10,7 +10,7 @@ #include #include "default_init_allocator.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/PixelFormat.hpp" namespace wpi::cs { @@ -53,20 +53,20 @@ class Image { cv::Mat AsMat() { int type; switch (pixelFormat) { - case VideoMode::kYUYV: - case VideoMode::kRGB565: - case VideoMode::kY16: - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kYUYV: + case wpi::util::PixelFormat::kRGB565: + case wpi::util::PixelFormat::kY16: + case wpi::util::PixelFormat::kUYVY: type = CV_8UC2; break; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: type = CV_8UC3; break; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: type = CV_8UC4; break; - case VideoMode::kGray: - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kGray: + case wpi::util::PixelFormat::kMJPEG: default: type = CV_8UC1; break; @@ -76,18 +76,18 @@ class Image { int GetStride() const { switch (pixelFormat) { - case VideoMode::kYUYV: - case VideoMode::kRGB565: - case VideoMode::kY16: - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kYUYV: + case wpi::util::PixelFormat::kRGB565: + case wpi::util::PixelFormat::kY16: + case wpi::util::PixelFormat::kUYVY: return 2 * width; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: return 3 * width; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: return 4 * width; - case VideoMode::kGray: + case wpi::util::PixelFormat::kGray: return width; - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: default: return 0; } @@ -98,15 +98,16 @@ class Image { bool Is(int width_, int height_) { return width == width_ && height == height_; } - bool Is(int width_, int height_, VideoMode::PixelFormat pixelFormat_) { + bool Is(int width_, int height_, wpi::util::PixelFormat pixelFormat_) { return width == width_ && height == height_ && pixelFormat == pixelFormat_; } - bool Is(int width_, int height_, VideoMode::PixelFormat pixelFormat_, + bool Is(int width_, int height_, wpi::util::PixelFormat pixelFormat_, int jpegQuality_) { // Consider +/-5 on JPEG quality to be "close enough" return width == width_ && height == height_ && pixelFormat == pixelFormat_ && - (pixelFormat != VideoMode::kMJPEG || jpegQuality_ == -1 || + (pixelFormat != wpi::util::PixelFormat::kMJPEG || + jpegQuality_ == -1 || (jpegQuality != -1 && std::abs(jpegQuality - jpegQuality_) <= 5)); } bool IsLarger(int width_, int height_) { @@ -122,7 +123,7 @@ class Image { std::vector m_data; public: - VideoMode::PixelFormat pixelFormat{VideoMode::kUnknown}; + wpi::util::PixelFormat pixelFormat{wpi::util::PixelFormat::kUnknown}; int width{0}; int height{0}; int jpegQuality{-1}; diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/cscore/src/main/native/cpp/MjpegServerImpl.cpp index bc6fb4feba..3fdcf148c3 100644 --- a/cscore/src/main/native/cpp/MjpegServerImpl.cpp +++ b/cscore/src/main/native/cpp/MjpegServerImpl.cpp @@ -445,28 +445,28 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::util::raw_ostream& os, for (auto mode : source.EnumerateVideoModes(&status)) { os << ""; switch (mode.pixelFormat) { - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: os << "MJPEG"; break; - case VideoMode::kYUYV: + case wpi::util::PixelFormat::kYUYV: os << "YUYV"; break; - case VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: os << "RGB565"; break; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: os << "BGR"; break; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: os << "BGRA"; break; - case VideoMode::kGray: + case wpi::util::PixelFormat::kGray: os << "gray"; break; - case VideoMode::kY16: + case wpi::util::PixelFormat::kY16: os << "Y16"; break; - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kUYVY: os << "UYVY"; break; default: @@ -565,25 +565,25 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::util::raw_ostream& os, os << '{'; os << "\n\"pixelFormat\": \""; switch (mode.pixelFormat) { - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: os << "MJPEG"; break; - case VideoMode::kYUYV: + case wpi::util::PixelFormat::kYUYV: os << "YUYV"; break; - case VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: os << "RGB565"; break; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: os << "BGR"; break; - case VideoMode::kGray: + case wpi::util::PixelFormat::kGray: os << "gray"; break; - case VideoMode::kY16: + case wpi::util::PixelFormat::kY16: os << "Y16"; break; - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kUYVY: os << "UYVY"; break; default: @@ -752,15 +752,15 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::net::raw_socket_ostream& os) { bool addDHT = false; size_t locSOF = size; switch (image->pixelFormat) { - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: // Determine if we need to add DHT to it, and allocate enough space // for adding it if required. addDHT = JpegNeedsDHT(data, &size, &locSOF); break; - case VideoMode::kUYVY: - case VideoMode::kRGB565: - case VideoMode::kYUYV: - case VideoMode::kY16: + case wpi::util::PixelFormat::kUYVY: + case wpi::util::PixelFormat::kRGB565: + case wpi::util::PixelFormat::kYUYV: + case wpi::util::PixelFormat::kY16: default: // Bad frame; sleep for 10 ms so we don't consume all processor time. std::this_thread::sleep_for(std::chrono::milliseconds(10)); diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.hpp b/cscore/src/main/native/cpp/MjpegServerImpl.hpp index 2397980f2b..80ffce62dd 100644 --- a/cscore/src/main/native/cpp/MjpegServerImpl.hpp +++ b/cscore/src/main/native/cpp/MjpegServerImpl.hpp @@ -13,12 +13,7 @@ #include "SinkImpl.hpp" #include "wpi/net/NetworkAcceptor.hpp" -#include "wpi/net/NetworkStream.hpp" -#include "wpi/net/raw_socket_ostream.hpp" #include "wpi/util/SafeThread.hpp" -#include "wpi/util/SmallVector.hpp" -#include "wpi/util/raw_istream.hpp" -#include "wpi/util/raw_ostream.hpp" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/Notifier.hpp b/cscore/src/main/native/cpp/Notifier.hpp index aef6557004..20565025ca 100644 --- a/cscore/src/main/native/cpp/Notifier.hpp +++ b/cscore/src/main/native/cpp/Notifier.hpp @@ -8,7 +8,8 @@ #include #include "Handle.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/RawEvent.hpp" +#include "wpi/cs/VideoMode.hpp" #include "wpi/util/CallbackManager.hpp" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/PropertyContainer.hpp b/cscore/src/main/native/cpp/PropertyContainer.hpp index b1f25439e1..2461d44548 100644 --- a/cscore/src/main/native/cpp/PropertyContainer.hpp +++ b/cscore/src/main/native/cpp/PropertyContainer.hpp @@ -13,7 +13,7 @@ #include #include "PropertyImpl.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/cscore_c.h" #include "wpi/util/StringMap.hpp" #include "wpi/util/json_fwd.hpp" #include "wpi/util/mutex.hpp" diff --git a/cscore/src/main/native/cpp/RawSinkImpl.cpp b/cscore/src/main/native/cpp/RawSinkImpl.cpp index 9447714669..0afb06619e 100644 --- a/cscore/src/main/native/cpp/RawSinkImpl.cpp +++ b/cscore/src/main/native/cpp/RawSinkImpl.cpp @@ -100,7 +100,7 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame, auto width = rawFrame.width; auto height = rawFrame.height; auto pixelFormat = - static_cast(rawFrame.pixelFormat); + static_cast(rawFrame.pixelFormat); if (width <= 0 || height <= 0) { width = incomingFrame.GetOriginalWidth(); height = incomingFrame.GetOriginalHeight(); @@ -118,7 +118,7 @@ uint64_t RawSinkImpl::GrabFrameImpl(WPI_RawFrame& rawFrame, rawFrame.height = newImage->height; rawFrame.width = newImage->width; rawFrame.stride = newImage->GetStride(); - rawFrame.pixelFormat = newImage->pixelFormat; + rawFrame.pixelFormat = static_cast(newImage->pixelFormat); rawFrame.size = newImage->size(); std::copy(newImage->data(), newImage->data() + rawFrame.size, rawFrame.data); rawFrame.timestamp = incomingFrame.GetTime(); diff --git a/cscore/src/main/native/cpp/RawSinkImpl.hpp b/cscore/src/main/native/cpp/RawSinkImpl.hpp index ab4ed8e492..7e6261ce1c 100644 --- a/cscore/src/main/native/cpp/RawSinkImpl.hpp +++ b/cscore/src/main/native/cpp/RawSinkImpl.hpp @@ -13,8 +13,7 @@ #include "Frame.hpp" #include "SinkImpl.hpp" -#include "wpi/cs/cscore_raw.h" -#include "wpi/util/condition_variable.hpp" +#include "wpi/util/RawFrame.h" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/RawSourceImpl.cpp b/cscore/src/main/native/cpp/RawSourceImpl.cpp index 40ed58ec6c..649945f748 100644 --- a/cscore/src/main/native/cpp/RawSourceImpl.cpp +++ b/cscore/src/main/native/cpp/RawSourceImpl.cpp @@ -24,7 +24,7 @@ RawSourceImpl::~RawSourceImpl() = default; void RawSourceImpl::PutFrame(const WPI_RawFrame& image) { auto currentTime = wpi::util::Now(); std::string_view data_view{reinterpret_cast(image.data), image.size}; - SourceImpl::PutFrame(static_cast(image.pixelFormat), + SourceImpl::PutFrame(static_cast(image.pixelFormat), image.width, image.height, data_view, currentTime); } diff --git a/cscore/src/main/native/cpp/RawSourceImpl.hpp b/cscore/src/main/native/cpp/RawSourceImpl.hpp index d1864a485d..585fafd1d0 100644 --- a/cscore/src/main/native/cpp/RawSourceImpl.hpp +++ b/cscore/src/main/native/cpp/RawSourceImpl.hpp @@ -5,15 +5,10 @@ #pragma once #include -#include -#include -#include #include -#include #include "ConfigurableSourceImpl.hpp" #include "SourceImpl.hpp" -#include "wpi/cs/cscore_raw.h" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/SinkImpl.hpp b/cscore/src/main/native/cpp/SinkImpl.hpp index c1494d1436..865420a1cd 100644 --- a/cscore/src/main/native/cpp/SinkImpl.hpp +++ b/cscore/src/main/native/cpp/SinkImpl.hpp @@ -11,7 +11,6 @@ #include "SourceImpl.hpp" #include "wpi/util/Logger.hpp" #include "wpi/util/json_fwd.hpp" -#include "wpi/util/mutex.hpp" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp index 8bcfc58f25..f30c53077d 100644 --- a/cscore/src/main/native/cpp/SourceImpl.cpp +++ b/cscore/src/main/native/cpp/SourceImpl.cpp @@ -14,6 +14,7 @@ #include "Log.hpp" #include "Notifier.hpp" #include "Telemetry.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/StringExtras.hpp" #include "wpi/util/json.hpp" #include "wpi/util/timestamp.hpp" @@ -149,7 +150,7 @@ VideoMode SourceImpl::GetVideoMode(CS_Status* status) const { return m_mode; } -bool SourceImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat, +bool SourceImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) { auto mode = GetVideoMode(status); if (!mode) { @@ -199,21 +200,21 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, try { auto str = config.at("pixel format").get(); if (wpi::util::equals_lower(str, "mjpeg")) { - mode.pixelFormat = wpi::cs::VideoMode::kMJPEG; + mode.pixelFormat = wpi::util::PixelFormat::kMJPEG; } else if (wpi::util::equals_lower(str, "yuyv")) { - mode.pixelFormat = wpi::cs::VideoMode::kYUYV; + mode.pixelFormat = wpi::util::PixelFormat::kYUYV; } else if (wpi::util::equals_lower(str, "rgb565")) { - mode.pixelFormat = wpi::cs::VideoMode::kRGB565; + mode.pixelFormat = wpi::util::PixelFormat::kRGB565; } else if (wpi::util::equals_lower(str, "bgr")) { - mode.pixelFormat = wpi::cs::VideoMode::kBGR; + mode.pixelFormat = wpi::util::PixelFormat::kBGR; } else if (wpi::util::equals_lower(str, "bgra")) { - mode.pixelFormat = wpi::cs::VideoMode::kBGRA; + mode.pixelFormat = wpi::util::PixelFormat::kBGRA; } else if (wpi::util::equals_lower(str, "gray")) { - mode.pixelFormat = wpi::cs::VideoMode::kGray; + mode.pixelFormat = wpi::util::PixelFormat::kGray; } else if (wpi::util::equals_lower(str, "y16")) { - mode.pixelFormat = wpi::cs::VideoMode::kY16; + mode.pixelFormat = wpi::util::PixelFormat::kY16; } else if (wpi::util::equals_lower(str, "uyvy")) { - mode.pixelFormat = wpi::cs::VideoMode::kUYVY; + mode.pixelFormat = wpi::util::PixelFormat::kUYVY; } else { SWARNING("SetConfigJson: could not understand pixel format value '{}'", str); @@ -251,19 +252,18 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config, } // if all of video mode is set, use SetVideoMode, otherwise piecemeal it - if (mode.pixelFormat != VideoMode::kUnknown && mode.width != 0 && + if (mode.pixelFormat != wpi::util::PixelFormat::kUnknown && mode.width != 0 && mode.height != 0 && mode.fps != 0) { SINFO( "SetConfigJson: setting video mode to pixelFormat {}, width {}, height " "{}, fps {}", - mode.pixelFormat, mode.width, mode.height, mode.fps); + static_cast(mode.pixelFormat), mode.width, mode.height, mode.fps); SetVideoMode(mode, status); } else { - if (mode.pixelFormat != wpi::cs::VideoMode::kUnknown) { - SINFO("SetConfigJson: setting pixelFormat {}", mode.pixelFormat); - SetPixelFormat( - static_cast(mode.pixelFormat), - status); + if (mode.pixelFormat != wpi::util::PixelFormat::kUnknown) { + SINFO("SetConfigJson: setting pixelFormat {}", + static_cast(mode.pixelFormat)); + SetPixelFormat(mode.pixelFormat, status); } if (mode.width != 0 && mode.height != 0) { SINFO("SetConfigJson: setting width {}, height {}", mode.width, @@ -362,28 +362,28 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) { // pixel format std::string_view pixelFormat; switch (m_mode.pixelFormat) { - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: pixelFormat = "mjpeg"; break; - case VideoMode::kYUYV: + case wpi::util::PixelFormat::kYUYV: pixelFormat = "yuyv"; break; - case VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: pixelFormat = "rgb565"; break; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: pixelFormat = "bgr"; break; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: pixelFormat = "bgra"; break; - case VideoMode::kGray: + case wpi::util::PixelFormat::kGray: pixelFormat = "gray"; break; - case VideoMode::kY16: + case wpi::util::PixelFormat::kY16: pixelFormat = "y16"; break; - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kUYVY: pixelFormat = "uyvy"; break; default: @@ -429,7 +429,7 @@ std::vector SourceImpl::EnumerateVideoModes( } std::unique_ptr SourceImpl::AllocImage( - VideoMode::PixelFormat pixelFormat, int width, int height, size_t size) { + wpi::util::PixelFormat pixelFormat, int width, int height, size_t size) { std::unique_ptr image; { std::scoped_lock lock{m_poolMutex}; @@ -464,10 +464,10 @@ std::unique_ptr SourceImpl::AllocImage( return image; } -void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width, +void SourceImpl::PutFrame(wpi::util::PixelFormat pixelFormat, int width, int height, std::string_view data, Frame::Time time, WPI_TimestampSource timeSrc) { - if (pixelFormat == VideoMode::PixelFormat::kBGRA) { + if (pixelFormat == wpi::util::PixelFormat::kBGRA) { // Write BGRA as BGR to save a copy auto image = CreateImageFromBGRA(this, width, height, width * 4, diff --git a/cscore/src/main/native/cpp/SourceImpl.hpp b/cscore/src/main/native/cpp/SourceImpl.hpp index b2d64c10ad..13e548390a 100644 --- a/cscore/src/main/native/cpp/SourceImpl.hpp +++ b/cscore/src/main/native/cpp/SourceImpl.hpp @@ -12,11 +12,12 @@ #include #include "Frame.hpp" -#include "Handle.hpp" #include "Image.hpp" #include "PropertyContainer.hpp" -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_c.h" #include "wpi/util/Logger.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/RawFrame.h" #include "wpi/util/condition_variable.hpp" #include "wpi/util/json_fwd.hpp" @@ -119,7 +120,7 @@ class SourceImpl : public PropertyContainer { // These have default implementations but can be overridden for custom // or optimized behavior. - virtual bool SetPixelFormat(VideoMode::PixelFormat pixelFormat, + virtual bool SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status); virtual bool SetResolution(int width, int height, CS_Status* status); virtual bool SetFPS(int fps, CS_Status* status); @@ -131,7 +132,7 @@ class SourceImpl : public PropertyContainer { std::vector EnumerateVideoModes(CS_Status* status) const; - std::unique_ptr AllocImage(VideoMode::PixelFormat pixelFormat, + std::unique_ptr AllocImage(wpi::util::PixelFormat pixelFormat, int width, int height, size_t size); protected: @@ -139,7 +140,7 @@ class SourceImpl : public PropertyContainer { void UpdatePropertyValue(int property, bool setString, int value, std::string_view valueStr) override; - void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height, + void PutFrame(wpi::util::PixelFormat pixelFormat, int width, int height, std::string_view data, Frame::Time time, WPI_TimestampSource timeSrc = WPI_TIMESRC_FRAME_DEQUEUE); void PutFrame(std::unique_ptr image, Frame::Time time, diff --git a/cscore/src/main/native/cpp/Telemetry.cpp b/cscore/src/main/native/cpp/Telemetry.cpp index a52720b097..cf02d46c22 100644 --- a/cscore/src/main/native/cpp/Telemetry.cpp +++ b/cscore/src/main/native/cpp/Telemetry.cpp @@ -12,7 +12,6 @@ #include "Notifier.hpp" #include "SourceImpl.hpp" #include "wpi/util/DenseMap.hpp" -#include "wpi/util/timestamp.h" using namespace wpi::cs; diff --git a/cscore/src/main/native/cpp/Telemetry.hpp b/cscore/src/main/native/cpp/Telemetry.hpp index a6b5e24c41..e200b1945c 100644 --- a/cscore/src/main/native/cpp/Telemetry.hpp +++ b/cscore/src/main/native/cpp/Telemetry.hpp @@ -4,7 +4,7 @@ #pragma once -#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/cscore_c.h" #include "wpi/util/SafeThread.hpp" namespace wpi::cs { diff --git a/cscore/src/main/native/cpp/VideoSink.cpp b/cscore/src/main/native/cpp/VideoSink.cpp new file mode 100644 index 0000000000..946a304b73 --- /dev/null +++ b/cscore/src/main/native/cpp/VideoSink.cpp @@ -0,0 +1,44 @@ +// 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 "wpi/cs/VideoSink.hpp" + +#include + +#include + +#include "wpi/util/json.hpp" + +using namespace wpi::cs; + +wpi::util::json VideoSink::GetConfigJsonObject() const { + m_status = 0; + return GetSinkConfigJsonObject(m_handle, &m_status); +} + +std::vector VideoSink::EnumerateProperties() const { + wpi::util::SmallVector handles_buf; + CS_Status status = 0; + auto handles = EnumerateSinkProperties(m_handle, handles_buf, &status); + + std::vector properties; + properties.reserve(handles.size()); + for (CS_Property handle : handles) { + properties.emplace_back(VideoProperty{handle}); + } + return properties; +} + +std::vector VideoSink::EnumerateSinks() { + wpi::util::SmallVector handles_buf; + CS_Status status = 0; + auto handles = ::wpi::cs::EnumerateSinkHandles(handles_buf, &status); + + std::vector sinks; + sinks.reserve(handles.size()); + for (int handle : handles) { + sinks.emplace_back(VideoSink{handle}); + } + return sinks; +} diff --git a/cscore/src/main/native/cpp/cscore_oo.cpp b/cscore/src/main/native/cpp/VideoSource.cpp similarity index 62% rename from cscore/src/main/native/cpp/cscore_oo.cpp rename to cscore/src/main/native/cpp/VideoSource.cpp index 04e464482b..e526352743 100644 --- a/cscore/src/main/native/cpp/cscore_oo.cpp +++ b/cscore/src/main/native/cpp/VideoSource.cpp @@ -2,13 +2,13 @@ // 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 "wpi/cs/cscore_oo.hpp" +#include "wpi/cs/VideoSource.hpp" -#include #include #include +#include "wpi/cs/VideoSink.hpp" #include "wpi/util/json.hpp" using namespace wpi::cs; @@ -18,11 +18,6 @@ wpi::util::json VideoSource::GetConfigJsonObject() const { return GetSourceConfigJsonObject(m_handle, &m_status); } -wpi::util::json VideoSink::GetConfigJsonObject() const { - m_status = 0; - return GetSinkConfigJsonObject(m_handle, &m_status); -} - std::vector VideoSource::EnumerateProperties() const { wpi::util::SmallVector handles_buf; CS_Status status = 0; @@ -61,29 +56,3 @@ std::vector VideoSource::EnumerateSources() { } return sources; } - -std::vector VideoSink::EnumerateProperties() const { - wpi::util::SmallVector handles_buf; - CS_Status status = 0; - auto handles = EnumerateSinkProperties(m_handle, handles_buf, &status); - - std::vector properties; - properties.reserve(handles.size()); - for (CS_Property handle : handles) { - properties.emplace_back(VideoProperty{handle}); - } - return properties; -} - -std::vector VideoSink::EnumerateSinks() { - wpi::util::SmallVector handles_buf; - CS_Status status = 0; - auto handles = ::wpi::cs::EnumerateSinkHandles(handles_buf, &status); - - std::vector sinks; - sinks.reserve(handles.size()); - for (int handle : handles) { - sinks.emplace_back(VideoSink{handle}); - } - return sinks; -} diff --git a/cscore/src/main/native/cpp/cscore_c.cpp b/cscore/src/main/native/cpp/cscore_c.cpp index 5e9c34ada0..382cb82f8d 100644 --- a/cscore/src/main/native/cpp/cscore_c.cpp +++ b/cscore/src/main/native/cpp/cscore_c.cpp @@ -13,6 +13,7 @@ #include "c_util.hpp" #include "wpi/cs/cscore_cpp.hpp" #include "wpi/util/MemAlloc.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallString.hpp" #include "wpi/util/string.hpp" @@ -178,9 +179,9 @@ CS_Bool CS_SetSourceVideoModeDiscrete(CS_Source source, CS_Status* status) { return wpi::cs::SetSourceVideoMode( source, - wpi::cs::VideoMode{static_cast( - static_cast(pixelFormat)), - width, height, fps}, + wpi::cs::VideoMode{ + static_cast(static_cast(pixelFormat)), + width, height, fps}, status); } @@ -189,8 +190,7 @@ CS_Bool CS_SetSourcePixelFormat(CS_Source source, CS_Status* status) { return wpi::cs::SetSourcePixelFormat( source, - static_cast( - static_cast(pixelFormat)), + static_cast(static_cast(pixelFormat)), status); } diff --git a/cscore/src/main/native/cpp/cscore_cpp.cpp b/cscore/src/main/native/cpp/cscore_cpp.cpp index 9dc6d45631..23ae7cc8cc 100644 --- a/cscore/src/main/native/cpp/cscore_cpp.cpp +++ b/cscore/src/main/native/cpp/cscore_cpp.cpp @@ -17,6 +17,7 @@ #include "SourceImpl.hpp" #include "Telemetry.hpp" #include "wpi/net/hostname.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallString.hpp" #include "wpi/util/json.hpp" @@ -323,7 +324,7 @@ bool SetSourceVideoMode(CS_Source source, const VideoMode& mode, return data->source->SetVideoMode(mode, status); } -bool SetSourcePixelFormat(CS_Source source, VideoMode::PixelFormat pixelFormat, +bool SetSourcePixelFormat(CS_Source source, wpi::util::PixelFormat pixelFormat, CS_Status* status) { auto data = Instance::GetInstance().GetSource(source); if (!data) { diff --git a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp index 347da052fd..cef3811d0c 100644 --- a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp +++ b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp @@ -10,9 +10,10 @@ #define WPI_RAWFRAME_JNI #include "org_wpilib_vision_camera_CameraServerJNI.h" -#include "wpi/cs/cscore_raw.h" +#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/cs/cscore_raw.hpp" #include "wpi/cs/cscore_runloop.hpp" -#include "wpi/util/RawFrame.h" +#include "wpi/util/RawFrame.hpp" #include "wpi/util/SmallString.hpp" #include "wpi/util/jni_util.hpp" @@ -583,10 +584,9 @@ Java_org_wpilib_vision_camera_CameraServerJNI_createRawSource CS_Status status = 0; auto val = wpi::cs::CreateRawSource( JStringRef{env, name}.str(), isCv, - wpi::cs::VideoMode{ - static_cast(pixelFormat), - static_cast(width), static_cast(height), - static_cast(fps)}, + wpi::cs::VideoMode{static_cast(pixelFormat), + static_cast(width), static_cast(height), + static_cast(fps)}, &status); CheckStatus(env, status); return val; @@ -771,9 +771,8 @@ Java_org_wpilib_vision_camera_CameraServerJNI_setSourceVideoMode CS_Status status = 0; auto val = wpi::cs::SetSourceVideoMode( source, - wpi::cs::VideoMode( - static_cast(pixelFormat), width, - height, fps), + wpi::cs::VideoMode(static_cast(pixelFormat), + width, height, fps), &status); CheckStatus(env, status); return val; @@ -790,8 +789,7 @@ Java_org_wpilib_vision_camera_CameraServerJNI_setSourcePixelFormat { CS_Status status = 0; auto val = wpi::cs::SetSourcePixelFormat( - source, static_cast(pixelFormat), - &status); + source, static_cast(pixelFormat), &status); CheckStatus(env, status); return val; } diff --git a/cscore/src/main/native/include/wpi/cs/CvSink.hpp b/cscore/src/main/native/include/wpi/cs/CvSink.hpp new file mode 100644 index 0000000000..216d0a044f --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/CvSink.hpp @@ -0,0 +1,229 @@ +// 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. + +#pragma once + +#include + +#include + +#include + +#include "wpi/cs/ImageSink.hpp" +#include "wpi/cs/cscore_raw.hpp" +#include "wpi/util/PixelFormat.hpp" +#include "wpi/util/RawFrame.hpp" + +namespace wpi::cs { + +/** + * A sink for user code to accept video frames as OpenCV images. + * + * This is not dependent on any opencv binary ABI, and can be used + * with versions of most versions of OpenCV. + */ +class CvSink : public ImageSink { + public: + CvSink() = default; + CvSink(const CvSink& sink) : ImageSink{sink}, pixelFormat{sink.pixelFormat} {} + + /** + * Create a sink for accepting OpenCV images. + * + *

WaitForFrame() must be called on the created sink to get each new + * image. + * + * @param name Source name (arbitrary unique identifier) + * @param pixelFormat The pixel format to read + */ + explicit CvSink(std::string_view name, wpi::util::PixelFormat pixelFormat = + wpi::util::PixelFormat::kBGR) { + m_handle = CreateRawSink(name, true, &m_status); + this->pixelFormat = pixelFormat; + } + + /** + * Wait for the next frame and get the image. + * Times out (returning 0) after timeout seconds. + * The provided image will have the pixelFormat this class was constructed + * with. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225) { + cv::Mat tmpnam; + auto retVal = GrabFrameDirect(tmpnam); + if (retVal <= 0) { + return retVal; + } + tmpnam.copyTo(image); + return retVal; + } + + /** + * Wait for the next frame and get the image. May block forever. + * The provided image will have the pixelFormat this class was constructed + * with. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameNoTimeout(cv::Mat& image) { + cv::Mat tmpnam; + auto retVal = GrabFrameNoTimeoutDirect(tmpnam); + if (retVal <= 0) { + return retVal; + } + tmpnam.copyTo(image); + return retVal; + } + + /** + * Wait for the next frame and get the image. + * Times out (returning 0) after timeout seconds. + * The provided image will have the pixelFormat this class was constructed + * with. The data is backed by data in the CvSink. It will be invalidated by + * any grabFrame*() call on the sink. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225) { + rawFrame.height = 0; + rawFrame.width = 0; + rawFrame.stride = 0; + rawFrame.pixelFormat = static_cast(pixelFormat); + auto timestamp = + GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status); + if (m_status != CS_OK) { + return 0; + } + image = + cv::Mat{rawFrame.height, rawFrame.width, + GetCvFormat(static_cast(rawFrame.pixelFormat)), + rawFrame.data, static_cast(rawFrame.stride)}; + return timestamp; + } + + /** + * Wait for the next frame and get the image. May block forever. + * The provided image will have the pixelFormat this class was constructed + * with. The data is backed by data in the CvSink. It will be invalidated by + * any grabFrame*() call on the sink. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image) { + rawFrame.height = 0; + rawFrame.width = 0; + rawFrame.stride = 0; + rawFrame.pixelFormat = static_cast(pixelFormat); + auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status); + if (m_status != CS_OK) { + return 0; + } + image = + cv::Mat{rawFrame.height, rawFrame.width, + GetCvFormat(static_cast(rawFrame.pixelFormat)), + rawFrame.data, static_cast(rawFrame.stride)}; + return timestamp; + } + + /** + * Wait for the next frame and get the image. + * Times out (returning 0) after timeout seconds. + * The provided image will have the pixelFormat this class was constructed + * with. The data is backed by data in the CvSink. It will be invalidated by + * any grabFrame*() call on the sink. + * + *

If lastFrameTime is provided and non-zero, the sink will fill image with + * the first frame from the source that is not equal to lastFrameTime. If + * lastFrameTime is zero, the time of the current frame owned by the CvSource + * is used, and this function will block until the connected CvSource provides + * a new frame. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime, + double timeout = 0.225) { + rawFrame.height = 0; + rawFrame.width = 0; + rawFrame.stride = 0; + rawFrame.pixelFormat = static_cast(pixelFormat); + auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout, + lastFrameTime, &m_status); + if (m_status != CS_OK) { + return 0; + } + image = + cv::Mat{rawFrame.height, rawFrame.width, + GetCvFormat(static_cast(rawFrame.pixelFormat)), + rawFrame.data, static_cast(rawFrame.stride)}; + return timestamp; + } + + /** + * Get the last time a frame was grabbed. This uses the same time base as + * wpi::util::Now(). + * + * @return Time in 1 us increments. + */ + [[nodiscard]] + uint64_t LastFrameTime() { + return rawFrame.timestamp; + } + + /** + * Get the time source for the timestamp the last frame was grabbed at. + * + * @return Time source + */ + [[nodiscard]] + WPI_TimestampSource LastFrameTimeSource() { + return static_cast(rawFrame.timestampSrc); + } + + private: + constexpr int GetCvFormat(WPI_PixelFormat pixelFormat) { + int type = 0; + switch (pixelFormat) { + case WPI_PIXFMT_YUYV: + case WPI_PIXFMT_RGB565: + case WPI_PIXFMT_Y16: + case WPI_PIXFMT_UYVY: + type = CV_8UC2; + break; + case WPI_PIXFMT_BGR: + type = CV_8UC3; + break; + case WPI_PIXFMT_BGRA: + type = CV_8UC4; + break; + case WPI_PIXFMT_GRAY: + case WPI_PIXFMT_MJPEG: + default: + type = CV_8UC1; + break; + } + return type; + } + + wpi::util::RawFrame rawFrame; + wpi::util::PixelFormat pixelFormat; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/CvSource.hpp b/cscore/src/main/native/include/wpi/cs/CvSource.hpp new file mode 100644 index 0000000000..448dcc3611 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/CvSource.hpp @@ -0,0 +1,188 @@ +// 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. + +#pragma once + +#include + +#include + +#include "wpi/cs/ImageSource.hpp" +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_raw.hpp" + +namespace wpi::cs { + +/** + * A source for user code to provide OpenCV images as video frames. + * + * This is not dependent on any opencv binary ABI, and can be used + * with versions of most versions of OpenCV. + */ +class CvSource : public ImageSource { + public: + CvSource() = default; + + /** + * Create an OpenCV source. + * + * @param name Source name (arbitrary unique identifier) + * @param mode Video mode being generated + */ + CvSource(std::string_view name, const VideoMode& mode) { + m_handle = CreateRawSource(name, true, mode, &m_status); + } + + /** + * Create an OpenCV source. + * + * @param name Source name (arbitrary unique identifier) + * @param pixelFormat Pixel format + * @param width width + * @param height height + * @param fps fps + */ + CvSource(std::string_view name, wpi::util::PixelFormat pixelFormat, int width, + int height, int fps) { + m_handle = CreateRawSource( + name, true, VideoMode{pixelFormat, width, height, fps}, &m_status); + } + + /** + * Put an OpenCV image and notify sinks + * + *

+ * The image format is guessed from the number of channels. The channel + * mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel + * numbers will throw an error. If your image is an in alternate format, use + * the overload that takes a PixelFormat. + * + * @param image OpenCV Image + */ + void PutFrame(cv::Mat& image) { + // We only support 8-bit images; convert if necessary. + cv::Mat finalImage; + if (image.depth() == CV_8U) { + finalImage = image; + } else { + image.convertTo(finalImage, CV_8U); + } + + int channels = finalImage.channels(); + wpi::util::PixelFormat format; + if (channels == 1) { + // 1 channel is assumed Grayscale + format = wpi::util::PixelFormat::kGray; + } else if (channels == 2) { + // 2 channels is assumed YUYV + format = wpi::util::PixelFormat::kYUYV; + } else if (channels == 3) { + // 3 channels is assumed BGR + format = wpi::util::PixelFormat::kBGR; + } else if (channels == 4) { + // 4 channels is assumed BGRA + format = wpi::util::PixelFormat::kBGRA; + } else { + // TODO Error + return; + } + + PutFrame(finalImage, format, true); + } + + /** + * Put an OpenCV image and notify sinks. + * + *

+ * The format of the Mat must match the PixelFormat. You will corrupt memory + * if they dont. With skipVerification false, we will verify the number of + * channels matches the pixel format. If skipVerification is true, this step + * is skipped and is passed straight through. + * + * @param image OpenCV image + * @param pixelFormat The pixel format of the image + * @param skipVerification skip verifying pixel format + */ + void PutFrame(cv::Mat& image, wpi::util::PixelFormat pixelFormat, + bool skipVerification) { + // We only support 8-bit images; convert if necessary. + cv::Mat finalImage; + if (image.depth() == CV_8U) { + finalImage = image; + } else { + image.convertTo(finalImage, CV_8U); + } + + if (!skipVerification) { + if (!VerifyFormat(finalImage, pixelFormat)) { + // TODO Error + return; + } + } + + WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor + frame.data = finalImage.data; + frame.freeFunc = nullptr; + frame.freeCbData = nullptr; + frame.size = finalImage.total() * finalImage.channels(); + frame.width = finalImage.cols; + frame.height = finalImage.rows; + frame.stride = finalImage.step; + frame.pixelFormat = static_cast(pixelFormat); + m_status = 0; + PutSourceFrame(m_handle, frame, &m_status); + } + + private: + static bool VerifyFormat(cv::Mat& image, wpi::util::PixelFormat pixelFormat) { + int channels = image.channels(); + switch (pixelFormat) { + case wpi::util::PixelFormat::kBGR: + if (channels == 3) { + return true; + } + break; + case wpi::util::PixelFormat::kBGRA: + if (channels == 4) { + return true; + } + break; + case wpi::util::PixelFormat::kGray: + if (channels == 1) { + return true; + } + break; + case wpi::util::PixelFormat::kRGB565: + if (channels == 2) { + return true; + } + break; + case wpi::util::PixelFormat::kUYVY: + if (channels == 2) { + return true; + } + break; + case wpi::util::PixelFormat::kY16: + if (channels == 2) { + return true; + } + break; + case wpi::util::PixelFormat::kYUYV: + if (channels == 2) { + return true; + } + break; + case wpi::util::PixelFormat::kMJPEG: + if (channels == 1) { + return true; + } + break; + default: + break; + } + return false; + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/HttpCamera.hpp b/cscore/src/main/native/include/wpi/cs/HttpCamera.hpp new file mode 100644 index 0000000000..fa1e2a2530 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/HttpCamera.hpp @@ -0,0 +1,151 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +#include "wpi/cs/VideoCamera.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A source that represents a MJPEG-over-HTTP (IP) camera. + */ +class HttpCamera : public VideoCamera { + public: + /** + * HTTP camera kind. + */ + enum HttpCameraKind { + /// Unknown camera kind. + kUnknown = CS_HTTP_UNKNOWN, + /// MJPG Streamer camera. + kMJPGStreamer = CS_HTTP_MJPGSTREAMER, + /// CS Core camera. + kCSCore = CS_HTTP_CSCORE, + }; + + /** + * Create a source for a MJPEG-over-HTTP (IP) camera. + * + * @param name Source name (arbitrary unique identifier) + * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") + * @param kind Camera kind (e.g. kAxis) + */ + HttpCamera(std::string_view name, std::string_view url, + HttpCameraKind kind = kUnknown) { + m_handle = CreateHttpCamera( + name, url, static_cast(static_cast(kind)), + &m_status); + } + + /** + * Create a source for a MJPEG-over-HTTP (IP) camera. + * + * @param name Source name (arbitrary unique identifier) + * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") + * @param kind Camera kind (e.g. kAxis) + */ + HttpCamera(std::string_view name, const char* url, + HttpCameraKind kind = kUnknown) { + m_handle = CreateHttpCamera( + name, url, static_cast(static_cast(kind)), + &m_status); + } + + /** + * Create a source for a MJPEG-over-HTTP (IP) camera. + * + * @param name Source name (arbitrary unique identifier) + * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") + * @param kind Camera kind (e.g. kAxis) + */ + HttpCamera(std::string_view name, const std::string& url, + HttpCameraKind kind = kUnknown) + : HttpCamera(name, std::string_view{url}, kind) {} + + /** + * Create a source for a MJPEG-over-HTTP (IP) camera. + * + * @param name Source name (arbitrary unique identifier) + * @param urls Array of Camera URLs + * @param kind Camera kind (e.g. kAxis) + */ + HttpCamera(std::string_view name, std::span urls, + HttpCameraKind kind = kUnknown) { + m_handle = CreateHttpCamera( + name, urls, static_cast(static_cast(kind)), + &m_status); + } + + /** + * Create a source for a MJPEG-over-HTTP (IP) camera. + * + * @param name Source name (arbitrary unique identifier) + * @param urls Array of Camera URLs + * @param kind Camera kind (e.g. kAxis) + */ + template + HttpCamera(std::string_view name, std::initializer_list urls, + HttpCameraKind kind = kUnknown) { + std::vector vec; + vec.reserve(urls.size()); + for (const auto& url : urls) { + vec.emplace_back(url); + } + m_handle = CreateHttpCamera( + name, vec, static_cast(static_cast(kind)), + &m_status); + } + + /** + * Get the kind of HTTP camera. + * + *

Autodetection can result in returning a different value than the camera + * was created with. + */ + HttpCameraKind GetHttpCameraKind() const { + m_status = 0; + return static_cast( + static_cast(::wpi::cs::GetHttpCameraKind(m_handle, &m_status))); + } + + /** + * Change the URLs used to connect to the camera. + */ + void SetUrls(std::span urls) { + m_status = 0; + ::wpi::cs::SetHttpCameraUrls(m_handle, urls, &m_status); + } + + /** + * Change the URLs used to connect to the camera. + */ + template + void SetUrls(std::initializer_list urls) { + std::vector vec; + vec.reserve(urls.size()); + for (const auto& url : urls) { + vec.emplace_back(url); + } + m_status = 0; + ::wpi::cs::SetHttpCameraUrls(m_handle, vec, &m_status); + } + + /** + * Get the URLs used to connect to the camera. + */ + std::vector GetUrls() const { + m_status = 0; + return ::wpi::cs::GetHttpCameraUrls(m_handle, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/ImageSink.hpp b/cscore/src/main/native/include/wpi/cs/ImageSink.hpp new file mode 100644 index 0000000000..cbf10afe79 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/ImageSink.hpp @@ -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. + +#pragma once + +#include +#include + +#include "wpi/cs/VideoSink.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A base class for single image reading sinks. + */ +class ImageSink : public VideoSink { + protected: + ImageSink() = default; + + public: + /** + * Set sink description. + * + * @param description Description + */ + void SetDescription(std::string_view description) { + m_status = 0; + SetSinkDescription(m_handle, description, &m_status); + } + + /** + * Get error string. Call this if WaitForFrame() returns 0 to determine + * what the error is. + */ + std::string GetError() const { + m_status = 0; + return GetSinkError(m_handle, &m_status); + } + + /** + * Enable or disable getting new frames. + * + *

Disabling will cause processFrame (for callback-based CvSinks) to not + * be called and WaitForFrame() to not return. This can be used to save + * processor resources when frames are not needed. + */ + void SetEnabled(bool enabled) { + m_status = 0; + SetSinkEnabled(m_handle, enabled, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/ImageSource.hpp b/cscore/src/main/native/include/wpi/cs/ImageSource.hpp new file mode 100644 index 0000000000..7e34747a43 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/ImageSource.hpp @@ -0,0 +1,170 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include + +#include "wpi/cs/VideoProperty.hpp" +#include "wpi/cs/VideoSource.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A base class for single image providing sources. + */ +class ImageSource : public VideoSource { + protected: + ImageSource() = default; + + public: + /** + * Signal sinks that an error has occurred. This should be called instead + * of NotifyFrame when an error occurs. + * + * @param msg Notification message. + */ + void NotifyError(std::string_view msg) { + m_status = 0; + NotifySourceError(m_handle, msg, &m_status); + } + + /** + * Set source connection status. Defaults to true. + * + * @param connected True for connected, false for disconnected + */ + void SetConnected(bool connected) { + m_status = 0; + SetSourceConnected(m_handle, connected, &m_status); + } + + /** + * Set source description. + * + * @param description Description + */ + void SetDescription(std::string_view description) { + m_status = 0; + SetSourceDescription(m_handle, description, &m_status); + } + + /** + * Create a property. + * + * @param name Property name + * @param kind Property kind + * @param minimum Minimum value + * @param maximum Maximum value + * @param step Step value + * @param defaultValue Default value + * @param value Current value + * @return Property + */ + VideoProperty CreateProperty(std::string_view name, VideoProperty::Kind kind, + int minimum, int maximum, int step, + int defaultValue, int value) { + m_status = 0; + return VideoProperty{CreateSourceProperty( + m_handle, name, static_cast(static_cast(kind)), + minimum, maximum, step, defaultValue, value, &m_status)}; + } + + /** + * Create an integer property. + * + * @param name Property name + * @param minimum Minimum value + * @param maximum Maximum value + * @param step Step value + * @param defaultValue Default value + * @param value Current value + * @return Property + */ + VideoProperty CreateIntegerProperty(std::string_view name, int minimum, + int maximum, int step, int defaultValue, + int value) { + m_status = 0; + return VideoProperty{CreateSourceProperty( + m_handle, name, + static_cast( + static_cast(VideoProperty::Kind::kInteger)), + minimum, maximum, step, defaultValue, value, &m_status)}; + } + + /** + * Create a boolean property. + * + * @param name Property name + * @param defaultValue Default value + * @param value Current value + * @return Property + */ + VideoProperty CreateBooleanProperty(std::string_view name, bool defaultValue, + bool value) { + m_status = 0; + return VideoProperty{CreateSourceProperty( + m_handle, name, + static_cast( + static_cast(VideoProperty::Kind::kBoolean)), + 0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)}; + } + + /** + * Create a string property. + * + * @param name Property name + * @param value Current value + * @return Property + */ + VideoProperty CreateStringProperty(std::string_view name, + std::string_view value) { + m_status = 0; + auto prop = VideoProperty{CreateSourceProperty( + m_handle, name, + static_cast( + static_cast(VideoProperty::Kind::kString)), + 0, 0, 0, 0, 0, &m_status)}; + prop.SetString(value); + return prop; + } + + /** + * Configure enum property choices. + * + * @param property Property + * @param choices Choices + */ + void SetEnumPropertyChoices(const VideoProperty& property, + std::span choices) { + m_status = 0; + SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices, + &m_status); + } + + /** + * Configure enum property choices. + * + * @param property Property + * @param choices Choices + */ + template + void SetEnumPropertyChoices(const VideoProperty& property, + std::initializer_list choices) { + std::vector vec; + vec.reserve(choices.size()); + for (const auto& choice : choices) { + vec.emplace_back(choice); + } + m_status = 0; + SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/MjpegServer.hpp b/cscore/src/main/native/include/wpi/cs/MjpegServer.hpp new file mode 100644 index 0000000000..c9556d2158 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/MjpegServer.hpp @@ -0,0 +1,120 @@ +// 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. + +#pragma once + +#include +#include + +#include "wpi/cs/VideoSink.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A sink that acts as a MJPEG-over-HTTP network server. + */ +class MjpegServer : public VideoSink { + public: + MjpegServer() = default; + + /** + * Create a MJPEG-over-HTTP server sink. + * + * @param name Sink name (arbitrary unique identifier) + * @param listenAddress TCP listen address (empty string for all addresses) + * @param port TCP port number + */ + MjpegServer(std::string_view name, std::string_view listenAddress, int port) { + m_handle = CreateMjpegServer(name, listenAddress, port, &m_status); + } + + /** + * Create a MJPEG-over-HTTP server sink. + * + * @param name Sink name (arbitrary unique identifier) + * @param port TCP port number + */ + MjpegServer(std::string_view name, int port) : MjpegServer(name, "", port) {} + + /** + * Get the listen address of the server. + */ + std::string GetListenAddress() const { + m_status = 0; + return wpi::cs::GetMjpegServerListenAddress(m_handle, &m_status); + } + + /** + * Get the port number of the server. + */ + int GetPort() const { + m_status = 0; + return wpi::cs::GetMjpegServerPort(m_handle, &m_status); + } + + /** + * Set the stream resolution for clients that don't specify it. + * + *

It is not necessary to set this if it is the same as the source + * resolution. + * + *

Setting this different than the source resolution will result in + * increased CPU usage, particularly for MJPEG source cameras, as it will + * decompress, resize, and recompress the image, instead of using the + * camera's MJPEG image directly. + * + * @param width width, 0 for unspecified + * @param height height, 0 for unspecified + */ + void SetResolution(int width, int height) { + m_status = 0; + SetProperty(GetSinkProperty(m_handle, "width", &m_status), width, + &m_status); + SetProperty(GetSinkProperty(m_handle, "height", &m_status), height, + &m_status); + } + + /** + * Set the stream frames per second (FPS) for clients that don't specify it. + * + *

It is not necessary to set this if it is the same as the source FPS. + * + * @param fps FPS, 0 for unspecified + */ + void SetFPS(int fps) { + m_status = 0; + SetProperty(GetSinkProperty(m_handle, "fps", &m_status), fps, &m_status); + } + + /** + * Set the compression for clients that don't specify it. + * + *

Setting this will result in increased CPU usage for MJPEG source cameras + * as it will decompress and recompress the image instead of using the + * camera's MJPEG image directly. + * + * @param quality JPEG compression quality (0-100), -1 for unspecified + */ + void SetCompression(int quality) { + m_status = 0; + SetProperty(GetSinkProperty(m_handle, "compression", &m_status), quality, + &m_status); + } + + /** + * Set the default compression used for non-MJPEG sources. If not set, + * 80 is used. This function has no effect on MJPEG source cameras; use + * SetCompression() instead to force recompression of MJPEG source images. + * + * @param quality JPEG compression quality (0-100) + */ + void SetDefaultCompression(int quality) { + m_status = 0; + SetProperty(GetSinkProperty(m_handle, "default_compression", &m_status), + quality, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/RawEvent.hpp b/cscore/src/main/native/include/wpi/cs/RawEvent.hpp new file mode 100644 index 0000000000..b8e31b901b --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/RawEvent.hpp @@ -0,0 +1,91 @@ +// 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. + +#pragma once + +#include +#include + +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_c.h" + +namespace wpi::cs { + +/** + * Listener event + */ +struct RawEvent { + enum Kind { + kSourceCreated = CS_SOURCE_CREATED, + kSourceDestroyed = CS_SOURCE_DESTROYED, + kSourceConnected = CS_SOURCE_CONNECTED, + kSourceDisconnected = CS_SOURCE_DISCONNECTED, + kSourceVideoModesUpdated = CS_SOURCE_VIDEOMODES_UPDATED, + kSourceVideoModeChanged = CS_SOURCE_VIDEOMODE_CHANGED, + kSourcePropertyCreated = CS_SOURCE_PROPERTY_CREATED, + kSourcePropertyValueUpdated = CS_SOURCE_PROPERTY_VALUE_UPDATED, + kSourcePropertyChoicesUpdated = CS_SOURCE_PROPERTY_CHOICES_UPDATED, + kSinkSourceChanged = CS_SINK_SOURCE_CHANGED, + kSinkCreated = CS_SINK_CREATED, + kSinkDestroyed = CS_SINK_DESTROYED, + kSinkEnabled = CS_SINK_ENABLED, + kSinkDisabled = CS_SINK_DISABLED, + kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED, + kTelemetryUpdated = CS_TELEMETRY_UPDATED, + kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED, + kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED, + kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED, + kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED + }; + + RawEvent() = default; + explicit RawEvent(RawEvent::Kind kind_) : kind{kind_} {} + RawEvent(std::string_view name_, CS_Handle handle_, RawEvent::Kind kind_) + : kind{kind_}, name{name_} { + if (kind_ == kSinkCreated || kind_ == kSinkDestroyed || + kind_ == kSinkEnabled || kind_ == kSinkDisabled) { + sinkHandle = handle_; + } else { + sourceHandle = handle_; + } + } + RawEvent(std::string_view name_, CS_Source source_, const VideoMode& mode_) + : kind{kSourceVideoModeChanged}, + sourceHandle{source_}, + name{name_}, + mode{mode_} {} + RawEvent(std::string_view name_, CS_Source source_, RawEvent::Kind kind_, + CS_Property property_, CS_PropertyKind propertyKind_, int value_, + std::string_view valueStr_) + : kind{kind_}, + sourceHandle{source_}, + name{name_}, + propertyHandle{property_}, + propertyKind{propertyKind_}, + value{value_}, + valueStr{valueStr_} {} + + Kind kind; + + // Valid for kSource* and kSink* respectively + CS_Source sourceHandle = CS_INVALID_HANDLE; + CS_Sink sinkHandle = CS_INVALID_HANDLE; + + // Source/sink/property name + std::string name; + + // Fields for kSourceVideoModeChanged event + VideoMode mode; + + // Fields for kSourceProperty* events + CS_Property propertyHandle; + CS_PropertyKind propertyKind; + int value; + std::string valueStr; + + // Listener that was triggered + CS_Listener listener{0}; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/RawSink.hpp b/cscore/src/main/native/include/wpi/cs/RawSink.hpp new file mode 100644 index 0000000000..dfda97e8d7 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/RawSink.hpp @@ -0,0 +1,107 @@ +// 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. + +#pragma once + +#include +#include + +#include "wpi/cs/ImageSink.hpp" +#include "wpi/cs/cscore_raw.hpp" +#include "wpi/util/RawFrame.hpp" + +namespace wpi::cs { + +/** + * A sink for user code to accept video frames as raw bytes. + * + * This is a complex API, most cases should use CvSource. + */ +class RawSink : public ImageSink { + public: + RawSink() = default; + + /** + * Create a sink for accepting raw images. + * + *

GrabFrame() must be called on the created sink to get each new + * image. + * + * @param name Source name (arbitrary unique identifier) + */ + explicit RawSink(std::string_view name) { + m_handle = CreateRawSink(name, false, &m_status); + } + + /** + * Create a sink for accepting raws images in a separate thread. + * + *

A thread will be created that calls WaitForFrame() and calls the + * processFrame() callback each time a new frame arrives. + * + * @param name Source name (arbitrary unique identifier) + * @param processFrame Frame processing function; will be called with a + * time=0 if an error occurred. processFrame should call GetImage() + * or GetError() as needed, but should not call (except in very + * unusual circumstances) WaitForImage(). + */ + RawSink(std::string_view name, + std::function processFrame) { + m_handle = CreateRawSinkCallback(name, false, processFrame, &m_status); + } + + protected: + /** + * Wait for the next frame and get the image. + * Times out (returning 0) after timeout seconds. + * The provided image will have three 8-bit channels stored in BGR order. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrame(wpi::util::RawFrame& image, double timeout = 0.225) const { + m_status = 0; + return GrabSinkFrameTimeout(m_handle, image, timeout, &m_status); + } + + /** + * Wait for the next frame and get the image. May block forever. + * The provided image will have three 8-bit channels stored in BGR order. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameNoTimeout(wpi::util::RawFrame& image) const { + m_status = 0; + return GrabSinkFrame(m_handle, image, &m_status); + } + + /** + * Wait for the next frame and get the image. May block forever. + * The provided image will have three 8-bit channels stored in BGR order. + * + *

If lastFrameTime is provided and non-zero, the sink will fill image with + * the first frame from the source that is not equal to lastFrameTime. If + * lastFrameTime is zero, the time of the current frame owned by the CvSource + * is used, and this function will block until the connected CvSource provides + * a new frame. + * + * @return Frame time, or 0 on error (call GetError() to obtain the error + * message); the frame time is in the same time base as + * wpi::util::Now(), and is in 1 us increments. + */ + [[nodiscard]] + uint64_t GrabFrameLastTime(wpi::util::RawFrame& image, uint64_t lastFrameTime, + double timeout = 0.225) const { + m_status = 0; + return GrabSinkFrameTimeoutLastTime(m_handle, image, timeout, lastFrameTime, + &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/RawSource.hpp b/cscore/src/main/native/include/wpi/cs/RawSource.hpp new file mode 100644 index 0000000000..4efcf7953e --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/RawSource.hpp @@ -0,0 +1,62 @@ +// 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. + +#pragma once + +#include + +#include "wpi/cs/ImageSource.hpp" +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_raw.hpp" +#include "wpi/util/RawFrame.hpp" + +namespace wpi::cs { + +/** + * A source for user code to provide video frames as raw bytes. + * + * This is a complex API, most cases should use CvSource. + */ +class RawSource : public ImageSource { + public: + RawSource() = default; + + /** + * Create a raw frame source. + * + * @param name Source name (arbitrary unique identifier) + * @param mode Video mode being generated + */ + RawSource(std::string_view name, const VideoMode& mode) { + m_handle = CreateRawSource(name, false, mode, &m_status); + } + + /** + * Create a raw frame source. + * + * @param name Source name (arbitrary unique identifier) + * @param pixelFormat Pixel format + * @param width width + * @param height height + * @param fps fps + */ + RawSource(std::string_view name, VideoMode::PixelFormat pixelFormat, + int width, int height, int fps) { + m_handle = CreateRawSource( + name, false, VideoMode{pixelFormat, width, height, fps}, &m_status); + } + + protected: + /** + * Put a raw image and notify sinks. + * + * @param image raw frame image + */ + void PutFrame(wpi::util::RawFrame& image) { + m_status = 0; + PutSourceFrame(m_handle, image, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/UsbCamera.hpp b/cscore/src/main/native/include/wpi/cs/UsbCamera.hpp new file mode 100644 index 0000000000..f36c263266 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/UsbCamera.hpp @@ -0,0 +1,89 @@ +// 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. + +#pragma once + +#include +#include +#include + +#include "wpi/cs/VideoCamera.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A source that represents a USB camera. + */ +class UsbCamera : public VideoCamera { + public: + UsbCamera() = default; + + /** + * Create a source for a USB camera based on device number. + * + * @param name Source name (arbitrary unique identifier) + * @param dev Device number (e.g. 0 for /dev/video0) + */ + UsbCamera(std::string_view name, int dev) { + m_handle = CreateUsbCameraDev(name, dev, &m_status); + } + + /** + * Create a source for a USB camera based on device path. + * + * @param name Source name (arbitrary unique identifier) + * @param path Path to device (e.g. "/dev/video0" on Linux) + */ + UsbCamera(std::string_view name, std::string_view path) { + m_handle = CreateUsbCameraPath(name, path, &m_status); + } + + /** + * Enumerate USB cameras on the local system. + * + * @return Vector of USB camera information (one for each camera) + */ + static std::vector EnumerateUsbCameras() { + CS_Status status = 0; + return ::wpi::cs::EnumerateUsbCameras(&status); + } + + /** + * Change the path to the device. + */ + void SetPath(std::string_view path) { + m_status = 0; + return ::wpi::cs::SetUsbCameraPath(m_handle, path, &m_status); + } + + /** + * Get the path to the device. + */ + std::string GetPath() const { + m_status = 0; + return ::wpi::cs::GetUsbCameraPath(m_handle, &m_status); + } + + /** + * Get the full camera information for the device. + */ + UsbCameraInfo GetInfo() const { + m_status = 0; + return ::wpi::cs::GetUsbCameraInfo(m_handle, &m_status); + } + + /** + * Set how verbose the camera connection messages are. + * + * @param level 0=don't display Connecting message, 1=do display message + */ + void SetConnectVerbose(int level) { + m_status = 0; + SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status), + level, &m_status); + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp b/cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp new file mode 100644 index 0000000000..a62d50f756 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/UsbCameraInfo.hpp @@ -0,0 +1,30 @@ +// 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. + +#pragma once + +#include +#include + +namespace wpi::cs { + +/** + * USB camera information + */ +struct UsbCameraInfo { + /** Device number (e.g. N in '/dev/videoN' on Linux) */ + int dev = -1; + /** Path to device if available (e.g. '/dev/video0' on Linux) */ + std::string path; + /** Vendor/model name of the camera as provided by the USB driver */ + std::string name; + /** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux) */ + std::vector otherPaths; + /** USB Vendor Id */ + int vendorId = -1; + /** USB Product Id */ + int productId = -1; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoCamera.hpp b/cscore/src/main/native/include/wpi/cs/VideoCamera.hpp new file mode 100644 index 0000000000..2a9d82e534 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoCamera.hpp @@ -0,0 +1,103 @@ +// 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. + +#pragma once + +#include "wpi/cs/VideoSource.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * A source that represents a video camera. + */ +class VideoCamera : public VideoSource { + public: + /** + * White balance. + */ + enum WhiteBalance { + /// Fixed indoor white balance. + kFixedIndoor = 3000, + /// Fixed outdoor white balance 1. + kFixedOutdoor1 = 4000, + /// Fixed outdoor white balance 2. + kFixedOutdoor2 = 5000, + /// Fixed fluorescent white balance 1. + kFixedFluorescent1 = 5100, + /// Fixed fluorescent white balance 2. + kFixedFlourescent2 = 5200 + }; + + VideoCamera() = default; + + /** + * Set the brightness, as a percentage (0-100). + */ + void SetBrightness(int brightness) { + m_status = 0; + SetCameraBrightness(m_handle, brightness, &m_status); + } + + /** + * Get the brightness, as a percentage (0-100). + */ + int GetBrightness() { + m_status = 0; + return GetCameraBrightness(m_handle, &m_status); + } + + /** + * Set the white balance to auto. + */ + void SetWhiteBalanceAuto() { + m_status = 0; + SetCameraWhiteBalanceAuto(m_handle, &m_status); + } + + /** + * Set the white balance to hold current. + */ + void SetWhiteBalanceHoldCurrent() { + m_status = 0; + SetCameraWhiteBalanceHoldCurrent(m_handle, &m_status); + } + + /** + * Set the white balance to manual, with specified color temperature. + */ + void SetWhiteBalanceManual(int value) { + m_status = 0; + SetCameraWhiteBalanceManual(m_handle, value, &m_status); + } + + /** + * Set the exposure to auto aperture. + */ + void SetExposureAuto() { + m_status = 0; + SetCameraExposureAuto(m_handle, &m_status); + } + + /** + * Set the exposure to hold current. + */ + void SetExposureHoldCurrent() { + m_status = 0; + SetCameraExposureHoldCurrent(m_handle, &m_status); + } + + /** + * Set the exposure to manual, as a percentage (0-100). + */ + void SetExposureManual(int value) { + m_status = 0; + SetCameraExposureManual(m_handle, value, &m_status); + } + + protected: + explicit VideoCamera(CS_Source handle) : VideoSource(handle) {} +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoEvent.hpp b/cscore/src/main/native/include/wpi/cs/VideoEvent.hpp new file mode 100644 index 0000000000..7503b22523 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoEvent.hpp @@ -0,0 +1,51 @@ +// 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. + +#pragma once + +#include "wpi/cs/VideoProperty.hpp" +#include "wpi/cs/VideoSink.hpp" +#include "wpi/cs/VideoSource.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * An event generated by the library and provided to event listeners. + */ +class VideoEvent : public RawEvent { + public: + /** + * Returns the source associated with the event (if any). + * + * @return The source associated with the event (if any). + */ + VideoSource GetSource() const { + CS_Status status = 0; + return VideoSource{sourceHandle == 0 ? 0 + : CopySource(sourceHandle, &status)}; + } + + /** + * Returns the sink associated with the event (if any). + * + * @return The sink associated with the event (if any). + */ + VideoSink GetSink() const { + CS_Status status = 0; + return VideoSink{sinkHandle == 0 ? 0 : CopySink(sinkHandle, &status)}; + } + + /** + * Returns the property associated with the event (if any). + * + * @return The property associated with the event (if any). + */ + VideoProperty GetProperty() const { + return VideoProperty{propertyHandle, + static_cast(propertyKind)}; + } +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoListener.hpp b/cscore/src/main/native/include/wpi/cs/VideoListener.hpp new file mode 100644 index 0000000000..28764103ce --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoListener.hpp @@ -0,0 +1,69 @@ +// 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. + +#pragma once + +#include +#include + +#include "wpi/cs/VideoEvent.hpp" +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * An event listener. This calls back to a designated callback function when + * an event matching the specified mask is generated by the library. + */ +class VideoListener { + public: + VideoListener() = default; + + /** + * Create an event listener. + * + * @param callback Callback function + * @param eventMask Bitmask of VideoEvent::Kind values + * @param immediateNotify Whether callback should be immediately called with + * a representative set of events for the current library state. + */ + VideoListener(std::function callback, + int eventMask, bool immediateNotify) { + CS_Status status = 0; + m_handle = AddListener( + [=](const RawEvent& event) { + callback(static_cast(event)); + }, + eventMask, immediateNotify, &status); + } + + VideoListener(const VideoListener&) = delete; + VideoListener& operator=(const VideoListener&) = delete; + + VideoListener(VideoListener&& other) noexcept : VideoListener() { + swap(*this, other); + } + + VideoListener& operator=(VideoListener&& other) noexcept { + swap(*this, other); + return *this; + } + + ~VideoListener() { + CS_Status status = 0; + if (m_handle != 0) { + RemoveListener(m_handle, &status); + } + } + + friend void swap(VideoListener& first, VideoListener& second) noexcept { + using std::swap; + swap(first.m_handle, second.m_handle); + } + + private: + CS_Listener m_handle{0}; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoMode.hpp b/cscore/src/main/native/include/wpi/cs/VideoMode.hpp new file mode 100644 index 0000000000..37584f29d5 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoMode.hpp @@ -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. + +#pragma once + +#include "wpi/cs/cscore_c.h" +#include "wpi/util/PixelFormat.hpp" + +namespace wpi::cs { + +/** + * Video mode + */ +struct VideoMode { + VideoMode() = default; + VideoMode(wpi::util::PixelFormat pixelFormat_, int width_, int height_, + int fps_) + : pixelFormat{pixelFormat_}, width{width_}, height{height_}, fps{fps_} {} + VideoMode(const CS_VideoMode& mode) // NOLINT + : pixelFormat{static_cast(mode.pixelFormat)}, + width{mode.width}, + height{mode.height}, + fps{mode.fps} {} + + operator CS_VideoMode() const { // NOLINT + CS_VideoMode mode; + mode.pixelFormat = static_cast(pixelFormat); + mode.width = width; + mode.height = height; + mode.fps = fps; + return mode; + } + + explicit operator bool() const { + return pixelFormat == wpi::util::PixelFormat::kUnknown; + } + + bool operator==(const VideoMode& other) const { + return pixelFormat == other.pixelFormat && width == other.width && + height == other.height && fps == other.fps; + } + + bool CompareWithoutFps(const VideoMode& other) const { + return pixelFormat == other.pixelFormat && width == other.width && + height == other.height; + } + + wpi::util::PixelFormat pixelFormat = wpi::util::PixelFormat::kUnknown; + int width = 0; + int height = 0; + int fps = 0; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoProperty.hpp b/cscore/src/main/native/include/wpi/cs/VideoProperty.hpp new file mode 100644 index 0000000000..88e4b2ffc3 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoProperty.hpp @@ -0,0 +1,243 @@ +// 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. + +#pragma once + +#include +#include +#include + +#include "wpi/cs/cscore_cpp.hpp" + +namespace wpi::cs { + +/** + * @defgroup cscore_oo cscore C++ object-oriented API + * + * Recommended interface for C++, identical to Java API. + * + *

The classes are RAII and handle reference counting internally. + * + * @{ + */ + +// Forward declarations so friend declarations work correctly +class ImageSource; +class VideoEvent; +class VideoSink; +class VideoSource; + +/** + * A source or sink property. + */ +class VideoProperty { + friend class ImageSource; + friend class VideoEvent; + friend class VideoSink; + friend class VideoSource; + + public: + enum Kind { + /// No specific property. + kNone = CS_PROP_NONE, + /// Boolean property. + kBoolean = CS_PROP_BOOLEAN, + /// Integer property. + kInteger = CS_PROP_INTEGER, + /// String property. + kString = CS_PROP_STRING, + /// Enum property. + kEnum = CS_PROP_ENUM + }; + + VideoProperty() = default; + + /** + * Returns property name. + * + * @return Property name. + */ + std::string GetName() const { + m_status = 0; + return GetPropertyName(m_handle, &m_status); + } + + /** + * Returns property kind. + * + * @return Property kind. + */ + Kind GetKind() const { return m_kind; } + + /** + * Returns true if property is valid. + * + * @return True if property is valid. + */ + explicit operator bool() const { return m_kind != kNone; } + + /** + * Returns true if property is a boolean. + * + * @return True if property is a boolean. + */ + bool IsBoolean() const { return m_kind == kBoolean; } + + /** + * Returns true if property is an integer. + * + * @return True if property is an integer. + */ + bool IsInteger() const { return m_kind == kInteger; } + + /** + * Returns true if property is a string. + * + * @return True if property is a string. + */ + bool IsString() const { return m_kind == kString; } + + /** + * Returns true if property is an enum. + * + * @return True if property is an enum. + */ + bool IsEnum() const { return m_kind == kEnum; } + + /** + * Returns property value. + * + * @return Property value. + */ + int Get() const { + m_status = 0; + return GetProperty(m_handle, &m_status); + } + + /** + * Sets property value. + * + * @param value Property value. + */ + void Set(int value) { + m_status = 0; + SetProperty(m_handle, value, &m_status); + } + + /** + * Returns property minimum value. + * + * @return Property minimum value. + */ + int GetMin() const { + m_status = 0; + return GetPropertyMin(m_handle, &m_status); + } + + /** + * Returns property maximum value. + * + * @return Property maximum value. + */ + int GetMax() const { + m_status = 0; + return GetPropertyMax(m_handle, &m_status); + } + + /** + * Returns property step size. + * + * @return Property step size. + */ + int GetStep() const { + m_status = 0; + return GetPropertyStep(m_handle, &m_status); + } + + /** + * Returns property default value. + * + * @return Property default value. + */ + int GetDefault() const { + m_status = 0; + return GetPropertyDefault(m_handle, &m_status); + } + + /** + * Returns the string property value. + * + *

This function is string-specific. + * + * @return The string property value. + */ + std::string GetString() const { + m_status = 0; + return GetStringProperty(m_handle, &m_status); + } + + /** + * Returns the string property value as a reference to the given buffer. + * + * This function is string-specific. + * + * @param buf The backing storage to which to write the property value. + * @return The string property value as a reference to the given buffer. + */ + std::string_view GetString(wpi::util::SmallVectorImpl& buf) const { + m_status = 0; + return GetStringProperty(m_handle, buf, &m_status); + } + + /** + * Sets the string property value. + * + * This function is string-specific. + * + * @param value String property value. + */ + void SetString(std::string_view value) { + m_status = 0; + SetStringProperty(m_handle, value, &m_status); + } + + /** + * Returns the possible values for the enum property value. + * + * This function is enum-specific. + * + * @return The possible values for the enum property value. + */ + std::vector GetChoices() const { + m_status = 0; + return GetEnumPropertyChoices(m_handle, &m_status); + } + + /** + * Returns the last status. + * + * @return The last status. + */ + CS_Status GetLastStatus() const { return m_status; } + + private: + explicit VideoProperty(CS_Property handle) : m_handle(handle) { + m_status = 0; + if (handle == 0) { + m_kind = kNone; + } else { + m_kind = static_cast( + static_cast(GetPropertyKind(handle, &m_status))); + } + } + + VideoProperty(CS_Property handle, Kind kind) + : m_handle(handle), m_kind(kind) {} + + mutable CS_Status m_status{0}; + CS_Property m_handle{0}; + Kind m_kind{kNone}; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoSink.hpp b/cscore/src/main/native/include/wpi/cs/VideoSink.hpp new file mode 100644 index 0000000000..93a2b1e8e3 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoSink.hpp @@ -0,0 +1,232 @@ +// 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. + +#pragma once + +#include +#include +#include +#include + +#include "wpi/cs/VideoProperty.hpp" +#include "wpi/cs/VideoSource.hpp" +#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/json_fwd.hpp" + +namespace wpi::cs { + +class VideoEvent; + +/** + * A sink for video that accepts a sequence of frames. + */ +class VideoSink { + friend class VideoEvent; + friend class VideoSource; + + public: + enum Kind { + /// Unknown sink type. + kUnknown = CS_SINK_UNKNOWN, + /// MJPEG video sink. + kMjpeg = CS_SINK_MJPEG, + /// CV video sink. + kCv = CS_SINK_CV, + /// Raw video sink. + kRaw = CS_SINK_RAW, + }; + + VideoSink() noexcept = default; + + VideoSink(const VideoSink& sink) + : m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {} + + VideoSink(VideoSink&& other) noexcept : VideoSink() { swap(*this, other); } + + VideoSink& operator=(VideoSink other) noexcept { + swap(*this, other); + return *this; + } + + ~VideoSink() { + m_status = 0; + if (m_handle != 0) { + ReleaseSink(m_handle, &m_status); + } + } + + /** + * Returns true if the VideoSink is valid. + * + * @return True if the VideoSink is valid. + */ + explicit operator bool() const { return m_handle != 0; } + + /** + * Returns the VideoSink handle. + * + * @return The VideoSink handle. + */ + int GetHandle() const { return m_handle; } + + bool operator==(const VideoSink& other) const { + return m_handle == other.m_handle; + } + + /** + * Get the kind of the sink. + */ + Kind GetKind() const { + m_status = 0; + return static_cast(GetSinkKind(m_handle, &m_status)); + } + + /** + * Get the name of the sink. The name is an arbitrary identifier + * provided when the sink is created, and should be unique. + */ + std::string GetName() const { + m_status = 0; + return GetSinkName(m_handle, &m_status); + } + + /** + * Get the sink description. This is sink-kind specific. + */ + std::string GetDescription() const { + m_status = 0; + return GetSinkDescription(m_handle, &m_status); + } + + /** + * Get a property of the sink. + * + * @param name Property name + * @return Property (kind Property::kNone if no property with + * the given name exists) + */ + VideoProperty GetProperty(std::string_view name) { + m_status = 0; + return VideoProperty{GetSinkProperty(m_handle, name, &m_status)}; + } + + /** + * Enumerate all properties of this sink. + */ + std::vector EnumerateProperties() const; + + /** + * Set properties from a JSON configuration string. + * + * The format of the JSON input is: + * + *

+   * {
+   *     "properties": [
+   *         {
+   *             "name": property name
+   *             "value": property value
+   *         }
+   *     ]
+   * }
+   * 
+ * + * @param config configuration + * @return True if set successfully + */ + bool SetConfigJson(std::string_view config) { + m_status = 0; + return SetSinkConfigJson(m_handle, config, &m_status); + } + + /** + * Set properties from a JSON configuration object. + * + * @param config configuration + * @return True if set successfully + */ + bool SetConfigJson(const wpi::util::json& config) { + m_status = 0; + return SetSinkConfigJson(m_handle, config, &m_status); + } + + /** + * Get a JSON configuration string. + * + * @return JSON configuration string + */ + std::string GetConfigJson() const { + m_status = 0; + return GetSinkConfigJson(m_handle, &m_status); + } + + /** + * Get a JSON configuration object. + * + * @return JSON configuration object + */ + wpi::util::json GetConfigJsonObject() const; + + /** + * Configure which source should provide frames to this sink. Each sink + * can accept frames from only a single source, but a single source can + * provide frames to multiple clients. + * + * @param source Source + */ + void SetSource(VideoSource source) { + m_status = 0; + if (!source) { + SetSinkSource(m_handle, 0, &m_status); + } else { + SetSinkSource(m_handle, source.m_handle, &m_status); + } + } + + /** + * Get the connected source. + * + * @return Connected source (empty if none connected). + */ + VideoSource GetSource() const { + m_status = 0; + auto handle = GetSinkSource(m_handle, &m_status); + return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)}; + } + + /** + * Get a property of the associated source. + * + * @param name Property name + * @return Property (kind Property::kNone if no property with + * the given name exists or no source connected) + */ + VideoProperty GetSourceProperty(std::string_view name) { + m_status = 0; + return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)}; + } + + CS_Status GetLastStatus() const { return m_status; } + + /** + * Enumerate all existing sinks. + * + * @return Vector of sinks. + */ + static std::vector EnumerateSinks(); + + friend void swap(VideoSink& first, VideoSink& second) noexcept { + using std::swap; + swap(first.m_status, second.m_status); + swap(first.m_handle, second.m_handle); + } + + protected: + explicit VideoSink(CS_Sink handle) : m_handle(handle) {} + + mutable CS_Status m_status = 0; + CS_Sink m_handle{0}; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/VideoSource.hpp b/cscore/src/main/native/include/wpi/cs/VideoSource.hpp new file mode 100644 index 0000000000..b4d60d4e5f --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/VideoSource.hpp @@ -0,0 +1,378 @@ +// 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. + +#pragma once + +#include + +#include +#include +#include + +#include "wpi/cs/VideoProperty.hpp" +#include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/PixelFormat.hpp" + +namespace wpi::cs { + +class VideoEvent; +class VideoSink; + +/** + * A source for video that provides a sequence of frames. + */ +class VideoSource { + friend class VideoEvent; + friend class VideoSink; + + public: + /** + * Video source kind. + */ + enum Kind { + /// Unknown video source. + kUnknown = CS_SOURCE_UNKNOWN, + /// USB video source. + kUsb = CS_SOURCE_USB, + /// HTTP video source. + kHttp = CS_SOURCE_HTTP, + /// CV video source. + kCv = CS_SOURCE_CV, + /// Raw video source. + kRaw = CS_SOURCE_RAW, + }; + + /** Connection strategy. Used for SetConnectionStrategy(). */ + enum ConnectionStrategy { + /** + * Automatically connect or disconnect based on whether any sinks are + * connected to this source. This is the default behavior. + */ + kConnectionAutoManage = CS_CONNECTION_AUTO_MANAGE, + + /** + * Try to keep the connection open regardless of whether any sinks are + * connected. + */ + kConnectionKeepOpen = CS_CONNECTION_KEEP_OPEN, + + /** + * Never open the connection. If this is set when the connection is open, + * close the connection. + */ + kConnectionForceClose = CS_CONNECTION_FORCE_CLOSE + }; + + VideoSource() noexcept = default; + + VideoSource(const VideoSource& source) + : m_handle( + source.m_handle == 0 ? 0 : CopySource(source.m_handle, &m_status)) { + } + + VideoSource(VideoSource&& other) noexcept : VideoSource() { + swap(*this, other); + } + + VideoSource& operator=(VideoSource other) noexcept { + swap(*this, other); + return *this; + } + + ~VideoSource() { + m_status = 0; + if (m_handle != 0) { + ReleaseSource(m_handle, &m_status); + } + } + + explicit operator bool() const { return m_handle != 0; } + + int GetHandle() const { return m_handle; } + + bool operator==(const VideoSource& other) const { + return m_handle == other.m_handle; + } + + /** + * Get the kind of the source. + */ + Kind GetKind() const { + m_status = 0; + return static_cast(GetSourceKind(m_handle, &m_status)); + } + + /** + * Get the name of the source. The name is an arbitrary identifier + * provided when the source is created, and should be unique. + */ + std::string GetName() const { + m_status = 0; + return GetSourceName(m_handle, &m_status); + } + + /** + * Get the source description. This is source-kind specific. + */ + std::string GetDescription() const { + m_status = 0; + return GetSourceDescription(m_handle, &m_status); + } + + /** + * Get the last time a frame was captured. + * This uses the same time base as wpi::util::Now(). + * + * @return Time in 1 us increments. + */ + uint64_t GetLastFrameTime() const { + m_status = 0; + return GetSourceLastFrameTime(m_handle, &m_status); + } + + /** + * Sets the connection strategy. By default, the source will automatically + * connect or disconnect based on whether any sinks are connected. + * + *

This function is non-blocking; look for either a connection open or + * close event or call IsConnected() to determine the connection state. + * + * @param strategy connection strategy (auto, keep open, or force close) + */ + void SetConnectionStrategy(ConnectionStrategy strategy) { + m_status = 0; + SetSourceConnectionStrategy( + m_handle, + static_cast(static_cast(strategy)), + &m_status); + } + + /** + * Is the source currently connected to whatever is providing the images? + */ + bool IsConnected() const { + m_status = 0; + return IsSourceConnected(m_handle, &m_status); + } + + /** + * Gets source enable status. This is determined with a combination of + * connection strategy and the number of sinks connected. + * + * @return True if enabled, false otherwise. + */ + bool IsEnabled() const { + m_status = 0; + return IsSourceEnabled(m_handle, &m_status); + } + + /** Get a property. + * + * @param name Property name + * @return Property contents (of kind Property::kNone if no property with + * the given name exists) + */ + VideoProperty GetProperty(std::string_view name) { + m_status = 0; + return VideoProperty{GetSourceProperty(m_handle, name, &m_status)}; + } + + /** + * Enumerate all properties of this source. + */ + std::vector EnumerateProperties() const; + + /** + * Get the current video mode. + */ + VideoMode GetVideoMode() const { + m_status = 0; + return GetSourceVideoMode(m_handle, &m_status); + } + + /** + * Set the video mode. + * + * @param mode Video mode + */ + bool SetVideoMode(const VideoMode& mode) { + m_status = 0; + return SetSourceVideoMode(m_handle, mode, &m_status); + } + + /** + * Set the video mode. + * + * @param pixelFormat desired pixel format + * @param width desired width + * @param height desired height + * @param fps desired FPS + * @return True if set successfully + */ + bool SetVideoMode(wpi::util::PixelFormat pixelFormat, int width, int height, + int fps) { + m_status = 0; + return SetSourceVideoMode( + m_handle, VideoMode{pixelFormat, width, height, fps}, &m_status); + } + + /** + * Set the pixel format. + * + * @param pixelFormat desired pixel format + * @return True if set successfully + */ + bool SetPixelFormat(wpi::util::PixelFormat pixelFormat) { + m_status = 0; + return SetSourcePixelFormat(m_handle, pixelFormat, &m_status); + } + + /** + * Set the resolution. + * + * @param width desired width + * @param height desired height + * @return True if set successfully + */ + bool SetResolution(int width, int height) { + m_status = 0; + return SetSourceResolution(m_handle, width, height, &m_status); + } + + /** + * Set the frames per second (FPS). + * + * @param fps desired FPS + * @return True if set successfully + */ + bool SetFPS(int fps) { + m_status = 0; + return SetSourceFPS(m_handle, fps, &m_status); + } + + /** + * Set video mode and properties from a JSON configuration string. + * + * The format of the JSON input is: + * + *

+   * {
+   *     "pixel format": "MJPEG", "YUYV", etc
+   *     "width": video mode width
+   *     "height": video mode height
+   *     "fps": video mode fps
+   *     "brightness": percentage brightness
+   *     "white balance": "auto", "hold", or value
+   *     "exposure": "auto", "hold", or value
+   *     "properties": [
+   *         {
+   *             "name": property name
+   *             "value": property value
+   *         }
+   *     ]
+   * }
+   * 
+ * + * @param config configuration + * @return True if set successfully + */ + bool SetConfigJson(std::string_view config) { + m_status = 0; + return SetSourceConfigJson(m_handle, config, &m_status); + } + + /** + * Set video mode and properties from a JSON configuration object. + * + * @param config configuration + * @return True if set successfully + */ + bool SetConfigJson(const wpi::util::json& config) { + m_status = 0; + return SetSourceConfigJson(m_handle, config, &m_status); + } + + /** + * Get a JSON configuration string. + * + * @return JSON configuration string + */ + std::string GetConfigJson() const { + m_status = 0; + return GetSourceConfigJson(m_handle, &m_status); + } + + /** + * Get a JSON configuration object. + * + * @return JSON configuration object + */ + wpi::util::json GetConfigJsonObject() const; + + /** + * Get the actual FPS. + * + *

SetTelemetryPeriod() must be called for this to be valid. + * + * @return Actual FPS averaged over the telemetry period. + */ + double GetActualFPS() const { + m_status = 0; + return wpi::cs::GetTelemetryAverageValue( + m_handle, CS_SOURCE_FRAMES_RECEIVED, &m_status); + } + + /** + * Get the data rate (in bytes per second). + * + *

SetTelemetryPeriod() must be called for this to be valid. + * + * @return Data rate averaged over the telemetry period. + */ + double GetActualDataRate() const { + m_status = 0; + return wpi::cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_BYTES_RECEIVED, + &m_status); + } + + /** + * Enumerate all known video modes for this source. + */ + std::vector EnumerateVideoModes() const { + CS_Status status = 0; + return EnumerateSourceVideoModes(m_handle, &status); + } + + CS_Status GetLastStatus() const { return m_status; } + + /** + * Enumerate all sinks connected to this source. + * + * @return Vector of sinks. + */ + std::vector EnumerateSinks(); + + /** + * Enumerate all existing sources. + * + * @return Vector of sources. + */ + static std::vector EnumerateSources(); + + friend void swap(VideoSource& first, VideoSource& second) noexcept { + using std::swap; + swap(first.m_status, second.m_status); + swap(first.m_handle, second.m_handle); + } + + protected: + explicit VideoSource(CS_Source handle) : m_handle(handle) {} + + mutable CS_Status m_status = 0; + + /// Video source handle. + CS_Source m_handle{0}; +}; + +} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/cscore.h b/cscore/src/main/native/include/wpi/cs/cscore.h deleted file mode 100644 index 88e0934638..0000000000 --- a/cscore/src/main/native/include/wpi/cs/cscore.h +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -#pragma once - -/* C API */ -#include "wpi/cs/cscore_c.h" - -#ifdef __cplusplus -/* C++ API */ -#include "wpi/cs/cscore_cpp.hpp" -#include "wpi/cs/cscore_oo.hpp" -#endif /* __cplusplus */ diff --git a/cscore/src/main/native/include/wpi/cs/cscore_c.h b/cscore/src/main/native/include/wpi/cs/cscore_c.h index 10dd483dfb..64badd6363 100644 --- a/cscore/src/main/native/include/wpi/cs/cscore_c.h +++ b/cscore/src/main/native/include/wpi/cs/cscore_c.h @@ -4,17 +4,12 @@ #pragma once +#include // NOLINT #include -#include "wpi/util/RawFrame.h" +#include "wpi/util/PixelFormat.h" #include "wpi/util/string.h" -#ifdef __cplusplus -#include -#else -#include -#endif - #ifdef __cplusplus extern "C" { #endif diff --git a/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp b/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp index d1750679a7..d581ea2857 100644 --- a/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp +++ b/cscore/src/main/native/include/wpi/cs/cscore_cpp.hpp @@ -12,8 +12,11 @@ #include #include +#include "wpi/cs/RawEvent.hpp" +#include "wpi/cs/UsbCameraInfo.hpp" +#include "wpi/cs/VideoMode.hpp" #include "wpi/cs/cscore_c.h" -#include "wpi/util/RawFrame.h" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallVector.hpp" #include "wpi/util/json_fwd.hpp" @@ -36,140 +39,6 @@ namespace wpi::cs { * @{ */ -/** - * USB camera information - */ -struct UsbCameraInfo { - /** Device number (e.g. N in '/dev/videoN' on Linux) */ - int dev = -1; - /** Path to device if available (e.g. '/dev/video0' on Linux) */ - std::string path; - /** Vendor/model name of the camera as provided by the USB driver */ - std::string name; - /** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux) */ - std::vector otherPaths; - /** USB Vendor Id */ - int vendorId = -1; - /** USB Product Id */ - int productId = -1; -}; - -/** - * Video mode - */ -struct VideoMode : public CS_VideoMode { - enum PixelFormat { - kUnknown = WPI_PIXFMT_UNKNOWN, - kMJPEG = WPI_PIXFMT_MJPEG, - kYUYV = WPI_PIXFMT_YUYV, - kRGB565 = WPI_PIXFMT_RGB565, - kBGR = WPI_PIXFMT_BGR, - kGray = WPI_PIXFMT_GRAY, - kY16 = WPI_PIXFMT_Y16, - kUYVY = WPI_PIXFMT_UYVY, - kBGRA = WPI_PIXFMT_BGRA, - }; - VideoMode() { - pixelFormat = 0; - width = 0; - height = 0; - fps = 0; - } - VideoMode(PixelFormat pixelFormat_, int width_, int height_, int fps_) { - pixelFormat = pixelFormat_; - width = width_; - height = height_; - fps = fps_; - } - explicit operator bool() const { return pixelFormat == kUnknown; } - - bool operator==(const VideoMode& other) const { - return pixelFormat == other.pixelFormat && width == other.width && - height == other.height && fps == other.fps; - } - - bool CompareWithoutFps(const VideoMode& other) const { - return pixelFormat == other.pixelFormat && width == other.width && - height == other.height; - } -}; - -/** - * Listener event - */ -struct RawEvent { - enum Kind { - kSourceCreated = CS_SOURCE_CREATED, - kSourceDestroyed = CS_SOURCE_DESTROYED, - kSourceConnected = CS_SOURCE_CONNECTED, - kSourceDisconnected = CS_SOURCE_DISCONNECTED, - kSourceVideoModesUpdated = CS_SOURCE_VIDEOMODES_UPDATED, - kSourceVideoModeChanged = CS_SOURCE_VIDEOMODE_CHANGED, - kSourcePropertyCreated = CS_SOURCE_PROPERTY_CREATED, - kSourcePropertyValueUpdated = CS_SOURCE_PROPERTY_VALUE_UPDATED, - kSourcePropertyChoicesUpdated = CS_SOURCE_PROPERTY_CHOICES_UPDATED, - kSinkSourceChanged = CS_SINK_SOURCE_CHANGED, - kSinkCreated = CS_SINK_CREATED, - kSinkDestroyed = CS_SINK_DESTROYED, - kSinkEnabled = CS_SINK_ENABLED, - kSinkDisabled = CS_SINK_DISABLED, - kNetworkInterfacesChanged = CS_NETWORK_INTERFACES_CHANGED, - kTelemetryUpdated = CS_TELEMETRY_UPDATED, - kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED, - kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED, - kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED, - kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED - }; - - RawEvent() = default; - explicit RawEvent(RawEvent::Kind kind_) : kind{kind_} {} - RawEvent(std::string_view name_, CS_Handle handle_, RawEvent::Kind kind_) - : kind{kind_}, name{name_} { - if (kind_ == kSinkCreated || kind_ == kSinkDestroyed || - kind_ == kSinkEnabled || kind_ == kSinkDisabled) { - sinkHandle = handle_; - } else { - sourceHandle = handle_; - } - } - RawEvent(std::string_view name_, CS_Source source_, const VideoMode& mode_) - : kind{kSourceVideoModeChanged}, - sourceHandle{source_}, - name{name_}, - mode{mode_} {} - RawEvent(std::string_view name_, CS_Source source_, RawEvent::Kind kind_, - CS_Property property_, CS_PropertyKind propertyKind_, int value_, - std::string_view valueStr_) - : kind{kind_}, - sourceHandle{source_}, - name{name_}, - propertyHandle{property_}, - propertyKind{propertyKind_}, - value{value_}, - valueStr{valueStr_} {} - - Kind kind; - - // Valid for kSource* and kSink* respectively - CS_Source sourceHandle = CS_INVALID_HANDLE; - CS_Sink sinkHandle = CS_INVALID_HANDLE; - - // Source/sink/property name - std::string name; - - // Fields for kSourceVideoModeChanged event - VideoMode mode; - - // Fields for kSourceProperty* events - CS_Property propertyHandle; - CS_PropertyKind propertyKind; - int value; - std::string valueStr; - - // Listener that was triggered - CS_Listener listener{0}; -}; - /** * @defgroup cscore_property_func Property Functions * @{ @@ -238,7 +107,7 @@ std::span EnumerateSourceProperties( VideoMode GetSourceVideoMode(CS_Source source, CS_Status* status); bool SetSourceVideoMode(CS_Source source, const VideoMode& mode, CS_Status* status); -bool SetSourcePixelFormat(CS_Source source, VideoMode::PixelFormat pixelFormat, +bool SetSourcePixelFormat(CS_Source source, wpi::util::PixelFormat pixelFormat, CS_Status* status); bool SetSourceResolution(CS_Source source, int width, int height, CS_Status* status); @@ -316,10 +185,10 @@ void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property, */ CS_Sink CreateMjpegServer(std::string_view name, std::string_view listenAddress, int port, CS_Status* status); -CS_Sink CreateCvSink(std::string_view name, VideoMode::PixelFormat pixelFormat, +CS_Sink CreateCvSink(std::string_view name, wpi::util::PixelFormat pixelFormat, CS_Status* status); CS_Sink CreateCvSinkCallback(std::string_view name, - VideoMode::PixelFormat pixelFormat, + wpi::util::PixelFormat pixelFormat, std::function processFrame, CS_Status* status); diff --git a/cscore/src/main/native/include/wpi/cs/cscore_cv.hpp b/cscore/src/main/native/include/wpi/cs/cscore_cv.hpp deleted file mode 100644 index 1a2b6a6eea..0000000000 --- a/cscore/src/main/native/include/wpi/cs/cscore_cv.hpp +++ /dev/null @@ -1,431 +0,0 @@ -// 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. - -#pragma once - -#include - -#include "wpi/cs/cscore_oo.hpp" -#include "wpi/cs/cscore_raw.h" -#include "wpi/util/RawFrame.hpp" - -namespace wpi::cs { -/** - * A source for user code to provide OpenCV images as video frames. - * - * This is not dependent on any opencv binary ABI, and can be used - * with versions of most versions of OpenCV. - */ -class CvSource : public ImageSource { - public: - CvSource() = default; - - /** - * Create an OpenCV source. - * - * @param name Source name (arbitrary unique identifier) - * @param mode Video mode being generated - */ - CvSource(std::string_view name, const VideoMode& mode); - - /** - * Create an OpenCV source. - * - * @param name Source name (arbitrary unique identifier) - * @param pixelFormat Pixel format - * @param width width - * @param height height - * @param fps fps - */ - CvSource(std::string_view name, VideoMode::PixelFormat pixelFormat, int width, - int height, int fps); - - /** - * Put an OpenCV image and notify sinks - * - *

- * The image format is guessed from the number of channels. The channel - * mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel - * numbers will throw an error. If your image is an in alternate format, use - * the overload that takes a PixelFormat. - * - * @param image OpenCV Image - */ - void PutFrame(cv::Mat& image); - - /** - * Put an OpenCV image and notify sinks. - * - *

- * The format of the Mat must match the PixelFormat. You will corrupt memory - * if they dont. With skipVerification false, we will verify the number of - * channels matches the pixel format. If skipVerification is true, this step - * is skipped and is passed straight through. - * - * @param image OpenCV image - * @param pixelFormat The pixel format of the image - * @param skipVerification skip verifying pixel format - */ - void PutFrame(cv::Mat& image, VideoMode::PixelFormat pixelFormat, - bool skipVerification); - - private: - static bool VerifyFormat(cv::Mat& image, VideoMode::PixelFormat pixelFormat); -}; - -/** - * A source for user code to accept video frames as OpenCV images. - * - * This is not dependent on any opencv binary ABI, and can be used - * with versions of most versions of OpenCV. - */ -class CvSink : public ImageSink { - public: - CvSink() = default; - CvSink(const CvSink& sink); - - /** - * Create a sink for accepting OpenCV images. - * - *

WaitForFrame() must be called on the created sink to get each new - * image. - * - * @param name Source name (arbitrary unique identifier) - * @param pixelFormat The pixel format to read - */ - explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat = - VideoMode::PixelFormat::kBGR); - - /** - * Wait for the next frame and get the image. - * Times out (returning 0) after timeout seconds. - * The provided image will have the pixelFormat this class was constructed - * with. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225); - - /** - * Wait for the next frame and get the image. May block forever. - * The provided image will have the pixelFormat this class was constructed - * with. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameNoTimeout(cv::Mat& image); - - /** - * Wait for the next frame and get the image. - * Times out (returning 0) after timeout seconds. - * The provided image will have the pixelFormat this class was constructed - * with. The data is backed by data in the CvSink. It will be invalidated by - * any grabFrame*() call on the sink. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225); - - /** - * Wait for the next frame and get the image. May block forever. - * The provided image will have the pixelFormat this class was constructed - * with. The data is backed by data in the CvSink. It will be invalidated by - * any grabFrame*() call on the sink. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image); - - /** - * Wait for the next frame and get the image. - * Times out (returning 0) after timeout seconds. - * The provided image will have the pixelFormat this class was constructed - * with. The data is backed by data in the CvSink. It will be invalidated by - * any grabFrame*() call on the sink. - * - *

If lastFrameTime is provided and non-zero, the sink will fill image with - * the first frame from the source that is not equal to lastFrameTime. If - * lastFrameTime is zero, the time of the current frame owned by the CvSource - * is used, and this function will block until the connected CvSource provides - * a new frame. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameDirectLastTime(cv::Mat& image, uint64_t lastFrameTime, - double timeout = 0.225); - - /** - * Get the last time a frame was grabbed. This uses the same time base as - * wpi::util::Now(). - * - * @return Time in 1 us increments. - */ - [[nodiscard]] - uint64_t LastFrameTime(); - - /** - * Get the time source for the timestamp the last frame was grabbed at. - * - * @return Time source - */ - [[nodiscard]] - WPI_TimestampSource LastFrameTimeSource(); - - private: - constexpr int GetCvFormat(WPI_PixelFormat pixelFormat); - - wpi::util::RawFrame rawFrame; - VideoMode::PixelFormat pixelFormat; -}; - -inline CvSource::CvSource(std::string_view name, const VideoMode& mode) { - m_handle = CreateRawSource(name, true, mode, &m_status); -} - -inline CvSource::CvSource(std::string_view name, VideoMode::PixelFormat format, - int width, int height, int fps) { - m_handle = CreateRawSource(name, true, VideoMode{format, width, height, fps}, - &m_status); -} - -inline bool CvSource::VerifyFormat(cv::Mat& image, - VideoMode::PixelFormat pixelFormat) { - int channels = image.channels(); - switch (pixelFormat) { - case VideoMode::PixelFormat::kBGR: - if (channels == 3) { - return true; - } - break; - case VideoMode::PixelFormat::kBGRA: - if (channels == 4) { - return true; - } - break; - case VideoMode::PixelFormat::kGray: - if (channels == 1) { - return true; - } - break; - case VideoMode::PixelFormat::kRGB565: - if (channels == 2) { - return true; - } - break; - case VideoMode::PixelFormat::kUYVY: - if (channels == 2) { - return true; - } - break; - case VideoMode::PixelFormat::kY16: - if (channels == 2) { - return true; - } - break; - case VideoMode::PixelFormat::kYUYV: - if (channels == 2) { - return true; - } - break; - case VideoMode::PixelFormat::kMJPEG: - if (channels == 1) { - return true; - } - break; - default: - break; - } - return false; -} - -inline void CvSource::PutFrame(cv::Mat& image) { - // We only support 8-bit images; convert if necessary. - cv::Mat finalImage; - if (image.depth() == CV_8U) { - finalImage = image; - } else { - image.convertTo(finalImage, CV_8U); - } - - int channels = finalImage.channels(); - VideoMode::PixelFormat format; - if (channels == 1) { - // 1 channel is assumed Grayscale - format = VideoMode::PixelFormat::kGray; - } else if (channels == 2) { - // 2 channels is assumed YUYV - format = VideoMode::PixelFormat::kYUYV; - } else if (channels == 3) { - // 3 channels is assumed BGR - format = VideoMode::PixelFormat::kBGR; - } else if (channels == 4) { - // 4 channels is assumed BGRA - format = VideoMode::PixelFormat::kBGRA; - } else { - // TODO Error - return; - } - - PutFrame(finalImage, format, true); -} - -inline void CvSource::PutFrame(cv::Mat& image, - VideoMode::PixelFormat pixelFormat, - bool skipVerification) { - // We only support 8-bit images; convert if necessary. - cv::Mat finalImage; - if (image.depth() == CV_8U) { - finalImage = image; - } else { - image.convertTo(finalImage, CV_8U); - } - - if (!skipVerification) { - if (!VerifyFormat(finalImage, pixelFormat)) { - // TODO Error - return; - } - } - - WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor - frame.data = finalImage.data; - frame.freeFunc = nullptr; - frame.freeCbData = nullptr; - frame.size = finalImage.total() * finalImage.channels(); - frame.width = finalImage.cols; - frame.height = finalImage.rows; - frame.stride = finalImage.step; - frame.pixelFormat = pixelFormat; - m_status = 0; - PutSourceFrame(m_handle, frame, &m_status); -} - -inline CvSink::CvSink(std::string_view name, - VideoMode::PixelFormat pixelFormat) { - m_handle = CreateRawSink(name, true, &m_status); - this->pixelFormat = pixelFormat; -} - -inline CvSink::CvSink(const CvSink& sink) - : ImageSink{sink}, pixelFormat{sink.pixelFormat} {} - -inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) { - cv::Mat tmpnam; - auto retVal = GrabFrameDirect(tmpnam); - if (retVal <= 0) { - return retVal; - } - tmpnam.copyTo(image); - return retVal; -} - -inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) { - cv::Mat tmpnam; - auto retVal = GrabFrameNoTimeoutDirect(tmpnam); - if (retVal <= 0) { - return retVal; - } - tmpnam.copyTo(image); - return retVal; -} - -inline constexpr int CvSink::GetCvFormat(WPI_PixelFormat pixelFormat) { - int type = 0; - switch (pixelFormat) { - case WPI_PIXFMT_YUYV: - case WPI_PIXFMT_RGB565: - case WPI_PIXFMT_Y16: - case WPI_PIXFMT_UYVY: - type = CV_8UC2; - break; - case WPI_PIXFMT_BGR: - type = CV_8UC3; - break; - case WPI_PIXFMT_BGRA: - type = CV_8UC4; - break; - case WPI_PIXFMT_GRAY: - case WPI_PIXFMT_MJPEG: - default: - type = CV_8UC1; - break; - } - return type; -} - -inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) { - rawFrame.height = 0; - rawFrame.width = 0; - rawFrame.stride = 0; - rawFrame.pixelFormat = pixelFormat; - auto timestamp = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status); - if (m_status != CS_OK) { - return 0; - } - image = - cv::Mat{rawFrame.height, rawFrame.width, - GetCvFormat(static_cast(rawFrame.pixelFormat)), - rawFrame.data, static_cast(rawFrame.stride)}; - return timestamp; -} - -inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) { - rawFrame.height = 0; - rawFrame.width = 0; - rawFrame.stride = 0; - rawFrame.pixelFormat = pixelFormat; - auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status); - if (m_status != CS_OK) { - return 0; - } - image = - cv::Mat{rawFrame.height, rawFrame.width, - GetCvFormat(static_cast(rawFrame.pixelFormat)), - rawFrame.data, static_cast(rawFrame.stride)}; - return timestamp; -} - -inline uint64_t CvSink::GrabFrameDirectLastTime(cv::Mat& image, - uint64_t lastFrameTime, - double timeout) { - rawFrame.height = 0; - rawFrame.width = 0; - rawFrame.stride = 0; - rawFrame.pixelFormat = pixelFormat; - auto timestamp = GrabSinkFrameTimeoutLastTime(m_handle, rawFrame, timeout, - lastFrameTime, &m_status); - if (m_status != CS_OK) { - return 0; - } - image = - cv::Mat{rawFrame.height, rawFrame.width, - GetCvFormat(static_cast(rawFrame.pixelFormat)), - rawFrame.data, static_cast(rawFrame.stride)}; - return timestamp; -} - -inline uint64_t CvSink::LastFrameTime() { - return rawFrame.timestamp; -} - -inline WPI_TimestampSource CvSink::LastFrameTimeSource() { - return static_cast(rawFrame.timestampSrc); -} - -} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/cscore_oo.hpp b/cscore/src/main/native/include/wpi/cs/cscore_oo.hpp deleted file mode 100644 index 960abe1f34..0000000000 --- a/cscore/src/main/native/include/wpi/cs/cscore_oo.hpp +++ /dev/null @@ -1,1500 +0,0 @@ -// 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. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "wpi/cs/cscore_cpp.hpp" - -namespace wpi::cs { - -/** - * @defgroup cscore_oo cscore C++ object-oriented API - * - * Recommended interface for C++, identical to Java API. - * - *

The classes are RAII and handle reference counting internally. - * - * @{ - */ - -// Forward declarations so friend declarations work correctly -class ImageSource; -class VideoEvent; -class VideoSink; -class VideoSource; - -/** - * A source or sink property. - */ -class VideoProperty { - friend class ImageSource; - friend class VideoEvent; - friend class VideoSink; - friend class VideoSource; - - public: - enum Kind { - /// No specific property. - kNone = CS_PROP_NONE, - /// Boolean property. - kBoolean = CS_PROP_BOOLEAN, - /// Integer property. - kInteger = CS_PROP_INTEGER, - /// String property. - kString = CS_PROP_STRING, - /// Enum property. - kEnum = CS_PROP_ENUM - }; - - VideoProperty() = default; - - /** - * Returns property name. - * - * @return Property name. - */ - std::string GetName() const { - m_status = 0; - return GetPropertyName(m_handle, &m_status); - } - - /** - * Returns property kind. - * - * @return Property kind. - */ - Kind GetKind() const { return m_kind; } - - /** - * Returns true if property is valid. - * - * @return True if property is valid. - */ - explicit operator bool() const { return m_kind != kNone; } - - /** - * Returns true if property is a boolean. - * - * @return True if property is a boolean. - */ - bool IsBoolean() const { return m_kind == kBoolean; } - - /** - * Returns true if property is an integer. - * - * @return True if property is an integer. - */ - bool IsInteger() const { return m_kind == kInteger; } - - /** - * Returns true if property is a string. - * - * @return True if property is a string. - */ - bool IsString() const { return m_kind == kString; } - - /** - * Returns true if property is an enum. - * - * @return True if property is an enum. - */ - bool IsEnum() const { return m_kind == kEnum; } - - /** - * Returns property value. - * - * @return Property value. - */ - int Get() const { - m_status = 0; - return GetProperty(m_handle, &m_status); - } - - /** - * Sets property value. - * - * @param value Property value. - */ - void Set(int value) { - m_status = 0; - SetProperty(m_handle, value, &m_status); - } - - /** - * Returns property minimum value. - * - * @return Property minimum value. - */ - int GetMin() const { - m_status = 0; - return GetPropertyMin(m_handle, &m_status); - } - - /** - * Returns property maximum value. - * - * @return Property maximum value. - */ - int GetMax() const { - m_status = 0; - return GetPropertyMax(m_handle, &m_status); - } - - /** - * Returns property step size. - * - * @return Property step size. - */ - int GetStep() const { - m_status = 0; - return GetPropertyStep(m_handle, &m_status); - } - - /** - * Returns property default value. - * - * @return Property default value. - */ - int GetDefault() const { - m_status = 0; - return GetPropertyDefault(m_handle, &m_status); - } - - /** - * Returns the string property value. - * - *

This function is string-specific. - * - * @return The string property value. - */ - std::string GetString() const { - m_status = 0; - return GetStringProperty(m_handle, &m_status); - } - - /** - * Returns the string property value as a reference to the given buffer. - * - * This function is string-specific. - * - * @param buf The backing storage to which to write the property value. - * @return The string property value as a reference to the given buffer. - */ - std::string_view GetString(wpi::util::SmallVectorImpl& buf) const { - m_status = 0; - return GetStringProperty(m_handle, buf, &m_status); - } - - /** - * Sets the string property value. - * - * This function is string-specific. - * - * @param value String property value. - */ - void SetString(std::string_view value) { - m_status = 0; - SetStringProperty(m_handle, value, &m_status); - } - - /** - * Returns the possible values for the enum property value. - * - * This function is enum-specific. - * - * @return The possible values for the enum property value. - */ - std::vector GetChoices() const { - m_status = 0; - return GetEnumPropertyChoices(m_handle, &m_status); - } - - /** - * Returns the last status. - * - * @return The last status. - */ - CS_Status GetLastStatus() const { return m_status; } - - private: - explicit VideoProperty(CS_Property handle) : m_handle(handle) { - m_status = 0; - if (handle == 0) { - m_kind = kNone; - } else { - m_kind = static_cast( - static_cast(GetPropertyKind(handle, &m_status))); - } - } - - VideoProperty(CS_Property handle, Kind kind) - : m_handle(handle), m_kind(kind) {} - - mutable CS_Status m_status{0}; - CS_Property m_handle{0}; - Kind m_kind{kNone}; -}; - -/** - * A source for video that provides a sequence of frames. - */ -class VideoSource { - friend class VideoEvent; - friend class VideoSink; - - public: - /** - * Video source kind. - */ - enum Kind { - /// Unknown video source. - kUnknown = CS_SOURCE_UNKNOWN, - /// USB video source. - kUsb = CS_SOURCE_USB, - /// HTTP video source. - kHttp = CS_SOURCE_HTTP, - /// CV video source. - kCv = CS_SOURCE_CV, - /// Raw video source. - kRaw = CS_SOURCE_RAW, - }; - - /** Connection strategy. Used for SetConnectionStrategy(). */ - enum ConnectionStrategy { - /** - * Automatically connect or disconnect based on whether any sinks are - * connected to this source. This is the default behavior. - */ - kConnectionAutoManage = CS_CONNECTION_AUTO_MANAGE, - - /** - * Try to keep the connection open regardless of whether any sinks are - * connected. - */ - kConnectionKeepOpen = CS_CONNECTION_KEEP_OPEN, - - /** - * Never open the connection. If this is set when the connection is open, - * close the connection. - */ - kConnectionForceClose = CS_CONNECTION_FORCE_CLOSE - }; - - VideoSource() noexcept = default; - - VideoSource(const VideoSource& source) - : m_handle( - source.m_handle == 0 ? 0 : CopySource(source.m_handle, &m_status)) { - } - - VideoSource(VideoSource&& other) noexcept : VideoSource() { - swap(*this, other); - } - - VideoSource& operator=(VideoSource other) noexcept { - swap(*this, other); - return *this; - } - - ~VideoSource() { - m_status = 0; - if (m_handle != 0) { - ReleaseSource(m_handle, &m_status); - } - } - - explicit operator bool() const { return m_handle != 0; } - - int GetHandle() const { return m_handle; } - - bool operator==(const VideoSource& other) const { - return m_handle == other.m_handle; - } - - /** - * Get the kind of the source. - */ - Kind GetKind() const { - m_status = 0; - return static_cast(GetSourceKind(m_handle, &m_status)); - } - - /** - * Get the name of the source. The name is an arbitrary identifier - * provided when the source is created, and should be unique. - */ - std::string GetName() const { - m_status = 0; - return GetSourceName(m_handle, &m_status); - } - - /** - * Get the source description. This is source-kind specific. - */ - std::string GetDescription() const { - m_status = 0; - return GetSourceDescription(m_handle, &m_status); - } - - /** - * Get the last time a frame was captured. - * This uses the same time base as wpi::util::Now(). - * - * @return Time in 1 us increments. - */ - uint64_t GetLastFrameTime() const { - m_status = 0; - return GetSourceLastFrameTime(m_handle, &m_status); - } - - /** - * Sets the connection strategy. By default, the source will automatically - * connect or disconnect based on whether any sinks are connected. - * - *

This function is non-blocking; look for either a connection open or - * close event or call IsConnected() to determine the connection state. - * - * @param strategy connection strategy (auto, keep open, or force close) - */ - void SetConnectionStrategy(ConnectionStrategy strategy) { - m_status = 0; - SetSourceConnectionStrategy( - m_handle, - static_cast(static_cast(strategy)), - &m_status); - } - - /** - * Is the source currently connected to whatever is providing the images? - */ - bool IsConnected() const { - m_status = 0; - return IsSourceConnected(m_handle, &m_status); - } - - /** - * Gets source enable status. This is determined with a combination of - * connection strategy and the number of sinks connected. - * - * @return True if enabled, false otherwise. - */ - bool IsEnabled() const { - m_status = 0; - return IsSourceEnabled(m_handle, &m_status); - } - - /** Get a property. - * - * @param name Property name - * @return Property contents (of kind Property::kNone if no property with - * the given name exists) - */ - VideoProperty GetProperty(std::string_view name) { - m_status = 0; - return VideoProperty{GetSourceProperty(m_handle, name, &m_status)}; - } - - /** - * Enumerate all properties of this source. - */ - std::vector EnumerateProperties() const; - - /** - * Get the current video mode. - */ - VideoMode GetVideoMode() const { - m_status = 0; - return GetSourceVideoMode(m_handle, &m_status); - } - - /** - * Set the video mode. - * - * @param mode Video mode - */ - bool SetVideoMode(const VideoMode& mode) { - m_status = 0; - return SetSourceVideoMode(m_handle, mode, &m_status); - } - - /** - * Set the video mode. - * - * @param pixelFormat desired pixel format - * @param width desired width - * @param height desired height - * @param fps desired FPS - * @return True if set successfully - */ - bool SetVideoMode(VideoMode::PixelFormat pixelFormat, int width, int height, - int fps) { - m_status = 0; - return SetSourceVideoMode( - m_handle, VideoMode{pixelFormat, width, height, fps}, &m_status); - } - - /** - * Set the pixel format. - * - * @param pixelFormat desired pixel format - * @return True if set successfully - */ - bool SetPixelFormat(VideoMode::PixelFormat pixelFormat) { - m_status = 0; - return SetSourcePixelFormat(m_handle, pixelFormat, &m_status); - } - - /** - * Set the resolution. - * - * @param width desired width - * @param height desired height - * @return True if set successfully - */ - bool SetResolution(int width, int height) { - m_status = 0; - return SetSourceResolution(m_handle, width, height, &m_status); - } - - /** - * Set the frames per second (FPS). - * - * @param fps desired FPS - * @return True if set successfully - */ - bool SetFPS(int fps) { - m_status = 0; - return SetSourceFPS(m_handle, fps, &m_status); - } - - /** - * Set video mode and properties from a JSON configuration string. - * - * The format of the JSON input is: - * - *

-   * {
-   *     "pixel format": "MJPEG", "YUYV", etc
-   *     "width": video mode width
-   *     "height": video mode height
-   *     "fps": video mode fps
-   *     "brightness": percentage brightness
-   *     "white balance": "auto", "hold", or value
-   *     "exposure": "auto", "hold", or value
-   *     "properties": [
-   *         {
-   *             "name": property name
-   *             "value": property value
-   *         }
-   *     ]
-   * }
-   * 
- * - * @param config configuration - * @return True if set successfully - */ - bool SetConfigJson(std::string_view config) { - m_status = 0; - return SetSourceConfigJson(m_handle, config, &m_status); - } - - /** - * Set video mode and properties from a JSON configuration object. - * - * @param config configuration - * @return True if set successfully - */ - bool SetConfigJson(const wpi::util::json& config) { - m_status = 0; - return SetSourceConfigJson(m_handle, config, &m_status); - } - - /** - * Get a JSON configuration string. - * - * @return JSON configuration string - */ - std::string GetConfigJson() const { - m_status = 0; - return GetSourceConfigJson(m_handle, &m_status); - } - - /** - * Get a JSON configuration object. - * - * @return JSON configuration object - */ - wpi::util::json GetConfigJsonObject() const; - - /** - * Get the actual FPS. - * - *

SetTelemetryPeriod() must be called for this to be valid. - * - * @return Actual FPS averaged over the telemetry period. - */ - double GetActualFPS() const { - m_status = 0; - return wpi::cs::GetTelemetryAverageValue( - m_handle, CS_SOURCE_FRAMES_RECEIVED, &m_status); - } - - /** - * Get the data rate (in bytes per second). - * - *

SetTelemetryPeriod() must be called for this to be valid. - * - * @return Data rate averaged over the telemetry period. - */ - double GetActualDataRate() const { - m_status = 0; - return wpi::cs::GetTelemetryAverageValue(m_handle, CS_SOURCE_BYTES_RECEIVED, - &m_status); - } - - /** - * Enumerate all known video modes for this source. - */ - std::vector EnumerateVideoModes() const { - CS_Status status = 0; - return EnumerateSourceVideoModes(m_handle, &status); - } - - CS_Status GetLastStatus() const { return m_status; } - - /** - * Enumerate all sinks connected to this source. - * - * @return Vector of sinks. - */ - std::vector EnumerateSinks(); - - /** - * Enumerate all existing sources. - * - * @return Vector of sources. - */ - static std::vector EnumerateSources(); - - friend void swap(VideoSource& first, VideoSource& second) noexcept { - using std::swap; - swap(first.m_status, second.m_status); - swap(first.m_handle, second.m_handle); - } - - protected: - explicit VideoSource(CS_Source handle) : m_handle(handle) {} - - mutable CS_Status m_status = 0; - - /// Video source handle. - CS_Source m_handle{0}; -}; - -/** - * A source that represents a video camera. - */ -class VideoCamera : public VideoSource { - public: - /** - * White balance. - */ - enum WhiteBalance { - /// Fixed indoor white balance. - kFixedIndoor = 3000, - /// Fixed outdoor white balance 1. - kFixedOutdoor1 = 4000, - /// Fixed outdoor white balance 2. - kFixedOutdoor2 = 5000, - /// Fixed fluorescent white balance 1. - kFixedFluorescent1 = 5100, - /// Fixed fluorescent white balance 2. - kFixedFlourescent2 = 5200 - }; - - VideoCamera() = default; - - /** - * Set the brightness, as a percentage (0-100). - */ - void SetBrightness(int brightness) { - m_status = 0; - SetCameraBrightness(m_handle, brightness, &m_status); - } - - /** - * Get the brightness, as a percentage (0-100). - */ - int GetBrightness() { - m_status = 0; - return GetCameraBrightness(m_handle, &m_status); - } - - /** - * Set the white balance to auto. - */ - void SetWhiteBalanceAuto() { - m_status = 0; - SetCameraWhiteBalanceAuto(m_handle, &m_status); - } - - /** - * Set the white balance to hold current. - */ - void SetWhiteBalanceHoldCurrent() { - m_status = 0; - SetCameraWhiteBalanceHoldCurrent(m_handle, &m_status); - } - - /** - * Set the white balance to manual, with specified color temperature. - */ - void SetWhiteBalanceManual(int value) { - m_status = 0; - SetCameraWhiteBalanceManual(m_handle, value, &m_status); - } - - /** - * Set the exposure to auto aperture. - */ - void SetExposureAuto() { - m_status = 0; - SetCameraExposureAuto(m_handle, &m_status); - } - - /** - * Set the exposure to hold current. - */ - void SetExposureHoldCurrent() { - m_status = 0; - SetCameraExposureHoldCurrent(m_handle, &m_status); - } - - /** - * Set the exposure to manual, as a percentage (0-100). - */ - void SetExposureManual(int value) { - m_status = 0; - SetCameraExposureManual(m_handle, value, &m_status); - } - - protected: - explicit VideoCamera(CS_Source handle) : VideoSource(handle) {} -}; - -/** - * A source that represents a USB camera. - */ -class UsbCamera : public VideoCamera { - public: - UsbCamera() = default; - - /** - * Create a source for a USB camera based on device number. - * - * @param name Source name (arbitrary unique identifier) - * @param dev Device number (e.g. 0 for /dev/video0) - */ - UsbCamera(std::string_view name, int dev) { - m_handle = CreateUsbCameraDev(name, dev, &m_status); - } - - /** - * Create a source for a USB camera based on device path. - * - * @param name Source name (arbitrary unique identifier) - * @param path Path to device (e.g. "/dev/video0" on Linux) - */ - UsbCamera(std::string_view name, std::string_view path) { - m_handle = CreateUsbCameraPath(name, path, &m_status); - } - - /** - * Enumerate USB cameras on the local system. - * - * @return Vector of USB camera information (one for each camera) - */ - static std::vector EnumerateUsbCameras() { - CS_Status status = 0; - return ::wpi::cs::EnumerateUsbCameras(&status); - } - - /** - * Change the path to the device. - */ - void SetPath(std::string_view path) { - m_status = 0; - return ::wpi::cs::SetUsbCameraPath(m_handle, path, &m_status); - } - - /** - * Get the path to the device. - */ - std::string GetPath() const { - m_status = 0; - return ::wpi::cs::GetUsbCameraPath(m_handle, &m_status); - } - - /** - * Get the full camera information for the device. - */ - UsbCameraInfo GetInfo() const { - m_status = 0; - return ::wpi::cs::GetUsbCameraInfo(m_handle, &m_status); - } - - /** - * Set how verbose the camera connection messages are. - * - * @param level 0=don't display Connecting message, 1=do display message - */ - void SetConnectVerbose(int level) { - m_status = 0; - SetProperty(GetSourceProperty(m_handle, "connect_verbose", &m_status), - level, &m_status); - } -}; - -/** - * A source that represents a MJPEG-over-HTTP (IP) camera. - */ -class HttpCamera : public VideoCamera { - public: - /** - * HTTP camera kind. - */ - enum HttpCameraKind { - /// Unknown camera kind. - kUnknown = CS_HTTP_UNKNOWN, - /// MJPG Streamer camera. - kMJPGStreamer = CS_HTTP_MJPGSTREAMER, - /// CS Core camera. - kCSCore = CS_HTTP_CSCORE - }; - - /** - * Create a source for a MJPEG-over-HTTP (IP) camera. - * - * @param name Source name (arbitrary unique identifier) - * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") - * @param kind Camera kind (e.g. kCSCore) - */ - HttpCamera(std::string_view name, std::string_view url, - HttpCameraKind kind = kUnknown) { - m_handle = CreateHttpCamera( - name, url, static_cast(static_cast(kind)), - &m_status); - } - - /** - * Create a source for a MJPEG-over-HTTP (IP) camera. - * - * @param name Source name (arbitrary unique identifier) - * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") - * @param kind Camera kind (e.g. kCSCore) - */ - HttpCamera(std::string_view name, const char* url, - HttpCameraKind kind = kUnknown) { - m_handle = CreateHttpCamera( - name, url, static_cast(static_cast(kind)), - &m_status); - } - - /** - * Create a source for a MJPEG-over-HTTP (IP) camera. - * - * @param name Source name (arbitrary unique identifier) - * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg") - * @param kind Camera kind (e.g. kCSCore) - */ - HttpCamera(std::string_view name, const std::string& url, - HttpCameraKind kind = kUnknown) - : HttpCamera(name, std::string_view{url}, kind) {} - - /** - * Create a source for a MJPEG-over-HTTP (IP) camera. - * - * @param name Source name (arbitrary unique identifier) - * @param urls Array of Camera URLs - * @param kind Camera kind (e.g. kCSCore) - */ - HttpCamera(std::string_view name, std::span urls, - HttpCameraKind kind = kUnknown) { - m_handle = CreateHttpCamera( - name, urls, static_cast(static_cast(kind)), - &m_status); - } - - /** - * Create a source for a MJPEG-over-HTTP (IP) camera. - * - * @param name Source name (arbitrary unique identifier) - * @param urls Array of Camera URLs - * @param kind Camera kind (e.g. kCSCore) - */ - template - HttpCamera(std::string_view name, std::initializer_list urls, - HttpCameraKind kind = kUnknown) { - std::vector vec; - vec.reserve(urls.size()); - for (const auto& url : urls) { - vec.emplace_back(url); - } - m_handle = CreateHttpCamera( - name, vec, static_cast(static_cast(kind)), - &m_status); - } - - /** - * Get the kind of HTTP camera. - * - *

Autodetection can result in returning a different value than the camera - * was created with. - */ - HttpCameraKind GetHttpCameraKind() const { - m_status = 0; - return static_cast( - static_cast(::wpi::cs::GetHttpCameraKind(m_handle, &m_status))); - } - - /** - * Change the URLs used to connect to the camera. - */ - void SetUrls(std::span urls) { - m_status = 0; - ::wpi::cs::SetHttpCameraUrls(m_handle, urls, &m_status); - } - - /** - * Change the URLs used to connect to the camera. - */ - template - void SetUrls(std::initializer_list urls) { - std::vector vec; - vec.reserve(urls.size()); - for (const auto& url : urls) { - vec.emplace_back(url); - } - m_status = 0; - ::wpi::cs::SetHttpCameraUrls(m_handle, vec, &m_status); - } - - /** - * Get the URLs used to connect to the camera. - */ - std::vector GetUrls() const { - m_status = 0; - return ::wpi::cs::GetHttpCameraUrls(m_handle, &m_status); - } -}; - -/** - * A base class for single image providing sources. - */ -class ImageSource : public VideoSource { - protected: - ImageSource() = default; - - public: - /** - * Signal sinks that an error has occurred. This should be called instead - * of NotifyFrame when an error occurs. - * - * @param msg Notification message. - */ - void NotifyError(std::string_view msg) { - m_status = 0; - NotifySourceError(m_handle, msg, &m_status); - } - - /** - * Set source connection status. Defaults to true. - * - * @param connected True for connected, false for disconnected - */ - void SetConnected(bool connected) { - m_status = 0; - SetSourceConnected(m_handle, connected, &m_status); - } - - /** - * Set source description. - * - * @param description Description - */ - void SetDescription(std::string_view description) { - m_status = 0; - SetSourceDescription(m_handle, description, &m_status); - } - - /** - * Create a property. - * - * @param name Property name - * @param kind Property kind - * @param minimum Minimum value - * @param maximum Maximum value - * @param step Step value - * @param defaultValue Default value - * @param value Current value - * @return Property - */ - VideoProperty CreateProperty(std::string_view name, VideoProperty::Kind kind, - int minimum, int maximum, int step, - int defaultValue, int value) { - m_status = 0; - return VideoProperty{CreateSourceProperty( - m_handle, name, static_cast(static_cast(kind)), - minimum, maximum, step, defaultValue, value, &m_status)}; - } - - /** - * Create an integer property. - * - * @param name Property name - * @param minimum Minimum value - * @param maximum Maximum value - * @param step Step value - * @param defaultValue Default value - * @param value Current value - * @return Property - */ - VideoProperty CreateIntegerProperty(std::string_view name, int minimum, - int maximum, int step, int defaultValue, - int value) { - m_status = 0; - return VideoProperty{CreateSourceProperty( - m_handle, name, - static_cast( - static_cast(VideoProperty::Kind::kInteger)), - minimum, maximum, step, defaultValue, value, &m_status)}; - } - - /** - * Create a boolean property. - * - * @param name Property name - * @param defaultValue Default value - * @param value Current value - * @return Property - */ - VideoProperty CreateBooleanProperty(std::string_view name, bool defaultValue, - bool value) { - m_status = 0; - return VideoProperty{CreateSourceProperty( - m_handle, name, - static_cast( - static_cast(VideoProperty::Kind::kBoolean)), - 0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)}; - } - - /** - * Create a string property. - * - * @param name Property name - * @param value Current value - * @return Property - */ - VideoProperty CreateStringProperty(std::string_view name, - std::string_view value) { - m_status = 0; - auto prop = VideoProperty{CreateSourceProperty( - m_handle, name, - static_cast( - static_cast(VideoProperty::Kind::kString)), - 0, 0, 0, 0, 0, &m_status)}; - prop.SetString(value); - return prop; - } - - /** - * Configure enum property choices. - * - * @param property Property - * @param choices Choices - */ - void SetEnumPropertyChoices(const VideoProperty& property, - std::span choices) { - m_status = 0; - SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices, - &m_status); - } - - /** - * Configure enum property choices. - * - * @param property Property - * @param choices Choices - */ - template - void SetEnumPropertyChoices(const VideoProperty& property, - std::initializer_list choices) { - std::vector vec; - vec.reserve(choices.size()); - for (const auto& choice : choices) { - vec.emplace_back(choice); - } - m_status = 0; - SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status); - } -}; - -/** - * A sink for video that accepts a sequence of frames. - */ -class VideoSink { - friend class VideoEvent; - friend class VideoSource; - - public: - enum Kind { - /// Unknown sink type. - kUnknown = CS_SINK_UNKNOWN, - /// MJPEG video sink. - kMjpeg = CS_SINK_MJPEG, - /// CV video sink. - kCv = CS_SINK_CV, - /// Raw video sink. - kRaw = CS_SINK_RAW, - }; - - VideoSink() noexcept = default; - - VideoSink(const VideoSink& sink) - : m_handle(sink.m_handle == 0 ? 0 : CopySink(sink.m_handle, &m_status)) {} - - VideoSink(VideoSink&& other) noexcept : VideoSink() { swap(*this, other); } - - VideoSink& operator=(VideoSink other) noexcept { - swap(*this, other); - return *this; - } - - ~VideoSink() { - m_status = 0; - if (m_handle != 0) { - ReleaseSink(m_handle, &m_status); - } - } - - /** - * Returns true if the VideoSink is valid. - * - * @return True if the VideoSink is valid. - */ - explicit operator bool() const { return m_handle != 0; } - - /** - * Returns the VideoSink handle. - * - * @return The VideoSink handle. - */ - int GetHandle() const { return m_handle; } - - bool operator==(const VideoSink& other) const { - return m_handle == other.m_handle; - } - - /** - * Get the kind of the sink. - */ - Kind GetKind() const { - m_status = 0; - return static_cast(GetSinkKind(m_handle, &m_status)); - } - - /** - * Get the name of the sink. The name is an arbitrary identifier - * provided when the sink is created, and should be unique. - */ - std::string GetName() const { - m_status = 0; - return GetSinkName(m_handle, &m_status); - } - - /** - * Get the sink description. This is sink-kind specific. - */ - std::string GetDescription() const { - m_status = 0; - return GetSinkDescription(m_handle, &m_status); - } - - /** - * Get a property of the sink. - * - * @param name Property name - * @return Property (kind Property::kNone if no property with - * the given name exists) - */ - VideoProperty GetProperty(std::string_view name) { - m_status = 0; - return VideoProperty{GetSinkProperty(m_handle, name, &m_status)}; - } - - /** - * Enumerate all properties of this sink. - */ - std::vector EnumerateProperties() const; - - /** - * Set properties from a JSON configuration string. - * - * The format of the JSON input is: - * - *

-   * {
-   *     "properties": [
-   *         {
-   *             "name": property name
-   *             "value": property value
-   *         }
-   *     ]
-   * }
-   * 
- * - * @param config configuration - * @return True if set successfully - */ - bool SetConfigJson(std::string_view config) { - m_status = 0; - return SetSinkConfigJson(m_handle, config, &m_status); - } - - /** - * Set properties from a JSON configuration object. - * - * @param config configuration - * @return True if set successfully - */ - bool SetConfigJson(const wpi::util::json& config) { - m_status = 0; - return SetSinkConfigJson(m_handle, config, &m_status); - } - - /** - * Get a JSON configuration string. - * - * @return JSON configuration string - */ - std::string GetConfigJson() const { - m_status = 0; - return GetSinkConfigJson(m_handle, &m_status); - } - - /** - * Get a JSON configuration object. - * - * @return JSON configuration object - */ - wpi::util::json GetConfigJsonObject() const; - - /** - * Configure which source should provide frames to this sink. Each sink - * can accept frames from only a single source, but a single source can - * provide frames to multiple clients. - * - * @param source Source - */ - void SetSource(VideoSource source) { - m_status = 0; - if (!source) { - SetSinkSource(m_handle, 0, &m_status); - } else { - SetSinkSource(m_handle, source.m_handle, &m_status); - } - } - - /** - * Get the connected source. - * - * @return Connected source (empty if none connected). - */ - VideoSource GetSource() const { - m_status = 0; - auto handle = GetSinkSource(m_handle, &m_status); - return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)}; - } - - /** - * Get a property of the associated source. - * - * @param name Property name - * @return Property (kind Property::kNone if no property with - * the given name exists or no source connected) - */ - VideoProperty GetSourceProperty(std::string_view name) { - m_status = 0; - return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)}; - } - - CS_Status GetLastStatus() const { return m_status; } - - /** - * Enumerate all existing sinks. - * - * @return Vector of sinks. - */ - static std::vector EnumerateSinks(); - - friend void swap(VideoSink& first, VideoSink& second) noexcept { - using std::swap; - swap(first.m_status, second.m_status); - swap(first.m_handle, second.m_handle); - } - - protected: - explicit VideoSink(CS_Sink handle) : m_handle(handle) {} - - mutable CS_Status m_status = 0; - CS_Sink m_handle{0}; -}; - -/** - * A sink that acts as a MJPEG-over-HTTP network server. - */ -class MjpegServer : public VideoSink { - public: - MjpegServer() = default; - - /** - * Create a MJPEG-over-HTTP server sink. - * - * @param name Sink name (arbitrary unique identifier) - * @param listenAddress TCP listen address (empty string for all addresses) - * @param port TCP port number - */ - MjpegServer(std::string_view name, std::string_view listenAddress, int port) { - m_handle = CreateMjpegServer(name, listenAddress, port, &m_status); - } - - /** - * Create a MJPEG-over-HTTP server sink. - * - * @param name Sink name (arbitrary unique identifier) - * @param port TCP port number - */ - MjpegServer(std::string_view name, int port) : MjpegServer(name, "", port) {} - - /** - * Get the listen address of the server. - */ - std::string GetListenAddress() const { - m_status = 0; - return wpi::cs::GetMjpegServerListenAddress(m_handle, &m_status); - } - - /** - * Get the port number of the server. - */ - int GetPort() const { - m_status = 0; - return wpi::cs::GetMjpegServerPort(m_handle, &m_status); - } - - /** - * Set the stream resolution for clients that don't specify it. - * - *

It is not necessary to set this if it is the same as the source - * resolution. - * - *

Setting this different than the source resolution will result in - * increased CPU usage, particularly for MJPEG source cameras, as it will - * decompress, resize, and recompress the image, instead of using the - * camera's MJPEG image directly. - * - * @param width width, 0 for unspecified - * @param height height, 0 for unspecified - */ - void SetResolution(int width, int height) { - m_status = 0; - SetProperty(GetSinkProperty(m_handle, "width", &m_status), width, - &m_status); - SetProperty(GetSinkProperty(m_handle, "height", &m_status), height, - &m_status); - } - - /** - * Set the stream frames per second (FPS) for clients that don't specify it. - * - *

It is not necessary to set this if it is the same as the source FPS. - * - * @param fps FPS, 0 for unspecified - */ - void SetFPS(int fps) { - m_status = 0; - SetProperty(GetSinkProperty(m_handle, "fps", &m_status), fps, &m_status); - } - - /** - * Set the compression for clients that don't specify it. - * - *

Setting this will result in increased CPU usage for MJPEG source cameras - * as it will decompress and recompress the image instead of using the - * camera's MJPEG image directly. - * - * @param quality JPEG compression quality (0-100), -1 for unspecified - */ - void SetCompression(int quality) { - m_status = 0; - SetProperty(GetSinkProperty(m_handle, "compression", &m_status), quality, - &m_status); - } - - /** - * Set the default compression used for non-MJPEG sources. If not set, - * 80 is used. This function has no effect on MJPEG source cameras; use - * SetCompression() instead to force recompression of MJPEG source images. - * - * @param quality JPEG compression quality (0-100) - */ - void SetDefaultCompression(int quality) { - m_status = 0; - SetProperty(GetSinkProperty(m_handle, "default_compression", &m_status), - quality, &m_status); - } -}; - -/** - * A base class for single image reading sinks. - */ -class ImageSink : public VideoSink { - protected: - ImageSink() = default; - - public: - /** - * Set sink description. - * - * @param description Description - */ - void SetDescription(std::string_view description) { - m_status = 0; - SetSinkDescription(m_handle, description, &m_status); - } - - /** - * Get error string. Call this if WaitForFrame() returns 0 to determine - * what the error is. - */ - std::string GetError() const { - m_status = 0; - return GetSinkError(m_handle, &m_status); - } - - /** - * Enable or disable getting new frames. - * - *

Disabling will cause processFrame (for callback-based CvSinks) to not - * be called and WaitForFrame() to not return. This can be used to save - * processor resources when frames are not needed. - */ - void SetEnabled(bool enabled) { - m_status = 0; - SetSinkEnabled(m_handle, enabled, &m_status); - } -}; - -/** - * An event generated by the library and provided to event listeners. - */ -class VideoEvent : public RawEvent { - public: - /** - * Returns the source associated with the event (if any). - * - * @return The source associated with the event (if any). - */ - VideoSource GetSource() const { - CS_Status status = 0; - return VideoSource{sourceHandle == 0 ? 0 - : CopySource(sourceHandle, &status)}; - } - - /** - * Returns the sink associated with the event (if any). - * - * @return The sink associated with the event (if any). - */ - VideoSink GetSink() const { - CS_Status status = 0; - return VideoSink{sinkHandle == 0 ? 0 : CopySink(sinkHandle, &status)}; - } - - /** - * Returns the property associated with the event (if any). - * - * @return The property associated with the event (if any). - */ - VideoProperty GetProperty() const { - return VideoProperty{propertyHandle, - static_cast(propertyKind)}; - } -}; - -/** - * An event listener. This calls back to a designated callback function when - * an event matching the specified mask is generated by the library. - */ -class VideoListener { - public: - VideoListener() = default; - - /** - * Create an event listener. - * - * @param callback Callback function - * @param eventMask Bitmask of VideoEvent::Kind values - * @param immediateNotify Whether callback should be immediately called with - * a representative set of events for the current library state. - */ - VideoListener(std::function callback, - int eventMask, bool immediateNotify) { - CS_Status status = 0; - m_handle = AddListener( - [=](const RawEvent& event) { - callback(static_cast(event)); - }, - eventMask, immediateNotify, &status); - } - - VideoListener(const VideoListener&) = delete; - VideoListener& operator=(const VideoListener&) = delete; - - VideoListener(VideoListener&& other) noexcept : VideoListener() { - swap(*this, other); - } - - VideoListener& operator=(VideoListener&& other) noexcept { - swap(*this, other); - return *this; - } - - ~VideoListener() { - CS_Status status = 0; - if (m_handle != 0) { - RemoveListener(m_handle, &status); - } - } - - friend void swap(VideoListener& first, VideoListener& second) noexcept { - using std::swap; - swap(first.m_handle, second.m_handle); - } - - private: - CS_Listener m_handle{0}; -}; - -/** @} */ - -} // namespace wpi::cs diff --git a/cscore/src/main/native/include/wpi/cs/cscore_raw.h b/cscore/src/main/native/include/wpi/cs/cscore_raw.h index cfeb840df0..0181b74f7e 100644 --- a/cscore/src/main/native/include/wpi/cs/cscore_raw.h +++ b/cscore/src/main/native/include/wpi/cs/cscore_raw.h @@ -4,20 +4,11 @@ #pragma once -#include - #include "wpi/cs/cscore_c.h" -// NOLINTBEGIN -#ifdef __cplusplus -#include "wpi/cs/cscore_oo.hpp" -#include "wpi/util/RawFrame.hpp" -#endif - #ifdef __cplusplus extern "C" { #endif -// NOLINTEND /** * @defgroup cscore_raw_cfunc Raw Image Functions @@ -50,192 +41,3 @@ CS_Source CS_CreateRawSource(const struct WPI_String* name, CS_Bool isCv, #ifdef __cplusplus } // extern "C" #endif - -#ifdef __cplusplus -namespace wpi::cs { - -/** - * @defgroup cscore_raw_func Raw Image Functions - * @{ - */ - -CS_Source CreateRawSource(std::string_view name, bool isCv, - const VideoMode& mode, CS_Status* status); - -CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status); -CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv, - std::function processFrame, - CS_Status* status); - -void PutSourceFrame(CS_Source source, const WPI_RawFrame& image, - CS_Status* status); -uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status); -uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout, - CS_Status* status); -uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image, - double timeout, uint64_t lastFrameTime, - CS_Status* status); - -/** - * A source for user code to provide video frames as raw bytes. - * - * This is a complex API, most cases should use CvSource. - */ -class RawSource : public ImageSource { - public: - RawSource() = default; - - /** - * Create a raw frame source. - * - * @param name Source name (arbitrary unique identifier) - * @param mode Video mode being generated - */ - RawSource(std::string_view name, const VideoMode& mode); - - /** - * Create a raw frame source. - * - * @param name Source name (arbitrary unique identifier) - * @param pixelFormat Pixel format - * @param width width - * @param height height - * @param fps fps - */ - RawSource(std::string_view name, VideoMode::PixelFormat pixelFormat, - int width, int height, int fps); - - protected: - /** - * Put a raw image and notify sinks. - * - * @param image raw frame image - */ - void PutFrame(wpi::util::RawFrame& image); -}; - -/** - * A sink for user code to accept video frames as raw bytes. - * - * This is a complex API, most cases should use CvSource. - */ -class RawSink : public ImageSink { - public: - RawSink() = default; - - /** - * Create a sink for accepting raw images. - * - *

GrabFrame() must be called on the created sink to get each new - * image. - * - * @param name Source name (arbitrary unique identifier) - */ - explicit RawSink(std::string_view name); - - /** - * Create a sink for accepting raws images in a separate thread. - * - *

A thread will be created that calls WaitForFrame() and calls the - * processFrame() callback each time a new frame arrives. - * - * @param name Source name (arbitrary unique identifier) - * @param processFrame Frame processing function; will be called with a - * time=0 if an error occurred. processFrame should call GetImage() - * or GetError() as needed, but should not call (except in very - * unusual circumstances) WaitForImage(). - */ - RawSink(std::string_view name, - std::function processFrame); - - protected: - /** - * Wait for the next frame and get the image. - * Times out (returning 0) after timeout seconds. - * The provided image will have three 8-bit channels stored in BGR order. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrame(wpi::util::RawFrame& image, double timeout = 0.225) const; - - /** - * Wait for the next frame and get the image. May block forever. - * The provided image will have three 8-bit channels stored in BGR order. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameNoTimeout(wpi::util::RawFrame& image) const; - - /** - * Wait for the next frame and get the image. May block forever. - * The provided image will have three 8-bit channels stored in BGR order. - * - *

If lastFrameTime is provided and non-zero, the sink will fill image with - * the first frame from the source that is not equal to lastFrameTime. If - * lastFrameTime is zero, the time of the current frame owned by the CvSource - * is used, and this function will block until the connected CvSource provides - * a new frame. - * - * @return Frame time, or 0 on error (call GetError() to obtain the error - * message); the frame time is in the same time base as - * wpi::util::Now(), and is in 1 us increments. - */ - [[nodiscard]] - uint64_t GrabFrameLastTime(wpi::util::RawFrame& image, uint64_t lastFrameTime, - double timeout = 0.225) const; -}; - -inline RawSource::RawSource(std::string_view name, const VideoMode& mode) { - m_handle = CreateRawSource(name, false, mode, &m_status); -} - -inline RawSource::RawSource(std::string_view name, - VideoMode::PixelFormat format, int width, - int height, int fps) { - m_handle = CreateRawSource(name, false, VideoMode{format, width, height, fps}, - &m_status); -} - -inline void RawSource::PutFrame(wpi::util::RawFrame& image) { - m_status = 0; - PutSourceFrame(m_handle, image, &m_status); -} - -inline RawSink::RawSink(std::string_view name) { - m_handle = CreateRawSink(name, false, &m_status); -} - -inline RawSink::RawSink(std::string_view name, - std::function processFrame) { - m_handle = CreateRawSinkCallback(name, false, processFrame, &m_status); -} - -inline uint64_t RawSink::GrabFrame(wpi::util::RawFrame& image, - double timeout) const { - m_status = 0; - return GrabSinkFrameTimeout(m_handle, image, timeout, &m_status); -} - -inline uint64_t RawSink::GrabFrameNoTimeout(wpi::util::RawFrame& image) const { - m_status = 0; - return GrabSinkFrame(m_handle, image, &m_status); -} - -inline uint64_t RawSink::GrabFrameLastTime(wpi::util::RawFrame& image, - uint64_t lastFrameTime, - double timeout) const { - m_status = 0; - return GrabSinkFrameTimeoutLastTime(m_handle, image, timeout, lastFrameTime, - &m_status); -} -/** @} */ - -} // namespace wpi::cs - -#endif diff --git a/cscore/src/main/native/include/wpi/cs/cscore_raw.hpp b/cscore/src/main/native/include/wpi/cs/cscore_raw.hpp new file mode 100644 index 0000000000..9464181bc1 --- /dev/null +++ b/cscore/src/main/native/include/wpi/cs/cscore_raw.hpp @@ -0,0 +1,42 @@ +// 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. + +#pragma once + +#include + +#include +#include + +#include "wpi/cs/VideoMode.hpp" +#include "wpi/cs/cscore_c.h" +#include "wpi/util/RawFrame.h" + +namespace wpi::cs { + +/** + * @defgroup cscore_raw_func Raw Image Functions + * @{ + */ + +CS_Source CreateRawSource(std::string_view name, bool isCv, + const VideoMode& mode, CS_Status* status); + +CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status); +CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv, + std::function processFrame, + CS_Status* status); + +void PutSourceFrame(CS_Source source, const WPI_RawFrame& image, + CS_Status* status); +uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status); +uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout, + CS_Status* status); +uint64_t GrabSinkFrameTimeoutLastTime(CS_Sink sink, WPI_RawFrame& image, + double timeout, uint64_t lastFrameTime, + CS_Status* status); + +/** @} */ + +} // namespace wpi::cs diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp index 6e673c962e..daeaf28f46 100644 --- a/cscore/src/main/native/linux/UsbCameraImpl.cpp +++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp @@ -37,6 +37,7 @@ #include "wpi/util/SmallString.hpp" #include "wpi/util/StringExtras.hpp" #include "wpi/util/fs.hpp" +#include "wpi/util/raw_istream.hpp" #include "wpi/util/timestamp.hpp" using namespace wpi::cs; @@ -70,47 +71,47 @@ static inline struct v4l2_fract FPSToFract(int fps) { } // Conversion from v4l2_format pixelformat to VideoMode::PixelFormat -static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) { +static wpi::util::PixelFormat ToPixelFormat(__u32 pixelFormat) { switch (pixelFormat) { case V4L2_PIX_FMT_MJPEG: - return VideoMode::kMJPEG; + return wpi::util::PixelFormat::kMJPEG; case V4L2_PIX_FMT_YUYV: - return VideoMode::kYUYV; + return wpi::util::PixelFormat::kYUYV; case V4L2_PIX_FMT_RGB565: - return VideoMode::kRGB565; + return wpi::util::PixelFormat::kRGB565; case V4L2_PIX_FMT_BGR24: - return VideoMode::kBGR; + return wpi::util::PixelFormat::kBGR; case V4L2_PIX_FMT_ABGR32: - return VideoMode::kBGRA; + return wpi::util::PixelFormat::kBGRA; case V4L2_PIX_FMT_GREY: - return VideoMode::kGray; + return wpi::util::PixelFormat::kGray; case V4L2_PIX_FMT_Y16: - return VideoMode::kY16; + return wpi::util::PixelFormat::kY16; case V4L2_PIX_FMT_UYVY: - return VideoMode::kUYVY; + return wpi::util::PixelFormat::kUYVY; default: - return VideoMode::kUnknown; + return wpi::util::PixelFormat::kUnknown; } } // Conversion from VideoMode::PixelFormat to v4l2_format pixelformat -static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) { +static __u32 FromPixelFormat(wpi::util::PixelFormat pixelFormat) { switch (pixelFormat) { - case VideoMode::kMJPEG: + case wpi::util::PixelFormat::kMJPEG: return V4L2_PIX_FMT_MJPEG; - case VideoMode::kYUYV: + case wpi::util::PixelFormat::kYUYV: return V4L2_PIX_FMT_YUYV; - case VideoMode::kRGB565: + case wpi::util::PixelFormat::kRGB565: return V4L2_PIX_FMT_RGB565; - case VideoMode::kBGR: + case wpi::util::PixelFormat::kBGR: return V4L2_PIX_FMT_BGR24; - case VideoMode::kBGRA: + case wpi::util::PixelFormat::kBGRA: return V4L2_PIX_FMT_ABGR32; - case VideoMode::kGray: + case wpi::util::PixelFormat::kGray: return V4L2_PIX_FMT_GREY; - case VideoMode::kY16: + case wpi::util::PixelFormat::kY16: return V4L2_PIX_FMT_Y16; - case VideoMode::kUYVY: + case wpi::util::PixelFormat::kUYVY: return V4L2_PIX_FMT_UYVY; default: return 0; @@ -553,7 +554,7 @@ void UsbCameraImpl::CameraThreadMain() { int width = m_mode.width; int height = m_mode.height; bool good = true; - if (m_mode.pixelFormat == VideoMode::kMJPEG && + if (m_mode.pixelFormat == wpi::util::PixelFormat::kMJPEG && !GetJpegSize(image, &width, &height)) { SWARNING("invalid JPEG image received from camera"); good = false; @@ -603,8 +604,8 @@ void UsbCameraImpl::CameraThreadMain() { "Got valid copy time for frame - default to wpi::util::Now"); } - PutFrame(static_cast(m_mode.pixelFormat), - width, height, image, frameTime, timeSource); + PutFrame(m_mode.pixelFormat, width, height, image, frameTime, + timeSource); } } @@ -821,7 +822,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode( std::unique_lock& lock, const Message& msg) { VideoMode newMode; if (msg.kind == Message::kCmdSetMode) { - newMode.pixelFormat = msg.data[0]; + newMode.pixelFormat = static_cast(msg.data[0]); newMode.width = msg.data[1]; newMode.height = msg.data[2]; newMode.fps = msg.data[3]; @@ -830,7 +831,7 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode( m_modeSetFPS = true; } else if (msg.kind == Message::kCmdSetPixelFormat) { newMode = m_mode; - newMode.pixelFormat = msg.data[0]; + newMode.pixelFormat = static_cast(msg.data[0]); m_modeSetPixelFormat = true; } else if (msg.kind == Message::kCmdSetResolution) { newMode = m_mode; @@ -1017,22 +1018,21 @@ void UsbCameraImpl::DeviceSetMode() { : 0; #endif vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vfmt.fmt.pix.pixelformat = - FromPixelFormat(static_cast(m_mode.pixelFormat)); + vfmt.fmt.pix.pixelformat = FromPixelFormat(m_mode.pixelFormat); if (vfmt.fmt.pix.pixelformat == 0) { SWARNING("could not set format {}, defaulting to MJPEG", - m_mode.pixelFormat); + static_cast(m_mode.pixelFormat)); vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; } vfmt.fmt.pix.width = m_mode.width; vfmt.fmt.pix.height = m_mode.height; vfmt.fmt.pix.field = V4L2_FIELD_ANY; if (DoIoctl(fd, VIDIOC_S_FMT, &vfmt) != 0) { - SWARNING("could not set format {} res {}x{}", m_mode.pixelFormat, - m_mode.width, m_mode.height); + SWARNING("could not set format {} res {}x{}", + static_cast(m_mode.pixelFormat), m_mode.width, m_mode.height); } else { - SINFO("set format {} res {}x{}", m_mode.pixelFormat, m_mode.width, - m_mode.height); + SINFO("set format {} res {}x{}", static_cast(m_mode.pixelFormat), + m_mode.width, m_mode.height); } } @@ -1079,10 +1079,10 @@ void UsbCameraImpl::DeviceCacheMode() { if (DoIoctl(fd, VIDIOC_G_FMT, &vfmt) != 0) { SERROR("could not read current video mode"); std::scoped_lock lock(m_mutex); - m_mode = VideoMode{VideoMode::kMJPEG, 320, 240, 30}; + m_mode = VideoMode{wpi::util::PixelFormat::kMJPEG, 320, 240, 30}; return; } - VideoMode::PixelFormat pixelFormat = ToPixelFormat(vfmt.fmt.pix.pixelformat); + wpi::util::PixelFormat pixelFormat = ToPixelFormat(vfmt.fmt.pix.pixelformat); int width = vfmt.fmt.pix.width; int height = vfmt.fmt.pix.height; @@ -1104,13 +1104,13 @@ void UsbCameraImpl::DeviceCacheMode() { // User set pixel format if (pixelFormat != m_mode.pixelFormat) { formatChanged = true; - pixelFormat = static_cast(m_mode.pixelFormat); + pixelFormat = static_cast(m_mode.pixelFormat); } } else { // Default to MJPEG - if (pixelFormat != VideoMode::kMJPEG) { + if (pixelFormat != wpi::util::PixelFormat::kMJPEG) { formatChanged = true; - pixelFormat = VideoMode::kMJPEG; + pixelFormat = wpi::util::PixelFormat::kMJPEG; } } @@ -1319,8 +1319,8 @@ void UsbCameraImpl::DeviceCacheVideoModes() { std::memset(&fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for (fmt.index = 0; TryIoctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0; ++fmt.index) { - VideoMode::PixelFormat pixelFormat = ToPixelFormat(fmt.pixelformat); - if (pixelFormat == VideoMode::kUnknown) { + wpi::util::PixelFormat pixelFormat = ToPixelFormat(fmt.pixelformat); + if (pixelFormat == wpi::util::PixelFormat::kUnknown) { continue; } @@ -1359,8 +1359,9 @@ void UsbCameraImpl::DeviceCacheVideoModes() { // provide a set of discrete modes; list based on // https://picamera.readthedocs.io/en/release-1.10/fov.html if (modes.empty() && m_picamera) { - for (VideoMode::PixelFormat pixelFormat : - {VideoMode::kYUYV, VideoMode::kMJPEG, VideoMode::kBGR}) { + for (wpi::util::PixelFormat pixelFormat : + {wpi::util::PixelFormat::kYUYV, wpi::util::PixelFormat::kMJPEG, + wpi::util::PixelFormat::kBGR}) { modes.emplace_back(pixelFormat, 1920, 1080, 30); modes.emplace_back(pixelFormat, 2592, 1944, 15); modes.emplace_back(pixelFormat, 1296, 972, 42); @@ -1566,7 +1567,7 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) { bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { Message msg{Message::kCmdSetMode}; - msg.data[0] = mode.pixelFormat; + msg.data[0] = static_cast(mode.pixelFormat); msg.data[1] = mode.width; msg.data[2] = mode.height; msg.data[3] = mode.fps; @@ -1574,10 +1575,10 @@ bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { return *status == CS_OK; } -bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat, +bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) { Message msg{Message::kCmdSetPixelFormat}; - msg.data[0] = pixelFormat; + msg.data[0] = static_cast(pixelFormat); *status = SendAndWait(std::move(msg)); return *status == CS_OK; } diff --git a/cscore/src/main/native/linux/UsbCameraImpl.hpp b/cscore/src/main/native/linux/UsbCameraImpl.hpp index 203a42eea7..7cef5e8708 100644 --- a/cscore/src/main/native/linux/UsbCameraImpl.hpp +++ b/cscore/src/main/native/linux/UsbCameraImpl.hpp @@ -17,11 +17,9 @@ #include "SourceImpl.hpp" #include "UsbCameraBuffer.hpp" #include "UsbCameraProperty.hpp" -#include "wpi/util/SmallVector.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/condition_variable.hpp" #include "wpi/util/mutex.hpp" -#include "wpi/util/raw_istream.hpp" -#include "wpi/util/raw_ostream.hpp" namespace wpi::cs { @@ -53,7 +51,7 @@ class UsbCameraImpl : public SourceImpl { void SetExposureManual(int value, CS_Status* status) override; bool SetVideoMode(const VideoMode& mode, CS_Status* status) override; - bool SetPixelFormat(VideoMode::PixelFormat pixelFormat, + bool SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) override; bool SetResolution(int width, int height, CS_Status* status) override; bool SetFPS(int fps, CS_Status* status) override; diff --git a/cscore/src/main/native/objcpp/UsbCameraImpl.hpp b/cscore/src/main/native/objcpp/UsbCameraImpl.hpp index 4fe886d2df..e051f70a9d 100644 --- a/cscore/src/main/native/objcpp/UsbCameraImpl.hpp +++ b/cscore/src/main/native/objcpp/UsbCameraImpl.hpp @@ -12,6 +12,7 @@ #include #include +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/StringMap.hpp" #include "SourceImpl.hpp" @@ -56,7 +57,7 @@ class UsbCameraImpl : public SourceImpl { void SetExposureManual(int value, CS_Status* status) override; bool SetVideoMode(const VideoMode& mode, CS_Status* status) override; - bool SetPixelFormat(VideoMode::PixelFormat pixelFormat, + bool SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) override; bool SetResolution(int width, int height, CS_Status* status) override; bool SetFPS(int fps, CS_Status* status) override; diff --git a/cscore/src/main/native/objcpp/UsbCameraImpl.mm b/cscore/src/main/native/objcpp/UsbCameraImpl.mm index 3d87763fa3..a892556837 100644 --- a/cscore/src/main/native/objcpp/UsbCameraImpl.mm +++ b/cscore/src/main/native/objcpp/UsbCameraImpl.mm @@ -18,6 +18,7 @@ #include "Instance.hpp" #include "c_util.hpp" #include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/PixelFormat.hpp" #include "UsbCameraImpl.hpp" namespace wpi::cs { @@ -88,7 +89,7 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) { bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { return [m_objc setVideoMode:mode status:status]; } -bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat, +bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) { return [m_objc setPixelFormat:pixelFormat status:status]; } diff --git a/cscore/src/main/native/objcpp/UsbCameraImplObjc.hpp b/cscore/src/main/native/objcpp/UsbCameraImplObjc.hpp index f80eadfca3..18df399328 100644 --- a/cscore/src/main/native/objcpp/UsbCameraImplObjc.hpp +++ b/cscore/src/main/native/objcpp/UsbCameraImplObjc.hpp @@ -13,6 +13,7 @@ #import "UvcControlImpl.hpp" #include "wpi/cs/cscore_cpp.hpp" +#include "wpi/util/PixelFormat.hpp" // Quirk: exposure auto is 3 for on, 1 for off #define kPropertyAutoExposureOn 3 @@ -83,7 +84,7 @@ class UsbCameraImpl; - (void)setExposureManual:(int)value status:(CS_Status*)status; - (bool)setVideoMode:(const wpi::cs::VideoMode&)mode status:(CS_Status*)status; -- (bool)setPixelFormat:(wpi::cs::VideoMode::PixelFormat)pixelFormat +- (bool)setPixelFormat:(wpi::util::PixelFormat)pixelFormat status:(CS_Status*)status; - (bool)setResolutionWidth:(int)width withHeight:(int)height diff --git a/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm b/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm index d9f2861b66..7c400c10fd 100644 --- a/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm +++ b/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm @@ -10,6 +10,7 @@ #include "Notifier.hpp" #include "Log.hpp" #include "UsbCameraImpl.hpp" +#include "wpi/util/PixelFormat.hpp" template inline void NamedLog(UsbCameraImplObjc* objc, unsigned int level, @@ -414,7 +415,7 @@ using namespace wpi::cs; }); return true; } -- (bool)setPixelFormat:(wpi::cs::VideoMode::PixelFormat)pixelFormat +- (bool)setPixelFormat:(wpi::util::PixelFormat)pixelFormat status:(CS_Status*)status { dispatch_async_and_wait(self.sessionQueue, ^{ auto sharedThis = self.cppImpl.lock(); @@ -464,8 +465,8 @@ using namespace wpi::cs; } if (newMode != sharedThis->objcGetVideoMode()) { - OBJCDEBUG3("Trying Mode {} {} {} {}", newMode.pixelFormat, newMode.width, - newMode.height, newMode.fps); + OBJCDEBUG3("Trying Mode {} {} {} {}", static_cast(newMode.pixelFormat), + newMode.width, newMode.height, newMode.fps); int localFPS = 0; AVCaptureDeviceFormat* newModeType = [self deviceCheckModeValid:&newMode withFps:&localFPS]; @@ -693,13 +694,13 @@ using namespace wpi::cs; propertyAutoCache[nameStr] = propID; } -static wpi::cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) { +static wpi::util::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) { switch (fourcc) { case kCVPixelFormatType_422YpCbCr8_yuvs: case kCVPixelFormatType_422YpCbCr8FullRange: - return wpi::cs::VideoMode::PixelFormat::kYUYV; + return wpi::util::PixelFormat::kYUYV; default: - return wpi::cs::VideoMode::PixelFormat::kBGR; + return wpi::util::PixelFormat::kBGR; } } @@ -768,7 +769,8 @@ static wpi::cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) return nil; } - OBJCDEBUG3("Checking mode {} {} {} {}", toCheck->pixelFormat, toCheck->width, + OBJCDEBUG3("Checking mode {} {} {} {}", + static_cast(toCheck->pixelFormat), toCheck->width, toCheck->height, toCheck->fps); std::vector& platformModes = sharedThis->objcGetPlatformVideoModes(); diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp index af6f7eaa3f..33fba6d4a0 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.cpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp @@ -36,6 +36,7 @@ #include "wpi/cs/cscore_cpp.hpp" #include "wpi/util/ConvertUTF.hpp" #include "wpi/util/MemAlloc.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallString.hpp" #include "wpi/util/StringExtras.hpp" #include "wpi/util/timestamp.hpp" @@ -135,13 +136,13 @@ void UsbCameraImpl::SetExposureManual(int value, CS_Status* status) { } bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { - if (mode.pixelFormat == VideoMode::kUnknown) { + if (mode.pixelFormat == wpi::util::PixelFormat::kUnknown) { *status = CS_UNSUPPORTED_MODE; return false; } Message msg{Message::kCmdSetMode}; - msg.data[0] = mode.pixelFormat; + msg.data[0] = static_cast(mode.pixelFormat); msg.data[1] = mode.width; msg.data[2] = mode.height; msg.data[3] = mode.fps; @@ -152,14 +153,14 @@ bool UsbCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) { return result == 0; } -bool UsbCameraImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat, +bool UsbCameraImpl::SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) { - if (pixelFormat == VideoMode::kUnknown) { + if (pixelFormat == wpi::util::PixelFormat::kUnknown) { *status = CS_UNSUPPORTED_MODE; return false; } Message msg{Message::kCmdSetPixelFormat}; - msg.data[0] = pixelFormat; + msg.data[0] = static_cast(pixelFormat); auto result = m_messagePump->SendWindowMessage( SetCameraMessage, msg.kind, &msg); @@ -340,7 +341,7 @@ void UsbCameraImpl::ProcessFrame(IMFSample* videoSample, } std::string_view data_view{reinterpret_cast(ptr), length}; - SourceImpl::PutFrame(static_cast(mode.pixelFormat), + SourceImpl::PutFrame(static_cast(mode.pixelFormat), mode.width, mode.height, data_view, currentTime); if (buffer2d) { @@ -425,22 +426,22 @@ LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam, return 0l; } -static wpi::cs::VideoMode::PixelFormat GetFromGUID(const GUID& guid) { +static wpi::util::PixelFormat GetFromGUID(const GUID& guid) { // Compare GUID to one of the supported ones if (IsEqualGUID(guid, MFVideoFormat_L8)) { - return wpi::cs::VideoMode::PixelFormat::kGray; + return wpi::util::PixelFormat::kGray; } else if (IsEqualGUID(guid, MFVideoFormat_L16)) { - return wpi::cs::VideoMode::PixelFormat::kY16; + return wpi::util::PixelFormat::kY16; } else if (IsEqualGUID(guid, MFVideoFormat_YUY2)) { - return wpi::cs::VideoMode::PixelFormat::kYUYV; + return wpi::util::PixelFormat::kYUYV; } else if (IsEqualGUID(guid, MFVideoFormat_MJPG)) { - return wpi::cs::VideoMode::PixelFormat::kMJPEG; + return wpi::util::PixelFormat::kMJPEG; } else if (IsEqualGUID(guid, MFVideoFormat_RGB565)) { - return wpi::cs::VideoMode::PixelFormat::kRGB565; + return wpi::util::PixelFormat::kRGB565; } else if (IsEqualGUID(guid, MFVideoFormat_UYVY)) { - return wpi::cs::VideoMode::PixelFormat::kUYVY; + return wpi::util::PixelFormat::kUYVY; } else { - return wpi::cs::VideoMode::PixelFormat::kUnknown; + return wpi::util::PixelFormat::kUnknown; } } @@ -831,13 +832,13 @@ CS_StatusValue UsbCameraImpl::DeviceCmdSetMode( std::unique_lock& lock, const Message& msg) { VideoMode newMode; if (msg.kind == Message::kCmdSetMode) { - newMode.pixelFormat = msg.data[0]; + newMode.pixelFormat = static_cast(msg.data[0]); newMode.width = msg.data[1]; newMode.height = msg.data[2]; newMode.fps = msg.data[3]; } else if (msg.kind == Message::kCmdSetPixelFormat) { newMode = m_mode; - newMode.pixelFormat = msg.data[0]; + newMode.pixelFormat = static_cast(msg.data[0]); } else if (msg.kind == Message::kCmdSetResolution) { newMode = m_mode; newMode.width = msg.data[0]; @@ -991,7 +992,7 @@ void UsbCameraImpl::DeviceCacheVideoModes() { nativeType->GetGUID(MF_MT_SUBTYPE, &guid); auto format = GetFromGUID(guid); - if (format == VideoMode::kUnknown) { + if (format == wpi::util::PixelFormat::kUnknown) { count++; // Don't put in unknowns continue; diff --git a/cscore/src/main/native/windows/UsbCameraImpl.hpp b/cscore/src/main/native/windows/UsbCameraImpl.hpp index c31d39c49e..a3862ebd0c 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.hpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.hpp @@ -25,6 +25,7 @@ #include "SourceImpl.hpp" #include "UsbCameraProperty.hpp" #include "WindowsMessagePump.hpp" +#include "wpi/util/PixelFormat.hpp" #include "wpi/util/SmallVector.hpp" #include "wpi/util/condition_variable.hpp" #include "wpi/util/mutex.hpp" @@ -61,7 +62,7 @@ class UsbCameraImpl : public SourceImpl, void SetExposureManual(int value, CS_Status* status) override; bool SetVideoMode(const VideoMode& mode, CS_Status* status) override; - bool SetPixelFormat(VideoMode::PixelFormat pixelFormat, + bool SetPixelFormat(wpi::util::PixelFormat pixelFormat, CS_Status* status) override; bool SetResolution(int width, int height, CS_Status* status) override; bool SetFPS(int fps, CS_Status* status) override; diff --git a/cscore/src/test/native/cpp/CameraSourceTest.cpp b/cscore/src/test/native/cpp/CameraSourceTest.cpp index f87f2d9772..913396dde5 100644 --- a/cscore/src/test/native/cpp/CameraSourceTest.cpp +++ b/cscore/src/test/native/cpp/CameraSourceTest.cpp @@ -4,7 +4,8 @@ #include -#include "wpi/cs/cscore.h" +#include "wpi/cs/HttpCamera.hpp" +#include "wpi/cs/cscore_cpp.hpp" namespace wpi::cs { diff --git a/wpilibc/src/test/native/c/test.c b/wpilibc/src/test/native/c/test.c index c6689111dd..e1a728923b 100644 --- a/wpilibc/src/test/native/c/test.c +++ b/wpilibc/src/test/native/c/test.c @@ -2,6 +2,6 @@ // 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 "wpi/cs/cscore.h" +#include "wpi/cs/cscore_c.h" #include "wpi/hal/HAL.h" #include "wpi/nt/ntcore.h" diff --git a/wpilibcExamples/src/main/cpp/examples/HttpCamera/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/HttpCamera/cpp/Robot.cpp index 4857d7a0e4..836f87ef57 100644 --- a/wpilibcExamples/src/main/cpp/examples/HttpCamera/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/HttpCamera/cpp/Robot.cpp @@ -7,6 +7,7 @@ #include #include "wpi/cameraserver/CameraServer.hpp" +#include "wpi/cs/HttpCamera.hpp" #include "wpi/framework/TimedRobot.hpp" /** diff --git a/wpiutil/robotpy_pybind_build_info.bzl b/wpiutil/robotpy_pybind_build_info.bzl index 2205879cce..4af0c6a823 100644 --- a/wpiutil/robotpy_pybind_build_info.bzl +++ b/wpiutil/robotpy_pybind_build_info.bzl @@ -44,6 +44,14 @@ def wpiutil_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], inclu tmpl_class_names = [], trampolines = [], ), + struct( + class_name = "PixelFormat", + yml_file = "semiwrap/PixelFormat.yml", + header_root = "$(execpath :robotpy-native-wpiutil.copy_headers)", + header_file = "$(execpath :robotpy-native-wpiutil.copy_headers)/wpi/util/PixelFormat.hpp", + tmpl_class_names = [], + trampolines = [], + ), struct( class_name = "RawFrame_c", yml_file = "semiwrap/RawFrame_c.yml", diff --git a/wpiutil/src/main/native/include/wpi/util/PixelFormat.h b/wpiutil/src/main/native/include/wpi/util/PixelFormat.h new file mode 100644 index 0000000000..5f9c6fd1bf --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/util/PixelFormat.h @@ -0,0 +1,20 @@ +// 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. + +#pragma once + +/** + * Pixel formats + */ +enum WPI_PixelFormat { + WPI_PIXFMT_UNKNOWN = 0, // unknown + WPI_PIXFMT_MJPEG, // Motion-JPEG (compressed image data) + WPI_PIXFMT_YUYV, // YUV 4:2:2, 16 bpp + WPI_PIXFMT_RGB565, // RGB 5-6-5, 16 bpp + WPI_PIXFMT_BGR, // BGR 8-8-8, 24 bpp + WPI_PIXFMT_GRAY, // Grayscale, 8 bpp + WPI_PIXFMT_Y16, // Grayscale, 16 bpp + WPI_PIXFMT_UYVY, // YUV 4:2:2, 16 bpp + WPI_PIXFMT_BGRA, // BGRA 8-8-8-8-, 32 bpp +}; diff --git a/wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp b/wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp new file mode 100644 index 0000000000..132f20ebe5 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/util/PixelFormat.hpp @@ -0,0 +1,26 @@ +// 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. + +#pragma once + +#include "wpi/util/PixelFormat.h" + +namespace wpi::util { + +/** + * Pixel formats + */ +enum class PixelFormat { + kUnknown = WPI_PIXFMT_UNKNOWN, + kMJPEG = WPI_PIXFMT_MJPEG, + kYUYV = WPI_PIXFMT_YUYV, + kRGB565 = WPI_PIXFMT_RGB565, + kBGR = WPI_PIXFMT_BGR, + kGray = WPI_PIXFMT_GRAY, + kY16 = WPI_PIXFMT_Y16, + kUYVY = WPI_PIXFMT_UYVY, + kBGRA = WPI_PIXFMT_BGRA, +}; + +} // namespace wpi::util diff --git a/wpiutil/src/main/native/include/wpi/util/RawFrame.h b/wpiutil/src/main/native/include/wpi/util/RawFrame.h index b8f9298421..eb0d723322 100644 --- a/wpiutil/src/main/native/include/wpi/util/RawFrame.h +++ b/wpiutil/src/main/native/include/wpi/util/RawFrame.h @@ -30,21 +30,6 @@ typedef struct WPI_RawFrame { // NOLINT int timestampSrc; // WPI_TimestampSource } WPI_RawFrame; -/** - * Pixel formats - */ -enum WPI_PixelFormat { - WPI_PIXFMT_UNKNOWN = 0, // unknown - WPI_PIXFMT_MJPEG, // Motion-JPEG (compressed image data) - WPI_PIXFMT_YUYV, // YUV 4:2:2, 16 bpp - WPI_PIXFMT_RGB565, // RGB 5-6-5, 16 bpp - WPI_PIXFMT_BGR, // BGR 8-8-8, 24 bpp - WPI_PIXFMT_GRAY, // Grayscale, 8 bpp - WPI_PIXFMT_Y16, // Grayscale, 16 bpp - WPI_PIXFMT_UYVY, // YUV 4:2:2, 16 bpp - WPI_PIXFMT_BGRA, // BGRA 8-8-8-8-, 32 bpp -}; - /** * Timestamp metadata. Timebase is the same as wpi::util::Now */ diff --git a/wpiutil/src/main/native/include/wpi/util/RawFrame.hpp b/wpiutil/src/main/native/include/wpi/util/RawFrame.hpp index 4fad0c9ee5..014cf278ab 100644 --- a/wpiutil/src/main/native/include/wpi/util/RawFrame.hpp +++ b/wpiutil/src/main/native/include/wpi/util/RawFrame.hpp @@ -4,6 +4,7 @@ #pragma once +#include "wpi/util/PixelFormat.h" #include "wpi/util/RawFrame.h" #ifdef WPI_RAWFRAME_JNI diff --git a/wpiutil/src/main/python/pyproject.toml b/wpiutil/src/main/python/pyproject.toml index 33e7e93045..7a9956cb1c 100644 --- a/wpiutil/src/main/python/pyproject.toml +++ b/wpiutil/src/main/python/pyproject.toml @@ -71,6 +71,7 @@ Color = "wpi/util/Color.hpp" Color8Bit = "wpi/util/Color8Bit.hpp" StackTrace = "wpi/util/StackTrace.hpp" Synchronization = "wpi/util/Synchronization.hpp" +PixelFormat = "wpi/util/PixelFormat.hpp" RawFrame_c = "wpi/util/RawFrame.h" RawFrame = "wpi/util/RawFrame.hpp" diff --git a/wpiutil/src/main/python/semiwrap/PixelFormat.yml b/wpiutil/src/main/python/semiwrap/PixelFormat.yml new file mode 100644 index 0000000000..d869c60134 --- /dev/null +++ b/wpiutil/src/main/python/semiwrap/PixelFormat.yml @@ -0,0 +1,2 @@ +enums: + PixelFormat: