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

RecorderDirectoryCleanercleaner = nullptr
 
QElapsedTimer elapsedTimer
 
QScopedPointer< Exporter > exporter
 
RecorderExportq
 
RecorderExportSettingssettings
 
int spinInputFPSMaxValue = 0
 
int spinInputFPSMinValue = 0
 
QScopedPointer< Ui::RecorderExport > ui
 

Static Public Attributes

static constexpr int DIMENSION_LIMIT = 1920
 

Detailed Description

Definition at line 61 of file recorder_export.cpp.

Constructor & Destructor Documentation

◆ Private()

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

Definition at line 78 of file recorder_export.cpp.

79 : q(q_ptr)
80 , ui(new Ui::RecorderExport)
81 , settings(q_ptr->settings)
82 {
83 }
QScopedPointer< Ui::RecorderExport > ui
RecorderExportSettings * settings
RecorderExportSettings * settings

Member Function Documentation

◆ applyVariables()

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

Definition at line 336 of file recorder_export.cpp.

337 {
338 const QSize &outSize = settings->resize ? settings->size : settings->imageSize;
339 const int previewLength = settings->resultPreview ? settings->firstFrameSec : 0;
340 const int resultLength = settings->extendResult ? settings->lastFrameSec : 0;
341 const float transitionLength = settings->resultPreview ? 0.7 : 0;
342 return QString(templateArguments)
343 .replace("$IN_FPS", QString::number(settings->inputFps))
344 .replace("$OUT_FPS", QString::number(settings->fps))
345 .replace("$WIDTH", QString::number(outSize.width()))
346 .replace("$HEIGHT", QString::number(outSize.height()))
347 .replace("$FRAMES", QString::number(settings->framesCount))
348 .replace("$INPUT_DIR", settings->inputDirectory)
349 .replace("$FIRST_FRAME_SEC", QString::number(previewLength))
350 .replace("$TRANSITION_LENGTH", QString::number(transitionLength))
351 .replace("$H264_ENCODER", settings->h264Encoder)
352 .replace("$LAST_FRAME_SEC", QString::number(resultLength))
354 }
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 86 of file recorder_export.cpp.

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

328 {
329 if (exporter) {
330 exporter->reset();
331 exporter.reset();
332 }
333 }
QScopedPointer< Exporter > exporter

References exporter.

◆ desktopServicesOpenPath()

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

Definition at line 445 of file recorder_export.cpp.

446 {
447#ifdef Q_OS_ANDROID
448 // QDesktopServices doesn't clear exceptions
449 KisAndroidUtils::clearJniException(QStringLiteral("before opening ") + path);
450 QDesktopServices::openUrl(QUrl(path));
451 KisAndroidUtils::clearJniException(QStringLiteral("after opening ") + path);
452#else
453 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
454#endif
455 }
void clearJniException(const QString &location)

References KisAndroidUtils::clearJniException().

◆ fillComboProfiles()

void RecorderExport::Private::fillComboProfiles ( )
inline

Definition at line 116 of file recorder_export.cpp.

117 {
118 int indexToSelect;
119 {
120 QSignalBlocker blocker(ui->comboProfile);
121 ui->comboProfile->clear();
122#ifdef Q_OS_ANDROID
124 int count = formats.size();
125 if (count == 0) {
126 return;
127 }
128
129 indexToSelect = -1;
130 for (int i = 0, count = formats.size(); i < count; ++i) {
131 QString key = formats[i]->key();
132 ui->comboProfile->addItem(formats[i]->title(), QVariant(key));
133 if (key == settings->selectedFormat) {
134 indexToSelect = i;
135 }
136 }
137
138 if (indexToSelect == -1) {
139 indexToSelect = 0;
140 settings->selectedFormat = formats[0]->key();
141 }
142#else
143 for (const RecorderProfile &profile : settings->profiles) {
144 ui->comboProfile->addItem(profile.name);
145 }
146 indexToSelect = settings->profileIndex;
147#endif
148 }
149 ui->comboProfile->setCurrentIndex(indexToSelect);
150 }
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 372 of file recorder_export.cpp.

373 {
374 QString result;
375 const long ms = (durationMs % 1000) / 10;
376
377 result += QString(".%1").arg(ms, 2, 10, QLatin1Char('0'));
378
379 long duration = durationMs / 1000;
380 const long seconds = duration % 60;
381 result = QString("%1%2").arg(seconds, 2, 10, QLatin1Char('0')).arg(result);
382
383 duration = duration / 60;
384 const long minutes = duration % 60;
385 if (minutes != 0) {
386 result = QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(result);
387
388 duration = duration / 60;
389 if (duration != 0)
390 result = QString("%1:%2").arg(duration, 2, 10, QLatin1Char('0')).arg(result);
391 }
392
393 return result;
394 }

◆ initDimensions()

void RecorderExport::Private::initDimensions ( )
inline

Definition at line 396 of file recorder_export.cpp.

397 {
398 // Video with dimensions above 1920 pixels isn't widely supported.
399 // They often fail to encode with mysterious errors, can't be played
400 // back properly and/or are rejected by websites where users attempt to
401 // upload the videos. This caps the dimensions to avoid that trap.
402 settings->resize = true;
403 settings->lockRatio = true;
404 if (settings->imageSize.isEmpty()) {
405 settings->size = QSize(1024, 1024);
406 } else {
407 int iw = settings->imageSize.width();
408 int ih = settings->imageSize.height();
409 if (iw > DIMENSION_LIMIT || ih > DIMENSION_LIMIT) {
410 int ow, oh;
411 if (iw >= ih) {
412 ow = DIMENSION_LIMIT;
413 oh = qRound(qreal(DIMENSION_LIMIT) / qreal(iw) * qreal(ih));
414 } else {
415 ow = qRound(qreal(DIMENSION_LIMIT) / qreal(ih) * qreal(iw));
416 oh = DIMENSION_LIMIT;
417 }
418 ow &= ~1;
419 oh &= ~1;
420 settings->size = QSize(ow, oh);
421 } else {
423 }
424 }
425 }
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 434 of file recorder_export.cpp.

435 {
436 KoFileDialog dialog(q, KoFileDialog::SaveFile, "ExportTimelapse");
437 dialog.setCaption(i18n("Export Timelapse Video As"));
438 if (!defaultDir.isEmpty()) {
439 dialog.setDefaultDir(defaultDir);
440 }
441 dialog.setMimeTypeFilters(QStringList(KisMimeDatabase::mimeTypeForSuffix(extension)));
442 return dialog.filename();
443 }
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 241 of file recorder_export.cpp.

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

◆ startExport()

void RecorderExport::Private::startExport ( )
inline

Definition at line 282 of file recorder_export.cpp.

283 {
284 Q_ASSERT(exporter == nullptr);
285
286#ifndef Q_OS_ANDROID
287 // We don't do this again on Android, it's mind-bogglingly slow.
289#endif
290
291 exporter.reset(new Exporter(q));
292 QObject::connect(exporter.data(), SIGNAL(sigStarted()), q, SLOT(onExporterStarted()));
293 QObject::connect(exporter.data(), SIGNAL(sigFinished()), q, SLOT(onExporterFinished()));
294 QObject::connect(exporter.data(), SIGNAL(sigFinishedWithError(QString)), q, SLOT(onExporterFinishedWithError(QString)));
295 QObject::connect(exporter.data(), SIGNAL(sigProgressUpdated(int)), q, SLOT(onExporterProgressUpdated(int)));
296
297#ifdef Q_OS_ANDROID
298 KisMediaEncoderWrapperSettings exporterSettings = {
300 settings->inputFilePaths,
302 settings->formatPreferences.value(settings->selectedFormat).toMap(),
305 settings->fps,
308 };
309#else
311 KisFFMpegWrapperSettings exporterSettings;
312 exporterSettings.processPath = settings->ffmpegPath;
313 exporterSettings.args = splitCommand(applyVariables(profile.arguments));
314 exporterSettings.outputFile = settings->videoFilePath;
315 exporterSettings.batchMode = true; //TODO: Consider renaming to 'silent' mode, meaning no window for extra window handling...
316#endif
317
318 ui->labelStatus->setText(i18nc("Status for the export of the video record", "Starting exporter..."));
319 ui->buttonCancelExport->setEnabled(false);
320 ui->progressExport->setValue(0);
321 elapsedTimer.start();
322
323 // Do this last, it may immediately emit a failure.
324 exporter->startNonBlocking(exporterSettings);
325 }
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::fps, KisMediaEncoderWrapper::getFormatByKey(), RecorderExportSettings::imageSize, RecorderExportSettings::inputFps, RecorderExportSettings::lastFrameSec, RecorderExport::onExporterFinished(), RecorderExport::onExporterFinishedWithError(), RecorderExport::onExporterProgressUpdated(), RecorderExport::onExporterStarted(), KisMediaEncoderWrapperSettings::outputFile, 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 226 of file recorder_export.cpp.

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

References cleanupExporter(), exporter, and q.

◆ updateFps()

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

Definition at line 209 of file recorder_export.cpp.

210 {
211 if (!settings->lockFps)
212 return;
213
214 if (takeFromInputFps) {
216 config.setFps(settings->fps);
217 ui->spinFps->setValue(settings->fps);
218 } else {
221 ui->spinInputFps->setValue(settings->inputFps);
222 }
224 }

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

◆ updateFrameInfo()

void RecorderExport::Private::updateFrameInfo ( )
inline

Definition at line 152 of file recorder_export.cpp.

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

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

◆ updateRatio()

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

Definition at line 192 of file recorder_export.cpp.

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

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

◆ updateVideoDuration()

void RecorderExport::Private::updateVideoDuration ( )
inline

◆ updateVideoFilePath()

◆ updateWarningVisibility()

void RecorderExport::Private::updateWarningVisibility ( )
inline

Definition at line 427 of file recorder_export.cpp.

428 {
429 ui->wdgWarnFps->setVisible(settings->fps > 30);
431 ui->wdgWarnSize->setVisible(size.width() > DIMENSION_LIMIT || size.height() > DIMENSION_LIMIT);
432 }
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 71 of file recorder_export.cpp.

◆ DIMENSION_LIMIT

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

Definition at line 64 of file recorder_export.cpp.

◆ elapsedTimer

QElapsedTimer RecorderExport::Private::elapsedTimer

Definition at line 73 of file recorder_export.cpp.

◆ exporter

QScopedPointer<Exporter> RecorderExport::Private::exporter

Definition at line 70 of file recorder_export.cpp.

◆ q

RecorderExport* RecorderExport::Private::q

Definition at line 66 of file recorder_export.cpp.

◆ settings

RecorderExportSettings* RecorderExport::Private::settings

Definition at line 68 of file recorder_export.cpp.

◆ spinInputFPSMaxValue

int RecorderExport::Private::spinInputFPSMaxValue = 0

Definition at line 76 of file recorder_export.cpp.

◆ spinInputFPSMinValue

int RecorderExport::Private::spinInputFPSMinValue = 0

Definition at line 75 of file recorder_export.cpp.

◆ ui

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

Definition at line 67 of file recorder_export.cpp.


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