2016-01-02 03:02:34 -08:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
/* 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. */
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
2014-10-29 13:38:43 -04:00
|
|
|
#include "CameraServer.h"
|
|
|
|
|
|
2016-05-20 17:30:37 -07:00
|
|
|
#include <netdb.h>
|
2016-05-25 22:38:11 -07:00
|
|
|
#include <string.h>
|
2014-10-29 13:38:43 -04:00
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <unistd.h>
|
2016-05-25 22:38:11 -07:00
|
|
|
|
2016-05-20 17:30:37 -07:00
|
|
|
#include <chrono>
|
|
|
|
|
#include <iostream>
|
2014-10-29 13:38:43 -04:00
|
|
|
|
2016-05-25 22:38:11 -07:00
|
|
|
#include "Utility.h"
|
|
|
|
|
#include "WPIErrors.h"
|
|
|
|
|
|
2014-10-29 13:38:43 -04:00
|
|
|
constexpr uint8_t CameraServer::kMagicNumber[];
|
|
|
|
|
|
2015-01-05 13:46:12 -05:00
|
|
|
CameraServer* CameraServer::GetInstance() {
|
2015-06-24 04:25:10 -07:00
|
|
|
static CameraServer instance;
|
|
|
|
|
return &instance;
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
CameraServer::CameraServer()
|
|
|
|
|
: m_camera(),
|
|
|
|
|
m_serverThread(&CameraServer::Serve, this),
|
|
|
|
|
m_captureThread(),
|
|
|
|
|
m_imageMutex(),
|
|
|
|
|
m_newImageVariable(),
|
|
|
|
|
m_dataPool(3),
|
|
|
|
|
m_quality(50),
|
|
|
|
|
m_autoCaptureStarted(false),
|
|
|
|
|
m_hwClient(true),
|
|
|
|
|
m_imageData(nullptr, 0, 0, false) {
|
|
|
|
|
for (int i = 0; i < 3; i++) m_dataPool.push_back(new uint8_t[kMaxImageSize]);
|
2015-01-05 13:46:12 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void CameraServer::FreeImageData(
|
|
|
|
|
std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData) {
|
|
|
|
|
if (std::get<3>(imageData))
|
|
|
|
|
imaqDispose(std::get<0>(imageData));
|
2015-01-05 13:46:12 -05:00
|
|
|
else if (std::get<0>(imageData) != nullptr) {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
m_dataPool.push_back(std::get<0>(imageData));
|
|
|
|
|
}
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void CameraServer::SetImageData(uint8_t* data, unsigned int size,
|
|
|
|
|
unsigned int start, bool imaqData) {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
FreeImageData(m_imageData);
|
|
|
|
|
m_imageData = std::make_tuple(data, size, start, imaqData);
|
|
|
|
|
m_newImageVariable.notify_all();
|
|
|
|
|
}
|
2015-06-25 15:07:55 -04:00
|
|
|
|
|
|
|
|
void CameraServer::SetImage(Image const* image) {
|
2015-01-05 13:46:12 -05:00
|
|
|
unsigned int dataSize = 0;
|
2015-06-25 15:07:55 -04:00
|
|
|
uint8_t* data =
|
|
|
|
|
(uint8_t*)imaqFlatten(image, IMAQ_FLATTEN_IMAGE, IMAQ_COMPRESSION_JPEG,
|
|
|
|
|
10 * m_quality, &dataSize);
|
2015-01-05 13:46:12 -05:00
|
|
|
|
|
|
|
|
// If we're using a HW camera, then find the start of the data
|
|
|
|
|
bool hwClient;
|
|
|
|
|
{
|
|
|
|
|
// Make a local copy of the hwClient variable so that we can safely use it.
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
hwClient = m_hwClient;
|
|
|
|
|
}
|
|
|
|
|
unsigned int start = 0;
|
|
|
|
|
if (hwClient) {
|
|
|
|
|
while (start < dataSize - 1) {
|
2015-06-25 15:07:55 -04:00
|
|
|
if (data[start] == 0xFF && data[start + 1] == 0xD8)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
start++;
|
2015-01-05 13:46:12 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dataSize -= start;
|
|
|
|
|
|
|
|
|
|
wpi_assert(dataSize > 2);
|
|
|
|
|
SetImageData(data, dataSize, start, true);
|
2015-06-25 15:07:55 -04:00
|
|
|
}
|
2015-01-05 13:46:12 -05:00
|
|
|
|
|
|
|
|
void CameraServer::AutoCapture() {
|
|
|
|
|
Image* frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
bool hwClient;
|
|
|
|
|
uint8_t* data = nullptr;
|
|
|
|
|
{
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
hwClient = m_hwClient;
|
|
|
|
|
if (hwClient) {
|
|
|
|
|
data = m_dataPool.back();
|
|
|
|
|
m_dataPool.pop_back();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hwClient) {
|
|
|
|
|
unsigned int size = m_camera->GetImageData(data, kMaxImageSize);
|
|
|
|
|
SetImageData(data, size);
|
|
|
|
|
} else {
|
|
|
|
|
m_camera->GetImage(frame);
|
|
|
|
|
SetImage(frame);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
void CameraServer::StartAutomaticCapture(char const* cameraName) {
|
|
|
|
|
std::shared_ptr<USBCamera> camera =
|
|
|
|
|
std::make_shared<USBCamera>(cameraName, true);
|
2015-01-05 13:46:12 -05:00
|
|
|
camera->OpenCamera();
|
|
|
|
|
StartAutomaticCapture(camera);
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:46:12 -05:00
|
|
|
void CameraServer::StartAutomaticCapture(std::shared_ptr<USBCamera> camera) {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
if (m_autoCaptureStarted) return;
|
|
|
|
|
|
|
|
|
|
m_camera = camera;
|
|
|
|
|
m_camera->StartCapture();
|
2014-10-29 13:38:43 -04:00
|
|
|
|
2015-01-05 13:46:12 -05:00
|
|
|
m_captureThread = std::thread(&CameraServer::AutoCapture, this);
|
|
|
|
|
m_captureThread.detach();
|
|
|
|
|
m_autoCaptureStarted = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CameraServer::IsAutoCaptureStarted() {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
return m_autoCaptureStarted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CameraServer::SetSize(unsigned int size) {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
if (!m_camera) return;
|
2015-06-25 15:07:55 -04:00
|
|
|
if (size == kSize160x120)
|
|
|
|
|
m_camera->SetSize(160, 120);
|
|
|
|
|
else if (size == kSize320x240)
|
|
|
|
|
m_camera->SetSize(320, 240);
|
|
|
|
|
else if (size == kSize640x480)
|
|
|
|
|
m_camera->SetSize(640, 480);
|
2015-01-05 13:46:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CameraServer::SetQuality(unsigned int quality) {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
m_quality = quality > 100 ? 100 : quality;
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:46:12 -05:00
|
|
|
unsigned int CameraServer::GetQuality() {
|
2015-09-01 16:47:57 -07:00
|
|
|
std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
return m_quality;
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:46:12 -05:00
|
|
|
void CameraServer::Serve() {
|
|
|
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
|
|
2015-11-15 14:49:50 -08:00
|
|
|
if (sock == -1) {
|
|
|
|
|
wpi_setErrnoError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-01-05 13:46:12 -05:00
|
|
|
|
|
|
|
|
int reuseAddr = 1;
|
2015-06-25 15:07:55 -04:00
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr,
|
|
|
|
|
sizeof(reuseAddr)) == -1)
|
2015-01-05 13:46:12 -05:00
|
|
|
wpi_setErrnoError();
|
|
|
|
|
|
|
|
|
|
sockaddr_in address, clientAddress;
|
|
|
|
|
|
|
|
|
|
memset(&address, 0, sizeof(address));
|
|
|
|
|
address.sin_family = AF_INET;
|
|
|
|
|
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
|
address.sin_port = htons(kPort);
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
if (bind(sock, (struct sockaddr*)&address, sizeof(address)) == -1)
|
2015-01-05 13:46:12 -05:00
|
|
|
wpi_setErrnoError();
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
if (listen(sock, 10) == -1) wpi_setErrnoError();
|
2015-01-05 13:46:12 -05:00
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
while (true) {
|
2015-01-05 13:46:12 -05:00
|
|
|
socklen_t clientAddressLen = sizeof(clientAddress);
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
int conn =
|
|
|
|
|
accept(sock, (struct sockaddr*)&clientAddress, &clientAddressLen);
|
2015-01-05 13:46:12 -05:00
|
|
|
if (conn == -1) {
|
|
|
|
|
wpi_setErrnoError();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Request req;
|
2016-02-24 00:18:59 -08:00
|
|
|
char reqBuf[sizeof(req)];
|
|
|
|
|
size_t reqPos = 0;
|
|
|
|
|
|
|
|
|
|
while (reqPos < sizeof(req)) {
|
|
|
|
|
ssize_t sizeRead = read(conn, &reqBuf[reqPos], sizeof(req) - reqPos);
|
|
|
|
|
if (sizeRead < 0) break;
|
|
|
|
|
reqPos += sizeRead;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reqPos < sizeof(req)) {
|
2015-01-05 13:46:12 -05:00
|
|
|
wpi_setErrnoError();
|
|
|
|
|
close(conn);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 00:18:59 -08:00
|
|
|
memcpy(&req, reqBuf, sizeof(req));
|
|
|
|
|
req.fps = ntohl(req.fps);
|
|
|
|
|
req.compression = ntohl(req.compression);
|
|
|
|
|
req.size = ntohl(req.size);
|
|
|
|
|
|
2015-06-25 15:07:55 -04:00
|
|
|
// TODO: Support the SW Compression. The rest of the code below will work as
|
2016-05-20 17:30:37 -07:00
|
|
|
// though this check isn't here
|
2015-01-05 13:46:12 -05:00
|
|
|
if (req.compression != kHardwareCompression) {
|
2015-06-25 15:07:55 -04:00
|
|
|
wpi_setWPIErrorWithContext(IncompatibleState,
|
|
|
|
|
"Choose \"USB Camera HW\" on the dashboard");
|
2015-01-05 13:46:12 -05:00
|
|
|
close(conn);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// Wait for the camera to be setw
|
2015-06-25 01:54:20 -07:00
|
|
|
std::unique_lock<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
if (!m_camera) {
|
|
|
|
|
std::cout << "Camera not yet ready, awaiting first image" << std::endl;
|
|
|
|
|
m_newImageVariable.wait(lock);
|
|
|
|
|
}
|
|
|
|
|
m_hwClient = req.compression == kHardwareCompression;
|
2015-06-25 15:07:55 -04:00
|
|
|
if (!m_hwClient)
|
|
|
|
|
SetQuality(100 - req.compression);
|
|
|
|
|
else if (m_camera)
|
|
|
|
|
m_camera->SetFPS(req.fps);
|
2015-01-05 13:46:12 -05:00
|
|
|
SetSize(req.size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto period = std::chrono::microseconds(1000000) / req.fps;
|
|
|
|
|
while (true) {
|
|
|
|
|
auto startTime = std::chrono::steady_clock::now();
|
|
|
|
|
std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData;
|
|
|
|
|
{
|
2015-06-25 01:54:20 -07:00
|
|
|
std::unique_lock<priority_recursive_mutex> lock(m_imageMutex);
|
2015-01-05 13:46:12 -05:00
|
|
|
m_newImageVariable.wait(lock);
|
|
|
|
|
imageData = m_imageData;
|
2015-06-23 04:49:51 -07:00
|
|
|
m_imageData = std::make_tuple<uint8_t*>(nullptr, 0, 0, false);
|
2015-01-05 13:46:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int size = std::get<1>(imageData);
|
|
|
|
|
unsigned int netSize = htonl(size);
|
|
|
|
|
unsigned int start = std::get<2>(imageData);
|
2015-06-25 15:07:55 -04:00
|
|
|
uint8_t* data = std::get<0>(imageData);
|
2015-01-05 13:46:12 -05:00
|
|
|
|
|
|
|
|
if (data == nullptr) continue;
|
|
|
|
|
|
|
|
|
|
if (write(conn, kMagicNumber, sizeof(kMagicNumber)) == -1) {
|
2015-06-25 15:07:55 -04:00
|
|
|
wpi_setErrnoErrorWithContext(
|
|
|
|
|
"[CameraServer] Error sending magic number");
|
2015-01-05 13:46:12 -05:00
|
|
|
FreeImageData(imageData);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (write(conn, &netSize, sizeof(netSize)) == -1) {
|
|
|
|
|
wpi_setErrnoErrorWithContext("[CameraServer] Error sending image size");
|
|
|
|
|
FreeImageData(imageData);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (write(conn, &data[start], sizeof(uint8_t) * size) == -1) {
|
|
|
|
|
wpi_setErrnoErrorWithContext("[CameraServer] Error sending image data");
|
|
|
|
|
FreeImageData(imageData);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
FreeImageData(imageData);
|
|
|
|
|
std::this_thread::sleep_until(startTime + period);
|
|
|
|
|
}
|
|
|
|
|
close(conn);
|
|
|
|
|
}
|
|
|
|
|
close(sock);
|
2014-10-29 13:38:43 -04:00
|
|
|
}
|