This article uses Qt to implement a network camera function, which includes a server and a client. The server is used to convert the USB camera into an IP camera. When a client is connected, the captured image is sent out through TCP; the client runs on a Linux board and is used to view the real-time image of the camera.
1. Required basic knowledge
This article requires writing a server and a client
1.1 QTcpSocket and QTcpServer
-
QTcpSocket , in Qt, Socket is encapsulated into QTcpSocket, which can be used to implement the functions of TCP client, as well as the processing of the client after the server receives the client.
-
QTcpServer , for the TCP server functions, you can use QTcpServer to complete.
Here is a summary of how to use TCP Socket in Qt. By combining it with Qt's signal and slot mechanism, you can implement the sending and receiving of server/client data.
1.2 QCamera related
-
QCamer , get the cameras available in the current system similar to getting the serial port
-
QCamerInfo , get the camera available in the current system similar to getting the serial port
-
QCameraViewfinder , viewfinder class, the real-time image of the camera is displayed in it
-
QCameraImageCapture , image recording class, can be used with QCamer to take pictures
2 Test on Win platform
First, use Qt Creator on the Windows platform to write the server and client programs and run the test.
2.1 Server
Let's first look at the final effect on the server side:
-
The left side is the camera display interface
-
You can switch between different cameras as video sources (the built-in camera of the laptop and the USB external camera)
-
You can switch the camera display resolution
-
You can choose to turn on or off the camera's IP service
2.1.1 Camera screen display
Widget::Widget(QWidget *parent):
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QComboBox *pCamType = new QComboBox();
m_pComboBox = ui->cbBox_resolution;
pCamType = ui->cbBox_cameras;
pCamType->clear();
cameraList = QCameraInfo::availableCameras();
foreach(const QCameraInfo &cameraInfo, cameraList)
{
qDebug() << "CameraInfo:" << cameraInfo;
pCamType->addItem(cameraInfo.description());
}
m_pCamViewFind = new QCameraViewfinder(this);
m_pCamViewFind->setGeometry(10, 10, W, H);
m_pCamViewFind->show();
m_pCam = new QCamera(this);
m_pCam->setViewfinder(m_pCamViewFind);
m_pCam->start();
}
2.1.2 Creating a Socket Service
void Widget::on_btn_IPServer_toggled(bool checked)
{
if (checked)
{
m_pServer = new QTcpServer(this);
if (!m_pServer->listen(QHostAddress::Any, 12345))
{
QMessageBox::critical(this, "error", "listen port failed");
exit(0);
}
qDebug() << "start IP server";
m_pTimer = new QTimer(this);
connect(m_pServer, SIGNAL(newConnection()), this, SLOT(new_client()));
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(timer_slot()));
m_pTimer->start(100);
ui->btn_IPServer->setText("Close IP service");
}
else
{
qDebug() << "stop IP server";
m_pServer->close();
delete m_pServer;
ui->btn_IPServer->setText("Enable IP service");
}
}
2.1.3 Read the image and send it to the client
First define the image transmission structure and transmission status:
enum TransStatus{
TS_IDLE, //Idle (image data can be updated)
TS_RUNNING, //Image data is being transmitted (image data cannot be updated yet)
TS_FIRST_DATA, //The first part of the image data needs to be sent
};
class ImgData {
public:
char data[LEN] = {0}; //image data
int totalLen = 0; //image size
int hasSentLen = 0; //The length of the data sent
TransStatus stats = TS_IDLE; //Working status
};
Specific implementation process:
void Widget::read_data()
{
QString str = m_pClient->readAll();
ImgData *pData = (ImgData*)m_pClient->userData(0);
QString s("newImage:%1");
if (str == "new_request")
{
qDebug() << "read_data, new_request, d->len:" << pData->totalLen << "d->stats:" << pData->stats;
if ((pData->totalLen > 0) && (pData->stats==TS_IDLE)) //The image size is not 0, indicating that the image data has been updated
{
pData->stats = TS_RUNNING;
m_pClient->write(s.arg(pData->totalLen).toUtf8());
pData->hasSentLen = 0;
}
else //The image data has not been updated
{
pData->stats = TS_FIRST_DATA; //Send "newImage..." in the timer slot function
}
}
else if (str == "ack")
{
int len_send = P_LEN; //The length to be sent this time
if (pData->hasSentLen >= pData->totalLen) //If the image has been transmitted
{
qDebug() << "read_data, send done! lenSent:" << pData->hasSentLen << "len" << pData->totalLen;
return;
}
// The last packet of data (less than P_LEN)
if ((pData->hasSentLen + P_LEN) > pData->totalLen)
{
len_send = pData->totalLen - pData->hasSentLen;
}
qDebug() << "read_data, ack, write len:" << len_send;
// Send data
pData->hasSentLen += m_pClient->write(pData->data + pData->hasSentLen, len_send);
if (pData->hasSentLen >= pData->totalLen)
{
pData->stats = TS_IDLE; //After the transfer is completed, change the status to updateable
pData->totalLen = 0;
}
}
}
It should be noted that images need to be transmitted in packets, and the last packet is usually not the maximum length set, so the data length of the last packet needs to be calculated.
2.2 Client
Let’s first look at the final effect of the client:
-
On the right is the display frame of the camera image
-
You can modify the IP address of the server to connect to
-
You can choose to turn the webcam on or off
2.2.1 Create a Socket Connection
void Widget::on_pushButton_toggled(bool checked)
{
if (checked)
{
QString ip = ui->lineEdit->text();
m_pSocket->connectToHost(ip, 12345);
if (!m_pSocket->waitForConnected(1000))
{
QMessageBox::critical(this, "error", "server connection failed");
return;
}
ui->pushButton->setText("Close");
m_iRecvLen = 0;
m_pSocket->write("new_request");
qDebug("on_bnt_connect_clicked, new_request");
}
else
{
m_pSocket->close();
ui->pushButton->setText("Open");
}
}
2.3.2 Receiving images from the server
void Widget::read_data()
{
int ret;
QTime qTime;
static int i = 0;
ret = m_pSocket->read(m_pData + m_iRecvLen, P_LEN);
if (0 == strncmp("newImage", m_pData + m_iRecvLen, 8))
{
m_iImgLen = atoi(m_pData + m_iRecvLen + 9);
i++;
}
else
{
m_iRecvLen += ret;
if (m_iRecvLen >= m_iImgLen)
{
QString timestamp = QString::number(QDateTime::currentMSecsSinceEpoch());
update();
return;
}
}
//Image transfer completed
m_pSocket->write("ack");
}
2.3.3 Display the image
void Widget::paintEvent(QPaintEvent *event)
{
QPixmap map;
if ((m_iRecvLen >= m_iImgLen) && (m_iImgLen > 0))
{
map.loadFromData((uchar *)m_pData, m_iImgLen);
QPainter p(this);
p.drawPixmap(140, 0, 640, 480, map);
m_pSocket->write("new_request");
m_iRecvLen = 0;
}
}
3 Testing on Embedded Linux Platform
3.1 Cross-compilation
Copy the source code of the client program to Ubunu for cross-compilation. For the specific compilation process, please refer to the previous article:
Embedded Qt - Write and run your first ARM-Qt program
The experimental environment of this article continues to use the Linux board with the system firmware of WildFire i.MX6ULL burned. The compiled program needs to be sent to the board through SSH. The operation of SSH file transfer can refer to the previous article:
Embedded Qt-Control Hardware: Slide bar controls RGB lights
3.2 Experimental Demonstration
4 Conclusion
This article introduces how to use Qt to implement a network camera function, convert a USB camera into an IP camera through the server, and use the client in the Linux board to connect to the server to display the real-time image of the camera.