libvisiontransfer  10.0.0
imagetransfer.cpp
1 /*******************************************************************************
2  * Copyright (c) 2022 Nerian Vision GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
15 #include <cstdio>
16 #include <iostream>
17 #include <cstring>
18 #include <memory>
19 #include <string>
20 #include <vector>
21 #include <mutex>
22 #include "visiontransfer/imagetransfer.h"
23 #include "visiontransfer/exceptions.h"
24 #include "visiontransfer/datablockprotocol.h"
25 #include "visiontransfer/networking.h"
26 
27 using namespace std;
28 using namespace visiontransfer;
29 using namespace visiontransfer::internal;
30 
31 namespace visiontransfer {
32 
33 /*************** Pimpl class containing all private members ***********/
34 
35 class ImageTransfer::Pimpl {
36 public:
37  Pimpl(const char* address, const char* service, ImageProtocol::ProtocolType protType,
38  bool server, int bufferSize, int maxUdpPacketSize);
39  ~Pimpl();
40 
41  // Redeclaration of public members
42  void setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
43  int firstTileWidth = 0, int middleTileWidth = 0, int lastTileWidth = 0);
44  void setRawValidBytes(const std::vector<int>& validBytes);
45  void setTransferImageSet(const ImageSet& imageSet);
46  TransferStatus transferData();
47  bool receiveImageSet(ImageSet& imageSet);
48  bool receivePartialImageSet(ImageSet& imageSet, int& validRows, bool& complete);
49  int getNumDroppedFrames() const;
50  bool isConnected() const;
51  void disconnect();
52  std::string getRemoteAddress() const;
53  bool tryAccept();
54 
55  std::string statusReport();
56 private:
57  // Configuration parameters
59  bool isServer;
60  int bufferSize;
61  int maxUdpPacketSize;
62 
63  // Thread synchronization
64  std::recursive_mutex receiveMutex;
65  std::recursive_mutex sendMutex;
66 
67  // Transfer related members
68  SOCKET clientSocket;
69  SOCKET tcpServerSocket;
70  sockaddr_in remoteAddress;
71 
72  // Object for encoding and decoding the network protocol
73  std::unique_ptr<ImageProtocol> protocol;
74 
75  // Outstanding network message that still has to be transferred
76  int currentMsgLen;
77  int currentMsgOffset;
78  const unsigned char* currentMsg;
79 
80  // Socket configuration
81  void setSocketOptions();
82 
83  // Network socket initialization
84  void initTcpServer(const addrinfo* addressInfo);
85  void initTcpClient(const addrinfo* addressInfo);
86  void initUdp(const addrinfo* addressInfo);
87 
88  // Data reception
89  bool receiveNetworkData(bool block);
90 
91  // Data transmission
92  bool sendNetworkMessage(const unsigned char* msg, int length);
93  void sendPendingControlMessages();
94 
95  bool selectSocket(bool read, bool wait);
96 };
97 
98 /******************** Stubs for all public members ********************/
99 
100 ImageTransfer::ImageTransfer(const char* address, const char* service,
101  ImageProtocol::ProtocolType protType, bool server, int bufferSize, int maxUdpPacketSize):
102  pimpl(new Pimpl(address, service, protType, server, bufferSize, maxUdpPacketSize)) {
103  // All initialization in the pimpl class
104 }
105 
106 ImageTransfer::ImageTransfer(const DeviceInfo& device, int bufferSize, int maxUdpPacketSize):
107  pimpl(new Pimpl(device.getIpAddress().c_str(), "7681", static_cast<ImageProtocol::ProtocolType>(device.getNetworkProtocol()),
108  false, bufferSize, maxUdpPacketSize)) {
109  // All initialization in the pimpl class
110 }
111 
112 ImageTransfer::~ImageTransfer() {
113  delete pimpl;
114 }
115 
116 void ImageTransfer::setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
117  int firstTileWidth, int middleTileWidth, int lastTileWidth) {
118  pimpl->setRawTransferData(metaData, rawData, firstTileWidth, middleTileWidth, lastTileWidth);
119 }
120 
121 void ImageTransfer::setRawValidBytes(const std::vector<int>& validBytes) {
122  pimpl->setRawValidBytes(validBytes);
123 }
124 
126  pimpl->setTransferImageSet(imageSet);
127 }
128 
130  return pimpl->transferData();
131 }
132 
134  return pimpl->receiveImageSet(imageSet);
135 }
136 
137 bool ImageTransfer::receivePartialImageSet(ImageSet& imageSet, int& validRows, bool& complete) {
138  return pimpl->receivePartialImageSet(imageSet, validRows, complete);
139 }
140 
142  return pimpl->getNumDroppedFrames();
143 }
144 
146  return pimpl->isConnected();
147 }
148 
150  pimpl->disconnect();
151 }
152 
153 std::string ImageTransfer::getRemoteAddress() const {
154  return pimpl->getRemoteAddress();
155 }
156 
158  return pimpl->tryAccept();
159 }
160 
161 /******************** Implementation in pimpl class *******************/
162 ImageTransfer::Pimpl::Pimpl(const char* address, const char* service,
163  ImageProtocol::ProtocolType protType, bool server, int
164  bufferSize, int maxUdpPacketSize)
165  : protType(protType), isServer(server), bufferSize(bufferSize),
166  maxUdpPacketSize(maxUdpPacketSize),
167  clientSocket(INVALID_SOCKET), tcpServerSocket(INVALID_SOCKET),
168  currentMsgLen(0), currentMsgOffset(0), currentMsg(nullptr) {
169 
170  Networking::initNetworking();
171 #ifndef _WIN32
172  // We don't want to be interrupted by the pipe signal
173  signal(SIGPIPE, SIG_IGN);
174 #endif
175 
176  memset(&remoteAddress, 0, sizeof(remoteAddress));
177 
178  // If address is null we use the any address
179  if(address == nullptr || string(address) == "") {
180  address = "0.0.0.0";
181  }
182 
183  addrinfo* addressInfo = Networking::resolveAddress(address, service);
184 
185  try {
186  if(protType == ImageProtocol::PROTOCOL_UDP) {
187  initUdp(addressInfo);
188  } else if(protType == ImageProtocol::PROTOCOL_TCP && isServer) {
189  initTcpServer(addressInfo);
190  } else {
191  initTcpClient(addressInfo);
192  }
193  } catch(...) {
194  freeaddrinfo(addressInfo);
195  throw;
196  }
197 
198  if(addressInfo != nullptr) {
199  freeaddrinfo(addressInfo);
200  }
201 }
202 
203 ImageTransfer::Pimpl::~Pimpl() {
204  if(clientSocket != INVALID_SOCKET) {
205  Networking::closeSocket(clientSocket);
206  }
207  if(tcpServerSocket != INVALID_SOCKET) {
208  Networking::closeSocket(tcpServerSocket);
209  }
210 }
211 
212 void ImageTransfer::Pimpl::initTcpClient(const addrinfo* addressInfo) {
213  protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_TCP));
214  clientSocket = Networking::connectTcpSocket(addressInfo);
215  memcpy(&remoteAddress, addressInfo->ai_addr, sizeof(remoteAddress));
216 
217  // Set special socket options
218  setSocketOptions();
219 }
220 
221 void ImageTransfer::Pimpl::initTcpServer(const addrinfo* addressInfo) {
222  protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_TCP));
223 
224  // Create socket
225  tcpServerSocket = ::socket(addressInfo->ai_family, addressInfo->ai_socktype,
226  addressInfo->ai_protocol);
227  if (tcpServerSocket == INVALID_SOCKET) {
228  TransferException ex("Error opening socket: " + Networking::getLastErrorString());
229  throw ex;
230  }
231 
232  // Enable reuse address
233  Networking::enableReuseAddress(tcpServerSocket, true);
234 
235  // Open a server port
236  Networking::bindSocket(tcpServerSocket, addressInfo);
237  clientSocket = INVALID_SOCKET;
238 
239  // Make the server socket non-blocking
240  Networking::setSocketBlocking(tcpServerSocket, false);
241 
242  // Listen on port
243  listen(tcpServerSocket, 1);
244 }
245 
246 void ImageTransfer::Pimpl::initUdp(const addrinfo* addressInfo) {
247  protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_UDP, maxUdpPacketSize));
248  // Create sockets
249  clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
250  if(clientSocket == INVALID_SOCKET) {
251  TransferException ex("Error creating receive socket: " + Networking::getLastErrorString());
252  throw ex;
253  }
254 
255  // Enable reuse address
256  Networking::enableReuseAddress(clientSocket, true);
257 
258  // Bind socket to port
259  if(isServer && addressInfo != nullptr) {
260  Networking::bindSocket(clientSocket, addressInfo);
261  }
262 
263  if(!isServer) {
264  memcpy(&remoteAddress, addressInfo->ai_addr, sizeof(remoteAddress));
265  }
266 
267  // Set special socket options
268  setSocketOptions();
269 }
270 
271 bool ImageTransfer::Pimpl::tryAccept() {
272  if(protType != ImageProtocol::PROTOCOL_TCP || ! isServer) {
273  throw TransferException("Connections can only be accepted in tcp server mode");
274  }
275 
276  // Accept one connection
277  SOCKET newSocket = Networking::acceptConnection(tcpServerSocket, remoteAddress);
278  if(newSocket == INVALID_SOCKET) {
279  // No connection
280  return false;
281  }
282 
283  // For a new connection we require locks
284  unique_lock<recursive_mutex> recvLock(receiveMutex);
285  unique_lock<recursive_mutex> sendLock(sendMutex);
286 
287  if(clientSocket != INVALID_SOCKET) {
288  Networking::closeSocket(clientSocket);
289  }
290  clientSocket = newSocket;
291 
292  // Set special socket options
293  setSocketOptions();
294 
295  // Reset connection data
296  protocol->resetTransfer();
297  protocol->resetReception();
298  currentMsg = nullptr;
299 
300  return true;
301 }
302 
303 std::string ImageTransfer::Pimpl::getRemoteAddress() const {
304  unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); // either mutex will work
305 
306  if(remoteAddress.sin_family != AF_INET) {
307  return "";
308  }
309 
310  char strPort[11];
311  snprintf(strPort, sizeof(strPort), ":%d", remoteAddress.sin_port);
312 
313  return string(inet_ntoa(remoteAddress.sin_addr)) + strPort;
314 }
315 
316 void ImageTransfer::Pimpl::setSocketOptions() {
317  // Set the socket buffer sizes
318  if(bufferSize > 0) {
319  setsockopt(clientSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
320  setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
321  }
322 
323  Networking::setSocketTimeout(clientSocket, 500);
324  Networking::setSocketBlocking(clientSocket, true);
325 }
326 
327 void ImageTransfer::Pimpl::setRawTransferData(const ImageSet& metaData,
328  const std::vector<unsigned char*>& rawDataVec, int firstTileWidth, int middleTileWidth, int lastTileWidth) {
329  unique_lock<recursive_mutex> sendLock(sendMutex);
330  protocol->setRawTransferData(metaData, rawDataVec, firstTileWidth, middleTileWidth, lastTileWidth);
331  currentMsg = nullptr;
332 }
333 
334 void ImageTransfer::Pimpl::setRawValidBytes(const std::vector<int>& validBytes) {
335  unique_lock<recursive_mutex> sendLock(sendMutex);
336  protocol->setRawValidBytes(validBytes);
337 }
338 
339 void ImageTransfer::Pimpl::setTransferImageSet(const ImageSet& imageSet) {
340  unique_lock<recursive_mutex> sendLock(sendMutex);
341  protocol->setTransferImageSet(imageSet);
342  currentMsg = nullptr;
343 }
344 
345 ImageTransfer::TransferStatus ImageTransfer::Pimpl::transferData() {
346  unique_lock<recursive_mutex> lock(sendMutex);
347 
348  // First receive data in case a control message arrives
349  if(protType == ImageProtocol::PROTOCOL_UDP) {
350  receiveNetworkData(false);
351  }
352 
353  if(remoteAddress.sin_family != AF_INET || !protocol->isConnected()) {
354  return NOT_CONNECTED;
355  }
356 
357 #ifndef _WIN32
358  // Cork TCP to prevent sending of small packets
359  if(protType == ImageProtocol::PROTOCOL_TCP) {
360  int flag = 1;
361  setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
362  }
363 #endif
364 
365  // Get first message to transfer
366  if(currentMsg == nullptr) {
367  currentMsgOffset = 0;
368  currentMsg = protocol->getTransferMessage(currentMsgLen);
369 
370  if(currentMsg == nullptr) {
371  if(protocol->transferComplete()) {
372  return ALL_TRANSFERRED;
373  } else {
374  return NO_VALID_DATA;
375  }
376  }
377  }
378 
379  // Try transferring messages
380  bool wouldBlock = false;
381  bool dataTransferred = (currentMsg != nullptr);
382  while(currentMsg != nullptr) {
383  int writing = (int)(currentMsgLen - currentMsgOffset);
384 
385  if(sendNetworkMessage(&currentMsg[currentMsgOffset], writing)) {
386  // Get next message
387  currentMsgOffset = 0;
388  currentMsg = protocol->getTransferMessage(currentMsgLen);
389  } else {
390  // The operation would block
391  wouldBlock = true;
392  break;
393  }
394  }
395 
396  if(dataTransferred && protType == ImageProtocol::PROTOCOL_TCP && protocol->transferComplete()) {
397 #ifndef _WIN32
398  // Uncork - sends the assembled messages
399  int flag = 0;
400  setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
401 #else
402  // Force a flush for TCP by turning the nagle algorithm off and on
403  int flag = 1;
404  setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
405  flag = 0;
406  setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
407 #endif
408  }
409 
410  // Also check for control messages at the end
411  if(protType == ImageProtocol::PROTOCOL_UDP) {
412  receiveNetworkData(false);
413  }
414 
415  if(protocol->transferComplete()) {
416  return ALL_TRANSFERRED;
417  } else if(wouldBlock) {
418  return WOULD_BLOCK;
419  } else {
420  return PARTIAL_TRANSFER;
421  }
422 }
423 
424 bool ImageTransfer::Pimpl::receiveImageSet(ImageSet& imageSet) {
425  int validRows = 0;
426  bool complete = false;
427 
428  std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
429  while(!complete) {
430  if(!receivePartialImageSet(imageSet, validRows, complete)) {
431  return false;
432  }
433 
434  unsigned int time = static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(
435  std::chrono::steady_clock::now() - startTime).count());
436  if(time > 100 && !complete) {
437  return false;
438  }
439  }
440 
441  return true;
442 }
443 
444 bool ImageTransfer::Pimpl::receivePartialImageSet(ImageSet& imageSet,
445  int& validRows, bool& complete) {
446  unique_lock<recursive_mutex> lock(receiveMutex);
447 
448  // Try to receive further image data if needed
449  bool block = true;
450  while(!protocol->imagesReceived() && receiveNetworkData(block)) {
451  block = false;
452  }
453 
454  // Get received image
455  return protocol->getPartiallyReceivedImageSet(imageSet, validRows, complete);
456 }
457 
458 bool ImageTransfer::Pimpl::receiveNetworkData(bool block) {
459  unique_lock<recursive_mutex> lock = block ?
460  unique_lock<recursive_mutex>(receiveMutex) : unique_lock<recursive_mutex>(receiveMutex, std::try_to_lock);
461 
462  if(clientSocket == INVALID_SOCKET) {
463  return false; // Not connected
464  }
465 
466  // First send control messages if necessary
467  sendPendingControlMessages();
468 
469  if(!lock.owns_lock()) {
470  // Waiting for the lock would block this call
471  return false;
472  }
473 
474  // Test if the socket has data available
475  if(!block && !selectSocket(true, false)) {
476  return false;
477  }
478 
479  int maxLength = 0;
480  char* buffer = reinterpret_cast<char*>(protocol->getNextReceiveBuffer(maxLength));
481 
482  // Receive data
483  sockaddr_in fromAddress;
484  socklen_t fromSize = sizeof(fromAddress);
485 
486  int bytesReceived = recvfrom(clientSocket, buffer, maxLength,
487  0, reinterpret_cast<sockaddr*>(&fromAddress), &fromSize);
488 
489  auto err = Networking::getErrno();
490  if(bytesReceived == 0 || (protType == ImageProtocol::PROTOCOL_TCP && bytesReceived < 0 && err == WSAECONNRESET)) {
491  // Connection closed
492  disconnect();
493  } else if(bytesReceived < 0 && err != EWOULDBLOCK && err != EINTR &&
494  err != ETIMEDOUT && err != WSA_IO_PENDING && err != WSAECONNRESET) {
495  TransferException ex("Error reading from socket: " + Networking::getErrorString(err));
496  throw ex;
497  } else if(bytesReceived > 0) {
498  protocol->processReceivedMessage(bytesReceived);
499  if(protocol->newClientConnected()) {
500  // We have just established a new connection
501  memcpy(&remoteAddress, &fromAddress, sizeof(remoteAddress));
502  }
503  }
504 
505  return bytesReceived > 0;
506 }
507 
508 void ImageTransfer::Pimpl::disconnect() {
509  // We just need to forget the remote address in order to
510  // disconnect
511  unique_lock<recursive_mutex> recvLock(receiveMutex);
512  unique_lock<recursive_mutex> sendLock(sendMutex);
513 
514  if(clientSocket != INVALID_SOCKET && protType == ImageProtocol::PROTOCOL_TCP) {
515  Networking::closeSocket(clientSocket);
516  }
517  memset(&remoteAddress, 0, sizeof(remoteAddress));
518 }
519 
520 bool ImageTransfer::Pimpl::isConnected() const {
521  unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); //either mutex will work
522 
523  return remoteAddress.sin_family == AF_INET && protocol->isConnected();
524 }
525 
526 bool ImageTransfer::Pimpl::sendNetworkMessage(const unsigned char* msg, int length) {
527  int written = 0;
528  if(protType == ImageProtocol::PROTOCOL_UDP) {
529  sockaddr_in destAddr;
530  SOCKET destSocket;
531  {
532  unique_lock<recursive_mutex> lock(sendMutex);
533  destAddr = remoteAddress;
534  destSocket = clientSocket;
535  }
536 
537  if(destAddr.sin_family != AF_INET) {
538  return false; // Not connected
539  }
540 
541  written = sendto(destSocket, reinterpret_cast<const char*>(msg), length, 0,
542  reinterpret_cast<sockaddr*>(&destAddr), sizeof(destAddr));
543  } else {
544  SOCKET destSocket;
545  {
546  unique_lock<recursive_mutex> lock(sendMutex);
547  destSocket = clientSocket;
548  }
549  written = send(destSocket, reinterpret_cast<const char*>(msg), length, 0);
550  }
551 
552  auto sendError = Networking::getErrno();
553 
554  if(written < 0) {
555  if(sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT) {
556  // The socket is not yet ready for a new transfer
557  return false;
558  } else if(sendError == EPIPE) {
559  // The connection has been closed
560  disconnect();
561  return false;
562  } else {
563  TransferException ex("Error sending network packet: " + Networking::getErrorString(sendError));
564  throw ex;
565  }
566  } else if(written != length) {
567  if(protType == ImageProtocol::PROTOCOL_UDP) {
568  // The message has been transmitted partially
569  throw TransferException("Unable to transmit complete UDP message");
570  } else {
571  // For TCP we can transmit the remaining data later
572  currentMsgOffset += written;
573  return false;
574  }
575  } else {
576  return true;
577  }
578 }
579 
580 void ImageTransfer::Pimpl::sendPendingControlMessages() {
581  const unsigned char* controlMsgData = nullptr;
582  int controlMsgLen = 0;
583 
584  while(true) {
585  unique_lock<recursive_mutex> lock(sendMutex);
586  if(remoteAddress.sin_family != AF_INET) {
587  return;
588  }
589 
590  controlMsgData = protocol->getNextControlMessage(controlMsgLen);
591 
592  if(controlMsgData != nullptr) {
593  sendNetworkMessage(controlMsgData, controlMsgLen);
594  } else {
595  break;
596  }
597  }
598 }
599 
600 int ImageTransfer::Pimpl::getNumDroppedFrames() const {
601  return protocol->getNumDroppedFrames();
602 }
603 
604 bool ImageTransfer::Pimpl::selectSocket(bool read, bool wait) {
605  SOCKET sock;
606  {
607  unique_lock<recursive_mutex> lock(sendMutex); // Either mutex will do
608  sock = clientSocket;
609  }
610 #ifdef _WIN32
611  fd_set fds;
612  struct timeval tv;
613  FD_ZERO(&fds);
614  FD_SET(sock, &fds);
615  tv.tv_sec = 0;
616  if(wait) {
617  tv.tv_usec = 100000;
618  } else {
619  tv.tv_usec = 0;
620  }
621 
622  if(select(sock+1, (read ? &fds : nullptr), (!read ? &fds : nullptr), nullptr, &tv) <= 0) {
623  // The socket is currently not ready
624  return false;
625  }
626 #else
627  // use poll() on non-Windows platform (glibc select() limitations)
628  constexpr int timeoutMillisec = 100;
629  pollfd pfd;
630  pfd.fd = sock;
631  pfd.events = POLLIN;
632  if (poll(&pfd, 1, wait ? timeoutMillisec: 0) <= 0) {
633  // The socket is currently not ready
634  return false;
635  }
636 #endif
637  // select (or poll) reported an event
638  return true;
639 }
640 
641 std::string ImageTransfer::statusReport() {
642  return pimpl->statusReport();
643 }
644 std::string ImageTransfer::Pimpl::statusReport() {
645  return protocol->statusReport();
646 }
647 
648 } // namespace
649 
std::string getRemoteAddress() const
Returns the address of the remote host.
int getNumDroppedFrames() const
Returns the number of frames that have been dropped since connecting to the current remote host...
bool receivePartialImageSet(ImageSet &imageSet, int &validRows, bool &complete)
Returns the received image set, even if it is not yet complete.
The operation would block and blocking as been disabled.
Definition: imagetransfer.h:51
The connection-less UDP transport protocol.
Definition: imageprotocol.h:48
bool tryAccept()
Tries to accept a client connection.
void disconnect()
Terminates the current connection.
void setRawValidBytes(const std::vector< int > &validBytes)
Updates the number of valid bytes in a partial raw transmission.
bool receiveImageSet(ImageSet &imageSet)
Waits for and receives a new image set.
A lightweight protocol for transferring image sets.
Definition: imageprotocol.h:40
No network connection has been established.
Definition: imagetransfer.h:54
TransferStatus transferData()
Performs a partial (or full) image transmission.
ProtocolType
Supported network protocols.
Definition: imageprotocol.h:43
bool isConnected() const
Returns true if a remote connection is established.
Aggregates information about a discovered device.
Definition: deviceinfo.h:47
There is currently no more data that could be transmitted.
Definition: imagetransfer.h:48
The image set has been transferred completely.
Definition: imagetransfer.h:41
A set of one to three images, but usually two (the left camera image and the disparity map)...
Definition: imageset.h:38
void setRawTransferData(const ImageSet &metaData, const std::vector< unsigned char *> &rawData, int firstTileWidth=0, int middleTileWidth=0, int lastTileWidth=0)
Sets the raw pixel data for a partial image transmission.
ImageTransfer(const char *address, const char *service="7681", ImageProtocol::ProtocolType protType=ImageProtocol::PROTOCOL_UDP, bool server=false, int bufferSize=16 *1048576, int maxUdpPacketSize=1472)
Creates a new transfer object by manually specifying the target address.
Exception class that is used for all transfer exceptions.
Definition: exceptions.h:33
void setTransferImageSet(const ImageSet &imageSet)
Sets a new image set that shall be transmitted.
The connection oriented TCP transport protocol.
Definition: imageprotocol.h:45
TransferStatus
The result of a partial image transfer.
Definition: imagetransfer.h:39
Nerian Vision Technologies