// LATEST

在Qt6中截取视频帧图片

导入一个视频文件并截取某一帧

简单介绍

Qt 6的Qt Multimedia模块替换了Qt 5.x的Qt Multimedia模块。使用Qt 5的Qt Multimedia的现有代码可以通过有限的努力进行移植。

也就是可以用QVideoSink来获取视频单帧了

准备工作

引入Multimedia

CMake示例:

find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia)
target_link_libraries(QtTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt6::Multimedia)

需要引用的头文件

#include <QMediaPlayer>
#include <QVideoSink>
#include <QVideoFrame>

使用教程

获取单帧

.h private添加

QMediaPlayer* m_player = nullptr;

.cpp使用样例

#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include <QFileDialog>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_player = new QMediaPlayer(this);
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::on_pushButton_clicked()
{
    QVideoSink* videoSink = new QVideoSink(this);
    m_player->setVideoOutput(videoSink);
    //加载视频文件
    QString str = QFileDialog::getOpenFileName();
    m_player->setSource(QUrl(str));
    //连接 QVideoSink 的 videoFrameChanged 信号
    connect(videoSink, &QVideoSink::videoFrameChanged, [&](const QVideoFrame &frame) {
            //获取帧时间戳
            m_player->setPosition(5000);
            QImage image = frame.toImage(); //将 QVideoFrame 转换为 QImage
            image.save("screenshot_at_5_seconds.png"); //保存图片
            m_player->stop(); // 停止播放
        }
    });
    // 开始播放视频
    m_player->play();
}

获取多帧

.h中添加loop

QEventLoop loop;

如果需要循环获取,可以使用loop进行局部循环

//循环七秒
for (int i = 0; i <= m_player->duration(); i += 7000)
{
    m_player->setPosition(i);
    //使用局部事件循环等待帧捕获
    bool frameCaptured = false;
    //连接视频帧捕获的信号
    connect(videoSink, &QVideoSink::videoFrameChanged, this, [&](const QVideoFrame &frame) mutable {
        if (!frameCaptured && frame.isValid()) {
            frameCaptured = true; //第一次捕获后设置为 true
            QImage image = frame.toImage();
            ui->label->setPixmap(QPixmap::fromImage(image));
            if (!image.isNull()) {
                //保存图像为 jpg
                image.save(QString::number(i) + ".jpg", "JPG");
            }
            //断开信号,停止等待
            disconnect(videoSink, &QVideoSink::videoFrameChanged, this, nullptr);
        }
    });
    //开启局部事件循环,等待帧捕获完成
    loop.exec();
}

还没有评论

欢迎留下你的观点,保持交流的清晰和友好。