61{
62#ifdef Q_OS_ANDROID
63 Q_UNUSED(savedFilesMask);
64
67
68 QVariantMap formatPreferences;
69 if (!options.videoFormatPreferencesJson.isEmpty()) {
70 formatPreferences = QJsonDocument::fromJson(options.videoFormatPreferencesJson.toUtf8()).toVariant().toMap();
71 }
72
76 QString(),
77 format,
78 formatPreferences,
83 0,
84 0,
85 0,
86 };
87
88 QString inputDir = framesDirectory;
89 if (!inputDir.endsWith(QStringLiteral("/"))) {
90 inputDir.append(QStringLiteral("/"));
91 }
92
93 settings.
inputFiles.reserve(savedFiles.size());
94 for (const QString &savedFile : savedFiles) {
95 settings.
inputFiles.append(inputDir + savedFile);
96 }
97
98
99
100 QTemporaryFile audioFile;
103 if (!audioFiles.isEmpty()) {
104 QString audioPath = audioFiles.first().filePath();
105 QString errorMessage;
107 settings.
audioFile = audioFile.fileName();
109 } else {
111 }
112 }
113 }
114
115
116
117
118
119
120
121
122
123
124 bool batchMode = false;
126#else
127 Q_UNUSED(framesDirectory);
128 Q_UNUSED(savedFiles);
129
130 if (!QFileInfo(options.
ffmpegPath).exists()) {
133 }
134
136
138
142
143
144 const QString exportDimensions =
145 QString("scale=w=")
146 .append(QString::number(options.
width))
147 .append(":h=")
148 .append(QString::number(options.
height))
149 .append(":flags=")
151
152
154 const QFileInfo resultFileInfo(resultFile);
155 const QDir videoDir(resultFileInfo.absolutePath());
156
157 const QString suffix = resultFileInfo.suffix().toLower();
158 const QString palettePath = videoDir.filePath("KritaTempPalettegen_\%06d.png");
159
161
163
164 {
169
170 args << "-y"
171 <<
"-r" << QString::number(options.
frameRate)
172 << "-start_number" << QString::number(sequenceStart) << "-start_number_range" << "1"
173 << "-i" << savedFilesMask;
174
175 const int lavfiOptionsIndex = additionalOptionsList.indexOf("-lavfi");
176
177 if ( lavfiOptionsIndex != -1 ) {
178 complexFilterArgs << additionalOptionsList.takeAt(lavfiOptionsIndex + 1);
179
180 additionalOptionsList.removeAt( lavfiOptionsIndex );
181 }
182
184 paletteArgs <<
"-r" << QString::number(options.
frameRate)
185 << "-start_number" << QString::number(sequenceStart) << "-start_number_range" << "1"
186 << "-i" << savedFilesMask;
187
188 const int paletteOptionsIndex = additionalOptionsList.indexOf("-palettegen");
189 QString palettegenString = "palettegen";
190
191 if ( paletteOptionsIndex != -1 ) {
192 palettegenString = additionalOptionsList.takeAt(paletteOptionsIndex + 1);
193
194 additionalOptionsList.removeAt( paletteOptionsIndex );
195 }
196
198 paletteArgs << "-vf" << (exportDimensions + "," + palettegenString );
199 } else {
200 paletteArgs << "-vf" << palettegenString;
201 }
202
203 paletteArgs << "-y" << palettePath;
204
206 ffmpegArgs << "-v" << "debug"
207 << paletteArgs;
208
210 ffmpegSettings.
args = ffmpegArgs;
212
214 ffmpegSettings.
progressMessage = i18nc(
"Animation export dialog for palette exporting. arg1: file-suffix",
215 "Creating palette for %1 file format.", "[suffix]");
216 ffmpegSettings.
logPath = QDir::tempPath() + QDir::separator() +
"krita" + QDir::separator() +
"ffmpeg.log";
217
219
220 if (!result.
isOk()) {
221 return result;
222 }
223
224 if (lavfiOptionsIndex == -1) {
225 complexFilterArgs << "[0:v][1:v] paletteuse";
226 }
227
228 args << "-i" << palettePath;
229
230
231 ffmpegWrapper->reset();
232 }
233
235 if (options.
includeAudio && audioFiles.count() > 0 && audioFiles.first().exists()) {
236 QFileInfo audioFileInfo = audioFiles.first();
237 const int msecPerFrame = (1000 / animation->
framerate());
238 const int msecStart = msecPerFrame * clipRange.
start();
239 const int msecDuration = msecPerFrame * clipRange.
duration();
240
241 const QTime startTime = QTime::fromMSecsSinceStartOfDay(msecStart);
242 const QTime durationTime = QTime::fromMSecsSinceStartOfDay(msecDuration);
243 const QString ffmpegTimeFormat = QStringLiteral("H:m:s.zzz");
244
245 args << "-ss" << QLocale::c().toString(startTime, ffmpegTimeFormat);
246 args << "-t" << QLocale::c().toString(durationTime, ffmpegTimeFormat);
247 args << "-i" << audioFileInfo.absoluteFilePath();
248 }
249
250
251
253 simpleFilterArgs << exportDimensions;
254 }
255
256 if ( !complexFilterArgs.isEmpty() ) {
257 args << "-lavfi" << (!simpleFilterArgs.isEmpty() ? simpleFilterArgs.join(",").append("[0:v];"):"") + complexFilterArgs.join(";");
258 } else if ( !simpleFilterArgs.isEmpty() ) {
259 args << "-vf" << simpleFilterArgs.join(",");
260 }
261
262 args << additionalOptionsList;
263
264 dbgFile <<
"savedFilesMask" << savedFilesMask
265 << "save files offset" << sequenceStart
266 <<
"start" << QString::number(clipRange.
start())
267 <<
"duration" << clipRange.
duration();
268
269
272 ffmpegSettings.
args = args;
275 ffmpegSettings.
logPath = QDir::tempPath() + QDir::separator() +
"krita" + QDir::separator() +
"ffmpeg.log";
276 ffmpegSettings.
progressMessage = i18nc(
"Animation export dialog for tracking ffmpeg progress. arg1: file-suffix, arg2: progress frame number, arg3: totalFrameCount.",
277 "Creating desired %1 file: %2/%3 frames.", "[suffix]", "[progress]", "[framecount]");
278
279 resultOuter = ffmpegWrapper->start(ffmpegSettings);
280 }
281
282
283 return resultOuter;
284#endif
285}
QList< QString > QStringList
QString resolveAbsoluteVideoFilePath(const QString &documentPath) const
QString customFFMpegOptions
void setErrorMessage(const QString &errMsg)
QVector< QFileInfo > getAudioTracks() const
KisImageAnimationInterface * animationInterface() const
static KisTimeSpan fromTimeToTime(int start, int end)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
bool copyFileToTemporary(const QString &inputPath, QTemporaryFile &outputFile, QString *outErrorMessage)
bool progressIndeterminate