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

目录

概念:

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

Changes to Qt Multimedia

New features in Qt 6

Removed 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 featureNotes or suggested alternative
Playlist in QMediaPlayerQMediaPlayer does not do any playlist handling anymore in Qt 6.
QMediaPlayListThis class has been removed from the API. It does however still exist as part of the Media Player Example.
QAudioProbe and QVideoProbeThe audio and video probing API has been removed.
QAudioRecorderUse the QMediaCaptureSession or CaptureSession QML type.
Audio QML typeUse MediaPlayer QML type.
QMediaObject and QMediaBindableInterfaceThese classes have been removed in favor of a more direct API for setting up connections between objects using, for example, setVideoOutput and QMediaCaptureSession.
QCameraViewFinderSettingsThis class has been removed. Use QCameraFormat to define the resolution and frame rate the camera should be using.
QMediaContentThe class has been removed. Use QUrl for individual media files instead.
QSoundUse QSoundEffect instead.
QVideoFilterRunnableUse shader effects in QML instead or access the QVideoFrame's content in C++.
Public back-end APIThe 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 pluginsQt 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 featureNotes
Handling of Camera resolutions and frame ratesHandling 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 typesQMediaMetaData 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.
QMediaFormatHandling 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 QImageCaptureNone
Audio inputs and outputsQMediaPlayer 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 videoA 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有一定差异,见下面的代码

#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
#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);
}



ps:

C++-CLION编写与调用DLL文件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值