QGIS API Documentation 3.99.0-Master (d270888f95f)
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
47}
48
52
57
59{
60 return mFeedback;
61}
62
63void QgsVideoExporter::setInputFiles( const QStringList &files )
64{
65 mInputFiles = files;
66}
67
68void QgsVideoExporter::setInputFilesByPattern( const QString &directory, const QString &pattern )
69{
70 QDirIterator it( directory, pattern.isEmpty() ? QStringList() : QStringList{ pattern }, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::NoIteratorFlags );
71 mInputFiles.clear();
72 while ( it.hasNext() )
73 {
74 const QString fullPath = it.next();
75 mInputFiles << fullPath;
76 }
77
78 std::sort( mInputFiles.begin(), mInputFiles.end() );
79}
80
82{
83 return mInputFiles;
84}
85
86void QgsVideoExporter::setFileFormat( QMediaFormat::FileFormat format )
87{
88 mFormat = format;
89}
90
91QMediaFormat::FileFormat QgsVideoExporter::fileFormat() const
92{
93 return mFormat;
94}
95
96void QgsVideoExporter::setVideoCodec( QMediaFormat::VideoCodec codec )
97{
98 mCodec = codec;
99}
100
101QMediaFormat::VideoCodec QgsVideoExporter::videoCodec() const
102{
103 return mCodec;
104}
105
106QMediaRecorder::Error QgsVideoExporter::error() const
107{
108 return mError;
109}
110
112{
113 return mErrorString;
114}
115
117{
118#if QT_VERSION < QT_VERSION_CHECK( 6, 8, 0 )
119 throw QgsNotSupportedException( u"Writing video is not supported on this system"_s );
120#else
121 mSession = std::make_unique< QMediaCaptureSession >();
122 mRecorder = std::make_unique< QMediaRecorder >();
123 mVideoInput = std::make_unique< QVideoFrameInput >();
124 mSession->setVideoFrameInput( mVideoInput.get() );
125 mSession->setRecorder( mRecorder.get() );
126 mRecorder->setOutputLocation( QUrl::fromLocalFile( mFileName ) );
127
128 QMediaFormat mediaFormat;
129 mediaFormat.setFileFormat( mFormat );
130 mediaFormat.setVideoCodec( mCodec );
131 mRecorder->setMediaFormat( mediaFormat );
132
133 // TODO: expose
134 mRecorder->setQuality( QMediaRecorder::Quality::VeryHighQuality );
135 mRecorder->setVideoBitRate( 2000 );
136 mRecorder->setEncodingMode( QMediaRecorder::EncodingMode::TwoPassEncoding );
137
138 mRecorder->setVideoResolution( mSize );
139 mRecorder->setVideoFrameRate( mFramesPerSecond );
140
141 QObject::connect( mVideoInput.get(), &QVideoFrameInput::readyToSendVideoFrame, this, &QgsVideoExporter::feedFrames );
142 QObject::connect( mRecorder.get(), &QMediaRecorder::recorderStateChanged, this, &QgsVideoExporter::checkStatus );
143 QObject::connect( mRecorder.get(), &QMediaRecorder::errorOccurred, this, &QgsVideoExporter::handleError );
144
145 mRecorder->record();
146
147 if ( mFeedback )
148 {
149 mFeedback->setProgress( 0 );
150 }
151 feedFrames();
152#endif
153}
154
155void QgsVideoExporter::feedFrames()
156{
157#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
158 if ( !mRecorder
159 || !mVideoInput
160 || mRecorder->recorderState() != QMediaRecorder::RecorderState::RecordingState )
161 return;
162
163 while ( mCurrentFrameIndex < mInputFiles.count() )
164 {
165 const QImage frame( mInputFiles.at( mCurrentFrameIndex ) );
166 QVideoFrame videoFrame( frame );
167 const qint64 startUs = mCurrentFrameIndex * mFrameDurationUs;
168 videoFrame.setStartTime( startUs );
169 videoFrame.setEndTime( startUs + mFrameDurationUs );
170
171 const bool sent = mVideoInput->sendVideoFrame( videoFrame );
172 if ( !sent )
173 return;
174
175 mCurrentFrameIndex++;
176
177 if ( mFeedback )
178 {
179 mFeedback->setProgress( 100.0 * static_cast< double >( mCurrentFrameIndex ) / static_cast< double >( mInputFiles.count() ) );
180 if ( mFeedback->isCanceled() )
181 {
182 mRecorder->stop();
183 return;
184 }
185 }
186 }
187
188 if ( mCurrentFrameIndex >= mInputFiles.count() )
189 {
190 mRecorder->stop();
191 }
192#endif
193}
194
195void QgsVideoExporter::checkStatus( QMediaRecorder::RecorderState state )
196{
197 switch ( state )
198 {
199 case QMediaRecorder::StoppedState:
200 {
201 if ( mCurrentFrameIndex >= mInputFiles.count() )
202 {
203 emit finished();
204 }
205 break;
206 }
207
208 case QMediaRecorder::RecordingState:
209 case QMediaRecorder::PausedState:
210 break;
211 }
212}
213
214void QgsVideoExporter::handleError( QMediaRecorder::Error error, const QString &errorString )
215{
216 mError = error;
217 mErrorString = errorString;
218}
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.