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:
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:
#include <QMediaPlayer>
#include <QVideoSink>
#include <QVideoFrame>Usage
Capture a single frame
Add in .h private:
QMediaPlayer* m_player = nullptr;Sample usage in .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);
// 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:
QEventLoop loop;If you need a looped capture, use a local event loop:
// 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();
}
No comments yet
Share your thoughts and keep the conversation clear and kind.
Write a comment
Sign in with GitHub first, then you can comment.