Miscellaneous notes on supporting voice and video instant messaging projects (2)

Table of Contents

concept:

Video Frame is the basic unit that makes up a video. It can be thought of as a still image that plays continuously over a certain period of time, resulting in a smooth video.

Changes to Qt Multimedia

New features in Qt 6

Removed features

Changed features

Application of regular expression verification in qt6

Concept:

Video Frame (Video Frame) is the basic unit that makes up a video. It can be thought of as a still image that plays continuously over a certain period of time, resulting in a smooth video.

Video frames are composed of pixels, each pixel representing the color value of a point in the image. The size of a video frame is usually described in terms of width and height, for example 1920×1080 means a width of 1920 pixels and a height of 1080 pixels. The color value of each pixel can be represented by different image formats, such as RGB, YUV, etc.

The time interval between video frames is called the frame rate, which is used to measure the number of video frames played per second. Common frame rates are 24fps, 30fps, 60fps, etc. Higher frame rates provide smoother video playback.

Understanding the concept of frames can be compared to an animation book. Each page is a still picture. When you quickly flip through these pictures, a continuous animation effect will be produced. Similarly, video frames work in a similar way. By continuously playing still video frames, we perceive dynamic video content.

In video processing and editing, you can perform various operations on video frames, such as editing, special effects processing, color correction, etc. By processing video frames, functions such as video editing, repair and enhancement can be achieved.

To sum up, video frames are still images that make up a video. By playing these images continuously, we can watch smooth dynamic video content.

QVideoFrame is a class in the Qt Multimedia module used to represent video frame data. It provides functions for processing and manipulating video frames.

Here are some common uses and effects of QVideoFrame:

  1. Get video frame data: Through QVideoFrame, you can get the original data of the video frame, including pixel data, image format, image size, etc. This allows you to do things like video processing, analysis, and editing.

  2. Format conversion: QVideoFrame provides methods to convert video frames to different image formats for compatibility with your application or other modules. You can use the QVideoFrame::map() function to map the frame data to a QImage or QPixmap object, and then perform format conversion.

  3. Video playback and display: QVideoFrame can be integrated with other Qt components (such as QImage, QLabel, QGraphicsView, etc.) , to realize video playback and display. By combining video frame data with corresponding image components, you can display video in real time in your user interface.

  4. Video input and output: With QVideoFrame, you can process and transmit video frame data. For example, you can save video frames to a file or receive video frames from a camera or other video source.

In summary, the QVideoFrame class provides access to and processing of video frames, allowing you to flexibly process video data to meet the needs of your application.

In the Qt project, the .pri file is a special file type called “Include File” (Include File). It is mainly used to abstract shared build settings and rules into a usable Reusable modules for easy reuse in other projects. .pri files can contain information such as compiler options, header file paths, library dependencies, and other custom build rules.

To create a .pri file, you can follow these steps:

  1. Open Qt Creator, and open the project to which you want to add the .pri file.

  2. In the project view, right-click the folder where you want to create the .pri file and select “New File”.

  3. In the pop-up dialog box, select “Other” (Other) -> “Pro File” (Pro File), and then click Next.

  4. Enter a file name and select a save location, for example: “myproject.pri” and click Next.

  5. In the next dialog box, confirm the file properties and choose to add the required modules or libraries, and you can define global variables, macro definitions, compiler options and other settings here. Then click Finish to create the file.

  6. Include the .pri file in your project file (such as a .pro file): Add the following statement to your project file:

    include(myproject.pri)
    

This will allow you to use the build rules and settings defined in the .pri file in your project. It should be noted that variables or macros defined in the .pri file are globally visible in the project where the file is introduced. Therefore, if you have multiple .pri files, it is recommended to place them in separate folders for easier management and maintenance.

The official website states:

Changes to Qt Multimedia

Qt 6 is a result of the conscious effort to make the framework more efficient and easy to use.

We try to maintain binary and source compatibility for all the public APIs in each release. But some changes were inevitable in an effort to make Qt a better framework.

The module has been refactored significantly and has changed classification, from essential to add-on. The Qt Multimedia module in Qt 6 replaces the Qt Multimedia module from Qt 5.x. Existing code that uses Qt Multimedia from Qt 5 can be ported with limited effort.

New features in Qt 6

There are a number of new features in Qt Multimedia:

  • QMediaCaptureSession class is the central object for media capture.
  • QMediaRecorder class is now a class limited to recording audio and video. It handles encoding of data produced in a capture session.
  • Using QMediaFormat and QMediaRecorder, setting up the desired encoding when recording has changed significantly.
  • You can now also monitor the audio recorded by a capture session.
  • Support for selection of audio, video and subtitle tracks when playing back media files has been added.
  • QAudioDecoder is now supported on all platforms.

Removed features

Removed feature Notes or suggested alternative
Playlist in QMediaPlayer QMediaPlayer does not do any playlist handling anymore in Qt 6.
QMediaPlayList This class has been removed from the API. It does however still exist as part of the Media Player Example.
QAudioProbe and QVideoProbe The audio and video probing API has been removed.
QAudioRecorder Use the QMediaCaptureSession or CaptureSession QML type.
Audio QML type Use MediaPlayer QML type.
QMediaObject and QMediaBindableInterface These classes have been removed in favor of a more direct API for setting up connections between objects using, for example, setVideoOutput and QMediaCaptureSession.
QCameraViewFinderSettings This class has been removed. Use QCameraFormat to define the resolution and frame rate the camera should be using.
QMediaContent The class has been removed. Use QUrl for individual media files instead.
QSound Use QSoundEffect instead.
QVideoFilterRunnable Use shader effects in QML instead or access the QVideoFrame’s content in C + + .
Public back-end API The back-end API of Qt Multimedia is private in Qt 6. This improves response time for supporting new multimedia use cases. Any classes that contain the words “Control” or “Abstract” in the class name in Qt 5 are now private in Qt 6.
Back- end plugins Qt Multimedia in Qt 6 does not use a plugin infrastructure for its back ends anymore. This means that users no longer need to ship those back ends with their application. Instead, the back end being used is determined at compile time based on the underlying operating system. Qt uses gstreamer on Linux, WMF on Windows, AVFoundation on macOS and iOS and the Android multimedia APIs on Android.

Changed features

A number of classes previously offered in Qt Multimedia have changed in ways that may affect previously written code. The following table highlights these changes.

Changed feature Notes
Handling of Camera resolutions and frame rates Handling of these has been simplified and a new QCameraFormat class helps with selecting the correct resolution and frame rate for the camera.
Video output handling on the C++ side has changed significantly. QAbstractVideoSurface has been replaced by the QVideoSink class, and generic rendering support has been enhanced to cover all pixel formats supported by Qt Multimedia.
Metadata types QMediaMetaData has changed significantly: mainly moving from string based to enum based keys, and reducing the set of supported keys to the ones that can be supported on most platforms.
QMediaFormat Handling of formats for encoded media and the settings for the media recorder have changed significantly. Qt 5 provides a string-based API, a separated file format, and audio and video codecs into three classes. However, Qt 6 unifies the formats in the QMediaFormat class. Additional settings are directly specified in QMediaRecorder. Setting up file formats and codecs is now implemented with enums and no longer uses strings. This puts some limitations on the set of codecs that can be used, but helps provide a consistent cross-platform API.
QCameraImageCapture renamed QImageCapture None
Audio inputs and outputs QMediaPlayer and QMediaCaptureSession (and the corresponding QML types MediaPlayer and CaptureSession) are not connected to any audio devices by default. Explicitly connect them to a QAudioInput/ AudioInput or QAudioOutput/AudioOutput to capture or play back audio.
Capturing video A capture session is by default not connected to a Camera. Connect it to a QCamera object (Camera item) to be able to capture video or still images.

Application of regular expression verification in qt6

The project involves IP address verification and other requirements, qt6 regular expressions are somewhat different from qt5, see the code below

#ifndef CLINEEDIT_H
#define CLINEEDIT_H

#include <QLineEdit>
#include <QEvent>

class QLabel;

class QIPLineEdit : public QLineEdit
{
Q_OBJECT

public:
QIPLineEdit(QWidget *parent = 0);
~QIPLineEdit();

void setText(const QString & strIP);
QString text() const;
protected:
bool eventFilter(QObject *obj, QEvent *ev);

int getIndex(QLineEdit *pEdit);
bool isTextValid(const QString & amp;strIP);
private:
QLineEdit *m_lineEidt[4];
};

/
class QMacLineEdit : public QLineEdit
{
    Q_OBJECT

public:
    QMacLineEdit(QWidget *parent = 0);
    ~QMacLineEdit();

    void setText(const QString & strMac);
    QString text() const;
protected:
// void paintEvent(QPaintEvent *event);
    bool eventFilter(QObject *obj, QEvent *ev);

    int getIndex(QLineEdit *pEdit);
    bool isTextValid(const QString & amp;strIP);
private:
    QLineEdit *m_lineEidt[6];
};

class QIconLineEdit : public QLineEdit {
    Q_OBJECT
public:
    QIconLineEdit(QWidget *parent = 0);
    ~QIconLineEdit();

    void SetIcon(const QPixmap & amp;pixmap);
private:
    QLabel *labelPixmap;
};

#endif // QIPLINEEDIT_H
#include "clineedit.h"
#include "ipvalidator.h"
#include <QLabel>
#include <QRegularExpressionValidator>
#include <QRegularExpression>
#include <QValidator>
#include <QPainter>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QMessageBox>
#include <QDebug>
#include <QRegularExpression>
#include <QValidator>





QIPLineEdit::QIPLineEdit(QWidget *parent)
    : QLineEdit(parent)
{
     QRegularExpression rx("(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})");
    //QRegExp rx("(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})");
    QHBoxLayout *pHBox = new QHBoxLayout(this);
    pHBox->setSpacing(2);
    pHBox->setContentsMargins(2, 2, 2, 2);
    QLabel *labelDot[3];
    for (int i = 0; i < 4; i + + )
    {
        m_lineEidt[i] = new QLineEdit(this);
        m_lineEidt[i]->setProperty("ip", true);
        m_lineEidt[i]->setFrame(false);
        m_lineEidt[i]->setMaxLength(3);
        m_lineEidt[i]->setAlignment(Qt::AlignCenter);
        m_lineEidt[i]->installEventFilter(this);
        //m_lineEidt[i]->setValidator(new QRegExpValidator(rx, this));
        m_lineEidt[i]->setValidator(new IPValidator(this));

        m_lineEidt[i]->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        pHBox->addWidget(m_lineEidt[i]);
        if (i < 3) {
            labelDot[i] = new QLabel(this);
            labelDot[i]->setText(".");
            labelDot[i]->setFixedWidth(2);
            pHBox->addWidget(labelDot[i]);
        }
    }
    this->setReadOnly(true);
    m_lineEidt[0]->setFocus();
    m_lineEidt[0]->selectAll();
}

QIPLineEdit::~QIPLineEdit()
{
}

// Get the current input box index
int QIPLineEdit::getIndex(QLineEdit *pEdit)
{
    int index = -1;
    for (int i = 0; i < 4; i + + )
    {
        if (pEdit == m_lineEidt[i])
            index = i;
    }
    return index;
}

//Event filter, determine key input
bool QIPLineEdit::eventFilter(QObject *obj, QEvent *ev)
{
    if (children().contains(obj) & amp; & amp; QEvent::KeyPress == ev->type())
    {
        QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(ev);
        QLineEdit *pCurrentEdit = qobject_cast<QLineEdit *>(obj);
        switch (keyEvent->key())
        {
        case Qt::Key_0:
        case Qt::Key_1:
        case Qt::Key_2:
        case Qt::Key_3:
        case Qt::Key_4:
        case Qt::Key_5:
        case Qt::Key_6:
        case Qt::Key_7:
        case Qt::Key_8:
        case Qt::Key_9:
        {
            QString strText = pCurrentEdit->text();
            if (pCurrentEdit->selectedText().length())
            {
                pCurrentEdit->text().replace(pCurrentEdit->selectedText(), QChar(keyEvent->key()));
            }
            else if (strText.length() <=3 & amp; & amp;
                     strText.toInt() * 10 > 255)
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 & amp; & amp; index != 3)
                {
                    m_lineEidt[index + 1]->setFocus();
                    m_lineEidt[index + 1]->selectAll();
                }
            }
            else if (strText.length() == 2 & amp; & amp; strText.toInt() * 10 < 255)
            {
                if (Qt::Key_0 == keyEvent->key() & amp; & amp; strText.toInt())
                {
                    pCurrentEdit->setText(strText.insert(pCurrentEdit->cursorPosition(),
                                                         QChar(Qt::Key_0)));
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            break;
        case Qt::Key_Backspace:
        {
            QString strText = pCurrentEdit->text();
            if (strText.isEmpty())
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 & amp; & amp; index != 0)
                {
                    m_lineEidt[index - 1]->setFocus();
                    int length = m_lineEidt[index - 1]->text().length();
                    m_lineEidt[index - 1]->setCursorPosition(length ? length : 0);
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
        case Qt::Key_Left:
        {
            if (!pCurrentEdit->cursorPosition())
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 & amp; & amp; index != 0)
                {
                    m_lineEidt[index - 1]->setFocus();
                    int length = m_lineEidt[index - 1]->text().length();
                    m_lineEidt[index - 1]->setCursorPosition(length ? length : 0);
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
        case Qt::Key_Right:
        {
            if (pCurrentEdit->cursorPosition() == pCurrentEdit->text().length())
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 & amp; & amp; index != 3)
                {
                    m_lineEidt[index + 1]->setFocus();
                    m_lineEidt[index + 1]->setCursorPosition(0);
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            //The "." sign on the small keyboard
        case Qt::Key_Period:
        {
            int index = getIndex(pCurrentEdit);
            if (index != -1 & amp; & amp; index != 3)
            {
                m_lineEidt[index + 1]->setFocus();
                m_lineEidt[index + 1]->setCursorPosition(0);
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            break;
        default:
            break;
        }
    }
    return false;
}

//Set information
void QIPLineEdit::setText(const QString & amp;strIP)
{
    // Is it an IP address?
    if (!isTextValid(strIP))
    {
        QMessageBox::warning(this, "Attention",
                             "Your IP Address is Invalid!",
                             QMessageBox::StandardButton::Ok);
        return;
    }
    else
    {
        int i = 0;
        QStringList ipList = strIP.split(".");

        foreach (QString ip,ipList)
        {
            m_lineEidt[i]->setText(ip);
            i + + ;
        }
    }
}

// Determine IP address
bool QIPLineEdit::isTextValid(const QString & amp;strIP)
{
    //QRegExp rx2("\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9 ]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) \b");
    QRegularExpression rx2("\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]? )\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\ \b");
    QRegularExpressionMatch match = rx2.match(strIP);
    if (!match.hasMatch()) {
        return false;
    }
    // if (!rx2.exactMatch(strIP))
// return false;
    return true;
}


// Get IP address
QString QIPLineEdit::text() const
{
    QString strIP;
    for (int i = 0; i < 4; i + + ) {
        strIP.append(m_lineEidt[i]->text());
        if (3 != i) {
            strIP.append(".");
        }
    }

    return strIP;
}

QMacLineEdit::QMacLineEdit(QWidget *parent)
    : QLineEdit(parent)
{
    //QRegExp rx("([0-9A-Fa-f]{2})");
    QRegularExpression rx("([0-9A-Fa-f]{2})");

    QHBoxLayout *pHBox = new QHBoxLayout(this);
    pHBox->setSpacing(2);
    pHBox->setContentsMargins(2, 2, 2, 2);
    QLabel *labelDot[5];
    for (int i = 0; i < 6; i + + )
    {
        m_lineEidt[i] = new QLineEdit(this);
        m_lineEidt[i]->setFrame(false);
        m_lineEidt[i]->setMaxLength(2);
        m_lineEidt[i]->setAlignment(Qt::AlignCenter);
        m_lineEidt[i]->installEventFilter(this);
        //m_lineEidt[i]->setValidator(new QRegExpValidator(rx, this));
        //m_lineEidt[i]->setValidator(new QValidator (0,100,this));
        QRegularExpressionValidator* validator = new QRegularExpressionValidator(QRegularExpression("(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})"), this) ;
        m_lineEidt[i]->setValidator(validator);
        m_lineEidt[i]->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        pHBox->addWidget(m_lineEidt[i]);
        if (i < 5) {
            labelDot[i] = new QLabel(this);
            labelDot[i]->setText("-");
            labelDot[i]->setFixedWidth(2);
            pHBox->addWidget(labelDot[i]);
        }
    }
    this->setReadOnly(true);
    m_lineEidt[0]->setFocus();
    m_lineEidt[0]->selectAll();
}

QMacLineEdit::~QMacLineEdit()
{
}

int QMacLineEdit::getIndex(QLineEdit *pEdit)
{
    int index = -1;
    for (int i = 0; i < 6; i + + )
    {
        if (pEdit == m_lineEidt[i])
            index = i;
    }
    return index;
}

//
bool QMacLineEdit::eventFilter(QObject *obj, QEvent *ev)
{
    if (children().contains(obj) & amp; & amp; QEvent::KeyPress == ev->type())
    {
        QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(ev);
        QLineEdit *pCurrentEdit = qobject_cast<QLineEdit *>(obj);

        switch (keyEvent->key())
        {
        case Qt::Key_0:
        case Qt::Key_1:
        case Qt::Key_2:
        case Qt::Key_3:
        case Qt::Key_4:
        case Qt::Key_5:
        case Qt::Key_6:
        case Qt::Key_7:
        case Qt::Key_8:
        case Qt::Key_9:
        case Qt::Key_A:
        case Qt::Key_B:
        case Qt::Key_C:
        case Qt::Key_D:
        case Qt::Key_E:
        case Qt::Key_F:
        {
            QString strText = pCurrentEdit->text();
            if (pCurrentEdit->selectedText().length())
            {
                pCurrentEdit->text().replace(pCurrentEdit->selectedText(),
                                             QChar(keyEvent->key()).toUpper());
            }
            else if (strText.length() == 2) {
                int index = getIndex(pCurrentEdit);
                if (0 <= index & amp; & amp; index < 5)
                {
                    m_lineEidt[index + 1]->setFocus();
                    m_lineEidt[index + 1]->selectAll();
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            break;
        case Qt::Key_Backspace:
        {
            QString strText = pCurrentEdit->text();
            if (strText.isEmpty())
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 & amp; & amp; index != 0)
                {
                    m_lineEidt[index - 1]->setFocus();
                    int length = m_lineEidt[index - 1]->text().length();
                    m_lineEidt[index - 1]->setCursorPosition(length ? length : 0);
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
        case Qt::Key_Period:
        {
            int index = getIndex(pCurrentEdit);
            QString strText = pCurrentEdit->text();
            if (strText.length() == 1) {
                pCurrentEdit->setText(strText.insert(0, QChar(Qt::Key_0)));
            }
            else if (strText.length() == 0) {
                pCurrentEdit->setText("00");
            }

            if (index != -1 & amp; & amp; index < 5)
            {
                m_lineEidt[index + 1]->setFocus();
                m_lineEidt[index + 1]->setCursorPosition(0);
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            break;
        default:
            break;
        }
    }
    return false;
}

//
void QMacLineEdit::setText(const QString & strMac)
{
    if (!isTextValid(strMac))
    {
        QMessageBox::warning(this, "Attention",
                             "Your MAC Address is Invalid!",
                             QMessageBox::StandardButton::Ok);
        return;
    }
    else
    {
        int i = 0;
        QStringList macList = strMac.split("-");

        foreach (QString mac,macList)
        {
            m_lineEidt[i]->setText(mac);
            i + + ;
        }
    }
}

bool QMacLineEdit::isTextValid(const QString & amp;strIP)
{
  //How to write qt6
    QRegularExpression rx2("([0-9A-Za-z]{2})([0-9A-Za-z:-]{3}){5}");
    QRegularExpressionMatch match = rx2.match(strIP);
    return match.hasMatch();
  //How to write qt5
  //QRegExp rx2("([0-9A-Za-z]{2})([0-9A-Za-z:-]{3}){5}");
  // if (!rx2.exactMatch(strIP))
  // return false;
  // return true;
}

QString QMacLineEdit::text() const
{
    QString strMac;
    for (int i = 0; i < 5; i + + ) {
        strMac.append(m_lineEidt[i]->text());
        if (3 != i) {
            strMac.append("-");
        }
    }
    return strMac;
}

QIconLineEdit::QIconLineEdit(QWidget *parent):
    QLineEdit(parent)
{
    labelPixmap = new QLabel(this);
    labelPixmap->setMinimumSize(16, 16);
    labelPixmap->setVisible(false);

}

QIconLineEdit::~QIconLineEdit()
{

}

void QIconLineEdit::SetIcon(const QPixmap & amp;pixmap)
{
    if (pixmap.isNull()) return;

    labelPixmap->setPixmap(pixmap);
    labelPixmap->setVisible(true);
    labelPixmap->setGeometry(5,(this->height() - pixmap.height()) / 2, 16, 16);
    this->setTextMargins(25, 1, 1, 1);
}



ps:

C++-CLION writing and calling DLL files