Implement CvSink.

* Make CvSink constructor explicit.

* Add SetDescription function.
This commit is contained in:
Peter Johnson
2016-10-26 23:37:00 -07:00
parent 9a44a38141
commit c66a55d81a
11 changed files with 263 additions and 64 deletions

View File

@@ -189,6 +189,8 @@ void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
//
// OpenCV Sink Functions
//
void CS_SetSinkDescription(CS_Sink sink, const char* description,
CS_Status* status);
uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image, CS_Status* status);
char* CS_GetSinkError(CS_Sink sink, CS_Status* status);
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status);

View File

@@ -182,6 +182,8 @@ void ReleaseSink(CS_Sink sink, CS_Status* status);
//
// OpenCV Sink Functions
//
void SetSinkDescription(CS_Sink sink, llvm::StringRef description,
CS_Status* status);
uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat& image, CS_Status* status);
std::string GetSinkError(CS_Sink sink, CS_Status* status);
llvm::StringRef GetSinkError(CS_Sink sink, llvm::SmallVectorImpl<char>& buf,

View File

@@ -340,7 +340,7 @@ class CvSink : public VideoSink {
/// WaitForFrame() must be called on the created sink to get each new
/// image.
/// @param name Source name (arbitrary unique identifier)
CvSink(llvm::StringRef name);
explicit CvSink(llvm::StringRef name);
/// Create a sink for accepting OpenCV images in a separate thread.
/// A thread will be created that calls WaitForFrame() and calls the
@@ -352,6 +352,10 @@ class CvSink : public VideoSink {
/// unusual circumstances) WaitForImage().
CvSink(llvm::StringRef name, std::function<void(uint64_t time)> processFrame);
/// Set sink description.
/// @param description Description
void SetDescription(llvm::StringRef description);
/// Wait for the next frame and get the image.
/// @return Frame time, or 0 on error (call GetError() to obtain the error
/// message);

View File

@@ -290,6 +290,11 @@ inline CvSink::CvSink(llvm::StringRef name,
m_handle = CreateCvSinkCallback(name, processFrame, &m_status);
}
inline void CvSink::SetDescription(llvm::StringRef description) {
m_status = 0;
SetSinkDescription(m_handle, description, &m_status);
}
inline uint64_t CvSink::GrabFrame(cv::Mat& image) const {
m_status = 0;
return GrabSinkFrame(m_handle, image, &m_status);

View File

@@ -742,6 +742,19 @@ JNIEXPORT void JNICALL Java_edu_wpi_cameraserver_CameraServerJNI_releaseSink
CheckStatus(env, status);
}
/*
* Class: edu_wpi_cameraserver_CameraServerJNI
* Method: setSinkDescription
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_edu_wpi_cameraserver_CameraServerJNI_setSinkDescription
(JNIEnv *env, jclass, jint sink, jstring description)
{
CS_Status status = 0;
cs::SetSinkDescription(sink, JStringRef{env, description}, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_cameraserver_CameraServerJNI
* Method: grabSinkFrame

View File

@@ -148,6 +148,7 @@ public class CameraServerJNI {
//
// OpenCV Sink Functions
//
public static native void setSinkDescription(int sink, String description);
public static native long grabSinkFrame(int sink, long imageNativeObj);
public static native String getSinkError(int sink);
public static native void setSinkEnabled(int sink, boolean enabled);

View File

@@ -32,6 +32,12 @@ public class CvSink extends VideoSink {
// super(CameraServerJNI.createCvSinkCallback(name, processFrame));
//}
/// Set sink description.
/// @param description Description
public void setDescription(String description) {
CameraServerJNI.setSinkDescription(m_handle, description);
}
/// Wait for the next frame and get the image.
/// @return Frame time, or 0 on error (call GetError() to obtain the error
/// message);

175
src/CvSinkImpl.cpp Normal file
View File

@@ -0,0 +1,175 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016. 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 "CvSinkImpl.h"
#include "llvm/SmallString.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "cameraserver_cpp.h"
#include "c_util.h"
#include "Handle.h"
#include "Log.h"
using namespace cs;
CvSinkImpl::CvSinkImpl(llvm::StringRef name) : SinkImpl{name} {
m_active = true;
// m_thread = std::thread(&CvSinkImpl::ThreadMain, this);
}
CvSinkImpl::CvSinkImpl(llvm::StringRef name,
std::function<void(uint64_t time)> processFrame)
: SinkImpl{name} {}
CvSinkImpl::~CvSinkImpl() { Stop(); }
void CvSinkImpl::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 CvSinkImpl::GrabFrame(cv::Mat& image) {
SetEnabled(true);
auto source = GetSource();
if (!source) return 0;
auto frame = source->GetNextFrame(); // blocks
if (!frame) return 0; // signal error
cv::imdecode(cv::InputArray{frame.data(), static_cast<int>(frame.size())},
cv::IMREAD_COLOR, &image);
return frame.time();
}
// Send HTTP response and a stream of JPG-frames
void CvSinkImpl::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;
}
DEBUG4("Cv: 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 CreateCvSink(llvm::StringRef name, CS_Status* status) {
auto sink = std::make_shared<CvSinkImpl>(name);
return Sinks::GetInstance().Allocate(SinkData::kCv, sink);
}
CS_Sink CreateCvSinkCallback(llvm::StringRef name,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
auto sink = std::make_shared<CvSinkImpl>(name, processFrame);
return Sinks::GetInstance().Allocate(SinkData::kCv, sink);
}
void SetSinkDescription(CS_Sink sink, llvm::StringRef description,
CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data || data->type != SinkData::kCv) {
*status = CS_INVALID_HANDLE;
return;
}
static_cast<CvSinkImpl&>(*data->sink).SetDescription(description);
}
uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat& image, CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data || data->type != SinkData::kCv) {
*status = CS_INVALID_HANDLE;
return 0;
}
return static_cast<CvSinkImpl&>(*data->sink).GrabFrame(image);
}
std::string GetSinkError(CS_Sink sink, CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data || data->type != SinkData::kCv) {
*status = CS_INVALID_HANDLE;
return std::string{};
}
return static_cast<CvSinkImpl&>(*data->sink).GetError();
}
llvm::StringRef GetSinkError(CS_Sink sink, llvm::SmallVectorImpl<char>& buf,
CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data || data->type != SinkData::kCv) {
*status = CS_INVALID_HANDLE;
return llvm::StringRef{};
}
return static_cast<CvSinkImpl&>(*data->sink).GetError(buf);
}
void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data || data->type != SinkData::kCv) {
*status = CS_INVALID_HANDLE;
return;
}
static_cast<CvSinkImpl&>(*data->sink).SetEnabled(enabled);
}
} // namespace cs
extern "C" {
CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) {
return cs::CreateCvSink(name, status);
}
CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
void (*processFrame)(void* data, uint64_t time),
CS_Status* status) {
return cs::CreateCvSinkCallback(
name, [=](uint64_t time) { processFrame(data, time); }, status);
}
void CS_SetSinkDescription(CS_Sink sink, const char* description,
CS_Status* status) {
return cs::SetSinkDescription(sink, description, status);
}
uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image,
CS_Status* status) {
auto mat = cv::cvarrToMat(image);
return cs::GrabSinkFrame(sink, mat, status);
}
char* CS_GetSinkError(CS_Sink sink, CS_Status* status) {
llvm::SmallString<128> buf;
auto str = cs::GetSinkError(sink, buf, status);
if (*status != 0) return nullptr;
return cs::ConvertToC(str);
}
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) {
return cs::SetSinkEnabled(sink, enabled, status);
}
} // extern "C"

53
src/CvSinkImpl.h Normal file
View File

@@ -0,0 +1,53 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016. 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 CAMERASERVER_CVSINKIMPL_H_
#define CAMERASERVER_CVSINKIMPL_H_
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "llvm/raw_ostream.h"
#include "llvm/SmallVector.h"
#include "llvm/StringRef.h"
#include "support/raw_istream.h"
#include "support/raw_socket_ostream.h"
#include "tcpsockets/NetworkAcceptor.h"
#include "tcpsockets/NetworkStream.h"
#include "SinkImpl.h"
namespace cs {
class SourceImpl;
class CvSinkImpl : public SinkImpl {
public:
CvSinkImpl(llvm::StringRef name);
CvSinkImpl(llvm::StringRef name,
std::function<void(uint64_t time)> processFrame);
~CvSinkImpl() override;
void Stop();
uint64_t GrabFrame(cv::Mat& image);
private:
void ThreadMain();
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 // CAMERASERVER_CVSINKIMPL_H_

View File

@@ -176,17 +176,6 @@ void CS_ReleaseSource(CS_Source source, CS_Status* status) {
return cs::ReleaseSource(source, status);
}
CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) {
return cs::CreateCvSink(name, status);
}
CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
void (*processFrame)(void* data, uint64_t time),
CS_Status* status) {
return cs::CreateCvSinkCallback(
name, [=](uint64_t time) { processFrame(data, time); }, status);
}
char* CS_GetSinkName(CS_Sink sink, CS_Status* status) {
llvm::SmallString<128> buf;
auto str = cs::GetSinkName(sink, buf, status);
@@ -222,23 +211,6 @@ void CS_ReleaseSink(CS_Sink sink, CS_Status* status) {
return cs::ReleaseSink(sink, status);
}
uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image,
CS_Status* status) {
auto mat = cv::cvarrToMat(image);
return cs::GrabSinkFrame(sink, mat, status);
}
char* CS_GetSinkError(CS_Sink sink, CS_Status* status) {
llvm::SmallString<128> buf;
auto str = cs::GetSinkError(sink, buf, status);
if (*status != 0) return nullptr;
return cs::ConvertToC(str);
}
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) {
return cs::SetSinkEnabled(sink, enabled, status);
}
CS_Listener CS_AddSourceListener(void* data,
void (*callback)(void* data, const char* name,
CS_Source source, int event),

View File

@@ -320,23 +320,10 @@ void ReleaseSource(CS_Source source, CS_Status* status) {
if (data->refCount-- == 0) inst.Free(source);
}
//
// Sink Creation Functions
//
CS_Sink CreateCvSink(llvm::StringRef name, CS_Status* status) {
return 0; // TODO
}
CS_Sink CreateCvSinkCallback(llvm::StringRef name,
std::function<void(uint64_t time)> processFrame,
CS_Status* status) {
return 0; // TODO
}
//
// Sink Functions
//
std::string GetSinkName(CS_Sink sink, CS_Status* status) {
auto data = Sinks::GetInstance().Get(sink);
if (!data) {
@@ -433,27 +420,6 @@ void ReleaseSink(CS_Sink sink, CS_Status* status) {
if (data->refCount-- == 0) inst.Free(sink);
}
//
// OpenCV Sink Functions
//
uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat& image, CS_Status* status) {
return 0; // TODO
}
std::string GetSinkError(CS_Sink sink, CS_Status* status) {
return std::string{}; // TODO
}
llvm::StringRef GetSinkError(CS_Sink sink, llvm::SmallVectorImpl<char>& buf,
CS_Status* status) {
return llvm::StringRef{}; // TODO
}
void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) {
// TODO
}
//
// Listener Functions
//