libvisiontransfer  8.3.0
imagetransfer.cpp
1 /*******************************************************************************
2  * Copyright (c) 2020 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  unique_lock<recursive_mutex> recvLock(receiveMutex);
277  unique_lock<recursive_mutex> sendLock(sendMutex);
278 
279  // Accept one connection
280  SOCKET newSocket = Networking::acceptConnection(tcpServerSocket, remoteAddress);
281  if(newSocket == INVALID_SOCKET) {
282  // No connection
283  return false;
284  }
285 
286  if(clientSocket != INVALID_SOCKET) {
287  Networking::closeSocket(clientSocket);
288  }
289  clientSocket = newSocket;
290 
291  // Set special socket options
292  setSocketOptions();
293 
294  // Reset connection data
295  protocol->resetTransfer();
296  protocol->resetReception();
297  currentMsg = nullptr;
298 
299  return true;
300 }
301 
302 std::string ImageTransfer::Pimpl::getRemoteAddress() const {
303  unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); // either mutex will work
304 
305  if(remoteAddress.sin_family != AF_INET) {
306  return "";
307  }
308 
309  char strPort[11];
310  snprintf(strPort, sizeof(strPort), ":%d", remoteAddress.sin_port);
311 
312  return string(inet_ntoa(remoteAddress.sin_addr)) + strPort;
313 }
314 
315 void ImageTransfer::Pimpl::setSocketOptions() {
316  // Set the socket buffer sizes
317  if(bufferSize > 0) {
318  setsockopt(clientSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
319  setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
320  }
321 
322  Networking::setSocketTimeout(clientSocket, 500);
323  Networking::setSocketBlocking(clientSocket, true);
324 }
325 
326 void ImageTransfer::Pimpl::setRawTransferData(const ImageSet& metaData,
327  const std::vector<unsigned char*>& rawDataVec, int firstTileWidth, int secondTileWidth, int validBytes) {
328  unique_lock<recursive_mutex> sendLock(sendMutex);
329  protocol->setRawTransferData(metaData, rawDataVec, firstTileWidth, secondTileWidth, validBytes);
330  currentMsg = nullptr;
331 }
332 
333 void ImageTransfer::Pimpl::setRawValidBytes(const std::vector<int>& validBytes) {
334  unique_lock<recursive_mutex> sendLock(sendMutex);
335  protocol->setRawValidBytes(validBytes);
336 }
337 
338 void ImageTransfer::Pimpl::setTransferImageSet(const ImageSet& imageSet) {
339  unique_lock<recursive_mutex> sendLock(sendMutex);
340  protocol->setTransferImageSet(imageSet);
341  currentMsg = nullptr;
342 }
343 
344 ImageTransfer::TransferStatus ImageTransfer::Pimpl::transferData() {
345  unique_lock<recursive_mutex> lock(sendMutex);
346 
347  // First receive data in case a control message arrives
348  if(protType == ImageProtocol::PROTOCOL_UDP) {
349  receiveNetworkData(false);
350  }
351 
352  if(remoteAddress.sin_family != AF_INET || !protocol->isConnected()) {
353  return NOT_CONNECTED;
354  }
355 
356 #ifndef _WIN32
357  // Cork TCP to prevent sending of small packets
358  if(protType == ImageProtocol::PROTOCOL_TCP) {
359  int flag = 1;
360  setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
361  }
362 #endif
363 
364  // Get first message to transfer
365  if(currentMsg == nullptr) {
366  currentMsgOffset = 0;
367  currentMsg = protocol->getTransferMessage(currentMsgLen);
368 
369  if(currentMsg == nullptr) {
370  if(protocol->transferComplete()) {
371  return ALL_TRANSFERRED;
372  } else {
373  return NO_VALID_DATA;
374  }
375  }
376  }
377 
378  // Try transferring messages
379  bool dataTransferred = (currentMsg != nullptr);
380  while(currentMsg != nullptr) {
381  int writing = (int)(currentMsgLen - currentMsgOffset);
382 
383  if(sendNetworkMessage(&currentMsg[currentMsgOffset], writing)) {
384  // Get next message
385  currentMsgOffset = 0;
386  currentMsg = protocol->getTransferMessage(currentMsgLen);
387  } else {
388  return WOULD_BLOCK;
389  }
390  }
391 
392  if(dataTransferred && protType == ImageProtocol::PROTOCOL_TCP && protocol->transferComplete()) {
393 #ifndef _WIN32
394  // Uncork - sends the assembled messages
395  int flag = 0;
396  setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
397 #else
398  // Force a flush for TCP by turning the nagle algorithm off and on
399  int flag = 1;
400  setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
401  flag = 0;
402  setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
403 #endif
404  }
405 
406  // Also check for control messages at the end
407  if(protType == ImageProtocol::PROTOCOL_UDP) {
408  receiveNetworkData(false);
409  }
410 
411  if(protocol->transferComplete()) {
412  return ALL_TRANSFERRED;
413  } else {
414  return PARTIAL_TRANSFER;
415  }
416 }
417 
418 bool ImageTransfer::Pimpl::receiveImageSet(ImageSet& imageSet) {
419  int validRows = 0;
420  bool complete = false;
421 
422  std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
423  while(!complete) {
424  if(!receivePartialImageSet(imageSet, validRows, complete)) {
425  return false;
426  }
427 
428  unsigned int time = static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(
429  std::chrono::steady_clock::now() - startTime).count());
430  if(time > 1000) {
431  return false;
432  }
433  }
434 
435  return true;
436 }
437 
438 bool ImageTransfer::Pimpl::receivePartialImageSet(ImageSet& imageSet,
439  int& validRows, bool& complete) {
440  unique_lock<recursive_mutex> lock(receiveMutex);
441 
442  // Try to receive further image data if needed
443  bool block = true;
444  while(!protocol->imagesReceived() && receiveNetworkData(block)) {
445  block = false;
446  }
447 
448  // Get received image
449  return protocol->getPartiallyReceivedImageSet(imageSet, validRows, complete);
450 }
451 
452 bool ImageTransfer::Pimpl::receiveNetworkData(bool block) {
453  unique_lock<recursive_mutex> lock = block ?
454  unique_lock<recursive_mutex>(receiveMutex) : unique_lock<recursive_mutex>(receiveMutex, std::try_to_lock);
455 
456  if(clientSocket == INVALID_SOCKET) {
457  return false; // Not connected
458  }
459 
460  // First send control messages if necessary
461  sendPendingControlMessages();
462 
463  if(!lock.owns_lock()) {
464  // Waiting for the lock would block this call
465  return false;
466  }
467 
468  // Test if the socket has data available
469  if(!block && !selectSocket(true, false)) {
470  return 0;
471  }
472 
473  int maxLength = 0;
474  char* buffer = reinterpret_cast<char*>(protocol->getNextReceiveBuffer(maxLength));
475 
476  // Receive data
477  sockaddr_in fromAddress;
478  socklen_t fromSize = sizeof(fromAddress);
479 
480  int bytesReceived = recvfrom(clientSocket, buffer, maxLength,
481  0, reinterpret_cast<sockaddr*>(&fromAddress), &fromSize);
482 
483  if(bytesReceived == 0 || (protType == ImageProtocol::PROTOCOL_TCP && bytesReceived < 0 && errno == WSAECONNRESET)) {
484  // Connection closed
485  disconnect();
486  } else if(bytesReceived < 0 && errno != EWOULDBLOCK && errno != EINTR &&
487  errno != ETIMEDOUT && errno != WSA_IO_PENDING && errno != WSAECONNRESET) {
488  TransferException ex("Error reading from socket: " + string(strerror(errno)));
489  throw ex;
490  } else if(bytesReceived > 0) {
491  protocol->processReceivedMessage(bytesReceived);
492  if(protocol->newClientConnected()) {
493  // We have just established a new connection
494  memcpy(&remoteAddress, &fromAddress, sizeof(remoteAddress));
495  }
496  }
497 
498  return bytesReceived > 0;
499 }
500 
501 void ImageTransfer::Pimpl::disconnect() {
502  // We just need to forget the remote address in order to
503  // disconnect
504  unique_lock<recursive_mutex> recvLock(receiveMutex);
505  unique_lock<recursive_mutex> sendLock(sendMutex);
506 
507  if(clientSocket != INVALID_SOCKET && protType == ImageProtocol::PROTOCOL_TCP) {
508  Networking::closeSocket(clientSocket);
509  }
510  memset(&remoteAddress, 0, sizeof(remoteAddress));
511 }
512 
513 bool ImageTransfer::Pimpl::isConnected() const {
514  unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); //either mutex will work
515 
516  return remoteAddress.sin_family == AF_INET && protocol->isConnected();
517 }
518 
519 bool ImageTransfer::Pimpl::sendNetworkMessage(const unsigned char* msg, int length) {
520  int written = 0;
521  if(protType == ImageProtocol::PROTOCOL_UDP) {
522  sockaddr_in destAddr;
523  SOCKET destSocket;
524  {
525  unique_lock<recursive_mutex> lock(sendMutex);
526  destAddr = remoteAddress;
527  destSocket = clientSocket;
528  }
529 
530  if(destAddr.sin_family != AF_INET) {
531  return false; // Not connected
532  }
533 
534  written = sendto(destSocket, reinterpret_cast<const char*>(msg), length, 0,
535  reinterpret_cast<sockaddr*>(&destAddr), sizeof(destAddr));
536  } else {
537  SOCKET destSocket;
538  {
539  unique_lock<recursive_mutex> lock(sendMutex);
540  destSocket = clientSocket;
541  }
542  written = send(destSocket, reinterpret_cast<const char*>(msg), length, 0);
543  }
544 
545  unsigned long sendError = errno;
546 
547  if(written < 0) {
548  if(sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT) {
549  // The socket is not yet ready for a new transfer
550  return false;
551  } else if(sendError == EPIPE) {
552  // The connection has been closed
553  disconnect();
554  return false;
555  } else {
556  TransferException ex("Error sending network packet: " + string(strerror(sendError)));
557  throw ex;
558  }
559  } else if(written != length) {
560  if(protType == ImageProtocol::PROTOCOL_UDP) {
561  // The message has been transmitted partially
562  throw TransferException("Unable to transmit complete UDP message");
563  } else {
564  // For TCP we can transmit the remaining data later
565  currentMsgOffset += written;
566  return false;
567  }
568  } else {
569  return true;
570  }
571 }
572 
573 void ImageTransfer::Pimpl::sendPendingControlMessages() {
574  const unsigned char* controlMsgData = nullptr;
575  int controlMsgLen = 0;
576 
577  while(true) {
578  unique_lock<recursive_mutex> lock(sendMutex);
579  if(remoteAddress.sin_family != AF_INET) {
580  return;
581  }
582 
583  controlMsgData = protocol->getNextControlMessage(controlMsgLen);
584 
585  if(controlMsgData != nullptr) {
586  sendNetworkMessage(controlMsgData, controlMsgLen);
587  } else {
588  break;
589  }
590  }
591 }
592 
593 int ImageTransfer::Pimpl::getNumDroppedFrames() const {
594  return protocol->getNumDroppedFrames();
595 }
596 
597 bool ImageTransfer::Pimpl::selectSocket(bool read, bool wait) {
598  SOCKET sock;
599  {
600  unique_lock<recursive_mutex> lock(sendMutex); // Either mutex will do
601  sock = clientSocket;
602  }
603 #ifdef _WIN32
604  fd_set fds;
605  struct timeval tv;
606  FD_ZERO(&fds);
607  FD_SET(sock, &fds);
608  tv.tv_sec = 0;
609  if(wait) {
610  tv.tv_usec = 100000;
611  } else {
612  tv.tv_usec = 0;
613  }
614 
615  if(select(sock+1, (read ? &fds : nullptr), (!read ? &fds : nullptr), nullptr, &tv) <= 0) {
616  // The socket is currently not ready
617  return false;
618  }
619 #else
620  // use poll() on non-Windows platform (glibc select() limitations)
621  constexpr int timeoutMillisec = 100;
622  pollfd pfd;
623  pfd.fd = sock;
624  pfd.events = POLLIN;
625  if (poll(&pfd, 1, wait ? timeoutMillisec: 0) <= 0) {
626  // The socket is currently not ready
627  return false;
628  }
629 #endif
630  // select (or poll) reported an event
631  return true;
632 }
633 
634 std::string ImageTransfer::statusReport() {
635  return pimpl->statusReport();
636 }
637 std::string ImageTransfer::Pimpl::statusReport() {
638  return protocol->statusReport();
639 }
640 
641 } // namespace
642 
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
ImageTransfer(const char *address, const char *service="7681", ImageProtocol::ProtocolType protType=ImageProtocol::PROTOCOL_UDP, bool server=false, int bufferSize=1048576, int maxUdpPacketSize=1472)
Creates a new transfer object by manually specifying the target address.
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
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