cristiprg
cristiprg

Reputation: 113

Qt Client-server app, “send image” having problems

I am trying to send an image (OpenCV Mat) from client to server over a QDataStream. The first item is an int, the size of the buffer. It works for 10 to 15 images, then the server reads the first int a random number (usual ~2^30) which causes the app to crash. This is the client side, in a loop (the images come from webcam with period 500ms):

//1) encode to jpg
 cv::vector<uchar> buf;
 cv::imencode(".jpg", *mat, buf);

 //2) write to buffer
 QByteArray buffer;
 QDataStream out(&buffer, QIODevice::WriteOnly);
 out << int(0); // save place for an int which represents the buffer size

 for(cv::vector<uchar>::iterator it = buf.begin(); it != buf.end(); ++it)
 {
  out << *it; //write each byte
 }

 out.device()->seek(0); //write the buffer size
 out << buffer.size();
 qDebug() << "Sent " << buffer.size() << "bytes";

 qint64 bytesSent = socket->write(buffer);
 if(bytesSent < buffer.size())
 {
  if(bytesSent != -1)
  {
   qDebug() << "Transmit Error! Sent " << bytesSent << " out of " << buffer.size();
  }
  else
  {
   qDebug() << socket->errorString();
  }
 }

Server side:

QDataStream in(socket);
 int msgSize = -1;

 //first read the size as an int
 if(socket->bytesAvailable())
 {
  in >> msgSize;
 }

 qDebug() << "Read image size: " << msgSize << "bytes";
 //wait until all bytes all available
 while(socket->bytesAvailable() < ( qint64 ) ( ( qint64 )msgSize - sizeof(int) ) )
 {
  if(!socket->waitForReadyRead())
  {
   qDebug() << "Disconnected: " << socket->errorString();
   socket->disconnectFromHost();
   break;
  }
 }
 qDebug() << "Bytes recieved: " << msgSize;


 QByteArray ba;
 quint8 byte;
 for(int i = 0; i < msgSize - 1; ++i)
 {
  in >> byte;
  ba.append(byte);
 }
        cv::Mat imgbuf = cv::Mat(FRAME_WIDTH, FRAME_HEIGHT, CV_8UC3, ba.data());
 cv::Mat matImg = cv::imdecode(imgbuf, CV_LOAD_IMAGE_COLOR);

Both sockets are QTcpSocket.

Output example:

The server side: 

Read image size: 67551 bytes 
Bytes recieved: 67551 
Read image size: 56924 bytes 
Bytes recieved: 56924 
Read image size: 70027 bytes 
Bytes recieved: 70027 
Read image size: -2046830337 bytes 
Bytes recieved: -2046830337 
Read image size: -536866742 bytes 
Bytes recieved: -536866742 
Read image size: 1179207168 bytes 

At this point it tries to read 1179207168 bytes. 

The client side: 
Sent 67551 bytes 
Sent 56924 bytes 
Sent 70027 bytes 
Sent 70277 bytes 
Sent 85633 bytes 
Sent 65155 bytes 
etc ... 

Only the first three are successful.

Upvotes: 0

Views: 1494

Answers (1)

mbroadst
mbroadst

Reputation: 888

First of all, I don't believe you need QDataStream for this task. QDataStream is meant for serializing/deserializing Qt types, and is specific to the version of Qt you are using. Since you simply want to write uchar's over the network, you can iterate through your cv::vector directly, and write those bytes directly over the wire (in fact, you don't need the intermediary QByteArray either since you already have the byteLength with your original vector).

However, you CAN use QDataStream (since you're working with very basic types that are not likely to change between QDataStream versions), and since you started that way, I'll provide an answer accordingly

This would look more like (client side):

// 1) encode to jpg
cv::vector<uchar> buf;
cv::imencode(".jpg", *mat, buf);

// 2) write to buffer, your 'protocol' assumes buffer length first, then the data.
// REMEMBER: QDataStream operates on -any- QIODevice, so no need for the extra buffer here
QDataStream out(socket, QIODevice::WriteOnly);
out << buf.size();

cv::vector<uchar>::const_iterator it;
cv::vector<uchar>::const_iterator itEnd = buf.end();
for (it = buf.begin(); it != itEnd; ++it)
    out << *it;

server side:

QDataStream in(socket, QIODevice::ReadOnly);
cv::vector<uchar> buf;
socket->waitForReadyRead();

qint64 bytesRead = 0;
qint64 size;
in >> size;

buf.reserve(size);
while (buf.size() < size) {
    if (!socket->bytesAvailable())
        socket->waitForReadyRead();

    qint8 byte;
    in >> byte;
    buf.push_back(byte);
}

cv::Mat imgbuf = cv::Mat(FRAME_WIDTH, FRAME_HEIGHT, CV_8UC3, buf);
cv::Mat matImg = cv::imdecode(imgbuf, CV_LOAD_IMAGE_COLOR);

NOTE: this is all off the cuff, it could very well not compile and there is certainly a much more efficient way of doing this, but I think it gives you the basic idea of how you could go about fixing your existing code

Upvotes: 1

Related Questions