1

I am creating a (very basic) MJPG server to show webcam data on a browser. I have partly managed to do it so far.

Here is my code:

TcpServer::TcpServer(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
// whenever a user connects, it will emit signal
connect(server, SIGNAL(newConnection()),
    this, SLOT(newConnection()));
if (!server->listen(QHostAddress::Any, 9999))
    qDebug() << "Server could not start";
else
    qDebug() << "Server started!";
}
...
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
    "Cache-Control: no-cache\r\n" \
    "Cache-Control: private\r\n" \
    "Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n");
socket->write(ContentType);
std::vector<uchar> buff;
Mat img; //OpenCV Material
while (1) {
    // Image to Byte Array via OPENCV Method
    buff.clear();buff.empty();
    vCapture->read(img); //Read from webcam

    imencode(".jpg", img, buff, compression_params);
    std::string content(buff.begin(), buff.end());
    QByteArray CurrentImg(QByteArray::fromStdString(content));
    QByteArray BoundaryString = ("--boundary\r\n" \
        "Content-Type: image/jpeg\r\n" \
        "Content-Length: ");
    BoundaryString.append(QString::number(CurrentImg.length()));
    BoundaryString.append("\r\n\r\n");

    socket->write(BoundaryString);
    socket->write(CurrentImg); // Write The Encoded Image
    socket->flush();
}

}

The Problem -

When I run this program, the first image is shown. After that, the following error is continuously printed on the app -

QIODevice::write (QTcpSocket): device not open

It looked like the socket got closed, so I used re-initialized the socket, like this - socket = server->nextPendingConnection();, although the app threw an error with this code. Any help on how to fix this?

EDIT -

I tried the lambda method and it worked fine. However, I still face 2 problems -

  1. The image size has to be excessively low (around 270x480 with lowest JPG quality)

  2. (MORE IMPORTANT) I have to manually press the reload button on browser to reload the image, it doesn't automatically change from one image to the other.

FadedCoder
  • 1,517
  • 1
  • 16
  • 36
  • Havn't used QT in a while, as a fact since [the Project you based your Code on](http://stackoverflow.com/questions/33064955/how-to-create-a-http-mjpeg-streaming-server-with-qtcp-server-sockets/33096855?noredirect=1#comment61611267_33096855) but I recommend you to use a delay between sending the frames (e.g. `QThread::usleep(500);`). Main Reason is that this Method (Speaking of the "MJPEG" Server at the time being is not very effective when speaking about Performance. In my tests when I set the delay between sending the frames to a low value the video always got stuck at some point. – hypnomaki May 04 '16 at 14:54

2 Answers2

2

It looked like the socket got closed

Rather than guessing, connect to the error signals of TCPServer and TCPSocket to know when errors occur, or when a client disconnects.

The problem you have is the while(1) loop. Qt is an event-driven framework, so having code in an infinite loop on the main thread is going to prevent events being delivered.

Instead of the infinite loop, connect to QTcpSocket::readyRead signal and handle the data when the connected slot is called.

Qt demonstrates this with the Fortune Server and Fortune Client example code.

If you're using C++ 11, you can use a connection to a lambda function to handle the readyRead, like this

void TcpServer::newConnection()
{
    ...

    QTcpSocket *m_TcpHttpClient = server->nextPendingConnection();
    connect(m_TcpHttpClient, &QTcpSocket::readyRead, [=](){

        // handle received data here

    });

}
Community
  • 1
  • 1
TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • I tried your answer, but now I am getting this error - `Unhandled exception at 0x000000C039E24388 (ucrtbase.dll) in app.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.` – FadedCoder May 04 '16 at 14:51
  • Please update your question to show the code that produces the error. – TheDarkKnight May 04 '16 at 14:55
  • I see the update, but the code is still the same. In order to help you with that error, we need to see the code that is producing it. – TheDarkKnight May 05 '16 at 08:01
  • That code is sending `ContentType` to the client every time the server receives data - I expect this is not what you're wanting, as you don't do that in the original code. I then suggest commenting out the openCV code and just sending a simple string between the client and server. If this is successful, the problem lies in handling the image, not the socket connection. – TheDarkKnight May 05 '16 at 12:26
  • *Erased the previous edit as it was fixed with code given by TheDarkKnight* - Your edit changes the question, which is not advisable on SO. I suggest accepting this question as answered (selecting the tick) and start a new SO question, which will give better visibility to the community and more likely to find acceptable answers. – TheDarkKnight May 06 '16 at 14:34
1

Here Is the Code I used to open the MJPEG Streaming Server in my original Project. Perhaps It can help you finding out your Problem.

void MjpegStreamingEngine::StartServer(){

    m_TcpHttpServer = new QTcpServer();

    m_TcpHttpServer->connect(m_TcpHttpServer,
                             SIGNAL(newConnection()),
                             this,
                             SLOT(TcpHttpconnected()));

    m_TcpHttpServer->listen(QHostAddress::Any,
                            8889);
}

void MjpegStreamingEngine::TcpHttpconnected(){

    m_TcpHttpClient = m_TcpHttpServer->nextPendingConnection();

    m_TcpHttpClient->connect(m_TcpHttpClient,
                             SIGNAL(readyRead()),
                             this,
                             SLOT(TcpHttpreadyRead()));

}

void MjpegStreamingEngine::TcpHttpreadyRead(){

    m_TcpHttpClient->readAll(); // Discard "Get Request String"

    QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
                              "Server: en.code-bude.net example server\r\n" \
                              "Cache-Control: no-cache\r\n" \
                              "Cache-Control: private\r\n" \
                              "Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n\r\n");

    m_TcpHttpClient->write(ContentType);


    while(1){

        if (m_TcpHttpClient->isOpen())
        {

            // Send Image

            QThread::msleep(10);

        }else{
            return;
        }

    }

    m_TcpHttpClient->disconnect(); //Should never be Reached
}
Community
  • 1
  • 1
hypnomaki
  • 593
  • 9
  • 22