blob: 54b7e0639e901e81cf7653f515ff2194c3defabf [file] [log] [blame]
Brian Silverman1a675112016-02-20 20:42:49 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
Brian Silverman26e4e522015-12-17 01:56:40 -05008#include "CameraServer.h"
9#include "WPIErrors.h"
10#include "Utility.h"
11
12#include <iostream>
13#include <chrono>
14#include <cstring>
15#include <sys/socket.h>
16#include <unistd.h>
17#include <netdb.h>
18
19constexpr uint8_t CameraServer::kMagicNumber[];
20
21CameraServer* CameraServer::GetInstance() {
22 static CameraServer instance;
23 return &instance;
24}
25
26CameraServer::CameraServer()
27 : m_camera(),
28 m_serverThread(&CameraServer::Serve, this),
29 m_captureThread(),
30 m_imageMutex(),
31 m_newImageVariable(),
32 m_dataPool(3),
33 m_quality(50),
34 m_autoCaptureStarted(false),
35 m_hwClient(true),
36 m_imageData(nullptr, 0, 0, false) {
37 for (int i = 0; i < 3; i++) m_dataPool.push_back(new uint8_t[kMaxImageSize]);
38}
39
40void CameraServer::FreeImageData(
41 std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData) {
42 if (std::get<3>(imageData))
43 imaqDispose(std::get<0>(imageData));
44 else if (std::get<0>(imageData) != nullptr) {
45 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
46 m_dataPool.push_back(std::get<0>(imageData));
47 }
48}
49
50void CameraServer::SetImageData(uint8_t* data, unsigned int size,
51 unsigned int start, bool imaqData) {
52 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
53 FreeImageData(m_imageData);
54 m_imageData = std::make_tuple(data, size, start, imaqData);
55 m_newImageVariable.notify_all();
56}
57
58void CameraServer::SetImage(Image const* image) {
59 unsigned int dataSize = 0;
60 uint8_t* data =
61 (uint8_t*)imaqFlatten(image, IMAQ_FLATTEN_IMAGE, IMAQ_COMPRESSION_JPEG,
62 10 * m_quality, &dataSize);
63
64 // If we're using a HW camera, then find the start of the data
65 bool hwClient;
66 {
67 // Make a local copy of the hwClient variable so that we can safely use it.
68 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
69 hwClient = m_hwClient;
70 }
71 unsigned int start = 0;
72 if (hwClient) {
73 while (start < dataSize - 1) {
74 if (data[start] == 0xFF && data[start + 1] == 0xD8)
75 break;
76 else
77 start++;
78 }
79 }
80 dataSize -= start;
81
82 wpi_assert(dataSize > 2);
83 SetImageData(data, dataSize, start, true);
84}
85
86void CameraServer::AutoCapture() {
87 Image* frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0);
88
89 while (true) {
90 bool hwClient;
91 uint8_t* data = nullptr;
92 {
93 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
94 hwClient = m_hwClient;
95 if (hwClient) {
96 data = m_dataPool.back();
97 m_dataPool.pop_back();
98 }
99 }
100
101 if (hwClient) {
102 unsigned int size = m_camera->GetImageData(data, kMaxImageSize);
103 SetImageData(data, size);
104 } else {
105 m_camera->GetImage(frame);
106 SetImage(frame);
107 }
108 }
109}
110
111void CameraServer::StartAutomaticCapture(char const* cameraName) {
112 std::shared_ptr<USBCamera> camera =
113 std::make_shared<USBCamera>(cameraName, true);
114 camera->OpenCamera();
115 StartAutomaticCapture(camera);
116}
117
118void CameraServer::StartAutomaticCapture(std::shared_ptr<USBCamera> camera) {
119 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
120 if (m_autoCaptureStarted) return;
121
122 m_camera = camera;
123 m_camera->StartCapture();
124
125 m_captureThread = std::thread(&CameraServer::AutoCapture, this);
126 m_captureThread.detach();
127 m_autoCaptureStarted = true;
128}
129
130bool CameraServer::IsAutoCaptureStarted() {
131 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
132 return m_autoCaptureStarted;
133}
134
135void CameraServer::SetSize(unsigned int size) {
136 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
137 if (!m_camera) return;
138 if (size == kSize160x120)
139 m_camera->SetSize(160, 120);
140 else if (size == kSize320x240)
141 m_camera->SetSize(320, 240);
142 else if (size == kSize640x480)
143 m_camera->SetSize(640, 480);
144}
145
146void CameraServer::SetQuality(unsigned int quality) {
147 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
148 m_quality = quality > 100 ? 100 : quality;
149}
150
151unsigned int CameraServer::GetQuality() {
152 std::lock_guard<priority_recursive_mutex> lock(m_imageMutex);
153 return m_quality;
154}
155
156void CameraServer::Serve() {
157 int sock = socket(AF_INET, SOCK_STREAM, 0);
158
159 if (sock == -1) {
160 wpi_setErrnoError();
161 return;
162 }
163
164 int reuseAddr = 1;
165 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr,
166 sizeof(reuseAddr)) == -1)
167 wpi_setErrnoError();
168
169 sockaddr_in address, clientAddress;
170
171 memset(&address, 0, sizeof(address));
172 address.sin_family = AF_INET;
173 address.sin_addr.s_addr = htonl(INADDR_ANY);
174 address.sin_port = htons(kPort);
175
176 if (bind(sock, (struct sockaddr*)&address, sizeof(address)) == -1)
177 wpi_setErrnoError();
178
179 if (listen(sock, 10) == -1) wpi_setErrnoError();
180
181 while (true) {
182 socklen_t clientAddressLen = sizeof(clientAddress);
183
184 int conn =
185 accept(sock, (struct sockaddr*)&clientAddress, &clientAddressLen);
186 if (conn == -1) {
187 wpi_setErrnoError();
188 continue;
189 }
190
191 Request req;
192 if (read(conn, &req, sizeof(req)) == -1) {
193 wpi_setErrnoError();
194 close(conn);
195 continue;
196 } else {
197 req.fps = ntohl(req.fps);
198 req.compression = ntohl(req.compression);
199 req.size = ntohl(req.size);
200 }
201
202 // TODO: Support the SW Compression. The rest of the code below will work as
203 // though this
204 // check isn't here
205 if (req.compression != kHardwareCompression) {
206 wpi_setWPIErrorWithContext(IncompatibleState,
207 "Choose \"USB Camera HW\" on the dashboard");
208 close(conn);
209 continue;
210 }
211
212 {
213 // Wait for the camera to be setw
214 std::unique_lock<priority_recursive_mutex> lock(m_imageMutex);
215 if (!m_camera) {
216 std::cout << "Camera not yet ready, awaiting first image" << std::endl;
217 m_newImageVariable.wait(lock);
218 }
219 m_hwClient = req.compression == kHardwareCompression;
220 if (!m_hwClient)
221 SetQuality(100 - req.compression);
222 else if (m_camera)
223 m_camera->SetFPS(req.fps);
224 SetSize(req.size);
225 }
226
227 auto period = std::chrono::microseconds(1000000) / req.fps;
228 while (true) {
229 auto startTime = std::chrono::steady_clock::now();
230 std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData;
231 {
232 std::unique_lock<priority_recursive_mutex> lock(m_imageMutex);
233 m_newImageVariable.wait(lock);
234 imageData = m_imageData;
235 m_imageData = std::make_tuple<uint8_t*>(nullptr, 0, 0, false);
236 }
237
238 unsigned int size = std::get<1>(imageData);
239 unsigned int netSize = htonl(size);
240 unsigned int start = std::get<2>(imageData);
241 uint8_t* data = std::get<0>(imageData);
242
243 if (data == nullptr) continue;
244
245 if (write(conn, kMagicNumber, sizeof(kMagicNumber)) == -1) {
246 wpi_setErrnoErrorWithContext(
247 "[CameraServer] Error sending magic number");
248 FreeImageData(imageData);
249 break;
250 }
251 if (write(conn, &netSize, sizeof(netSize)) == -1) {
252 wpi_setErrnoErrorWithContext("[CameraServer] Error sending image size");
253 FreeImageData(imageData);
254 break;
255 }
256 if (write(conn, &data[start], sizeof(uint8_t) * size) == -1) {
257 wpi_setErrnoErrorWithContext("[CameraServer] Error sending image data");
258 FreeImageData(imageData);
259 break;
260 }
261 FreeImageData(imageData);
262 std::this_thread::sleep_until(startTime + period);
263 }
264 close(conn);
265 }
266 close(sock);
267}