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