支持语音与视频即时通讯项目杂记(二)

目录

概念:

[视频帧(Video Frame)是组成视频的基本单元。它可以被视为一幅静止的图像,它在一定的时间间隔内连续播放,从而形成了流畅的视频。](#视频帧(Video Frame)是组成视频的基本单元。它可以被视为一幅静止的图像,它在一定的时间间隔内连续播放,从而形成了流畅的视频。)

[Changes to Qt Multimedia](#Changes to Qt Multimedia)

[New features in Qt 6](#New features in Qt 6)

[Removed features](#Removed features)

[Changed features](#Changed features)

qt6中的正则表达式校验的应用

概念:

视频帧(Video Frame)是组成视频的基本单元。它可以被视为一幅静止的图像,它在一定的时间间隔内连续播放,从而形成了流畅的视频。

视频帧由像素组成,每个像素代表了图像中的一个点的颜色值。视频帧的大小通常以宽度和高度来描述,例如 1920x1080 表示宽度为 1920 像素,高度为 1080 像素。每个像素的颜色值可以通过不同的图像格式来表示,例如 RGB、YUV 等。

视频帧之间的时间间隔称为帧率(Frame Rate),用于衡量每秒播放的视频帧数。常见的帧率有 24fps、30fps、60fps 等。较高的帧率可以提供更流畅的视频播放效果。

理解帧的概念可以类比为一本动画书,每页都是一个静止画面,当您快速翻动这些画面时,就会产生连续的动画效果。同样地,视频帧也是以相似的方式运作,通过连续播放静止的视频帧,我们感知到了动态的视频内容。

在视频处理和编辑中,您可以对视频帧进行各种操作,如剪辑、特效处理、颜色校正等。通过对视频帧的处理,可以实现视频的编辑、修复和增强等功能。

总结起来,视频帧是组成视频的静止图像,通过连续播放这些图像,我们可以观看到流畅的动态视频内容。

QVideoFrame 是 Qt Multimedia 模块中的一个类,用于表示视频帧的数据。它提供了处理和操作视频帧的功能。

下面是 QVideoFrame 的一些常见用途和作用:

  1. 获取视频帧数据:通过 QVideoFrame,您可以获取视频帧的原始数据,包括像素数据、图像格式、图像大小等。这使得您可以进行视频处理、分析和编辑等操作。

  2. 格式转换:QVideoFrame 提供了方法将视频帧转换为不同的图像格式,以便与您的应用程序或其他模块兼容。您可以使用 QVideoFrame::map() 函数将帧数据映射到一个 QImage 或者 QPixmap 对象中,然后进行格式转换。

  3. 视频播放和显示:QVideoFrame 可以与其他 Qt 组件(如 QImageQLabelQGraphicsView 等)进行集成,实现视频的播放和显示。通过将视频帧数据与相应的图像组件结合使用,您可以在用户界面中实时显示视频。

  4. 视频输入输出:通过 QVideoFrame,您可以处理和传输视频帧数据。例如,您可以将视频帧保存为文件,或者从摄像头或其他视频源接收视频帧。

综上所述,QVideoFrame 类提供了对视频帧的访问和处理功能,使您能够灵活地处理视频数据以满足应用程序的需求。

在Qt项目中,.pri文件是一种特殊的文件类型,称为"包含文件"(Include File),主要用于将共享的构建设置和规则抽象成一个可复用的模块,方便在其他项目中重复使用。.pri文件可以包含编译器选项、头文件路径、库依赖关系以及其他自定义构建规则等信息。

要创建一个.pri文件,您可以按照以下步骤操作:

  1. 打开Qt Creator,并且打开您要将.pri文件添加到的项目。

  2. 在项目视图中,右键单击您想要创建.pri文件的文件夹,选择"新建文件"(New File)。

  3. 在弹出的对话框中,选择"其他"(Other)-> "Pro 文件"(Pro File),然后点击下一步。

  4. 输入文件名并选择保存位置,例如:"myproject.pri",然后点击下一步。

  5. 在下一个对话框中,确认文件属性并且选择添加需要的模块或者库,并且可以在此处定义全局变量、宏定义、编译器选项等设置。然后点击完成来创建文件。

  6. 在您的项目文件(例如.pro文件)中包含该.pri文件:在您的项目文件中加入如下语句:

    复制代码
    include(myproject.pri)

这样就可以在您的项目中使用.pri文件中定义的构建规则和设置了。需要注意的是,.pri文件中定义的变量或宏在引入该文件的项目中是全局可见的。因此,如果您有多个.pri文件,建议将它们放在独立文件夹中,以便管理和维护。

官网中说明:

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.

qt6中的正则表达式校验的应用

项目中,涉及IP地址校验等需求,qt6 正则表达式和qt5有一定差异,见下面的代码

cpp 复制代码
#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 &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 &strIP);
private:
    QLineEdit *m_lineEidt[6];
};

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

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

#endif // QIPLINEEDIT_H
cpp 复制代码
#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()
{
}

// 获取当前输入框索引
int QIPLineEdit::getIndex(QLineEdit *pEdit)
{
    int index = -1;
    for (int i = 0; i < 4; i++)
    {
        if (pEdit == m_lineEidt[i])
            index = i;
    }
    return index;
}

// 事件过滤器,判断按键输入
bool QIPLineEdit::eventFilter(QObject *obj, QEvent *ev)
{
    if (children().contains(obj) && 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 &&
                     strText.toInt() * 10 > 255)
            {
                int index = getIndex(pCurrentEdit);
                if (index != -1 && index != 3)
                {
                    m_lineEidt[index + 1]->setFocus();
                    m_lineEidt[index + 1]->selectAll();
                }
            }
            else if (strText.length() == 2 && strText.toInt() * 10 < 255)
            {
                if (Qt::Key_0 == keyEvent->key() && 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 && 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 && 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 && index != 3)
                {
                    m_lineEidt[index + 1]->setFocus();
                    m_lineEidt[index + 1]->setCursorPosition(0);
                }
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            // 小键盘的"."号
        case Qt::Key_Period:
        {
            int index = getIndex(pCurrentEdit);
            if (index != -1 && index != 3)
            {
                m_lineEidt[index + 1]->setFocus();
                m_lineEidt[index + 1]->setCursorPosition(0);
            }
            return QLineEdit::eventFilter(obj, ev);
        }
            break;
        default:
            break;
        }
    }
    return false;
}

// 设置信息
void QIPLineEdit::setText(const QString &strIP)
{
    // 是否是IP地址
    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++;
        }
    }
}

// 判断IP地址
bool QIPLineEdit::isTextValid(const QString &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;
}


// 获取IP地址
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) && 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  && 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 && 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 && 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 &strIP)
{
  //qt6的写法
    QRegularExpression rx2("([0-9A-Za-z]{2})([0-9A-Za-z:-]{3}){5}");
    QRegularExpressionMatch match = rx2.match(strIP);
    return match.hasMatch();
  //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 &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);
}
相关推荐
安科瑞刘鸿鹏1 分钟前
老旧小区用电安全保护装置#限流式防火保护器参数介绍#
运维·服务器·物联网·能源
Rain_Rong20 分钟前
linux检测硬盘
linux·运维·服务器
小灰灰要减肥26 分钟前
装饰者模式
java
张铁铁是个小胖子37 分钟前
MyBatis学习
java·学习·mybatis
我曾经是个程序员1 小时前
鸿蒙学习记录之http网络请求
服务器·学习·http
真真-真真1 小时前
WebXR
linux·运维·服务器
Yan.love1 小时前
开发场景中Java 集合的最佳选择
java·数据结构·链表
椰椰椰耶1 小时前
【文档搜索引擎】搜索模块的完整实现
java·搜索引擎
大G哥1 小时前
java提高正则处理效率
java·开发语言
指尖上跳动的旋律1 小时前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql