Add raw sources and sinks to cscore (#1670)

In some cases, we don't want the cv requirement to get an image, for instance interop with other versions of opencv

This enables getting a raw image, and handling conversions from the user side.
This commit is contained in:
Thad House
2019-05-30 19:12:05 -07:00
committed by Peter Johnson
parent 7de9477347
commit fb1239a2ad
37 changed files with 2218 additions and 504 deletions

View File

@@ -0,0 +1,109 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "ConfigurableSourceImpl.h"
#include <wpi/timestamp.h>
#include "Handle.h"
#include "Instance.h"
#include "Log.h"
#include "Notifier.h"
using namespace cs;
ConfigurableSourceImpl::ConfigurableSourceImpl(const wpi::Twine& name,
wpi::Logger& logger,
Notifier& notifier,
Telemetry& telemetry,
const VideoMode& mode)
: SourceImpl{name, logger, notifier, telemetry} {
m_mode = mode;
m_videoModes.push_back(m_mode);
}
ConfigurableSourceImpl::~ConfigurableSourceImpl() {}
void ConfigurableSourceImpl::Start() {
m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
m_notifier.NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
m_notifier.NotifySourceVideoMode(*this, m_mode);
}
bool ConfigurableSourceImpl::SetVideoMode(const VideoMode& mode,
CS_Status* status) {
{
std::lock_guard<wpi::mutex> lock(m_mutex);
m_mode = mode;
m_videoModes[0] = mode;
}
m_notifier.NotifySourceVideoMode(*this, mode);
return true;
}
void ConfigurableSourceImpl::NumSinksChanged() {
// ignore
}
void ConfigurableSourceImpl::NumSinksEnabledChanged() {
// ignore
}
void ConfigurableSourceImpl::NotifyError(const wpi::Twine& msg) {
PutError(msg, wpi::Now());
}
int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
CS_PropertyKind kind, int minimum,
int maximum, int step,
int defaultValue, int value) {
std::lock_guard<wpi::mutex> lock(m_mutex);
int ndx = CreateOrUpdateProperty(name,
[=] {
return wpi::make_unique<PropertyImpl>(
name, kind, minimum, maximum, step,
defaultValue, value);
},
[&](PropertyImpl& prop) {
// update all but value
prop.propKind = kind;
prop.minimum = minimum;
prop.maximum = maximum;
prop.step = step;
prop.defaultValue = defaultValue;
value = prop.value;
});
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
kind, value, wpi::Twine{});
return ndx;
}
int ConfigurableSourceImpl::CreateProperty(
const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange) {
// TODO
return 0;
}
void ConfigurableSourceImpl::SetEnumPropertyChoices(
int property, wpi::ArrayRef<std::string> choices, CS_Status* status) {
std::lock_guard<wpi::mutex> lock(m_mutex);
auto prop = GetProperty(property);
if (!prop) {
*status = CS_INVALID_PROPERTY;
return;
}
if (prop->propKind != CS_PROP_ENUM) {
*status = CS_WRONG_PROPERTY_TYPE;
return;
}
prop->enumChoices = choices;
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
prop->name, property, CS_PROP_ENUM,
prop->value, wpi::Twine{});
}

View File

@@ -0,0 +1,56 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef CSCORE_CONFIGURABLESOURCEIMPL_H_
#define CSCORE_CONFIGURABLESOURCEIMPL_H_
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include "SourceImpl.h"
namespace cs {
class ConfigurableSourceImpl : public SourceImpl {
protected:
ConfigurableSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode);
public:
~ConfigurableSourceImpl() override;
void Start() override;
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
void NumSinksChanged() override;
void NumSinksEnabledChanged() override;
// OpenCV-specific functions
void NotifyError(const wpi::Twine& msg);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange);
void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
CS_Status* status);
private:
std::atomic_bool m_connected{true};
};
} // namespace cs
#endif // CSCORE_CONFIGURABLESOURCEIMPL_H_

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -138,10 +138,12 @@ CS_Sink CreateCvSinkCallback(const wpi::Twine& name,
inst.telemetry, processFrame));
}
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
void SetSinkDescription(CS_Sink sink, const wpi::Twine& description,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_CV) {
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}
@@ -169,7 +171,7 @@ uint64_t GrabSinkFrameTimeout(CS_Sink sink, cv::Mat& image, double timeout,
std::string GetSinkError(CS_Sink sink, CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_CV) {
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return std::string{};
}
@@ -179,7 +181,7 @@ std::string GetSinkError(CS_Sink sink, CS_Status* status) {
wpi::StringRef GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_CV) {
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return wpi::StringRef{};
}
@@ -188,7 +190,7 @@ wpi::StringRef GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_CV) {
if (!data || (data->kind & SinkMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -25,37 +25,10 @@ using namespace cs;
CvSourceImpl::CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode)
: SourceImpl{name, logger, notifier, telemetry} {
m_mode = mode;
m_videoModes.push_back(m_mode);
}
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
CvSourceImpl::~CvSourceImpl() {}
void CvSourceImpl::Start() {
m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
m_notifier.NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
m_notifier.NotifySourceVideoMode(*this, m_mode);
}
bool CvSourceImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
{
std::lock_guard<wpi::mutex> lock(m_mutex);
m_mode = mode;
m_videoModes[0] = mode;
}
m_notifier.NotifySourceVideoMode(*this, mode);
return true;
}
void CvSourceImpl::NumSinksChanged() {
// ignore
}
void CvSourceImpl::NumSinksEnabledChanged() {
// ignore
}
void CvSourceImpl::PutFrame(cv::Mat& image) {
// We only support 8-bit images; convert if necessary.
cv::Mat finalImage;
@@ -89,61 +62,6 @@ void CvSourceImpl::PutFrame(cv::Mat& image) {
SourceImpl::PutFrame(std::move(dest), wpi::Now());
}
void CvSourceImpl::NotifyError(const wpi::Twine& msg) {
PutError(msg, wpi::Now());
}
int CvSourceImpl::CreateProperty(const wpi::Twine& name, CS_PropertyKind kind,
int minimum, int maximum, int step,
int defaultValue, int value) {
std::lock_guard<wpi::mutex> lock(m_mutex);
int ndx = CreateOrUpdateProperty(name,
[=] {
return wpi::make_unique<PropertyImpl>(
name, kind, minimum, maximum, step,
defaultValue, value);
},
[&](PropertyImpl& prop) {
// update all but value
prop.propKind = kind;
prop.minimum = minimum;
prop.maximum = maximum;
prop.step = step;
prop.defaultValue = defaultValue;
value = prop.value;
});
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
kind, value, wpi::Twine{});
return ndx;
}
int CvSourceImpl::CreateProperty(
const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange) {
// TODO
return 0;
}
void CvSourceImpl::SetEnumPropertyChoices(int property,
wpi::ArrayRef<std::string> choices,
CS_Status* status) {
std::lock_guard<wpi::mutex> lock(m_mutex);
auto prop = GetProperty(property);
if (!prop) {
*status = CS_INVALID_PROPERTY;
return;
}
if (prop->propKind != CS_PROP_ENUM) {
*status = CS_WRONG_PROPERTY_TYPE;
return;
}
prop->enumChoices = choices;
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
prop->name, property, CS_PROP_ENUM,
prop->value, wpi::Twine{});
}
namespace cs {
CS_Source CreateCvSource(const wpi::Twine& name, const VideoMode& mode,
@@ -163,10 +81,12 @@ void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status) {
static_cast<CvSourceImpl&>(*data->source).PutFrame(image);
}
static constexpr unsigned SourceMask = CS_SINK_CV | CS_SINK_RAW;
void NotifySourceError(CS_Source source, const wpi::Twine& msg,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}
@@ -175,7 +95,7 @@ void NotifySourceError(CS_Source source, const wpi::Twine& msg,
void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}
@@ -185,7 +105,7 @@ void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) {
void SetSourceDescription(CS_Source source, const wpi::Twine& description,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}
@@ -197,7 +117,7 @@ CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
int step, int defaultValue, int value,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return -1;
}
@@ -212,7 +132,7 @@ CS_Property CreateSourcePropertyCallback(
int maximum, int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange, CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return -1;
}
@@ -226,7 +146,7 @@ void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
wpi::ArrayRef<std::string> choices,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_CV) {
if (!data || (data->kind & SourceMask) == 0) {
*status = CS_INVALID_HANDLE;
return;
}

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -18,33 +18,19 @@
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include "ConfigurableSourceImpl.h"
#include "SourceImpl.h"
namespace cs {
class CvSourceImpl : public SourceImpl {
class CvSourceImpl : public ConfigurableSourceImpl {
public:
CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry, const VideoMode& mode);
~CvSourceImpl() override;
void Start() override;
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
void NumSinksChanged() override;
void NumSinksEnabledChanged() override;
// OpenCV-specific functions
void PutFrame(cv::Mat& image);
void NotifyError(const wpi::Twine& msg);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value);
int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
int maximum, int step, int defaultValue, int value,
std::function<void(CS_Property property)> onChange);
void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
CS_Status* status);
private:
std::atomic_bool m_connected{true};

View File

@@ -0,0 +1,199 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "RawSinkImpl.h"
#include "Instance.h"
#include "cscore.h"
#include "cscore_raw.h"
using namespace cs;
RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry)
: SinkImpl{name, logger, notifier, telemetry} {
m_active = true;
// m_thread = std::thread(&RawSinkImpl::ThreadMain, this);
}
RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame)
: SinkImpl{name, logger, notifier, telemetry} {}
RawSinkImpl::~RawSinkImpl() { Stop(); }
void RawSinkImpl::Stop() {
m_active = false;
// wake up any waiters by forcing an empty frame to be sent
if (auto source = GetSource()) source->Wakeup();
// join thread
if (m_thread.joinable()) m_thread.join();
}
uint64_t RawSinkImpl::GrabFrame(CS_RawFrame& image) {
SetEnabled(true);
auto source = GetSource();
if (!source) {
// Source disconnected; sleep for one second
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
auto frame = source->GetNextFrame(); // blocks
if (!frame) {
// Bad frame; sleep for 20 ms so we don't consume all processor time.
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return 0; // signal error
}
return GrabFrameImpl(image, frame);
}
uint64_t RawSinkImpl::GrabFrame(CS_RawFrame& image, double timeout) {
SetEnabled(true);
auto source = GetSource();
if (!source) {
// Source disconnected; sleep for one second
std::this_thread::sleep_for(std::chrono::seconds(1));
return 0;
}
auto frame = source->GetNextFrame(timeout); // blocks
if (!frame) {
// Bad frame; sleep for 20 ms so we don't consume all processor time.
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return 0; // signal error
}
return GrabFrameImpl(image, frame);
}
uint64_t RawSinkImpl::GrabFrameImpl(CS_RawFrame& rawFrame,
Frame& incomingFrame) {
Image* newImage = nullptr;
if (rawFrame.pixelFormat == CS_PixelFormat::CS_PIXFMT_UNKNOWN) {
// Always get incoming image directly on unknown
newImage = incomingFrame.GetExistingImage(0);
} else {
// Format is known, ask for it
auto width = rawFrame.width;
auto height = rawFrame.height;
auto pixelFormat =
static_cast<VideoMode::PixelFormat>(rawFrame.pixelFormat);
if (width <= 0 || height <= 0) {
width = incomingFrame.GetOriginalWidth();
height = incomingFrame.GetOriginalHeight();
}
newImage = incomingFrame.GetImage(width, height, pixelFormat);
}
if (!newImage) {
// Shouldn't happen, but just in case...
std::this_thread::sleep_for(std::chrono::milliseconds(20));
return 0;
}
CS_AllocateRawFrameData(&rawFrame, newImage->size());
rawFrame.height = newImage->height;
rawFrame.width = newImage->width;
rawFrame.pixelFormat = newImage->pixelFormat;
rawFrame.totalData = newImage->size();
std::copy(newImage->data(), newImage->data() + rawFrame.totalData,
rawFrame.data);
return incomingFrame.GetTime();
}
// Send HTTP response and a stream of JPG-frames
void RawSinkImpl::ThreadMain() {
Enable();
while (m_active) {
auto source = GetSource();
if (!source) {
// Source disconnected; sleep for one second
std::this_thread::sleep_for(std::chrono::seconds(1));
continue;
}
SDEBUG4("waiting for frame");
Frame frame = source->GetNextFrame(); // blocks
if (!m_active) break;
if (!frame) {
// Bad frame; sleep for 10 ms so we don't consume all processor time.
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
// TODO m_processFrame();
}
Disable();
}
namespace cs {
CS_Sink CreateRawSink(const wpi::Twine& name, CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(CS_SINK_RAW,
std::make_shared<RawSinkImpl>(
name, inst.logger, inst.notifier, inst.telemetry));
}
CS_Sink CreateRawSinkCallback(const wpi::Twine& name,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSink(CS_SINK_RAW, std::make_shared<RawSinkImpl>(
name, inst.logger, inst.notifier,
inst.telemetry, processFrame));
}
uint64_t GrabSinkFrame(CS_Sink sink, CS_RawFrame& image, CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_RAW) {
*status = CS_INVALID_HANDLE;
return 0;
}
return static_cast<RawSinkImpl&>(*data->sink).GrabFrame(image);
}
uint64_t GrabSinkFrameTimeout(CS_Sink sink, CS_RawFrame& image, double timeout,
CS_Status* status) {
auto data = Instance::GetInstance().GetSink(sink);
if (!data || data->kind != CS_SINK_RAW) {
*status = CS_INVALID_HANDLE;
return 0;
}
return static_cast<RawSinkImpl&>(*data->sink).GrabFrame(image, timeout);
}
} // namespace cs
extern "C" {
CS_Sink CS_CreateRawSink(const char* name, CS_Status* status) {
return cs::CreateRawSink(name, status);
}
CS_Sink CS_CreateRawSinkCallback(const char* name, void* data,
void (*processFrame)(void* data,
uint64_t time),
CS_Status* status) {
return cs::CreateRawSinkCallback(
name, [=](uint64_t time) { processFrame(data, time); }, status);
}
uint64_t CS_GrabRawSinkFrame(CS_Sink sink, struct CS_RawFrame* image,
CS_Status* status) {
return cs::GrabSinkFrame(sink, *image, status);
}
uint64_t CS_GrabRawSinkFrameTimeout(CS_Sink sink, struct CS_RawFrame* image,
double timeout, CS_Status* status) {
return cs::GrabSinkFrameTimeout(sink, *image, timeout, status);
}
} // extern "C"

View File

@@ -0,0 +1,52 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef CSCORE_RAWSINKIMPL_H_
#define CSCORE_RAWSINKIMPL_H_
#include <stdint.h>
#include <atomic>
#include <functional>
#include <thread>
#include <wpi/Twine.h>
#include <wpi/condition_variable.h>
#include "Frame.h"
#include "SinkImpl.h"
#include "cscore_raw.h"
namespace cs {
class SourceImpl;
class RawSinkImpl : public SinkImpl {
public:
RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry);
RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry,
std::function<void(uint64_t time)> processFrame);
~RawSinkImpl() override;
void Stop();
uint64_t GrabFrame(CS_RawFrame& frame);
uint64_t GrabFrame(CS_RawFrame& frame, double timeout);
private:
void ThreadMain();
uint64_t GrabFrameImpl(CS_RawFrame& rawFrame, Frame& incomingFrame);
std::atomic_bool m_active; // set to false to terminate threads
std::thread m_thread;
std::function<void(uint64_t time)> m_processFrame;
};
} // namespace cs
#endif // CSCORE_RAWSINKIMPL_H_

View File

@@ -0,0 +1,83 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "RawSourceImpl.h"
#include <wpi/timestamp.h>
#include "Handle.h"
#include "Instance.h"
#include "Log.h"
#include "Notifier.h"
#include "cscore_raw.h"
using namespace cs;
RawSourceImpl::RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
Notifier& notifier, Telemetry& telemetry,
const VideoMode& mode)
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
RawSourceImpl::~RawSourceImpl() {}
void RawSourceImpl::PutFrame(const CS_RawFrame& image) {
int type;
switch (image.pixelFormat) {
case VideoMode::kYUYV:
case VideoMode::kRGB565:
type = CV_8UC2;
break;
case VideoMode::kBGR:
type = CV_8UC3;
break;
case VideoMode::kGray:
case VideoMode::kMJPEG:
default:
type = CV_8UC1;
break;
}
cv::Mat finalImage{image.height, image.width, type, image.data};
std::unique_ptr<Image> dest =
AllocImage(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
image.width, image.height, image.totalData);
finalImage.copyTo(dest->AsMat());
SourceImpl::PutFrame(std::move(dest), wpi::Now());
}
namespace cs {
CS_Source CreateRawSource(const wpi::Twine& name, const VideoMode& mode,
CS_Status* status) {
auto& inst = Instance::GetInstance();
return inst.CreateSource(CS_SOURCE_RAW, std::make_shared<RawSourceImpl>(
name, inst.logger, inst.notifier,
inst.telemetry, mode));
}
void PutSourceFrame(CS_Source source, const CS_RawFrame& image,
CS_Status* status) {
auto data = Instance::GetInstance().GetSource(source);
if (!data || data->kind != CS_SOURCE_RAW) {
*status = CS_INVALID_HANDLE;
return;
}
static_cast<RawSourceImpl&>(*data->source).PutFrame(image);
}
} // namespace cs
extern "C" {
CS_Source CS_CreateRawSource(const char* name, const CS_VideoMode* mode,
CS_Status* status) {
return cs::CreateRawSource(name, static_cast<const cs::VideoMode&>(*mode),
status);
}
void CS_PutRawSourceFrame(CS_Source source, const struct CS_RawFrame* image,
CS_Status* status) {
return cs::PutSourceFrame(source, *image, status);
}
} // extern "C"

View File

@@ -0,0 +1,41 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef CSCORE_RAWSOURCEIMPL_H_
#define CSCORE_RAWSOURCEIMPL_H_
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <wpi/ArrayRef.h>
#include <wpi/Twine.h>
#include "ConfigurableSourceImpl.h"
#include "SourceImpl.h"
#include "cscore_raw.h"
namespace cs {
class RawSourceImpl : public ConfigurableSourceImpl {
public:
RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
Telemetry& telemetry, const VideoMode& mode);
~RawSourceImpl() override;
// Raw-specific functions
void PutFrame(const CS_RawFrame& image);
private:
std::atomic_bool m_connected{true};
};
} // namespace cs
#endif // CSCORE_RAWSOURCEIMPL_H_

View File

@@ -16,6 +16,7 @@
#include "c_util.h"
#include "cscore_cpp.h"
#include "cscore_raw.h"
extern "C" {
@@ -440,4 +441,23 @@ void CS_FreeNetworkInterfaces(char** interfaces, int count) {
std::free(interfaces);
}
void CS_AllocateRawFrameData(CS_RawFrame* frame, int requestedSize) {
if (frame->dataLength >= requestedSize) return;
if (frame->data) {
frame->data =
static_cast<char*>(wpi::safe_realloc(frame->data, requestedSize));
} else {
frame->data = static_cast<char*>(wpi::safe_malloc(requestedSize));
}
frame->dataLength = requestedSize;
}
void CS_FreeRawFrameData(CS_RawFrame* frame) {
if (frame->data) {
std::free(frame->data);
frame->data = nullptr;
frame->dataLength = 0;
}
}
} // extern "C"

View File

@@ -10,8 +10,14 @@
#include <wpi/raw_ostream.h>
#include "cscore_cpp.h"
#include "cscore_cv.h"
#include "cscore_raw.h"
#include "edu_wpi_cscore_CameraServerJNI.h"
namespace cv {
class Mat;
} // namespace cv
using namespace wpi::java;
//
@@ -23,6 +29,7 @@ static JavaVM* jvm = nullptr;
static JClass usbCameraInfoCls;
static JClass videoModeCls;
static JClass videoEventCls;
static JClass rawFrameCls;
static JException videoEx;
static JException nullPointerEx;
static JException unsupportedEx;
@@ -32,7 +39,8 @@ static JNIEnv* listenerEnv = nullptr;
static const JClassInit classes[] = {
{"edu/wpi/cscore/UsbCameraInfo", &usbCameraInfoCls},
{"edu/wpi/cscore/VideoMode", &videoModeCls},
{"edu/wpi/cscore/VideoEvent", &videoEventCls}};
{"edu/wpi/cscore/VideoEvent", &videoEventCls},
{"edu/wpi/cscore/raw/RawFrame", &rawFrameCls}};
static const JExceptionInit exceptions[] = {
{"edu/wpi/cscore/VideoException", &videoEx},
@@ -506,12 +514,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createHttpCameraMulti
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Class: edu_wpi_cscore_CameraServerCvJNI
* Method: createCvSource
* Signature: (Ljava/lang/String;IIII)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_cscore_CameraServerJNI_createCvSource
Java_edu_wpi_cscore_CameraServerCvJNI_createCvSource
(JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
jint fps)
{
@@ -530,6 +538,31 @@ Java_edu_wpi_cscore_CameraServerJNI_createCvSource
return val;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: createRawSource
* Signature: (Ljava/lang/String;IIII)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_cscore_CameraServerJNI_createRawSource
(JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
jint fps)
{
if (!name) {
nullPointerEx.Throw(env, "name cannot be null");
return 0;
}
CS_Status status = 0;
auto val = cs::CreateRawSource(
JStringRef{env, name}.str(),
cs::VideoMode{static_cast<cs::VideoMode::PixelFormat>(pixelFormat),
static_cast<int>(width), static_cast<int>(height),
static_cast<int>(fps)},
&status);
CheckStatus(env, status);
return val;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getSourceKind
@@ -1054,12 +1087,12 @@ Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraUrls
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Class: edu_wpi_cscore_CameraServerCvJNI
* Method: putSourceFrame
* Signature: (IJ)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_cscore_CameraServerJNI_putSourceFrame
Java_edu_wpi_cscore_CameraServerCvJNI_putSourceFrame
(JNIEnv* env, jclass, jint source, jlong imageNativeObj)
{
cv::Mat& image = *((cv::Mat*)imageNativeObj);
@@ -1068,6 +1101,51 @@ Java_edu_wpi_cscore_CameraServerJNI_putSourceFrame
CheckStatus(env, status);
}
// int width, int height, int pixelFormat, int totalData
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: putRawSourceFrameBB
* Signature: (ILjava/lang/Object;IIII)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrameBB
(JNIEnv* env, jclass, jint source, jobject byteBuffer, jint width,
jint height, jint pixelFormat, jint totalData)
{
CS_RawFrame rawFrame;
rawFrame.data =
reinterpret_cast<char*>(env->GetDirectBufferAddress(byteBuffer));
rawFrame.totalData = totalData;
rawFrame.pixelFormat = pixelFormat;
rawFrame.width = width;
rawFrame.height = height;
CS_Status status = 0;
cs::PutSourceFrame(source, rawFrame, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: putRawSourceFrame
* Signature: (IJIIII)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrame
(JNIEnv* env, jclass, jint source, jlong ptr, jint width, jint height,
jint pixelFormat, jint totalData)
{
CS_RawFrame rawFrame;
rawFrame.data = reinterpret_cast<char*>(static_cast<intptr_t>(ptr));
rawFrame.totalData = totalData;
rawFrame.pixelFormat = pixelFormat;
rawFrame.width = width;
rawFrame.height = height;
CS_Status status = 0;
cs::PutSourceFrame(source, rawFrame, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: notifySourceError
@@ -1192,12 +1270,12 @@ Java_edu_wpi_cscore_CameraServerJNI_createMjpegServer
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Class: edu_wpi_cscore_CameraServerCvJNI
* Method: createCvSink
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_cscore_CameraServerJNI_createCvSink
Java_edu_wpi_cscore_CameraServerCvJNI_createCvSink
(JNIEnv* env, jclass, jstring name)
{
if (!name) {
@@ -1210,6 +1288,25 @@ Java_edu_wpi_cscore_CameraServerJNI_createCvSink
return val;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: createRawSink
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_cscore_CameraServerJNI_createRawSink
(JNIEnv* env, jclass, jstring name)
{
if (!name) {
nullPointerEx.Throw(env, "name cannot be null");
return 0;
}
CS_Status status = 0;
auto val = cs::CreateRawSink(JStringRef{env, name}.str(), &status);
CheckStatus(env, status);
return val;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getSinkKind
@@ -1449,12 +1546,12 @@ Java_edu_wpi_cscore_CameraServerJNI_setSinkDescription
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Class: edu_wpi_cscore_CameraServerCvJNI
* Method: grabSinkFrame
* Signature: (IJ)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_cscore_CameraServerJNI_grabSinkFrame
Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrame
(JNIEnv* env, jclass, jint sink, jlong imageNativeObj)
{
cv::Mat& image = *((cv::Mat*)imageNativeObj);
@@ -1465,12 +1562,12 @@ Java_edu_wpi_cscore_CameraServerJNI_grabSinkFrame
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Class: edu_wpi_cscore_CameraServerCvJNI
* Method: grabSinkFrameTimeout
* Signature: (IJD)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_cscore_CameraServerJNI_grabSinkFrameTimeout
Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrameTimeout
(JNIEnv* env, jclass, jint sink, jlong imageNativeObj, jdouble timeout)
{
cv::Mat& image = *((cv::Mat*)imageNativeObj);
@@ -1480,6 +1577,75 @@ Java_edu_wpi_cscore_CameraServerJNI_grabSinkFrameTimeout
return rv;
}
static void SetRawFrameData(JNIEnv* env, jobject rawFrameObj,
jobject byteBuffer, bool didChangeDataPtr,
const CS_RawFrame& frame) {
static jmethodID setMethod =
env->GetMethodID(rawFrameCls, "setData", "(Ljava/nio/ByteBuffer;JIIII)V");
jlong framePtr = static_cast<jlong>(reinterpret_cast<intptr_t>(frame.data));
if (didChangeDataPtr) {
byteBuffer = env->NewDirectByteBuffer(frame.data, frame.dataLength);
}
env->CallVoidMethod(
rawFrameObj, setMethod, byteBuffer, framePtr,
static_cast<jint>(frame.totalData), static_cast<jint>(frame.width),
static_cast<jint>(frame.height), static_cast<jint>(frame.pixelFormat));
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: grabRawSinkFrameImpl
* Signature: (ILjava/lang/Object;JLjava/lang/Object;III)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameImpl
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
jobject byteBuffer, jint width, jint height, jint pixelFormat)
{
CS_RawFrame* ptr =
reinterpret_cast<CS_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
auto origDataPtr = ptr->data;
ptr->width = width;
ptr->height = height;
ptr->pixelFormat = pixelFormat;
CS_Status status = 0;
auto rv = cs::GrabSinkFrame(static_cast<CS_Sink>(sink), *ptr, &status);
if (!CheckStatus(env, status)) {
return 0;
}
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
return rv;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: grabRawSinkFrameTimeoutImpl
* Signature: (ILjava/lang/Object;JLjava/lang/Object;IIID)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl
(JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
jobject byteBuffer, jint width, jint height, jint pixelFormat,
jdouble timeout)
{
CS_RawFrame* ptr =
reinterpret_cast<CS_RawFrame*>(static_cast<intptr_t>(rawFramePtr));
auto origDataPtr = ptr->data;
ptr->width = width;
ptr->height = height;
ptr->pixelFormat = pixelFormat;
CS_Status status = 0;
auto rv = cs::GrabSinkFrameTimeout(static_cast<CS_Sink>(sink), *ptr, timeout,
&status);
if (!CheckStatus(env, status)) {
return 0;
}
SetRawFrameData(env, rawFrameObj, byteBuffer, origDataPtr != ptr->data, *ptr);
return rv;
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: getSinkError
@@ -1781,4 +1947,32 @@ Java_edu_wpi_cscore_CameraServerJNI_setLogger
minLevel);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: allocateRawFrame
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_cscore_CameraServerJNI_allocateRawFrame
(JNIEnv*, jclass)
{
cs::RawFrame* rawFrame = new cs::RawFrame{};
intptr_t rawFrameIntPtr = reinterpret_cast<intptr_t>(rawFrame);
return static_cast<jlong>(rawFrameIntPtr);
}
/*
* Class: edu_wpi_cscore_CameraServerJNI
* Method: freeRawFrame
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_cscore_CameraServerJNI_freeRawFrame
(JNIEnv*, jclass, jlong rawFrame)
{
cs::RawFrame* ptr =
reinterpret_cast<cs::RawFrame*>(static_cast<intptr_t>(rawFrame));
delete ptr;
}
} // extern "C"