Krita Source Code Documentation
Loading...
Searching...
No Matches
RecorderExport::Private Class Reference

Public Member Functions

QString applyVariables (const QString &templateArguments)
 
void checkExporter ()
 
void cleanupExporter ()
 
void fillComboProfiles ()
 
QString formatDuration (long durationMs)
 
void initDimensions ()
 
 Private (RecorderExport *q_ptr)
 
QString requestFile (const QString &extension, const QString &defaultDir=QString())
 
QStringList splitCommand (const QString &command)
 
void startExport ()
 
bool tryAbortExport ()
 
void updateFps (RecorderExportConfig &config, bool takeFromInputFps=false)
 
void updateFrameInfo ()
 
void updateRatio (bool widthToHeight)
 
void updateVideoDuration ()
 
void updateVideoFilePath ()
 
void updateWarningVisibility ()
 

Static Public Member Functions

static void desktopServicesOpenPath (const QString &path)
 

Public Attributes

RecorderDirectoryCleaner * cleaner = nullptr
 
QElapsedTimer elapsedTimer
 
QScopedPointer< Exporter > exporter
 
QScopedPointer< RecorderExportPreprocessor > preprocessor
 
RecorderExport * q
 
RecorderExportSettings * settings
 
int spinInputFPSMaxValue = 0
 
int spinInputFPSMinValue = 0
 
QScopedPointer< Ui::RecorderExport > ui
 

Static Public Attributes

static constexpr int DIMENSION_LIMIT = 1920
 

Detailed Description

Definition at line 62 of file recorder_export.cpp.

Constructor & Destructor Documentation

◆ Private()

RecorderExport::Private::Private ( RecorderExport * q_ptr)
inline

Definition at line 80 of file recorder_export.cpp.

81 : q(q_ptr)
82 , ui(new Ui::RecorderExport)
83 , settings(q_ptr->settings)
85 {
86 }
QScopedPointer< RecorderExportPreprocessor > preprocessor
QScopedPointer< Ui::RecorderExport > ui
RecorderExportSettings * settings
RecorderExportSettings * settings

Member Function Documentation

◆ applyVariables()

QString RecorderExport::Private::applyVariables ( const QString & templateArguments)
inline

Definition at line 346 of file recorder_export.cpp.

347 {
348 const QSize &outSize = settings->resize ? settings->size : settings->imageSize;
349 const int previewLength = settings->resultPreview ? settings->firstFrameSec : 0;
350 const int resultLength = settings->extendResult ? settings->lastFrameSec : 0;
351 const float transitionLength = settings->resultPreview ? 0.7 : 0;
352 return QString(templateArguments)
353 .replace("$IN_FPS", QString::number(settings->inputFps))
354 .replace("$OUT_FPS", QString::number(settings->fps))
355 .replace("$WIDTH", QString::number(outSize.width()))
356 .replace("$HEIGHT", QString::number(outSize.height()))
357 .replace("$FRAMES", QString::number(settings->framesCount))
358 .replace("$INPUT_DIR", settings->inputDirectory)
359 .replace("$FIRST_FRAME_SEC", QString::number(previewLength))
360 .replace("$TRANSITION_LENGTH", QString::number(transitionLength))
361 .replace("$H264_ENCODER", settings->h264Encoder)
362 .replace("$LAST_FRAME_SEC", QString::number(resultLength))
364 }
QLatin1String fileExtension(RecorderFormat format)

References RecorderExportSettings::extendResult, RecorderFormatInfo::fileExtension(), RecorderExportSettings::firstFrameSec, RecorderExportSettings::format, RecorderExportSettings::fps, RecorderExportSettings::framesCount, RecorderExportSettings::h264Encoder, RecorderExportSettings::imageSize, RecorderExportSettings::inputDirectory, RecorderExportSettings::inputFps, RecorderExportSettings::lastFrameSec, RecorderExportSettings::resize, RecorderExportSettings::resultPreview, settings, and RecorderExportSettings::size.

◆ checkExporter()

void RecorderExport::Private::checkExporter ( )
inline

Definition at line 89 of file recorder_export.cpp.

90 {
91 const QJsonObject ffmpegJson = KisFFMpegWrapper::findFFMpeg(settings->ffmpegPath);
92 const bool success = ffmpegJson["enabled"].toBool();
93 const QIcon &icon = KisIconUtils::loadIcon(success ? "dialog-ok" : "window-close");
94 const QList<QAction *> &actions = ui->editFfmpegPath->actions();
95 QAction *action;
96
97 if (!actions.isEmpty()) {
98 action = actions.first();
99 action->setIcon(icon);
100 } else {
101 action = ui->editFfmpegPath->addAction(icon, QLineEdit::TrailingPosition);
102 }
103 if (success) {
104 const QJsonArray h264Encoders = ffmpegJson["codecs"].toObject()["h264"].toObject()["encoders"].toArray();
105 settings->ffmpegPath = ffmpegJson["path"].toString();
106 settings->h264Encoder = h264Encoders.contains("libopenh264") ? "libopenh264" : "libx264";
107 ui->editFfmpegPath->setText(settings->ffmpegPath);
108 action->setToolTip("Version: "+ffmpegJson["version"].toString()
109 +(ffmpegJson["codecs"].toObject()["h264"].toObject()["encoding"].toBool() ? "":" (MP4/MKV UNSUPPORTED)")
110 );
111 } else {
112 ui->editFfmpegPath->setText(i18nc("This text is displayed instead of path to external tool in case of external tool is not found", "[NOT FOUND]"));
113 action->setToolTip(i18n("FFmpeg executable location couldn't be detected, please install it or select its location manually"));
114 }
115 ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(success);
116 }
static QJsonObject findFFMpeg(const QString &customLocation)
QString toString(const QString &value)
QIcon loadIcon(const QString &name)

References RecorderExportSettings::ffmpegPath, KisFFMpegWrapper::findFFMpeg(), RecorderExportSettings::h264Encoder, KisIconUtils::loadIcon(), settings, and ui.

◆ cleanupExporter()

void RecorderExport::Private::cleanupExporter ( )
inline

Definition at line 337 of file recorder_export.cpp.

338 {
339 if (exporter) {
340 exporter->reset();
341 exporter.reset();
342 }
343 }
QScopedPointer< Exporter > exporter

References exporter.

◆ desktopServicesOpenPath()

static void RecorderExport::Private::desktopServicesOpenPath ( const QString & path)
inlinestatic

Definition at line 455 of file recorder_export.cpp.

456 {
457#ifdef Q_OS_ANDROID
458 // QDesktopServices doesn't clear exceptions
459 KisAndroidUtils::clearJniException(QStringLiteral("before opening ") + path);
460 QDesktopServices::openUrl(QUrl(path));
461 KisAndroidUtils::clearJniException(QStringLiteral("after opening ") + path);
462#else
463 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
464#endif
465 }
void clearJniException(const QString &location)

References KisAndroidUtils::clearJniException().

◆ fillComboProfiles()

void RecorderExport::Private::fillComboProfiles ( )
inline

Definition at line 119 of file recorder_export.cpp.

120 {
121 int indexToSelect;
122 {
123 QSignalBlocker blocker(ui->comboProfile);
124 ui->comboProfile->clear();
125#ifdef Q_OS_ANDROID
127 int count = formats.size();
128 if (count == 0) {
129 return;
130 }
131
132 indexToSelect = -1;
133 for (int i = 0, count = formats.size(); i < count; ++i) {
134 QString key = formats[i]->key();
135 ui->comboProfile->addItem(formats[i]->title(), QVariant(key));
136 if (key == settings->selectedFormat) {
137 indexToSelect = i;
138 }
139 }
140
141 if (indexToSelect == -1) {
142 indexToSelect = 0;
143 settings->selectedFormat = formats[0]->key();
144 }
145#else
146 for (const RecorderProfile &profile : settings->profiles) {
147 ui->comboProfile->addItem(profile.name);
148 }
149 indexToSelect = settings->profileIndex;
150#endif
151 }
152 ui->comboProfile->setCurrentIndex(indexToSelect);
153 }
static const QVector< KisMediaEncoderFormat * > & getSupportedFormats()

References KisMediaEncoderWrapper::getSupportedFormats(), RecorderExportSettings::profileIndex, RecorderExportSettings::profiles, settings, and ui.

◆ formatDuration()

QString RecorderExport::Private::formatDuration ( long durationMs)
inline

Definition at line 382 of file recorder_export.cpp.

383 {
384 QString result;
385 const long ms = (durationMs % 1000) / 10;
386
387 result += QString(".%1").arg(ms, 2, 10, QLatin1Char('0'));
388
389 long duration = durationMs / 1000;
390 const long seconds = duration % 60;
391 result = QString("%1%2").arg(seconds, 2, 10, QLatin1Char('0')).arg(result);
392
393 duration = duration / 60;
394 const long minutes = duration % 60;
395 if (minutes != 0) {
396 result = QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(result);
397
398 duration = duration / 60;
399 if (duration != 0)
400 result = QString("%1:%2").arg(duration, 2, 10, QLatin1Char('0')).arg(result);
401 }
402
403 return result;
404 }

◆ initDimensions()

void RecorderExport::Private::initDimensions ( )
inline

Definition at line 406 of file recorder_export.cpp.

407 {
408 // Video with dimensions above 1920 pixels isn't widely supported.
409 // They often fail to encode with mysterious errors, can't be played
410 // back properly and/or are rejected by websites where users attempt to
411 // upload the videos. This caps the dimensions to avoid that trap.
412 settings->resize = true;
413 settings->lockRatio = true;
414 if (settings->imageSize.isEmpty()) {
415 settings->size = QSize(1024, 1024);
416 } else {
417 int iw = settings->imageSize.width();
418 int ih = settings->imageSize.height();
419 if (iw > DIMENSION_LIMIT || ih > DIMENSION_LIMIT) {
420 int ow, oh;
421 if (iw >= ih) {
422 ow = DIMENSION_LIMIT;
423 oh = qRound(qreal(DIMENSION_LIMIT) / qreal(iw) * qreal(ih));
424 } else {
425 ow = qRound(qreal(DIMENSION_LIMIT) / qreal(ih) * qreal(iw));
426 oh = DIMENSION_LIMIT;
427 }
428 ow &= ~1;
429 oh &= ~1;
430 settings->size = QSize(ow, oh);
431 } else {
433 }
434 }
435 }
static constexpr int DIMENSION_LIMIT

References DIMENSION_LIMIT, RecorderExportSettings::imageSize, RecorderExportSettings::lockRatio, RecorderExportSettings::resize, settings, and RecorderExportSettings::size.

◆ requestFile()

QString RecorderExport::Private::requestFile ( const QString & extension,
const QString & defaultDir = QString() )
inline

Definition at line 444 of file recorder_export.cpp.

445 {
446 KoFileDialog dialog(q, KoFileDialog::SaveFile, "ExportTimelapse");
447 dialog.setCaption(i18n("Export Timelapse Video As"));
448 if (!defaultDir.isEmpty()) {
449 dialog.setDefaultDir(defaultDir);
450 }
451 dialog.setMimeTypeFilters(QStringList(KisMimeDatabase::mimeTypeForSuffix(extension)));
452 return dialog.filename();
453 }
QList< QString > QStringList
static QString mimeTypeForSuffix(const QString &suffix)
Find the mimetype for a given extension. The extension may have the form "*.xxx" or "xxx".

References KisMimeDatabase::mimeTypeForSuffix(), q, and KoFileDialog::SaveFile.

◆ splitCommand()

QStringList RecorderExport::Private::splitCommand ( const QString & command)
inline

Definition at line 244 of file recorder_export.cpp.

245 {
246 QStringList args;
247 QString tmp;
248 int quoteCount = 0;
249 bool inQuote = false;
250
251 // handle quoting. tokens can be surrounded by double quotes
252 // "hello world". three consecutive double quotes represent
253 // the quote character itself.
254 for (int i = 0; i < command.size(); ++i) {
255 if (command.at(i) == QLatin1Char('"')) {
256 ++quoteCount;
257 if (quoteCount == 3) {
258 // third consecutive quote
259 quoteCount = 0;
260 tmp += command.at(i);
261 }
262 continue;
263 }
264 if (quoteCount) {
265 if (quoteCount == 1)
266 inQuote = !inQuote;
267 quoteCount = 0;
268 }
269 if (!inQuote && command.at(i).isSpace()) {
270 if (!tmp.isEmpty()) {
271 args += tmp;
272 tmp.clear();
273 }
274 } else {
275 tmp += command.at(i);
276 }
277 }
278 if (!tmp.isEmpty())
279 args += tmp;
280
281 return args;
282 }

◆ startExport()

void RecorderExport::Private::startExport ( )
inline

Definition at line 285 of file recorder_export.cpp.

286 {
287 Q_ASSERT(exporter == nullptr);
288
289#ifndef Q_OS_ANDROID
290 // The android export system is robust enough to not need this
292 preprocessor->doPreprocessing();
293
294 // We don't do this again on Android, it's mind-bogglingly slow.
296#endif
297
298 exporter.reset(new Exporter(q));
299 QObject::connect(exporter.data(), SIGNAL(sigStarted()), q, SLOT(onExporterStarted()));
300 QObject::connect(exporter.data(), SIGNAL(sigFinished()), q, SLOT(onExporterFinished()));
301 QObject::connect(exporter.data(), SIGNAL(sigFinishedWithError(QString)), q, SLOT(onExporterFinishedWithError(QString)));
302 QObject::connect(exporter.data(), SIGNAL(sigProgressUpdated(int)), q, SLOT(onExporterProgressUpdated(int)));
303
304#ifdef Q_OS_ANDROID
305 KisMediaEncoderWrapperSettings exporterSettings = {
307 settings->inputFilePaths,
308 QString(),
310 settings->formatPreferences.value(settings->selectedFormat).toMap(),
311 QString(),
314 settings->fps,
317 0,
318 };
319#else
321 KisFFMpegWrapperSettings exporterSettings;
322 exporterSettings.processPath = settings->ffmpegPath;
323 exporterSettings.args = splitCommand(applyVariables(profile.arguments));
324 exporterSettings.outputFile = settings->videoFilePath;
325 exporterSettings.batchMode = true; //TODO: Consider renaming to 'silent' mode, meaning no window for extra window handling...
326#endif
327
328 ui->labelStatus->setText(i18nc("Status for the export of the video record", "Starting exporter..."));
329 ui->buttonCancelExport->setEnabled(false);
330 ui->progressExport->setValue(0);
331 elapsedTimer.start();
332
333 // Do this last, it may immediately emit a failure.
334 exporter->startNonBlocking(exporterSettings);
335 }
static KisMediaEncoderFormat * getFormatByKey(const QString &key)
QString applyVariables(const QString &templateArguments)
QStringList splitCommand(const QString &command)
void onExporterFinishedWithError(QString error)
void onExporterProgressUpdated(int frameNo)
QList< RecorderProfile > profiles

References applyVariables(), RecorderProfile::arguments, elapsedTimer, exporter, RecorderExportSettings::ffmpegPath, RecorderExportSettings::firstFrameSec, RecorderExportSettings::format, RecorderExportSettings::fps, KisMediaEncoderWrapper::getFormatByKey(), RecorderExportSettings::imageSize, RecorderExportSettings::inputDirectory, RecorderExportSettings::inputFps, RecorderExportSettings::lastFrameSec, RecorderExport::onExporterFinished(), RecorderExport::onExporterFinishedWithError(), RecorderExport::onExporterProgressUpdated(), RecorderExport::onExporterStarted(), KisMediaEncoderWrapperSettings::outputFile, preprocessor, RecorderExportSettings::profileIndex, RecorderExportSettings::profiles, q, RecorderExportSettings::resize, settings, RecorderExportSettings::size, splitCommand(), ui, updateFrameInfo(), and RecorderExportSettings::videoFilePath.

◆ tryAbortExport()

bool RecorderExport::Private::tryAbortExport ( )
inline

Definition at line 229 of file recorder_export.cpp.

230 {
231 if (!exporter)
232 return true;
233
234 if (QMessageBox::question(q, q->windowTitle(), i18n("Abort encoding the timelapse video?"))
235 == QMessageBox::Yes) {
237 return true;
238 }
239
240 return false;
241 }

References cleanupExporter(), exporter, and q.

◆ updateFps()

void RecorderExport::Private::updateFps ( RecorderExportConfig & config,
bool takeFromInputFps = false )
inline

Definition at line 212 of file recorder_export.cpp.

213 {
214 if (!settings->lockFps)
215 return;
216
217 if (takeFromInputFps) {
219 config.setFps(settings->fps);
220 ui->spinFps->setValue(settings->fps);
221 } else {
224 ui->spinInputFps->setValue(settings->inputFps);
225 }
227 }

References RecorderExportSettings::fps, RecorderExportSettings::inputFps, RecorderExportSettings::lockFps, RecorderExportConfig::setFps(), RecorderExportConfig::setInputFps(), settings, ui, and updateVideoDuration().

◆ updateFrameInfo()

void RecorderExport::Private::updateFrameInfo ( )
inline

Definition at line 155 of file recorder_export.cpp.

156 {
158 QDir::Name, QDir::Files | QDir::NoDotAndDotDot);
159 QStringList frames = dir.entryList(); // dir.count() calls entryList().count() internally
160 settings->framesCount = frames.count();
161 if (settings->framesCount != 0) {
162 const QString &fileName = settings->inputDirectory % QDir::separator() % frames.last();
163 settings->imageSize = QImageReader(fileName).size();
164 settings->imageSize.rwidth() &= ~1;
165 settings->imageSize.rheight() &= ~1;
166 }
167#ifdef Q_OS_ANDROID
168 // QDir::entryList is mind-bogglingly slow on Android, so we only load
169 // this once and cache the result. Not like these are supposed to change
170 // while this modal dialog is up anyway.
171 settings->inputFilePaths.clear();
172 settings->inputFilePaths.reserve(settings->framesCount);
173 for (const QString &frame : frames) {
174 settings->inputFilePaths.append(dir.filePath(frame));
175 }
176#endif
177 }

References RecorderFormatInfo::fileExtension(), RecorderExportSettings::format, RecorderExportSettings::framesCount, RecorderExportSettings::imageSize, RecorderExportSettings::inputDirectory, and settings.

◆ updateRatio()

void RecorderExport::Private::updateRatio ( bool widthToHeight)
inline

Definition at line 195 of file recorder_export.cpp.

196 {
197 const float ratio = static_cast<float>(settings->imageSize.width()) / static_cast<float>(settings->imageSize.height());
198 if (widthToHeight) {
199 settings->size.setHeight(static_cast<int>(settings->size.width() / ratio));
200 } else {
201 settings->size.setWidth(static_cast<int>(settings->size.height() * ratio));
202 }
203 // make width and height even
204 settings->size.rwidth() &= ~1;
205 settings->size.rheight() &= ~1;
206 QSignalBlocker blockerWidth(ui->spinScaleHeight);
207 QSignalBlocker blockerHeight(ui->spinScaleWidth);
208 ui->spinScaleHeight->setValue(settings->size.height());
209 ui->spinScaleWidth->setValue(settings->size.width());
210 }

References RecorderExportSettings::imageSize, settings, RecorderExportSettings::size, and ui.

◆ updateVideoDuration()

void RecorderExport::Private::updateVideoDuration ( )
inline

◆ updateVideoFilePath()

◆ updateWarningVisibility()

void RecorderExport::Private::updateWarningVisibility ( )
inline

Definition at line 437 of file recorder_export.cpp.

438 {
439 ui->wdgWarnFps->setVisible(settings->fps > 30);
441 ui->wdgWarnSize->setVisible(size.width() > DIMENSION_LIMIT || size.height() > DIMENSION_LIMIT);
442 }
int size(const Forest< T > &forest)
Definition KisForest.h:1232

References DIMENSION_LIMIT, RecorderExportSettings::fps, RecorderExportSettings::imageSize, RecorderExportSettings::resize, settings, RecorderExportSettings::size, and ui.

Member Data Documentation

◆ cleaner

RecorderDirectoryCleaner* RecorderExport::Private::cleaner = nullptr

Definition at line 73 of file recorder_export.cpp.

◆ DIMENSION_LIMIT

constexpr int RecorderExport::Private::DIMENSION_LIMIT = 1920
staticconstexpr

Definition at line 65 of file recorder_export.cpp.

◆ elapsedTimer

QElapsedTimer RecorderExport::Private::elapsedTimer

Definition at line 75 of file recorder_export.cpp.

◆ exporter

QScopedPointer<Exporter> RecorderExport::Private::exporter

Definition at line 72 of file recorder_export.cpp.

◆ preprocessor

QScopedPointer<RecorderExportPreprocessor> RecorderExport::Private::preprocessor

Definition at line 71 of file recorder_export.cpp.

◆ q

RecorderExport* RecorderExport::Private::q

Definition at line 67 of file recorder_export.cpp.

◆ settings

RecorderExportSettings* RecorderExport::Private::settings

Definition at line 69 of file recorder_export.cpp.

◆ spinInputFPSMaxValue

int RecorderExport::Private::spinInputFPSMaxValue = 0

Definition at line 78 of file recorder_export.cpp.

◆ spinInputFPSMinValue

int RecorderExport::Private::spinInputFPSMinValue = 0

Definition at line 77 of file recorder_export.cpp.

◆ ui

QScopedPointer<Ui::RecorderExport> RecorderExport::Private::ui

Definition at line 68 of file recorder_export.cpp.


The documentation for this class was generated from the following file: