Capture Video Frames in Qt6

Import a video file and capture a specific frame

Brief intro

The Qt Multimedia module in Qt 6 replaced the one in Qt 5.x. Existing Qt 5 Multimedia code can be migrated with limited effort.

That means you can use QVideoSink to capture a single frame.

Preparation

Include Multimedia.

CMake example:

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

Headers you need:

1
2
3
#include <QMediaPlayer>
#include <QVideoSink>
#include <QVideoFrame>

Usage

Capture a single frame

Add in .h private:

1
QMediaPlayer* m_player = nullptr;

Sample usage in .cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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);
// load video file
QString str = QFileDialog::getOpenFileName();
m_player->setSource(QUrl(str));
// connect QVideoSink videoFrameChanged signal
connect(videoSink, &QVideoSink::videoFrameChanged, [&](const QVideoFrame &frame) {
// get frame timestamp
m_player->setPosition(5000);
QImage image = frame.toImage(); // convert QVideoFrame to QImage
image.save("screenshot_at_5_seconds.png"); // save image
m_player->stop(); // stop playback
}
);
// start playback
m_player->play();
}

Capture multiple frames

Add a loop in .h:

1
QEventLoop loop;

If you need a looped capture, use a local event loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// every 7 seconds
for (int i = 0; i <= m_player->duration(); i += 7000)
{
m_player->setPosition(i);
// use a local event loop to wait for frame capture
bool frameCaptured = false;
// connect video frame capture signal
connect(videoSink, &QVideoSink::videoFrameChanged, this, [&](const QVideoFrame &frame) mutable {
if (!frameCaptured && frame.isValid()) {
frameCaptured = true; // set to true after first capture
QImage image = frame.toImage();
ui->label->setPixmap(QPixmap::fromImage(image));
if (!image.isNull()) {
// save image as jpg
image.save(QString::number(i) + ".jpg", "JPG");
}
// disconnect signal and stop waiting
disconnect(videoSink, &QVideoSink::videoFrameChanged, this, nullptr);
}
});
// start local event loop and wait for frame capture
loop.exec();
}

Capture Video Frames in Qt6
https://greatzaochen.dev/en/posts/a0033813/
Author
Zao_chen
Posted on
November 4, 2024
Licensed under