Krita Source Code Documentation
Loading...
Searching...
No Matches
KisFFMpegWrapper Class Reference

#include <KisFFMpegWrapper.h>

+ Inheritance diagram for KisFFMpegWrapper:

Signals

void sigFinished ()
 
void sigFinishedWithError (QString message)
 
void sigProgressUpdated (int frameNo)
 
void sigReadLine (int pipe, QString line)
 
void sigReadSTDERR (QByteArray stderrBuffer)
 
void sigReadSTDOUT (QByteArray stdoutBuffer)
 
void sigStarted ()
 

Public Member Functions

QJsonObject ffmpegProbe (const QString &inputFile, const QString &ffmpegPath, bool batchMode)
 
QJsonObject ffprobe (const QString &inputFile, const QString &ffprobePath)
 
 KisFFMpegWrapper (QObject *parent=nullptr)
 
void reset ()
 
KisImportExportErrorCode start (const KisFFMpegWrapperSettings &settings)
 
void startNonBlocking (const KisFFMpegWrapperSettings &settings)
 
bool waitForFinished (int msecs=FFMPEG_TIMEOUT)
 
 ~KisFFMpegWrapper ()
 

Static Public Member Functions

static ColorPrimaries colorPrimariesFromName (QString name)
 
static QString configuredFFMpegLocation ()
 
static QJsonObject findFFMpeg (const QString &customLocation)
 
static QJsonObject findFFProbe (const QString &customLocation)
 
static QJsonObject findProcessInfo (const QString &processName, const QString &processPath, bool includeProcessInfo)
 
static QJsonObject findProcessPath (const QString &processName, const QString &customLocation, bool processInfo)
 
static QStringList getSupportedCodecs (const QJsonObject &ffmpegJsonProcessInput)
 
static QByteArray runProcessAndReturn (const QString &processPath, const QStringList &args, int msecs=FFMPEG_TIMEOUT)
 
static void setConfiguredFFMpegLocation (QString &location)
 
static TransferCharacteristics transferCharacteristicsFromName (QString name)
 

Private Slots

void slotFinished (int exitCode)
 
void slotReadyReadSTDERR ()
 
void slotReadyReadSTDOUT ()
 
void slotStarted ()
 

Private Member Functions

bool ffprobeCheckStreamsValid (const QJsonObject &ffprobeJsonObj, const QString &ffprobeSTDERR)
 ffprobeCheckStreamsValid
 
void updateProgressDialog (int progressValue)
 

Static Private Member Functions

static void fixUpNonEmbeddedProcessEnvironment (const QString &processPath, QProcess &process)
 

Private Attributes

QString m_errorMessage
 
QScopedPointer< QProcess > m_process
 
KisFFMpegWrapperSettings m_processSettings
 
QString m_processSTDERR
 
QByteArray m_processSTDOUT
 
QSharedPointer< QProgressDialog > m_progress = nullptr
 
QString m_stderrBuffer
 
QString m_stdoutBuffer
 

Detailed Description

Definition at line 46 of file KisFFMpegWrapper.h.

Constructor & Destructor Documentation

◆ KisFFMpegWrapper()

KisFFMpegWrapper::KisFFMpegWrapper ( QObject * parent = nullptr)
explicit

Definition at line 36 of file KisFFMpegWrapper.cpp.

37 : QObject(parent)
38{
39}

◆ ~KisFFMpegWrapper()

KisFFMpegWrapper::~KisFFMpegWrapper ( )

Definition at line 41 of file KisFFMpegWrapper.cpp.

42{
43}

Member Function Documentation

◆ colorPrimariesFromName()

ColorPrimaries KisFFMpegWrapper::colorPrimariesFromName ( QString name)
static

Definition at line 791 of file KisFFMpegWrapper.cpp.

792{
793 if (name == "bt709") {
795 }
796 if (name == "bt470m") {
798 }
799 if (name == "bt470bg") {
801 }
802 if (name == "smpte170m") {
804 }
805 if (name == "smpte240m") {
807 }
808 if (name == "film") {
810 }
811 if (name == "bt2020") {
813 }
814 if (name.startsWith("smpte428")) {
816 }
817 if (name == "smpte431") {
819 }
820 if (name == "smpte432") {
822 }
823 if (name == "jedec-p22") {
825 }
826
828}
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ PRIMARIES_ITU_R_BT_470_6_SYSTEM_M
@ PRIMARIES_EBU_Tech_3213_E
@ PRIMARIES_UNSPECIFIED
@ PRIMARIES_ITU_R_BT_470_6_SYSTEM_B_G
@ PRIMARIES_SMPTE_240M
@ PRIMARIES_ITU_R_BT_601_6
@ PRIMARIES_SMPTE_RP_431_2
@ PRIMARIES_GENERIC_FILM
@ PRIMARIES_SMPTE_EG_432_1
@ PRIMARIES_ITU_R_BT_709_5
@ PRIMARIES_SMPTE_ST_428_1
const char * name(StandardAction id)

References PRIMARIES_EBU_Tech_3213_E, PRIMARIES_GENERIC_FILM, PRIMARIES_ITU_R_BT_2020_2_AND_2100_0, PRIMARIES_ITU_R_BT_470_6_SYSTEM_B_G, PRIMARIES_ITU_R_BT_470_6_SYSTEM_M, PRIMARIES_ITU_R_BT_601_6, PRIMARIES_ITU_R_BT_709_5, PRIMARIES_SMPTE_240M, PRIMARIES_SMPTE_EG_432_1, PRIMARIES_SMPTE_RP_431_2, PRIMARIES_SMPTE_ST_428_1, and PRIMARIES_UNSPECIFIED.

◆ configuredFFMpegLocation()

QString KisFFMpegWrapper::configuredFFMpegLocation ( )
static

Definition at line 437 of file KisFFMpegWrapper.cpp.

438{
439 KisConfig cfg(true);
440 return cfg.ffmpegLocation();
441}

References KisConfig::ffmpegLocation().

◆ ffmpegProbe()

QJsonObject KisFFMpegWrapper::ffmpegProbe ( const QString & inputFile,
const QString & ffmpegPath,
bool batchMode )

Definition at line 690 of file KisFFMpegWrapper.cpp.

691{
692 struct KisFFMpegWrapperSettings ffmpegSettings;
693
694 ffmpegSettings.processPath = ffmpegPath;
695 ffmpegSettings.storeOutput = true;
696 ffmpegSettings.progressMessage = i18nc("Video information probing dialog. arg1: frame number.", "Loading video data... %1 frames examined.", "[progress]");
697 ffmpegSettings.batchMode = batchMode;
698
699 ffmpegSettings.args << "-stats"
700 << "-v" << "info"
701 << "-progress" << "pipe:1"
702 << "-map" << "0:v:0"
703 << "-c" << "copy"
704 << "-f" << "null" << "pipe:1"
705 << "-i" << inputFile;
706
707
708 this->startNonBlocking(ffmpegSettings);
709 this->waitForFinished();
710
711
712 QString ffmpegSTDOUT = m_processSTDERR + "\n" + m_processSTDOUT;
713
714 dbgFile << "ffmpegProbe stdout:" << ffmpegSTDOUT;
715
716 QJsonObject ffmpegJsonObj;
717 QJsonArray ffmpegStreamsJsonArr;
718 QJsonObject ffmpegFormatJsonObj;
719 QJsonObject ffmpegProgressJsonObj;
720
721 QStringList stdoutLines = ffmpegSTDOUT.split('\n');
722
723 ffmpegJsonObj["error"] = FFProbeErrorCodes::UNSUPPORTED_CODEC;
724
725 for (const QString &line : stdoutLines) {
726 dbgFile << "ffmpeg probe stdout" << line;
727 QRegularExpression videoInfoInputRX("Duration: (\\d+):(\\d+):([\\d\\.]+),");
728 QRegularExpressionMatch durationMatch = videoInfoInputRX.match(line);
729
730 if (ffmpegFormatJsonObj.value("duration").isUndefined() && durationMatch.hasMatch()) {
731 ffmpegFormatJsonObj["duration"] = QString::number( (durationMatch.captured(1).toInt() * 60 * 60)
732 + (durationMatch.captured(2).toInt() * 60)
733 + durationMatch.captured(3).toFloat()
734 );
735 } else {
736 QRegularExpression videoInfoStreamRX(
737 QString("Stream #(\\d+):(\\d+)(?:[ ]*?\\((\\w+)\\)|): Video: (\\w+?)(?:[ ]*?\\((.+?)\\)|),[ ]*?")
738 .append("(\\w+?)(?:[ ]*?\\((.+?)\\)|),[ ]*?")
739 .append("(\\d+)x(\\d+)([ ]*?\\[.+?\\]|.*?),[ ]*?")
740 .append("(?:([\\d\\.]+) fps,|) (\\S+?) tbr, (.+?) tbn, (.+?) tbc")
741 );
742
743 QRegularExpressionMatch streamMatch = videoInfoStreamRX.match(line);
744
745 if (streamMatch.hasMatch() && ffmpegStreamsJsonArr[streamMatch.captured(1).toInt()].isUndefined() ) {
746 int index = streamMatch.captured(1).toInt();
747 QJsonObject ffmpegJsonOnStreamObj;
748
749 ffmpegJsonOnStreamObj["index"] = index;
750 ffmpegJsonOnStreamObj["codec_name"] = streamMatch.captured(4);
751 ffmpegJsonOnStreamObj["profile"] = streamMatch.captured(5);
752 ffmpegJsonOnStreamObj["pix_fmt"] = streamMatch.captured(6);
753 ffmpegJsonOnStreamObj["width"] = streamMatch.captured(8).toInt();
754 ffmpegJsonOnStreamObj["height"] = streamMatch.captured(9).toInt();
755
756 ffmpegJsonOnStreamObj["codec_type"] = "video";
757
758 if (streamMatch.captured(11).toFloat() > 0) {
759 float fps = streamMatch.captured(11).toFloat();
760
761 ffmpegProgressJsonObj["ffmpeg_fps"] = QString::number(fps);
762 ffmpegJsonOnStreamObj["r_frame_rate"] = QString::number( fps * 10000 ).append(QString("/10000"));
763 } else {
764 ffmpegProgressJsonObj["ffmpeg_fps"] = 0;
765 }
766
767 ffmpegJsonObj["error"] = FFProbeErrorCodes::NONE;
768
769 dbgFile << "ffmpegProbe stream:" << ffmpegJsonOnStreamObj;
770
771 ffmpegStreamsJsonArr.insert(index, ffmpegJsonOnStreamObj);
772
773 } else {
774
775 QRegularExpression videoInfoFrameRX("^(\\w+?)=([\\w\\./:]+?)$");
776 QRegularExpressionMatch frameMatch = videoInfoFrameRX.match(line);
777
778 if (frameMatch.hasMatch()) ffmpegProgressJsonObj[frameMatch.captured(1)] = frameMatch.captured(2);
779
780 }
781 }
782 }
783
784 ffmpegJsonObj.insert("streams",ffmpegStreamsJsonArr);
785 ffmpegJsonObj.insert("format",ffmpegFormatJsonObj);
786 ffmpegJsonObj.insert("progress",ffmpegProgressJsonObj);
787
788 return ffmpegJsonObj;
789}
@ UNSUPPORTED_CODEC
@ NONE
void startNonBlocking(const KisFFMpegWrapperSettings &settings)
QByteArray m_processSTDOUT
bool waitForFinished(int msecs=FFMPEG_TIMEOUT)
#define dbgFile
Definition kis_debug.h:53

References KisFFMpegWrapperSettings::args, KisFFMpegWrapperSettings::batchMode, dbgFile, m_processSTDERR, m_processSTDOUT, NONE, KisFFMpegWrapperSettings::processPath, KisFFMpegWrapperSettings::progressMessage, startNonBlocking(), KisFFMpegWrapperSettings::storeOutput, UNSUPPORTED_CODEC, and waitForFinished().

◆ ffprobe()

QJsonObject KisFFMpegWrapper::ffprobe ( const QString & inputFile,
const QString & ffprobePath )

Definition at line 653 of file KisFFMpegWrapper.cpp.

654{
655 struct KisFFMpegWrapperSettings ffprobeSettings;
656
657 ffprobeSettings.processPath = ffprobePath;
658 ffprobeSettings.storeOutput = true;
659 ffprobeSettings.defaultPrependArgs.clear();
660
661 ffprobeSettings.args << "-hide_banner"
662 << "-v" << "warning"
663 << "-of" << "json=compact=1"
664 << "-show_format"
665 << "-show_streams"
666 << "-i" << inputFile;
667 startNonBlocking(ffprobeSettings);
669
670 QString ffprobeSTDOUT = m_processSTDOUT;
671 QString ffprobeSTDERR = m_processSTDERR;
672
673 QJsonDocument ffprobeJsonDoc = QJsonDocument::fromJson(ffprobeSTDOUT.toUtf8());
674 QJsonObject ffprobeJsonObj;
675
676 if (ffprobeJsonDoc.isNull() || !ffprobeJsonDoc.isObject()) {
677 ffprobeJsonObj["error"] = FFProbeErrorCodes::INVALID_JSON;
678 return ffprobeJsonObj;
679 }
680
681 ffprobeJsonObj = ffprobeJsonDoc.object();
682
683 const bool hasValidStreams = ffprobeCheckStreamsValid(ffprobeJsonObj, ffprobeSTDERR);
684 ffprobeJsonObj["error"] = hasValidStreams ? FFProbeErrorCodes::NONE : FFProbeErrorCodes::UNSUPPORTED_CODEC;
685
686
687 return ffprobeJsonObj;
688}
@ INVALID_JSON
bool ffprobeCheckStreamsValid(const QJsonObject &ffprobeJsonObj, const QString &ffprobeSTDERR)
ffprobeCheckStreamsValid

References KisFFMpegWrapperSettings::args, KisFFMpegWrapperSettings::defaultPrependArgs, ffprobeCheckStreamsValid(), INVALID_JSON, m_processSTDERR, m_processSTDOUT, NONE, KisFFMpegWrapperSettings::processPath, startNonBlocking(), KisFFMpegWrapperSettings::storeOutput, UNSUPPORTED_CODEC, and waitForFinished().

◆ ffprobeCheckStreamsValid()

bool KisFFMpegWrapper::ffprobeCheckStreamsValid ( const QJsonObject & ffprobeJsonObj,
const QString & ffprobeSTDERR )
private

ffprobeCheckStreamsValid

Parameters
ffprobeJsonObjJsonObject resulting from ffprobe process.
ffprobeSTDERRSTDERR output from ffprobe.
Returns
bool -> Whether we support a video format based on it's internal streams.

Definition at line 229 of file KisFFMpegWrapper.cpp.

230{
231 // Sanity checks..
232 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(ffprobeJsonObj.contains("streams"), false);
233
234 QRegularExpression invalidStreamRX("(?:Unsupported codec with id .+? for input stream|Could not find codec parameters for stream) ([0-9]+)");
235 QRegularExpressionMatchIterator invalidStreamMatchList = invalidStreamRX.globalMatch(ffprobeSTDERR);
236
237 while (invalidStreamMatchList.hasNext()) {
238 QRegularExpressionMatch invalidStreamMatch = invalidStreamMatchList.next();
239
240 if (invalidStreamMatch.hasMatch()) {
241 const int invalidStreamId = invalidStreamMatch.captured(1).toInt();
242
243 // We make sure we're talking about video streams here.
244 if (ffprobeJsonObj["streams"].toArray()[invalidStreamId].toObject()["codec_type"] == "video") {
245 return false;
246 }
247 }
248 }
249
250 return true;
251}
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129

References KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE.

◆ findFFMpeg()

QJsonObject KisFFMpegWrapper::findFFMpeg ( const QString & customLocation)
static

Definition at line 643 of file KisFFMpegWrapper.cpp.

644{
645 return findProcessPath("ffmpeg", customLocation, true);
646}
static QJsonObject findProcessPath(const QString &processName, const QString &customLocation, bool processInfo)

References findProcessPath().

◆ findFFProbe()

QJsonObject KisFFMpegWrapper::findFFProbe ( const QString & customLocation)
static

Definition at line 648 of file KisFFMpegWrapper.cpp.

649{
650 return findProcessPath("ffprobe", customLocation, false);
651}

References findProcessPath().

◆ findProcessInfo()

QJsonObject KisFFMpegWrapper::findProcessInfo ( const QString & processName,
const QString & processPath,
bool includeProcessInfo )
static

Definition at line 522 of file KisFFMpegWrapper.cpp.

523{
524
525 QJsonObject ffmpegInfo {{"path", rawProcessPath},
526 {"enabled",false},
527 {"version", "0"},
528 {"encoder",QJsonValue::Object},
529 {"decoder",QJsonValue::Object}};
530
531 if (!QFile::exists(rawProcessPath)) return ffmpegInfo;
532
533 const QString processPath = QFileInfo(rawProcessPath).absoluteFilePath();
534 ffmpegInfo["path"] = processPath;
535
536 dbgFile << "Found process at:" << processPath;
537 QString processVersion = KisFFMpegWrapper::runProcessAndReturn(processPath, QStringList() << "-version", FFMPEG_TIMEOUT);
538
539 if (!processVersion.isEmpty()) {
540
541 QRegularExpressionMatch versionMatch = ffmpegVersionRX.match(processVersion);
542
543 if (versionMatch.hasMatch() && versionMatch.captured(1) == processName ) {
544 ffmpegInfo["version"] = versionMatch.captured(2);
545 dbgFile << "found version" << ffmpegInfo.value("version").toString();
546 ffmpegInfo["enabled"] = true;
547 }
548
549 if (!includeProcessInfo || !ffmpegInfo["enabled"].toBool()) return ffmpegInfo;
550
551 QString processCodecs = KisFFMpegWrapper::runProcessAndReturn(processPath, QStringList() << "-codecs", FFMPEG_TIMEOUT);
552
553 QJsonObject codecsJson {};
554
555 {
556 // For regular expression advice, check out https://regexr.com/.
557 // Beware: We need double backslashes here for C++, in regular regex it would be a single backslash instead.
558 QRegularExpression ffmpegCodecsRX("(D|\\.)(E|\\.)....\\s+(.+?)\\s+([^\\r\\n]*)");
559 QRegularExpressionMatchIterator codecsMatchList = ffmpegCodecsRX.globalMatch(processCodecs);
560
561 // Find out codec types.. (e.g. H264, VP9, etc)
562 while (codecsMatchList.hasNext()) {
563 QRegularExpressionMatch codecsMatch = codecsMatchList.next();
564
565 if (codecsMatch.hasMatch()) {
566 QJsonObject codecInfoJson {};
567
568 bool encodingSupported = codecsMatch.captured(2) == "E" ? true:false;
569 bool decodingSupported = codecsMatch.captured(1) == "D" ? true:false;
570 QString codecName = codecsMatch.captured(3);
571 QString codecRemainder = codecsMatch.captured(4);
572
573 codecInfoJson.insert("encoding", encodingSupported);
574 codecInfoJson.insert("decoding", decodingSupported);
575
576
577 // Regular expression for grouping specific encoders and decoders..
578 QRegularExpression ffmpegSpecificCodecRX("\\(decoders:(.+?)\\)|\\(encoders:(.+?)\\)");
579 QRegularExpressionMatchIterator specificCodecMatchList = ffmpegSpecificCodecRX.globalMatch(codecRemainder);
580
581 QJsonArray encodersList;
582 QJsonArray decodersList;
583
584 while (specificCodecMatchList.hasNext()) {
585 QRegularExpressionMatch specificCodecMatch = specificCodecMatchList.next();
586 if (specificCodecMatch.hasMatch()) {
587 // Add specific decoders..
588 QStringList decoders = specificCodecMatch.captured(1).split(" ");
589 Q_FOREACH(const QString& string, decoders) {
590 if (!string.isEmpty()) {
591 decodersList.push_back(string);
592 }
593 }
594
595 // Add specific encoders.. (e.g. for h264: h264_vaapi, libopenh264, etc )
596 QStringList encoders = specificCodecMatch.captured(2).split(" ");
597 Q_FOREACH(const QString& string, encoders) {
598 if (!string.isEmpty()) {
599 encodersList.push_back(string);
600 }
601 }
602 }
603 }
604
605 codecInfoJson.insert("encoders", encodersList);
606 codecInfoJson.insert("decoders", decodersList);
607 codecsJson.insert(codecName, codecInfoJson);
608 }
609 }
610 }
611
612 ffmpegInfo.insert("codecs", codecsJson);
613
614
615 dbgFile << "codec support:" << ffmpegInfo;
616 } else {
617 dbgFile << "Not a valid process at:" << processPath;
618 }
619
620 return ffmpegInfo;
621
622}
const int FFMPEG_TIMEOUT
QList< QString > QStringList
static QByteArray runProcessAndReturn(const QString &processPath, const QStringList &args, int msecs=FFMPEG_TIMEOUT)

References dbgFile, FFMPEG_TIMEOUT, and runProcessAndReturn().

◆ findProcessPath()

QJsonObject KisFFMpegWrapper::findProcessPath ( const QString & processName,
const QString & customLocation,
bool processInfo )
static

Definition at line 449 of file KisFFMpegWrapper.cpp.

450{
451 QJsonObject resultJsonObj;
452 QStringList proposedPaths;
453
454 // User-specified..
455 if (!customLocation.isEmpty()) {
456 proposedPaths << customLocation;
457 proposedPaths << customLocation + '/' + processName;
458 }
459
460 // Krita-bundled..
461 proposedPaths << KoResourcePaths::getApplicationRoot() + '/' + "bin" + '/' + processName;
462
463 // OS-specific..
464#ifdef Q_OS_WIN
465 // Look for winget-installed ffmpeg packages in C:\users\USERNAME\appdata\local\microsoft\winget\packages\FFMPEGVERSION\bin
466 QDir wingetPackageDir = QDir(QDir::homePath() + "/AppData/Local/Microsoft/WinGet/Packages/");
467 QStringList wingetPackageFolderNames = wingetPackageDir.entryList();
468 for (const auto &folderName : wingetPackageFolderNames) {
469 if (folderName.contains("ffmpeg", Qt::CaseInsensitive)) {
470 QDir ffmpegPackageFolder = QDir(wingetPackageDir.path() + "/" + folderName);
471 QStringList ffmpegRootNames = ffmpegPackageFolder.entryList();
472 for (const auto &ffmpegRootName : ffmpegRootNames) {
473 proposedPaths << ffmpegPackageFolder.path() + "/" + ffmpegRootName + "/bin/";
474 }
475 }
476 }
477#endif
478
479#ifdef Q_OS_MACOS
480 proposedPaths << QCoreApplication::applicationDirPath() + '/' + processName;
481#endif
482
483#ifndef Q_OS_WIN
484 proposedPaths << QDir::homePath() + "/bin/" + processName;
485 proposedPaths << "/usr/bin/" + processName;
486 proposedPaths << "/usr/local/bin/" + processName;
487#endif
488
489 dbgFile << proposedPaths;
490 for (int i = 0; i != proposedPaths.size(); ++i) {
491 if (proposedPaths[i].isEmpty()) continue;
492
493#ifdef Q_OS_WIN
494 proposedPaths[i] = QDir::toNativeSeparators(QDir::cleanPath(proposedPaths[i]));
495
496 if (proposedPaths[i].endsWith('/')) {
497 continue;
498 }
499
500 if (!proposedPaths[i].endsWith(".exe", Qt::CaseInsensitive)) {
501 if (!QFile::exists(proposedPaths[i])) {
502 proposedPaths[i] += ".exe";
503 if (!QFile::exists(proposedPaths[i])) {
504 continue;
505 }
506 }
507 }
508#endif
509
510 QJsonObject processInfoJsonObj = findProcessInfo(processName, proposedPaths[i], includeProcessInfo);
511 dbgFile << "PATH" << proposedPaths[i] << processInfoJsonObj.value("enabled").toBool();
512 if (processInfoJsonObj.value("enabled").toBool()) {
513 processInfoJsonObj["custom"]=(!customLocation.isEmpty() && i <= 1) ? true:false;
514 resultJsonObj = processInfoJsonObj;
515 break;
516 }
517 }
518
519 return resultJsonObj;
520}
static QJsonObject findProcessInfo(const QString &processName, const QString &processPath, bool includeProcessInfo)
static QString getApplicationRoot()

References dbgFile, findProcessInfo(), and KoResourcePaths::getApplicationRoot().

◆ fixUpNonEmbeddedProcessEnvironment()

void KisFFMpegWrapper::fixUpNonEmbeddedProcessEnvironment ( const QString & processPath,
QProcess & process )
staticprivate

Definition at line 369 of file KisFFMpegWrapper.cpp.

370{
371#ifdef Q_OS_LINUX
372
381 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
382
383 const QStringList libraryPaths = env.value("LD_LIBRARY_PATH").split(':');
384 const QString processAbsPath = QFileInfo(QFileInfo(processPath).absolutePath() + "/../").absoluteFilePath();
385
386 bool isCustomBuildOfFFmpeg = false;
387
388 Q_FOREACH (const QString &path, libraryPaths) {
389 const QString absPath1 = QFileInfo(path + "/").absoluteFilePath();
390 const QString absPath2 = QFileInfo(path + "/../").absoluteFilePath();
391
392 if (absPath1 == processAbsPath || absPath2 == processAbsPath) {
393 dbgFile << "Detected embedded ffmpeg:" << processPath;
394 dbgFile << " " << ppVar(processAbsPath);
395 dbgFile << " " << ppVar(absPath1);
396 dbgFile << " " << ppVar(absPath2);
397
398 isCustomBuildOfFFmpeg = true;
399
400 break;
401 }
402 }
403
404 if (!isCustomBuildOfFFmpeg) {
405 dbgFile << "Removing LD_LIBRARY_PATH for running" << processPath;
406
407 env.remove("LD_LIBRARY_PATH");
408 process.setProcessEnvironment(env);
409 }
410#else
411 Q_UNUSED(processPath);
412 Q_UNUSED(process);
413#endif /* Q_OS_LINUX */
414}
#define ppVar(var)
Definition kis_debug.h:155

References dbgFile, and ppVar.

◆ getSupportedCodecs()

QStringList KisFFMpegWrapper::getSupportedCodecs ( const QJsonObject & ffmpegJsonProcessInput)
static

Definition at line 624 of file KisFFMpegWrapper.cpp.

624 {
625 // TODO: I really don't like having to deal with JSON in C++ code as frequently as we are here.
626 // We should make a proper datatype for FFMPEG Process Information!
627
628 QStringList encodersToReturn = {};
629
630 // For now, I'm just treating this as strictly typed using KIS_SAFE_ASSERT_RECOVER_RETURN
631 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(ffmpegProcessInfo["enabled"].toBool(), encodersToReturn);
632
633 QJsonObject encoders = ffmpegProcessInfo["codecs"].toObject();
634 Q_FOREACH( const QString& key, encoders.keys()) {
635 if (encoders[key].toObject()["encoding"].toBool()) {
636 encodersToReturn << key;
637 }
638 }
639
640 return encodersToReturn;
641}

References KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE.

◆ reset()

void KisFFMpegWrapper::reset ( )

Definition at line 253 of file KisFFMpegWrapper.cpp.

254{
255 if (m_process == nullptr)
256 return;
257
258 m_process->disconnect(this);
259 if (m_process->state() != QProcess::NotRunning) {
260 m_process->kill();
261 }
262 m_process.reset();
263}
QScopedPointer< QProcess > m_process

References m_process.

◆ runProcessAndReturn()

QByteArray KisFFMpegWrapper::runProcessAndReturn ( const QString & processPath,
const QStringList & args,
int msecs = FFMPEG_TIMEOUT )
static

Definition at line 416 of file KisFFMpegWrapper.cpp.

417{
418 QProcess runProcess;
419
420 fixUpNonEmbeddedProcessEnvironment(processPath, runProcess);
421
422 runProcess.start(processPath, args);
423
424 if (runProcess.waitForStarted(msecs)) runProcess.waitForFinished(msecs);
425
426 const bool successfulStart =
427 runProcess.state() == QProcess::NotRunning &&
428 runProcess.error() == QProcess::UnknownError;
429
430 dbgFile << "runProcessAndReturn Success:" << successfulStart;
431
432 if (successfulStart) return runProcess.readAllStandardOutput();
433
434 return "";
435}
static void fixUpNonEmbeddedProcessEnvironment(const QString &processPath, QProcess &process)

References dbgFile, and fixUpNonEmbeddedProcessEnvironment().

◆ setConfiguredFFMpegLocation()

void KisFFMpegWrapper::setConfiguredFFMpegLocation ( QString & location)
static

Definition at line 443 of file KisFFMpegWrapper.cpp.

444{
445 KisConfig cfg(false);
446 cfg.setFFMpegLocation(location);
447}

References KisConfig::setFFMpegLocation().

◆ sigFinished

void KisFFMpegWrapper::sigFinished ( )
signal

◆ sigFinishedWithError

void KisFFMpegWrapper::sigFinishedWithError ( QString message)
signal

◆ sigProgressUpdated

void KisFFMpegWrapper::sigProgressUpdated ( int frameNo)
signal

◆ sigReadLine

void KisFFMpegWrapper::sigReadLine ( int pipe,
QString line )
signal

◆ sigReadSTDERR

void KisFFMpegWrapper::sigReadSTDERR ( QByteArray stderrBuffer)
signal

◆ sigReadSTDOUT

void KisFFMpegWrapper::sigReadSTDOUT ( QByteArray stdoutBuffer)
signal

◆ sigStarted

void KisFFMpegWrapper::sigStarted ( )
signal

◆ slotFinished

void KisFFMpegWrapper::slotFinished ( int exitCode)
privateslot

Definition at line 350 of file KisFFMpegWrapper.cpp.

351{
352 dbgFile << "FFMpeg finished with code" << exitCode;
354 m_progress->setValue(100);
355 }
356
357 if (exitCode != 0) {
358 m_errorMessage.remove(junkRegex);
359 if (m_process->exitStatus() == QProcess::CrashExit) {
360 m_errorMessage = i18n("FFMpeg Crashed") % "\n" % m_errorMessage;
361 }
362
364 } else {
365 Q_EMIT sigFinished();
366 }
367}
KisFFMpegWrapperSettings m_processSettings
QSharedPointer< QProgressDialog > m_progress
void sigFinishedWithError(QString message)

References KisFFMpegWrapperSettings::batchMode, dbgFile, m_errorMessage, m_process, m_processSettings, m_progress, sigFinished(), and sigFinishedWithError().

◆ slotReadyReadSTDERR

void KisFFMpegWrapper::slotReadyReadSTDERR ( )
privateslot

Definition at line 265 of file KisFFMpegWrapper.cpp.

266{
267 QByteArray stderrRawBuffer = m_process->readAllStandardError();
268
269 Q_EMIT sigReadSTDERR(stderrRawBuffer);
270 m_stderrBuffer += stderrRawBuffer;
271
272 int frameNo = -1;
273 int startPos = 0;
274 int endPos = 0;
275
276 while ((endPos = m_stderrBuffer.indexOf(lineDelimiter, startPos)) != -1) {
277 const QString &line = m_stderrBuffer.mid(startPos, endPos - startPos).trimmed();
278
280
281 Q_EMIT sigReadLine(2,line);
282
283 for (const QString &word : errorWords) {
284 if (line.contains(word)) {
285 m_errorMessage += line % "\n";
286 break;
287 }
288 }
289
290 const QRegularExpressionMatch &match = frameRegexp.match(line);
291
292 if (match.hasMatch()) {
293 frameNo = match.captured(1).toInt();
294 }
295
296
297 dbgFile << "ffmpeg stderr:" << line;
298 startPos = endPos + 1;
299 }
300
301 m_stderrBuffer.remove(0, startPos);
302
303 if (frameNo != -1) {
304 updateProgressDialog(frameNo);
305 Q_EMIT sigProgressUpdated(frameNo);
306 }
307
308}
void updateProgressDialog(int progressValue)
void sigProgressUpdated(int frameNo)
void sigReadLine(int pipe, QString line)
void sigReadSTDERR(QByteArray stderrBuffer)

References dbgFile, m_errorMessage, m_process, m_processSettings, m_processSTDERR, m_stderrBuffer, sigProgressUpdated(), sigReadLine(), sigReadSTDERR(), KisFFMpegWrapperSettings::storeOutput, and updateProgressDialog().

◆ slotReadyReadSTDOUT

void KisFFMpegWrapper::slotReadyReadSTDOUT ( )
privateslot

Definition at line 310 of file KisFFMpegWrapper.cpp.

311{
312 QByteArray stdoutRawBuffer = m_process->readAllStandardOutput();
313
314 Q_EMIT sigReadSTDOUT(stdoutRawBuffer);
315 m_stdoutBuffer += stdoutRawBuffer;
316
317
319 if (m_processSettings.storeOutput) m_processSTDOUT += stdoutRawBuffer;
320 } else {
321
322 int startPos = 0;
323 int endPos = 0;
324 QString str;
325
326 if (m_processSettings.storeOutput) m_processSTDOUT += stdoutRawBuffer + "\n";
327
328 // ffmpeg splits normal lines by '\n' and progress data lines by '\r'
329 while ((endPos = m_stdoutBuffer.indexOf(lineDelimiter, startPos)) != -1) {
330 const QString &line = m_stdoutBuffer.mid(startPos, endPos - startPos).trimmed();
331
332 dbgFile << "ffmpeg stdout:" << line;
333 Q_EMIT sigReadLine(1,line);
334 startPos = endPos + 1;
335 }
336
337 m_stdoutBuffer.remove(0, startPos);
338 }
339
340
341}
void sigReadSTDOUT(QByteArray stdoutBuffer)

References KisFFMpegWrapperSettings::binaryOutput, dbgFile, m_process, m_processSettings, m_processSTDOUT, m_stdoutBuffer, sigReadLine(), sigReadSTDOUT(), and KisFFMpegWrapperSettings::storeOutput.

◆ slotStarted

void KisFFMpegWrapper::slotStarted ( )
privateslot

Definition at line 343 of file KisFFMpegWrapper.cpp.

344{
345 dbgFile << "ffmpeg process started!";
346
347 Q_EMIT sigStarted();
348}

References dbgFile, and sigStarted().

◆ start()

KisImportExportErrorCode KisFFMpegWrapper::start ( const KisFFMpegWrapperSettings & settings)

Definition at line 163 of file KisFFMpegWrapper.cpp.

164{
165 struct ProcessResults {
166 bool finish = false;
167 QString error = QString();
168 };
169
170 QSharedPointer<ProcessResults> processResults = toQShared(new ProcessResults);
171
172 connect( this, &KisFFMpegWrapper::sigFinishedWithError, [processResults](const QString& errMsg){
173 processResults->finish = true;
174 processResults->error = errMsg;
175 });
176
177 connect( this, &KisFFMpegWrapper::sigFinished, [processResults](){
178 processResults->finish = true;
179 });
180
181 startNonBlocking(settings);
183
184 if (processResults->finish == true) {
185 if (processResults->error.isEmpty()) {
187 } else {
189 }
190 } else {
191 reset(); // Ensure process isn't running before returning failure here.
193 }
194}
QSharedPointer< T > toQShared(T *ptr)

References ImportExportCodes::Failure, FFMPEG_TIMEOUT, ImportExportCodes::OK, reset(), sigFinished(), sigFinishedWithError(), startNonBlocking(), toQShared(), and waitForFinished().

◆ startNonBlocking()

void KisFFMpegWrapper::startNonBlocking ( const KisFFMpegWrapperSettings & settings)

Definition at line 46 of file KisFFMpegWrapper.cpp.

47{
48 KIS_ASSERT(m_process == nullptr);
49
50 m_stdoutBuffer.clear();
51 m_errorMessage.clear();
52 m_processSTDOUT.clear();
53 m_processSTDERR.clear();
54
55 m_process.reset(new QProcess(this));
56 QStringList env(m_process->systemEnvironment());
57
58 m_processSettings = settings;
59
60 const QString renderLogPath = m_processSettings.outputFile + ".log";
61
62 // Create a new log per each ffmpeg operation..
63 if (QFile::exists(renderLogPath)) {
64 QFile existingFile(renderLogPath);
65 existingFile.remove();
66 }
67
68 QFile renderLog(renderLogPath); // Logs ONLY this render operation.
69
70 // Log ffmpeg command info..
71 if (renderLog.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
72 QString command = m_processSettings.processPath + " " + settings.defaultPrependArgs.join(" ") + " " + m_processSettings.args.join(" ") + " " + m_processSettings.outputFile;
73 renderLog.write(command.toUtf8());
74 renderLog.write("\n");
75 renderLog.write("=====================================================\n");
76
77 // Handle logged errors..
78 // Due to various reasons (including image preview), ffmpeg uses STDERR and not STDOUT for general output logging.
79 connect(this, &KisFFMpegWrapper::sigReadSTDERR, [renderLogPath](QByteArray stderrBuffer){
80 QFile renderLog(renderLogPath);
81 if (renderLog.open(QIODevice::WriteOnly | QIODevice::Append)) {
82 renderLog.write(stderrBuffer);
83 }
84 });
85 }
86
87 if (!settings.logPath.isEmpty()) {
88 QString sessionRenderLogPath(settings.logPath);
89 // Logs FULL history of Krita session render operations.
90 QFile sessionRenderLog(sessionRenderLogPath);
91
92 // Make directory..
93 const QString logDirPath = QFileInfo(sessionRenderLogPath).dir().path();
94 QDir().mkpath(logDirPath);
95
96 if (sessionRenderLog.open(QIODevice::WriteOnly | QIODevice::Append)) {
97 // Append finished renderlog to sessionlog..
98 connect(this, &KisFFMpegWrapper::sigFinishedWithError, [renderLogPath, sessionRenderLogPath](QString){
99 QFile renderLog(renderLogPath);
100 QFile sessionRenderLog(sessionRenderLogPath);
101 if (renderLog.open(QIODevice::ReadOnly) &&
102 sessionRenderLog.open(QIODevice::WriteOnly | QIODevice::Append)) {
103
104 QByteArray buffer;
105 int chunksize = 256;
106 while (!(buffer = renderLog.read(chunksize)).isEmpty()) {
107 sessionRenderLog.write(buffer);
108 }
109 }
110 });
111 }
112 }
113
115 QString progressText = m_processSettings.progressMessage;
116
117 progressText.replace("[progress]", "0");
118
119 m_progress = toQShared(new QProgressDialog(progressText, "", 0, 0));
120
121 m_progress->setWindowModality(Qt::ApplicationModal);
122 m_progress->setCancelButton(0);
123 m_progress->setMinimumDuration(0);
124 m_progress->setValue(0);
125
126 if (settings.progressIndeterminate) {
127 m_progress->setRange(0,0);
128 } else {
129 m_progress->setRange(0, 100);
130 }
131
132 connect(m_progress.data(), SIGNAL(canceled()), m_process.data(), SLOT(kill()));
133
134 m_progress->show();
135
136 dbgFile << "Open progress dialog!";
137 }
138
139 connect(m_process.data(), SIGNAL(readyReadStandardOutput()), SLOT(slotReadyReadSTDOUT()));
140 connect(m_process.data(), SIGNAL(readyReadStandardError()), SLOT(slotReadyReadSTDERR()));
141 connect(m_process.data(), SIGNAL(started()), SLOT(slotStarted()));
142 connect(m_process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(slotFinished(int)));
143
144 QStringList args;
145
146 if ( !settings.defaultPrependArgs.isEmpty() ) {
147 args << settings.defaultPrependArgs;
148 }
149
150 args << settings.args;
151
152 if ( !settings.outputFile.isEmpty() ) {
153 args << settings.outputFile;
154 }
155
156 dbgFile << "starting process: " << qUtf8Printable(settings.processPath) << args;
157
159
160 m_process->start(settings.processPath, args);
161}
void slotFinished(int exitCode)
#define KIS_ASSERT(cond)
Definition kis_assert.h:33

References KisFFMpegWrapperSettings::args, KisFFMpegWrapperSettings::batchMode, dbgFile, KisFFMpegWrapperSettings::defaultPrependArgs, fixUpNonEmbeddedProcessEnvironment(), KIS_ASSERT, KisFFMpegWrapperSettings::logPath, m_errorMessage, m_process, m_processSettings, m_processSTDERR, m_processSTDOUT, m_progress, m_stdoutBuffer, KisFFMpegWrapperSettings::outputFile, KisFFMpegWrapperSettings::processPath, KisFFMpegWrapperSettings::progressIndeterminate, KisFFMpegWrapperSettings::progressMessage, sigFinishedWithError(), sigReadSTDERR(), slotFinished(), slotReadyReadSTDERR(), slotReadyReadSTDOUT(), slotStarted(), and toQShared().

◆ transferCharacteristicsFromName()

TransferCharacteristics KisFFMpegWrapper::transferCharacteristicsFromName ( QString name)
static

Definition at line 830 of file KisFFMpegWrapper.cpp.

831{
832 if (name == "bt709") {
833 return TRC_ITU_R_BT_709_5;
834 }
835 if (name == "gamma22") {
837 }
838 if (name == "gamma28") {
840 }
841 if (name == "smpte170m") {
842 return TRC_ITU_R_BT_601_6;
843 }
844 if (name == "smpte240m") {
845 return TRC_SMPTE_240M;
846 }
847 if (name == "linear") {
848 return TRC_LINEAR;
849 }
850 if (name == "log" || name == "log100") {
851 return TRC_LOGARITHMIC_100;
852 }
853 if (name == "log316" || name == "log_sqrt") {
855 }
856 if (name == "iec61966_2_4" || name == "iec61966-2-4") {
857 return TRC_IEC_61966_2_4;
858 }
859 if (name.startsWith("bt1361")) {
860 return TRC_ITU_R_BT_1361;
861 }
862 if (name == "iec61966_2_1" || name == "iec61966-2-1") {
863 return TRC_IEC_61966_2_1;
864 }
865 if (name.startsWith("bt2020_10")) {
867 }
868 if (name.startsWith("bt2020_12")) {
870 }
871 if (name == "smpte2084") {
873 }
874 if (name == "smpte240m") {
875 return TRC_SMPTE_240M;
876 }
877 if (name.startsWith("smpte428")) {
878 return TRC_SMPTE_ST_428_1;
879 }
880 if (name == "arib-std-b67") {
882 }
883
884 return TRC_UNSPECIFIED;
885}
@ TRC_IEC_61966_2_4
@ TRC_ITU_R_BT_2020_2_10bit
@ TRC_LOGARITHMIC_100
@ TRC_ITU_R_BT_470_6_SYSTEM_M
@ TRC_ITU_R_BT_470_6_SYSTEM_B_G
@ TRC_ITU_R_BT_1361
@ TRC_ITU_R_BT_2100_0_HLG
@ TRC_ITU_R_BT_2100_0_PQ
@ TRC_ITU_R_BT_601_6
@ TRC_IEC_61966_2_1
@ TRC_ITU_R_BT_709_5
@ TRC_SMPTE_ST_428_1
@ TRC_LOGARITHMIC_100_sqrt10
@ TRC_ITU_R_BT_2020_2_12bit

References TRC_IEC_61966_2_1, TRC_IEC_61966_2_4, TRC_ITU_R_BT_1361, TRC_ITU_R_BT_2020_2_10bit, TRC_ITU_R_BT_2020_2_12bit, TRC_ITU_R_BT_2100_0_HLG, TRC_ITU_R_BT_2100_0_PQ, TRC_ITU_R_BT_470_6_SYSTEM_B_G, TRC_ITU_R_BT_470_6_SYSTEM_M, TRC_ITU_R_BT_601_6, TRC_ITU_R_BT_709_5, TRC_LINEAR, TRC_LOGARITHMIC_100, TRC_LOGARITHMIC_100_sqrt10, TRC_SMPTE_240M, TRC_SMPTE_ST_428_1, and TRC_UNSPECIFIED.

◆ updateProgressDialog()

void KisFFMpegWrapper::updateProgressDialog ( int progressValue)
private

Definition at line 207 of file KisFFMpegWrapper.cpp.

207 {
208
209 dbgFile << "Update Progress" << progressValue << "/" << m_processSettings.totalFrames;
210
211 if (!m_progress) return;
212
213 QString progressText = m_processSettings.progressMessage;
214 QStringList outputFileParts = m_processSettings.outputFile.split(".");
215 QString suffix = outputFileParts.size() == 2 ? outputFileParts[1] : m_processSettings.outputFile;
216
217 progressText.replace("[progress]", QString::number(progressValue));
218 progressText.replace("[framecount]", QString::number(m_processSettings.totalFrames));
219 progressText.replace("[suffix]", suffix );
220 m_progress->setLabelText(progressText);
221
222 if (m_processSettings.totalFrames > 0) m_progress->setValue(100 * progressValue / m_processSettings.totalFrames);
223
224 if (m_process && m_process->state() == QProcess::Running) {
225 QApplication::processEvents();
226 }
227}

References dbgFile, m_process, m_processSettings, m_progress, KisFFMpegWrapperSettings::outputFile, KisFFMpegWrapperSettings::progressMessage, and KisFFMpegWrapperSettings::totalFrames.

◆ waitForFinished()

bool KisFFMpegWrapper::waitForFinished ( int msecs = FFMPEG_TIMEOUT)

Definition at line 196 of file KisFFMpegWrapper.cpp.

197{
198 if (!m_process) return false;
199
200 if (m_process->waitForStarted(msecs)) {
201 return (m_process->waitForFinished(msecs) && m_process->exitStatus() == QProcess::ExitStatus::NormalExit);
202 }
203
204 return false;
205}

References m_process.

Member Data Documentation

◆ m_errorMessage

QString KisFFMpegWrapper::m_errorMessage
private

Definition at line 108 of file KisFFMpegWrapper.h.

◆ m_process

QScopedPointer<QProcess> KisFFMpegWrapper::m_process
private

Definition at line 102 of file KisFFMpegWrapper.h.

◆ m_processSettings

KisFFMpegWrapperSettings KisFFMpegWrapper::m_processSettings
private

Definition at line 104 of file KisFFMpegWrapper.h.

◆ m_processSTDERR

QString KisFFMpegWrapper::m_processSTDERR
private

Definition at line 111 of file KisFFMpegWrapper.h.

◆ m_processSTDOUT

QByteArray KisFFMpegWrapper::m_processSTDOUT
private

Definition at line 110 of file KisFFMpegWrapper.h.

◆ m_progress

QSharedPointer<QProgressDialog> KisFFMpegWrapper::m_progress = nullptr
private

Definition at line 103 of file KisFFMpegWrapper.h.

◆ m_stderrBuffer

QString KisFFMpegWrapper::m_stderrBuffer
private

Definition at line 107 of file KisFFMpegWrapper.h.

◆ m_stdoutBuffer

QString KisFFMpegWrapper::m_stdoutBuffer
private

Definition at line 106 of file KisFFMpegWrapper.h.


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