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