Using QT to develop mjpeg server

git source code portal

Basic description

The basic principle of mjpeg server is to send pictures to the client one by one, and then the client is like playing animation, but it is still pictures. The image tag of the browser can directly access the image stream of the service through the http protocol. It looks like the image tag has the function of video. In fact, it is spliced one image after another.

The format of the data stream is very important. When I first wrote the server, I found that there was an error in a space, and the client could not parse the image passed to him.

mjpeg is an application based on tcp protocol.

The message header used is: x-mixed-replace

Content-Type: multipart/x-mixed-replace;boundary=myboundary

Note: when writing code, spaces cannot be wrong. Otherwise, the client cannot resolve.

Myboundary is randomly generated by the server. I'm just doing experiments here, so write the data as a fixed value, and the value will also be used as the identification of data segmentation. For example, if there are two pictures to be passed, use -- myboundary to segment the data of the two pictures, and then the image tag can obtain the contents of the two pictures for display.

The content format is as follows:

--myboundary
Content-Type: image/jpeg
Content-length: 2918

{Picture binary raw data 1}
--myboundary
Content-Type: image/jpeg
Content-length: 2918

{Picture binary raw data 2}
--myboundary

 

Source code

The design idea of the following code is as follows:

1. Start the tcp listening port and set the user request signal slotNewConnection.

2. When a user makes a request

  • Use the Qist structure to record the client socket to facilitate data transmission.
  • Start timer
  • Create picture
  • Send the picture data to the client in mjpeg format.

3. When the client breaks the chain, clear the list of Qlist. In fact, there should be a BUG here, because the timer and cleaning are parallel, that is, it may have been cleaned, but the socket may remain.

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpServer>
class MainWindow : public QMainWindow {
    Q_OBJECT

   public:
    MainWindow(QWidget* parent = nullptr);
    ~MainWindow();
    QByteArray createImage();

   private:
    QTcpServer* m_pTcpServer;
    QList<QTcpSocket*> m_listTcpClient;
    QTimer* m_fTimer;
    QByteArray getReciveData();
   public slots:
    void slotNewConnection();
    void slotDisconnected();
    void slotDisplayError(QAbstractSocket::SocketError socketError);
    void slotUpdateImage();
    //   void slotTimerTimeout();
   signals:
    void sendMessage(const QString& msg);
    void sendMessageBinary(const QByteArray& msg);
};

#endif  // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QTcpSocket>
#include <QPen>
#include <QPainter>
#include <QDateTime>
#include <QBuffer>
#include <QDebug>
#include <QTimer>
MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
    , m_pTcpServer(new QTcpServer(this))
    , m_fTimer(new QTimer(this))
{
    connect(m_pTcpServer, &QTcpServer::newConnection, this,
            &MainWindow::slotNewConnection);
    connect(m_pTcpServer, &QTcpServer::acceptError, this,
            &MainWindow::slotDisplayError);
    m_fTimer->setInterval(1000);
    connect(m_fTimer, &QTimer::timeout, this, &MainWindow::slotUpdateImage);
    if (!m_pTcpServer->listen(QHostAddress::Any, 8884)) {
        qDebug() << "Server Could Not be Started";
        return;
    } else {
        qDebug() << "Server Started";
    }
}

MainWindow::~MainWindow() {}
// Create a picture and record the current time in the picture
QByteArray MainWindow::createImage()
{
    QImage image(400, 100, QImage::Format_RGB32);
    image.fill(Qt::transparent);
    QPainter p(&image);
    QString str = QDateTime::currentDateTimeUtc().toString();
    // p.setRenderHint(QPainter::Antialiasing, true);
    p.setRenderHint(QPainter::SmoothPixmapTransform, true);
    p.setPen(QPen(QColor(255, 0, 0, 255)));
    p.drawText(10, 10, QFontMetrics(str).width(str), QFontMetrics(str).height(),
               1, str);
    QByteArray ba;
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer, "JPEG", 80);
    return ba;
}
// Gets the data returned to the client.
QByteArray MainWindow::getReciveData()
{
    QByteArray text;
    auto blob = createImage();

    text += "Content-Type: image/jpeg\r\n";
    text += "Content-length: " + QString::number(blob.size()) + "\r\n\r\n";
    text.append(blob);
    text += "\r\n--myboundary\r\n";
    //  text += "\r\n\r\n";
    return text;
}

void MainWindow::slotNewConnection()
{
   // Get the socket link of the client
    QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection();
    m_listTcpClient.push_back(pClientConnection);
    m_fTimer->start();
    connect(pClientConnection, SIGNAL(disconnected()), this,
            SLOT(slotDisconnected()));

    qInfo() << "slotNewConnection" << QDateTime::currentDateTimeUtc();
    QByteArray text =
        "HTTP/1.1 200 OK\r\nContent-Type: multipart/x-mixed-replace;"
        "boundary=myboundary\r\n";
    text += "\r\n--myboundary\r\n";
    text += getReciveData();
    pClientConnection->write(text); // Returns the data to the linked client.
}

void MainWindow::slotDisconnected()
{
    QTcpSocket* pQTcpSocket = static_cast<QTcpSocket*>(sender());
    m_listTcpClient.removeOne(pQTcpSocket);
    qInfo() << "receive disconnect!" << pQTcpSocket;
    pQTcpSocket->deleteLater();
}

void MainWindow::slotDisplayError(QAbstractSocket::SocketError socketError)
{
    qInfo() << "SimpleTcpSocketServerDemo::displayError " << socketError;
}
// Update picture data regularly.
void MainWindow::slotUpdateImage()
{
    QByteArray text = getReciveData();

    foreach (auto item, m_listTcpClient) {
        item->write(text);
    }
}

Final effect

We can see that the service can be accessed directly in the browser. It is transmitted to the browser in the form of pictures and automatically displayed one by one. Is it a bit like animation. Combined with camera image capture, you can make a simple camera live server.

Of course, you can also use < image Src=“ http://127.0.0.1:884 "/ > to access.

Some threads that have not been implemented in the code start the service and manage the timer.

Keywords: Qt server

Added by sturoy on Fri, 17 Dec 2021 05:30:15 +0200