Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #include <Stronghold2016Robot.h> | |
| /*----------------------------------------------------------------------------*/ | |
| /* 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. */ | |
| /*----------------------------------------------------------------------------*/ | |
| constexpr uint8_t LCameraServer::kMagicNumber[]; | |
| bool LCameraServer::errored = false; | |
| LCameraServer* LCameraServer::GetInstance() { | |
| static LCameraServer instance; | |
| return &instance; | |
| } | |
| LCameraServer::LCameraServer() : | |
| m_camera(), m_serverThread(&LCameraServer::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]); | |
| } | |
| void LCameraServer::FreeImageData( | |
| std::tuple<uint8_t*, unsigned int, unsigned int, bool> imageData) { | |
| if (std::get<3>(imageData)) | |
| imaqDispose(std::get<0>(imageData)); | |
| else if (std::get<0>(imageData) != nullptr) { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| m_dataPool.push_back(std::get<0>(imageData)); | |
| } | |
| } | |
| void LCameraServer::SetImageData(uint8_t* data, unsigned int size, | |
| unsigned int start, bool imaqData) { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| FreeImageData(m_imageData); | |
| m_imageData = std::make_tuple(data, size, start, imaqData); | |
| m_newImageVariable.notify_all(); | |
| } | |
| void LCameraServer::SetImage(Image const* image) { | |
| unsigned int dataSize = 0; | |
| uint8_t* data = (uint8_t*) imaqFlatten(image, IMAQ_FLATTEN_IMAGE, | |
| IMAQ_COMPRESSION_JPEG, 10 * m_quality, &dataSize); | |
| // 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. | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| hwClient = m_hwClient; | |
| } | |
| unsigned int start = 0; | |
| if (hwClient) { | |
| while (start < dataSize - 1) { | |
| if (data[start] == 0xFF && data[start + 1] == 0xD8) | |
| break; | |
| else | |
| start++; | |
| } | |
| } | |
| dataSize -= start; | |
| wpi_assert(dataSize > 2); | |
| SetImageData(data, dataSize, start, true); | |
| } | |
| void LCameraServer::AutoCapture() { | |
| Image* frame = imaqCreateImage(IMAQ_IMAGE_RGB, 0); | |
| while (true) { | |
| bool hwClient; | |
| uint8_t* data = nullptr; | |
| { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| 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); | |
| } | |
| } | |
| } | |
| void LCameraServer::StartAutomaticCapture(char const* cameraName) { | |
| std::shared_ptr<USBCamera> camera = std::make_shared<USBCamera>(cameraName, | |
| true); | |
| camera->OpenCamera(); | |
| StartAutomaticCapture(camera); | |
| } | |
| void LCameraServer::StartAutomaticCapture(std::shared_ptr<USBCamera> camera) { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| if (m_autoCaptureStarted) | |
| return; | |
| m_camera = camera; | |
| m_camera->StartCapture(); | |
| m_captureThread = std::thread(&LCameraServer::AutoCapture, this); | |
| m_captureThread.detach(); | |
| m_autoCaptureStarted = true; | |
| } | |
| bool LCameraServer::IsAutoCaptureStarted() { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| return m_autoCaptureStarted; | |
| } | |
| void LCameraServer::SetSize(unsigned int size) { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| if (!m_camera) | |
| return; | |
| 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); | |
| } | |
| void LCameraServer::SetQuality(unsigned int quality) { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| m_quality = quality > 100 ? 100 : quality; | |
| } | |
| unsigned int LCameraServer::GetQuality() { | |
| std::lock_guard<priority_recursive_mutex> lock(m_imageMutex); | |
| return m_quality; | |
| } | |
| void LCameraServer::Serve() { | |
| printf("LCameraServer: Serve\n"); | |
| int sock = socket(AF_INET, SOCK_STREAM, 0); | |
| if (sock == -1) { | |
| wpi_setErrnoError(); | |
| return; | |
| } | |
| int reuseAddr = 1; | |
| if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, | |
| sizeof(reuseAddr)) == -1) | |
| 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); | |
| if (bind(sock, (struct sockaddr*) &address, sizeof(address)) == -1) | |
| wpi_setErrnoError(); | |
| if (listen(sock, 10) == -1) | |
| wpi_setErrnoError(); | |
| while (true) { | |
| socklen_t clientAddressLen = sizeof(clientAddress); | |
| int conn = accept(sock, (struct sockaddr*) &clientAddress, | |
| &clientAddressLen); | |
| if (conn == -1) { | |
| wpi_setErrnoError(); | |
| continue; | |
| } | |
| printf("LCameraServer: Got Connection!\n"); | |
| Request req; | |
| // the problem with the default implementation of receiving the Request in | |
| // CameraServer was that it required the entire buffer to already be there | |
| // by the time it got to read. Sometimes the buffer would not be there, which | |
| // caused a loop of SmartDashboard repeatedly trying to connect. The code | |
| // below makes sure we have the entire contents of the Request (12 bytes) | |
| // before we try to interpret the contents | |
| char requestBuffer[sizeof(Request)]; | |
| unsigned int index = 0; | |
| bool connFailed = false; | |
| while (index < sizeof(Request)) { | |
| char next; | |
| int sizeRead = read(conn, &next, sizeof(next)); | |
| if (sizeRead == -1) { | |
| wpi_setErrnoError(); | |
| close(conn); | |
| connFailed = true; | |
| break; | |
| } | |
| if (sizeRead < 1) | |
| continue; | |
| requestBuffer[index] = next; | |
| index++; | |
| } | |
| if (connFailed) { | |
| continue; | |
| } | |
| // slightly unsafe cast. We use sizeof(Request) so it should be ok | |
| memcpy(&req, &requestBuffer, sizeof(Request)); | |
| req.fps = ntohl(req.fps); | |
| req.compression = ntohl(req.compression); | |
| req.size = ntohl(req.size); | |
| // TODO: Support the SW Compression. The rest of the code below will work as | |
| // though this | |
| // check isn't here | |
| if (req.compression != kHardwareCompression) { | |
| if (!errored) { | |
| errored = true; | |
| printf("Request fps %i compression %i size %i\n", req.fps, | |
| req.compression, req.size); | |
| wpi_setWPIErrorWithContext(IncompatibleState, | |
| "Choose \"USB Camera HW\" on the dashboard"); | |
| } | |
| close(conn); | |
| continue; | |
| } else { | |
| errored = false; | |
| } | |
| { | |
| // Wait for the camera to be setw | |
| std::unique_lock<priority_recursive_mutex> lock(m_imageMutex); | |
| if (!m_camera) { | |
| std::cout << "Camera not yet ready, awaiting first image" | |
| << std::endl; | |
| m_newImageVariable.wait(lock); | |
| } | |
| m_hwClient = req.compression == kHardwareCompression; | |
| if (!m_hwClient) | |
| SetQuality(100 - req.compression); | |
| else if (m_camera) | |
| m_camera->SetFPS(req.fps); | |
| 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; | |
| { | |
| std::unique_lock<priority_recursive_mutex> lock(m_imageMutex); | |
| m_newImageVariable.wait(lock); | |
| imageData = m_imageData; | |
| m_imageData = std::make_tuple<uint8_t*>(nullptr, 0, 0, false); | |
| } | |
| unsigned int size = std::get<1>(imageData); | |
| unsigned int netSize = htonl(size); | |
| unsigned int start = std::get<2>(imageData); | |
| uint8_t* data = std::get<0>(imageData); | |
| if (data == nullptr) | |
| continue; | |
| if (write(conn, kMagicNumber, sizeof(kMagicNumber)) == -1) { | |
| wpi_setErrnoErrorWithContext( | |
| "[CameraServer] Error sending magic number"); | |
| 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); | |
| } |