15 #if __GNUC__ == 4 && __GNUC_MINOR__ < 9 18 #include <bits/c++config.h> 19 #undef _GLIBCXX_USE_CLOCK_MONOTONIC 26 #include <condition_variable> 32 #include "visiontransfer/asynctransfer.h" 33 #include "visiontransfer/alignedallocator.h" 43 class AsyncTransfer::Pimpl {
45 Pimpl(
const char* address,
const char* service,
47 int bufferSize,
int maxUdpPacketSize);
51 void sendImageSetAsync(
const ImageSet& imageSet,
bool deleteData);
52 bool collectReceivedImageSet(
ImageSet& imageSet,
double timeout);
53 int getNumDroppedFrames()
const;
54 bool isConnected()
const;
56 std::string getRemoteAddress()
const;
60 static constexpr
int NUM_BUFFERS = ImageSet::MAX_SUPPORTED_IMAGES * 2;
61 static constexpr
int SEND_THREAD_SHORT_WAIT_MS = 1;
62 static constexpr
int SEND_THREAD_LONG_WAIT_MS = 10;
68 volatile bool terminate;
72 std::thread sendThread;
74 std::condition_variable sendCond;
75 std::condition_variable sendWaitCond;
77 std::thread receiveThread;
78 std::timed_mutex receiveMutex;
79 std::condition_variable_any receiveCond;
80 std::condition_variable_any receiveWaitCond;
84 std::vector<unsigned char, AlignedAllocator<unsigned char> > receivedData[NUM_BUFFERS];
85 volatile bool newDataReceived;
92 std::exception_ptr receiveException;
93 std::exception_ptr sendException;
95 bool sendThreadCreated;
96 bool receiveThreadCreated;
104 void createSendThread();
109 AsyncTransfer::AsyncTransfer(
const char* address,
const char* service,
111 int bufferSize,
int maxUdpPacketSize)
112 : pimpl(new Pimpl(address, service, protType, server, bufferSize, maxUdpPacketSize)) {
116 : pimpl(new Pimpl(device.getIpAddress().c_str(),
"7681", static_cast<
ImageProtocol::ProtocolType>(device.getNetworkProtocol()),
117 false, bufferSize, maxUdpPacketSize)) {
120 AsyncTransfer::~AsyncTransfer() {
125 pimpl->sendImageSetAsync(imageSet, deleteData);
129 return pimpl->collectReceivedImageSet(imageSet, timeout);
133 return pimpl->getNumDroppedFrames();
137 return pimpl->isConnected();
141 return pimpl->disconnect();
145 return pimpl->getRemoteAddress();
149 return pimpl->tryAccept();
154 AsyncTransfer::Pimpl::Pimpl(
const char* address,
const char* service,
156 int bufferSize,
int maxUdpPacketSize)
157 : imgTrans(address, service, protType, server, bufferSize, maxUdpPacketSize),
158 terminate(
false), newDataReceived(
false), sendSetValid(
false),
159 deleteSendData(
false), sendThreadCreated(
false),
160 receiveThreadCreated(
false) {
167 AsyncTransfer::Pimpl::~Pimpl() {
170 sendCond.notify_all();
171 receiveCond.notify_all();
172 sendWaitCond.notify_all();
173 receiveWaitCond.notify_all();
175 if(sendThreadCreated && sendThread.joinable()) {
179 if(receiveThreadCreated && receiveThread.joinable()) {
180 receiveThread.join();
183 if(sendSetValid && deleteSendData) {
184 delete[] sendImageSet.getPixelData(0);
185 delete[] sendImageSet.getPixelData(1);
189 void AsyncTransfer::Pimpl::createSendThread() {
190 if(!sendThreadCreated) {
192 unique_lock<mutex> lock(sendMutex);
193 sendThread = thread(bind(&AsyncTransfer::Pimpl::sendLoop,
this));
194 sendThreadCreated =
true;
198 void AsyncTransfer::Pimpl::sendImageSetAsync(
const ImageSet& imageSet,
bool deleteData) {
202 unique_lock<mutex> lock(sendMutex);
206 std::rethrow_exception(sendException);
210 sendImageSet = imageSet;
212 deleteSendData = deleteData;
215 sendCond.notify_one();
220 sendWaitCond.wait(lock);
225 bool AsyncTransfer::Pimpl::collectReceivedImageSet(
ImageSet& imageSet,
double timeout) {
226 if(!receiveThreadCreated) {
228 unique_lock<timed_mutex> lock(receiveMutex);
229 receiveThreadCreated =
true;
230 receiveThread = thread(bind(&AsyncTransfer::Pimpl::receiveLoop,
this));
234 unique_lock<timed_mutex> lock(receiveMutex, std::defer_lock);
238 std::chrono::steady_clock::time_point lockStart =
239 std::chrono::steady_clock::now();
240 if(!lock.try_lock_for(std::chrono::microseconds(static_cast<unsigned int>(timeout*1e6)))) {
246 unsigned int lockDuration =
static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::microseconds>(
247 std::chrono::steady_clock::now() - lockStart).count());
248 timeout = std::max(0.0, timeout - lockDuration*1e-6);
252 if(receiveException) {
253 std::rethrow_exception(receiveException);
256 if(timeout == 0 && !newDataReceived) {
262 if(!newDataReceived) {
264 while(!terminate && !receiveException && !newDataReceived) {
265 receiveCond.wait(lock);
268 receiveCond.wait_for(lock, std::chrono::microseconds(static_cast<unsigned int>(timeout*1e6)));
273 if(receiveException) {
274 std::rethrow_exception(receiveException);
277 if(newDataReceived) {
279 imageSet = receivedSet;
281 newDataReceived =
false;
282 receiveWaitCond.notify_one();
290 void AsyncTransfer::Pimpl::sendLoop() {
293 unique_lock<mutex> lock(sendMutex);
297 bool deleteSet =
false;
303 unique_lock<mutex> lock(sendMutex);
305 bool firstWait =
true;
306 while(!terminate && !sendSetValid) {
307 imgTrans.transferData();
308 sendCond.wait_for(lock, std::chrono::milliseconds(
309 firstWait ? SEND_THREAD_SHORT_WAIT_MS : SEND_THREAD_LONG_WAIT_MS));
316 imgSet = sendImageSet;
317 deleteSet = deleteSendData;
318 sendSetValid =
false;
320 sendWaitCond.notify_one();
323 imgTrans.setTransferImageSet(imgSet);
329 std::this_thread::sleep_for(std::chrono::milliseconds(SEND_THREAD_LONG_WAIT_MS));
342 sendException = std::current_exception();
344 sendWaitCond.notify_all();
356 void AsyncTransfer::Pimpl::receiveLoop() {
359 unique_lock<timed_mutex> lock(receiveMutex);
368 if(!imgTrans.receiveImageSet(currentSet)) {
376 int newStride = currentSet.
getWidth() * bytesPerPixel;
377 int totalSize = currentSet.
getHeight() * newStride;
378 int bufIdxHere = (i + bufferIndex) % NUM_BUFFERS;
379 if(static_cast<int>(receivedData[bufIdxHere].size()) < totalSize) {
380 receivedData[bufIdxHere].resize(totalSize);
383 memcpy(&receivedData[bufIdxHere][0], currentSet.
getPixelData(i),
386 for(
int y = 0; y<currentSet.
getHeight(); y++) {
387 memcpy(&receivedData[bufIdxHere][y*newStride],
393 currentSet.
setPixelData(i, &receivedData[bufIdxHere][0]);
397 unique_lock<timed_mutex> lock(receiveMutex);
400 while(newDataReceived) {
401 receiveWaitCond.wait_for(lock, std::chrono::milliseconds(100));
408 newDataReceived =
true;
409 receivedSet = currentSet;
410 receiveCond.notify_one();
418 if(!receiveException) {
419 receiveException = std::current_exception();
421 receiveCond.notify_all();
425 bool AsyncTransfer::Pimpl::isConnected()
const {
426 return imgTrans.isConnected();
429 void AsyncTransfer::Pimpl::disconnect() {
430 imgTrans.disconnect();
433 std::string AsyncTransfer::Pimpl::getRemoteAddress()
const {
434 return imgTrans.getRemoteAddress();
437 int AsyncTransfer::Pimpl::getNumDroppedFrames()
const {
438 return imgTrans.getNumDroppedFrames();
441 bool AsyncTransfer::Pimpl::tryAccept() {
442 return imgTrans.tryAccept();
445 constexpr
int AsyncTransfer::Pimpl::NUM_BUFFERS;
446 constexpr
int AsyncTransfer::Pimpl::SEND_THREAD_SHORT_WAIT_MS;
447 constexpr
int AsyncTransfer::Pimpl::SEND_THREAD_LONG_WAIT_MS;
int getRowStride(int imageNumber) const
Returns the row stride for the pixel data of one image.
void setPixelData(int imageNumber, unsigned char *pixelData)
Sets the pixel data for the given image.
The operation would block and blocking as been disabled.
AsyncTransfer(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.
bool tryAccept()
Tries to accept a client connection.
int getNumberOfImages() const
Returns the number of images in this set.
Class for synchronous transfer of image sets.
int getHeight() const
Returns the height of each image.
A lightweight protocol for transferring image sets.
bool isConnected() const
Returns true if a remote connection is established.
void disconnect()
Terminates the current connection.
int getBytesPerPixel(int imageNumber) const
Returns the number of bytes that are required to store one image pixel.
int getNumDroppedFrames() const
Returns the number of frames that have been dropped since connecting to the current remote host...
ProtocolType
Supported network protocols.
std::string getRemoteAddress() const
Returns the address of the remote host.
void setRowStride(int imageNumber, int stride)
Sets a new row stride for the pixel data of one image.
Aggregates information about a discovered device.
unsigned char * getPixelData(int imageNumber) const
Returns the pixel data for the given image.
A set of one to three images, but usually two (the left camera image and the disparity map)...
int getWidth() const
Returns the width of each image.
bool collectReceivedImageSet(ImageSet &imageSet, double timeout=-1)
Collects the asynchronously received image.
void sendImageSetAsync(const ImageSet &imageSet, bool deleteData=false)
Starts an asynchronous transmission of the given image set.
TransferStatus
The result of a partial image transfer.