libvisiontransfer  9.0.3
imagetransfer.cpp
1 /*******************************************************************************
2  * Copyright (c) 2021 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 secondTileWidth = 0, int validBytes = 0x7FFFFFFF);
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 secondTileWidth, int validBytes) {
118  pimpl->setRawTransferData(metaData, rawData, firstTileWidth, secondTileWidth, validBytes);
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: " + string(strerror(errno)));
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: " + string(strerror(errno)));
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 secondTileWidth, int validBytes) {
329  unique_lock<recursive_mutex> sendLock(sendMutex);
330  protocol->setRawTransferData(metaData, rawDataVec, firstTileWidth, secondTileWidth, validBytes);
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  if(bytesReceived == 0 || (protType == ImageProtocol::PROTOCOL_TCP && bytesReceived < 0 && errno == WSAECONNRESET)) {
490  // Connection closed
491  disconnect();
492  } else if(bytesReceived < 0 && errno != EWOULDBLOCK && errno != EINTR &&
493  errno != ETIMEDOUT && errno != WSA_IO_PENDING && errno != WSAECONNRESET) {
494  TransferException ex("Error reading from socket: " + string(strerror(errno)));
495  throw ex;
496  } else if(bytesReceived > 0) {
497  protocol->processReceivedMessage(bytesReceived);
498  if(protocol->newClientConnected()) {
499  // We have just established a new connection
500  memcpy(&remoteAddress, &fromAddress, sizeof(remoteAddress));
501  }
502  }
503 
504  return bytesReceived > 0;
505 }
506 
507 void ImageTransfer::Pimpl::disconnect() {
508  // We just need to forget the remote address in order to
509  // disconnect
510  unique_lock<recursive_mutex> recvLock(receiveMutex);
511  unique_lock<recursive_mutex> sendLock(sendMutex);
512 
513  if(clientSocket != INVALID_SOCKET && protType == ImageProtocol::PROTOCOL_TCP) {
514  Networking::closeSocket(clientSocket);
515  }
516  memset(&remoteAddress, 0, sizeof(remoteAddress));
517 }
518 
519 bool ImageTransfer::Pimpl::isConnected() const {
520  unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); //either mutex will work
521 
522  return remoteAddress.sin_family == AF_INET && protocol->isConnected();
523 }
524 
525 bool ImageTransfer::Pimpl::sendNetworkMessage(const unsigned char* msg, int length) {
526  int written = 0;
527  if(protType == ImageProtocol::PROTOCOL_UDP) {
528  sockaddr_in destAddr;
529  SOCKET destSocket;
530  {
531  unique_lock<recursive_mutex> lock(sendMutex);
532  destAddr = remoteAddress;
533  destSocket = clientSocket;
534  }
535 
536  if(destAddr.sin_family != AF_INET) {
537  return false; // Not connected
538  }
539 
540  written = sendto(destSocket, reinterpret_cast<const char*>(msg), length, 0,
541  reinterpret_cast<sockaddr*>(&destAddr), sizeof(destAddr));
542  } else {
543  SOCKET destSocket;
544  {
545  unique_lock<recursive_mutex> lock(sendMutex);
546  destSocket = clientSocket;
547  }
548  written = send(destSocket, reinterpret_cast<const char*>(msg), length, 0);
549  }
550 
551  unsigned long sendError = errno;
552 
553  if(written < 0) {
554  if(sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT) {
555  // The socket is not yet ready for a new transfer
556  return false;
557  } else if(sendError == EPIPE) {
558  // The connection has been closed
559  disconnect();
560  return false;
561  } else {
562  TransferException ex("Error sending network packet: " + string(strerror(sendError)));
563  throw ex;
564  }
565  } else if(written != length) {
566  if(protType == ImageProtocol::PROTOCOL_UDP) {
567  // The message has been transmitted partially
568  throw TransferException("Unable to transmit complete UDP message");
569  } else {
570  // For TCP we can transmit the remaining data later
571  currentMsgOffset += written;
572  return false;
573  }
574  } else {
575  return true;
576  }
577 }
578 
579 void ImageTransfer::Pimpl::sendPendingControlMessages() {
580  const unsigned char* controlMsgData = nullptr;
581  int controlMsgLen = 0;
582 
583  while(true) {
584  unique_lock<recursive_mutex> lock(sendMutex);
585  if(remoteAddress.sin_family != AF_INET) {
586  return;
587  }
588 
589  controlMsgData = protocol->getNextControlMessage(controlMsgLen);
590 
591  if(controlMsgData != nullptr) {
592  sendNetworkMessage(controlMsgData, controlMsgLen);
593  } else {
594  break;
595  }
596  }
597 }
598 
599 int ImageTransfer::Pimpl::getNumDroppedFrames() const {
600  return protocol->getNumDroppedFrames();
601 }
602 
603 bool ImageTransfer::Pimpl::selectSocket(bool read, bool wait) {
604  SOCKET sock;
605  {
606  unique_lock<recursive_mutex> lock(sendMutex); // Either mutex will do
607  sock = clientSocket;
608  }
609 #ifdef _WIN32
610  fd_set fds;
611  struct timeval tv;
612  FD_ZERO(&fds);
613  FD_SET(sock, &fds);
614  tv.tv_sec = 0;
615  if(wait) {
616  tv.tv_usec = 100000;
617  } else {
618  tv.tv_usec = 0;
619  }
620 
621  if(select(sock+1, (read ? &fds : nullptr), (!read ? &fds : nullptr), nullptr, &tv) <= 0) {
622  // The socket is currently not ready
623  return false;
624  }
625 #else
626  // use poll() on non-Windows platform (glibc select() limitations)
627  constexpr int timeoutMillisec = 100;
628  pollfd pfd;
629  pfd.fd = sock;
630  pfd.events = POLLIN;
631  if (poll(&pfd, 1, wait ? timeoutMillisec: 0) <= 0) {
632  // The socket is currently not ready
633  return false;
634  }
635 #endif
636  // select (or poll) reported an event
637  return true;
638 }
639 
640 std::string ImageTransfer::statusReport() {
641  return pimpl->statusReport();
642 }
643 std::string ImageTransfer::Pimpl::statusReport() {
644  return protocol->statusReport();
645 }
646 
647 } // namespace
648 
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
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.
void setRawTransferData(const ImageSet &metaData, const std::vector< unsigned char *> &rawData, int firstTileWidth=0, int secondTileWidth=0, int validBytes=0x7FFFFFFF)
Sets the raw pixel data for a partial image transmission.
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