File indexing completed on 2025-01-19 03:57:00
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2017-07-04 0007 * Description : FFmpeg launcher to encode frames as video 0008 * 0009 * SPDX-FileCopyrightText: 2017-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "ffmpeglauncher.h" 0016 0017 // C++ includes 0018 0019 #include <cmath> 0020 0021 // Qt includes 0022 0023 #include <QEventLoop> 0024 #include <QDateTime> 0025 #include <QStringList> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "digikam_version.h" 0031 0032 namespace Digikam 0033 { 0034 0035 FFmpegLauncher::FFmpegLauncher(QObject* const parent) 0036 : ProcessLauncher(parent) 0037 { 0038 } 0039 0040 FFmpegLauncher::~FFmpegLauncher() 0041 { 0042 } 0043 0044 void FFmpegLauncher::setSettings(VidSlideSettings* const settings) 0045 { 0046 m_settings = settings; 0047 } 0048 0049 void FFmpegLauncher::encodeFrames() 0050 { 0051 // Run FFmpeg CLI to encode temporary JPEG frames 0052 // https://shotstack.io/learn/use-ffmpeg-to-convert-images-to-video/ 0053 // ffmpeg -f concat -i fileslist.txt -b:v 200000 -r 25 -vcodec mpeg4 -y output.mp4 0054 0055 qCDebug(DIGIKAM_GENERAL_LOG) << "Start encoding with FFmpeg"; 0056 0057 setWorkingDirectory(m_settings->outputDir); 0058 setProgram(m_settings->ffmpegPath); 0059 QStringList args; 0060 0061 // Frames input 0062 0063 args << QLatin1String("-f") 0064 << QLatin1String("concat") 0065 << QLatin1String("-i") 0066 << m_settings->filesList; // File list of frames to encode. 0067 0068 // Audio input 0069 0070 if (!m_settings->audioTrack.isEmpty()) 0071 { 0072 args << QLatin1String("-stream_loop") 0073 << QLatin1String("-1") 0074 << QLatin1String("-i") 0075 << m_settings->audioTrack // Audio file to use as soundtrack. 0076 << QLatin1String("-c:a") 0077 << QLatin1String("copy") // Do not reencode 0078 << QLatin1String("-shortest"); 0079 } 0080 0081 // Metadata (not supported by all media containers - work fine with MP4) 0082 0083 args << QLatin1String("-metadata") 0084 << QString::fromLatin1("date=\"%1\"").arg(QDateTime::currentDateTime().toString(Qt::ISODate)) 0085 << QLatin1String("-metadata") 0086 << QString::fromLatin1("description=\"Encoded with digiKam VideoSlideShow tool version %1\"").arg(digiKamVersion()); 0087 0088 // Egualize video luminosity. 0089 // https://ffmpeg.org/ffmpeg-filters.html#tmidequalizer 0090 0091 if (m_settings->equalize) 0092 { 0093 args << QLatin1String("-vf") 0094 << QString::fromLatin1("tmidequalizer=sigma=%1") 0095 .arg(m_settings->strength / 10.0); 0096 } 0097 0098 // Video encoding 0099 0100 args << QLatin1String("-b:v") // Video bits-rate/ 0101 << QString::number(m_settings->videoBitRate()) 0102 << QLatin1String("-r") // Video frames-rate. 0103 << QString::number(ceil(m_settings->videoFrameRate())) 0104 << QLatin1String("-vcodec") // Video codec. 0105 << m_settings->videoCodec() 0106 << QLatin1String("-y") // Overwrite target. 0107 << m_settings->outputFile; // Target video stream. 0108 0109 setArguments(args); 0110 startProcess(); 0111 } 0112 0113 QMap<QString, QString> FFmpegLauncher::supportedCodecs() 0114 { 0115 qCDebug(DIGIKAM_GENERAL_LOG) << "Get FFmpeg supported codecs"; 0116 0117 setConsoleTraces(false); 0118 setProgram(m_settings->ffmpegPath); 0119 setArguments(QStringList() << QLatin1String("-v") 0120 << QLatin1String("quiet") 0121 << QLatin1String("-codecs")); 0122 0123 QEventLoop loop; 0124 0125 connect(this, &ProcessLauncher::signalComplete, 0126 &loop, &QEventLoop::quit); 0127 0128 startProcess(); 0129 loop.exec(); 0130 0131 QMap<QString, QString> codecMap; // name, features 0132 QString out = output().section(QLatin1String("-------"), -1); 0133 QStringList codecs = out.split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0134 0135 Q_FOREACH (const QString& line, codecs) 0136 { 0137 QStringList sections = line.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts); 0138 0139 if (sections.size() >= 2) 0140 { 0141 codecMap.insert(sections[1], sections[0]); 0142 } 0143 } 0144 0145 return codecMap; 0146 } 0147 0148 QMap<QString, QString> FFmpegLauncher::supportedFormats() 0149 { 0150 qCDebug(DIGIKAM_GENERAL_LOG) << "Get FFmpeg supported formats"; 0151 0152 setConsoleTraces(false); 0153 setProgram(m_settings->ffmpegPath); 0154 setArguments(QStringList() << QLatin1String("-v") 0155 << QLatin1String("quiet") 0156 << QLatin1String("-formats")); 0157 0158 QEventLoop loop; 0159 0160 connect(this, &ProcessLauncher::signalComplete, 0161 &loop, &QEventLoop::quit); 0162 0163 startProcess(); 0164 loop.exec(); 0165 0166 QMap<QString, QString> formatMap; // name, features 0167 QString out = output().section(QLatin1String("--"), -1); 0168 QStringList formats = out.split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0169 0170 Q_FOREACH (const QString& line, formats) 0171 { 0172 QStringList sections = line.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts); 0173 0174 if (sections.size() >= 2) 0175 { 0176 formatMap.insert(sections[1], sections[0]); 0177 } 0178 } 0179 0180 return formatMap; 0181 } 0182 0183 QTime FFmpegLauncher::soundTrackLength(const QString& audioPath) 0184 { 0185 qCDebug(DIGIKAM_GENERAL_LOG) << "Get soundtrack length with FFmpeg"; 0186 0187 setConsoleTraces(false); 0188 setProgram(m_settings->ffmpegPath); 0189 setArguments(QStringList() << QLatin1String("-i") 0190 << audioPath); 0191 0192 QEventLoop loop; 0193 0194 connect(this, &ProcessLauncher::signalComplete, 0195 &loop, &QEventLoop::quit); 0196 0197 startProcess(); 0198 loop.exec(); 0199 0200 QStringList lines = output().split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0201 0202 Q_FOREACH (const QString& line, lines) 0203 { 0204 if (line.contains(QLatin1String("Duration"))) 0205 { 0206 QStringList sections = line.simplified().split(QLatin1Char(','), Qt::SkipEmptyParts); 0207 0208 if (!sections.isEmpty()) 0209 { 0210 QStringList sections2 = sections[0].split(QLatin1String(" "), Qt::SkipEmptyParts); 0211 0212 if (sections2.size() == 2) 0213 { 0214 return QTime::fromString(sections2[1], QLatin1String("hh:mm:ss.zz")); 0215 } 0216 } 0217 } 0218 } 0219 0220 return QTime(); 0221 } 0222 0223 } // namespace Digikam 0224 0225 #include "moc_ffmpeglauncher.cpp"