QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsvideoexporter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvideoexporter.h
3 --------------------------
4 begin : November 2025
5 copyright : (C) 2025 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsvideoexporter.h"
17
18#include "qgsfeedback.h"
19
20#include <QDirIterator>
21#include <QString>
22#include <QUrl>
23#include <QtMultimedia/QMediaCaptureSession>
24#include <QtMultimedia/QVideoFrame>
25#include <QtMultimedia/QVideoFrameInput>
26
27#include "moc_qgsvideoexporter.cpp"
28
29using namespace Qt::StringLiterals;
30
32{
33#if QT_VERSION < QT_VERSION_CHECK( 6, 8, 0 )
34 return false;
35#else
36 return true;
37#endif
38}
39
40QgsVideoExporter::QgsVideoExporter( const QString &filename, QSize size, double framesPerSecond )
41 : mFileName( filename )
42 , mSize( size )
43 , mFramesPerSecond( framesPerSecond )
44 , mFrameDurationUs( static_cast< qint64>( 1000000 / framesPerSecond ) )
45{}
46
49
54
56{
57 return mFeedback;
58}
59
60void QgsVideoExporter::setInputFiles( const QStringList &files )
61{
62 mInputFiles = files;
63}
64
65void QgsVideoExporter::setInputFilesByPattern( const QString &directory, const QString &pattern )
66{
67 QDirIterator it( directory, pattern.isEmpty() ? QStringList() : QStringList { pattern }, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::NoIteratorFlags );
68 mInputFiles.clear();
69 while ( it.hasNext() )
70 {
71 const QString fullPath = it.next();
72 mInputFiles << fullPath;
73 }
74
75 std::sort( mInputFiles.begin(), mInputFiles.end() );
76}
77
79{
80 return mInputFiles;
81}
82
83void QgsVideoExporter::setFileFormat( QMediaFormat::FileFormat format )
84{
85 mFormat = format;
86}
87
88QMediaFormat::FileFormat QgsVideoExporter::fileFormat() const
89{
90 return mFormat;
91}
92
93void QgsVideoExporter::setVideoCodec( QMediaFormat::VideoCodec codec )
94{
95 mCodec = codec;
96}
97
98QMediaFormat::VideoCodec QgsVideoExporter::videoCodec() const
99{
100 return mCodec;
101}
102
103QMediaRecorder::Error QgsVideoExporter::error() const
104{
105 return mError;
106}
107
109{
110 return mErrorString;
111}
112
114{
115#if QT_VERSION < QT_VERSION_CHECK( 6, 8, 0 )
116 throw QgsNotSupportedException( u"Writing video is not supported on this system"_s );
117#else
118 mSession = std::make_unique< QMediaCaptureSession >();
119 mRecorder = std::make_unique< QMediaRecorder >();
120 mVideoInput = std::make_unique< QVideoFrameInput >();
121 mSession->setVideoFrameInput( mVideoInput.get() );
122 mSession->setRecorder( mRecorder.get() );
123 mRecorder->setOutputLocation( QUrl::fromLocalFile( mFileName ) );
124
125 QMediaFormat mediaFormat;
126 mediaFormat.setFileFormat( mFormat );
127 mediaFormat.setVideoCodec( mCodec );
128 mRecorder->setMediaFormat( mediaFormat );
129
130 // TODO: expose
131 mRecorder->setQuality( QMediaRecorder::Quality::VeryHighQuality );
132 mRecorder->setVideoBitRate( 2000 );
133 mRecorder->setEncodingMode( QMediaRecorder::EncodingMode::TwoPassEncoding );
134
135 mRecorder->setVideoResolution( mSize );
136 mRecorder->setVideoFrameRate( mFramesPerSecond );
137
138 QObject::connect( mVideoInput.get(), &QVideoFrameInput::readyToSendVideoFrame, this, &QgsVideoExporter::feedFrames );
139 QObject::connect( mRecorder.get(), &QMediaRecorder::recorderStateChanged, this, &QgsVideoExporter::checkStatus );
140 QObject::connect( mRecorder.get(), &QMediaRecorder::errorOccurred, this, &QgsVideoExporter::handleError );
141
142 mRecorder->record();
143
144 if ( mFeedback )
145 {
146 mFeedback->setProgress( 0 );
147 }
148 feedFrames();
149#endif
150}
151
152void QgsVideoExporter::feedFrames()
153{
154#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
155 if ( !mRecorder || !mVideoInput || mRecorder->recorderState() != QMediaRecorder::RecorderState::RecordingState )
156 return;
157
158 while ( mCurrentFrameIndex < mInputFiles.count() )
159 {
160 const QImage frame( mInputFiles.at( mCurrentFrameIndex ) );
161 QVideoFrame videoFrame( frame );
162 const qint64 startUs = mCurrentFrameIndex * mFrameDurationUs;
163 videoFrame.setStartTime( startUs );
164 videoFrame.setEndTime( startUs + mFrameDurationUs );
165
166 const bool sent = mVideoInput->sendVideoFrame( videoFrame );
167 if ( !sent )
168 return;
169
170 mCurrentFrameIndex++;
171
172 if ( mFeedback )
173 {
174 mFeedback->setProgress( 100.0 * static_cast< double >( mCurrentFrameIndex ) / static_cast< double >( mInputFiles.count() ) );
175 if ( mFeedback->isCanceled() )
176 {
177 mRecorder->stop();
178 return;
179 }
180 }
181 }
182
183 if ( mCurrentFrameIndex >= mInputFiles.count() )
184 {
185 mRecorder->stop();
186 }
187#endif
188}
189
190void QgsVideoExporter::checkStatus( QMediaRecorder::RecorderState state )
191{
192 switch ( state )
193 {
194 case QMediaRecorder::StoppedState:
195 {
196 if ( mCurrentFrameIndex >= mInputFiles.count() )
197 {
198 emit finished();
199 }
200 break;
201 }
202
203 case QMediaRecorder::RecordingState:
204 case QMediaRecorder::PausedState:
205 break;
206 }
207}
208
209void QgsVideoExporter::handleError( QMediaRecorder::Error error, const QString &errorString )
210{
211 mError = error;
212 mErrorString = errorString;
213}
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
Custom exception class which is raised when an operation is not supported.
void setInputFiles(const QStringList &files)
Sets the list of input image files.
void finished()
Emitted when the video export finishes.
QMediaRecorder::Error error() const
Returns the last error received while writing the video.
QString errorString() const
Returns the string describing the last error received while writing the video.
~QgsVideoExporter() override
QgsVideoExporter(const QString &filename, QSize size, double framesPerSecond)
Constructor for QgsVideoExporter.
QStringList inputFiles() const
Returns the list of input image files.
double framesPerSecond() const
Returns the output video frames per second.
void writeVideo()
Starts the video export operation.
static bool isAvailable()
Returns true if the video export functionality is available on the current system.
QMediaFormat::VideoCodec videoCodec() const
Returns the output video codec.
void setInputFilesByPattern(const QString &directory, const QString &pattern)
Sets the input image files by searching a directory for files matching a pattern.
void setVideoCodec(QMediaFormat::VideoCodec codec)
Sets the output video codec.
void setFileFormat(QMediaFormat::FileFormat format)
Sets the output file format.
QMediaFormat::FileFormat fileFormat() const
Returns the output file format.
void setFeedback(QgsFeedback *feedback)
Sets an optional feedback object, for progress reports and cancellation support.
QSize size() const
Returns the output video frame size.
QgsFeedback * feedback()
Returns the optional feedback object.