Krita Source Code Documentation
Loading...
Searching...
No Matches
recorder_export_config.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Dmitrii Utkin <loentar@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 */
6
8
9#include <kis_config.h>
10
11#include <QString>
12#include <QDir>
13#include <QRegularExpression>
14
15namespace
16{
17const QString keyAnimationExport = "ANIMATION_EXPORT";
18const QString keyFfmpegPath = "ffmpeg_path";
19const QString keyVideoDirectory = "recorder_export/videodirectory";
20const QString keyInputFps = "recorder_export/inputfps";
21const QString keyFps = "recorder_export/fps";
22const QString keyResultPreview="recorder_export/resultpreview";
23const QString keyFirstFrameSec = "recorder_export/firstframesec";
24const QString keyExtendResult = "recorder_export/extendresult";
25const QString keyLastFrameSec = "recorder_export/lastframesec";
26const QString keyResize = "recorder_export/resize";
27const QString keySize = "recorder_export/size";
28const QString keyLockRatio = "recorder_export/lockratio";
29const QString keyLockFps = "recorder_export/lockfps";
30const QString keyProfileIndex = "recorder_export/profileIndex";
31const QString keyProfiles = "recorder_export/profiles";
32const QString keyEditedProfiles = "recorder_export/editedprofiles";
33
34const QString profilePrefix = "-reinit_filter 0\n-framerate $IN_FPS\n-i \"$INPUT_DIR%07d.$EXT\"\n-framerate $OUT_FPS\n-start_number $FRAMES-1\n-i \"$INPUT_DIR%07d.$EXT\"\n";
35
36const QList<RecorderProfile> defaultProfiles = {
37 { "MP4 x264", "mp4", profilePrefix % "-filter_complex \"\n"
38 /* Filter documentation https://trac.ffmpeg.org/wiki/FilteringGuide
39 * Inside the complex filter command each line is a different filter that is applied to an input stream.
40 * The input stream to transform is specified at the start of each line in the format '[$STREAM_NAME]' (ex. [p1], [v1], [v2]).
41 * Immediately following the input stream name will be the filter to apply (ex: trim, scale, loop)
42 * Depending on the filter it may or may have various options set
43 * After all options for the filter a name can be specified that will correspond to the output stream of the filter. If no name is specified it will be used as the final result for the output file.
44 */
45 " [1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))'[hold1];\n"
46 " [hold1]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2[hold2];\n"
47 " [hold2]setsar=1:1[hold3];\n"
48 " [hold3]split=3[preview1][transition1][end1];\n"
49
50 " [preview1]tpad=stop_mode=clone:stop_duration=$FIRST_FRAME_SEC[preview2];\n"
51 " [preview2]setpts=PTS-STARTPTS[preview3];\n"
52
53 " [transition1]tpad=stop_mode=clone:stop_duration=$TRANSITION_LENGTH[transition2];\n"
54 " [transition2]setpts=PTS-STARTPTS[transition3];\n"
55 " [transition3]framerate=$OUT_FPS[transition4];\n"
56
57 " [0]fps=$OUT_FPS[main1];\n"
58 " [main1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))':eval=frame[main2];\n"
59 " [main2]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2:eval=frame[main3];\n"
60 " [main3]setsar=1:1[main4];\n"
61 " [transition4][main4]xfade=transition=smoothright:duration=$TRANSITION_LENGTH:offset=0[main5];\n"
62
63 " [end1]tpad=stop_mode=clone:stop_duration=$LAST_FRAME_SEC[end2];\n"
64 " [end2]setpts=PTS-STARTPTS[end3];\n"
65
66 " [preview3][main5][end3]concat=n=3:v=1:a=0[final1];\n"
67 " [final1]setpts=PTS-STARTPTS\n"
68
69 "\"\n-stats\n-loglevel error\n-c:v $H264_ENCODER\n-r $OUT_FPS\n-pix_fmt yuv420p" },
70 { "GIF", "gif", profilePrefix % "-filter_complex \"\n"
71 " [1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))'[hold1];\n"
72 " [hold1]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2[hold2];\n"
73 " [hold2]setsar=1:1[hold3];\n"
74 " [hold3]split=3[preview1][transition1][end1];\n"
75
76 " [preview1]tpad=stop_mode=clone:stop_duration=$FIRST_FRAME_SEC[preview2];\n"
77 " [preview2]setpts=PTS-STARTPTS[preview3];\n"
78
79 " [transition1]tpad=stop_mode=clone:stop_duration=$TRANSITION_LENGTH[transition2];\n"
80 " [transition2]setpts=PTS-STARTPTS[transition3];\n"
81 " [transition3]framerate=$OUT_FPS[transition4];\n"
82
83 " [0]fps=$OUT_FPS[main1];\n"
84 " [main1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))':eval=frame[main2];\n"
85 " [main2]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2:eval=frame[main3];\n"
86 " [main3]setsar=1:1[main4];\n"
87 " [transition4][main4]xfade=transition=smoothright:duration=$TRANSITION_LENGTH:offset=0[main5];\n"
88
89 " [end1]tpad=stop_mode=clone:stop_duration=$LAST_FRAME_SEC[end2];\n"
90 " [end2]setpts=PTS-STARTPTS[end3];\n"
91
92 " [preview3][main5][end3]concat=n=3:v=1:a=0[final1];\n"
93 " [final1]setpts=PTS-STARTPTS[final2];\n"
94 " [final2]split[final3][final4];\n"
95 " [final3]palettegen[palettegen];\n"
96 " [final4][palettegen]paletteuse\n"
97 "\"\n-stats\n-loglevel error\n-r $OUT_FPS" },
98 { "Matroska", "mkv", profilePrefix % "-filter_complex \"\n"
99 " [1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))'[hold1];\n"
100 " [hold1]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2[hold2];\n"
101 " [hold2]setsar=1:1[hold3];\n"
102 " [hold3]split=3[preview1][transition1][end1];\n"
103
104 " [preview1]tpad=stop_mode=clone:stop_duration=$FIRST_FRAME_SEC[preview2];\n"
105 " [preview2]setpts=PTS-STARTPTS[preview3];\n"
106
107 " [transition1]tpad=stop_mode=clone:stop_duration=$TRANSITION_LENGTH[transition2];\n"
108 " [transition2]setpts=PTS-STARTPTS[transition3];\n"
109 " [transition3]framerate=$OUT_FPS[transition4];\n"
110
111 " [0]fps=$OUT_FPS[main1];\n"
112 " [main1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))':eval=frame[main2];\n"
113 " [main2]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2:eval=frame[main3];\n"
114 " [main3]setsar=1:1[main4];\n"
115 " [transition4][main4]xfade=transition=smoothright:duration=$TRANSITION_LENGTH:offset=0[main5];\n"
116
117 " [end1]tpad=stop_mode=clone:stop_duration=$LAST_FRAME_SEC[end2];\n"
118 " [end2]setpts=PTS-STARTPTS[end3];\n"
119
120 " [preview3][main5][end3]concat=n=3:v=1:a=0[final1];\n"
121 " [final1]setpts=PTS-STARTPTS\n"
122 "\"\n-stats\n-loglevel error\n-pix_fmt yuv420p\n-r $OUT_FPS" },
123 { "WebM", "webm", profilePrefix % "-filter_complex \"\n"
124 " [1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))'[hold1];\n"
125 " [hold1]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2[hold2];\n"
126 " [hold2]setsar=1:1[hold3];\n"
127 " [hold3]split=3[preview1][transition1][end1];\n"
128
129 " [preview1]tpad=stop_mode=clone:stop_duration=$FIRST_FRAME_SEC[preview2];\n"
130 " [preview2]setpts=PTS-STARTPTS[preview3];\n"
131
132 " [transition1]tpad=stop_mode=clone:stop_duration=$TRANSITION_LENGTH[transition2];\n"
133 " [transition2]setpts=PTS-STARTPTS[transition3];\n"
134 " [transition3]framerate=$OUT_FPS[transition4];\n"
135
136 " [0]fps=$OUT_FPS[main1];\n"
137 " [main1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))':eval=frame[main2];\n"
138 " [main2]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2:eval=frame[main3];\n"
139 " [main3]setsar=1:1[main4];\n"
140 " [transition4][main4]xfade=transition=smoothright:duration=$TRANSITION_LENGTH:offset=0[main5];\n"
141
142 " [end1]tpad=stop_mode=clone:stop_duration=$LAST_FRAME_SEC[end2];\n"
143 " [end2]setpts=PTS-STARTPTS[end3];\n"
144
145 " [preview3][main5][end3]concat=n=3:v=1:a=0[final1];\n"
146 " [final1]setpts=PTS-STARTPTS\n"
147 "\"\n-stats\n-loglevel error\n-r $OUT_FPS" },
148 { "MP4 x264 (Flash Effect)", "mp4", profilePrefix % "-filter_complex \"\n"
149 " [1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))'[hold1];\n"
150 " [hold1]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2[hold2];\n"
151 " [hold2]setsar=1:1[hold3];\n"
152 " [hold3]split=3[preview1][fade1][end1];\n"
153
154 " [preview1]tpad=stop_mode=clone:stop_duration=$FIRST_FRAME_SEC[preview2];\n"
155 " [preview2]setpts=PTS-STARTPTS[preview3];\n"
156
157 " [fade1]tpad=stop_mode=clone:stop_duration=$TRANSITION_LENGTH[fade2];\n"
158 " [fade2]setpts=PTS-STARTPTS[fade3];\n"
159 " [fade3]fade=out:duration=$TRANSITION_LENGTH:color=white[fade4];\n"
160
161 " [0]fps=$OUT_FPS[main1];\n"
162 " [main1]scale='min($WIDTH, iw*($HEIGHT/ih))':'min($HEIGHT, ih*($WIDTH/iw))':eval=frame[main2];\n"
163 " [main2]pad=$WIDTH:$HEIGHT:(ow-iw)/2:(oh-ih)/2:eval=frame[main3];\n"
164 " [main3]setsar=1:1[main4];\n"
165 " [fade4][main4]concat[main5];\n"
166
167 " [end1]tpad=stop_mode=clone:stop_duration=$LAST_FRAME_SEC[end2];\n"
168 " [end2]setpts=PTS-STARTPTS[end3];\n"
169
170 " [preview3][main5][end3]concat=n=3:v=1:a=0[final1];\n"
171 " [final1]setpts=PTS-STARTPTS\n"
172 "\"\n-stats\n-loglevel error\n-c:v $H264_ENCODER\n-r $OUT_FPS\n-pix_fmt yuv420p" },
173 { "Custom1", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" },
174 { "Custom2", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" },
175 { "Custom3", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" },
176 { "Custom4", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" }
177};
178}
179
181 : config(new KisConfig(readOnly))
182{
183}
184
189
191{
192 settings->inputFps = inputFps();
193 settings->fps = fps();
194 settings->resultPreview = resultPreview();
195 settings->firstFrameSec = firstFrameSec();
196 settings->extendResult = extendResult();
197 settings->lastFrameSec = lastFrameSec();
198 settings->resize = resize();
199 settings->size = size();
200 settings->lockRatio = lockRatio();
201 settings->ffmpegPath = ffmpegPath();
202 settings->profiles = profiles();
203 settings->defaultProfiles = defaultProfiles();
204 settings->profileIndex = profileIndex();
205 settings->videoDirectory = videoDirectory();
206 if (loadLockFps)
207 settings->lockFps = lockFps();
208}
209
211{
212 return config->readEntry(keyInputFps, 30);
213}
214
216{
217 config->writeEntry(keyInputFps, value);
218}
219
220
222{
223 return config->readEntry(keyFps, 30);
224}
225
227{
228 config->writeEntry(keyFps, value);
229}
230
232{
233 return config->readEntry(keyResultPreview, true);
234}
235
237{
238 config->writeEntry(keyResultPreview, value);
239}
240
242{
243 return config->readEntry(keyFirstFrameSec, 2);
244}
245
247{
248 config->writeEntry(keyFirstFrameSec, value);
249}
250
251
253{
254 return config->readEntry(keyExtendResult, true);
255}
256
258{
259 config->writeEntry(keyExtendResult, value);
260}
261
263{
264 return config->readEntry(keyLastFrameSec, 5);
265}
266
268{
269 config->writeEntry(keyLastFrameSec, value);
270}
271
273{
274 return config->readEntry(keyResize, false);
275}
276
278{
279 config->writeEntry(keyResize, value);
280}
281
282
284{
285 return config->readEntry(keySize, QSize(1024, 1024));
286}
287
289{
290 config->writeEntry(keySize, value);
291}
292
293
295{
296 return config->readEntry(keyLockRatio, false);
297}
298
300{
301 config->writeEntry(keyLockRatio, value);
302}
303
305{
306 return config->readEntry(keyLockFps, false);
307}
308
310{
311 config->writeEntry(keyLockFps, value);
312}
313
315{
316 return config->readEntry(keyProfileIndex, 0);
317}
318
320{
321 config->writeEntry(keyProfileIndex, value);
322}
323
325{
326 const QString &profilesStr = config->readEntry(keyProfiles, QString());
327 if (profilesStr.isEmpty())
328 return ::defaultProfiles;
329
330 const QSet<int> &editedIndexes = editedProfilesIndexes();
332 const QStringList &profilesList = profilesStr.split("\n");
333 int index = 0;
334 for (const QString &profileStr : profilesList) {
335 // take saved profile in case it's edited.. or something bad happen
336 if (editedIndexes.contains(index) || index >= ::defaultProfiles.size()) {
337 const QStringList& profileList = profileStr.split("|");
338 if (profileList.size() == 3) {
339 profiles.append({profileList[0], profileList[1], QString(profileList[2]).replace("\\n", "\n")});
340 }
341 } else {
342 // use default profile to make it possible to upgrade
343 profiles.append(::defaultProfiles[index]);
344 }
345
346 ++index;
347 }
348 return profiles;
349}
350
352{
353 Q_ASSERT(value.size() == ::defaultProfiles.size());
354
355 const QSet<int> &savedEditedProfilesIndexes = editedProfilesIndexes();
356 QSet<int> editedProfilesIndexes = savedEditedProfilesIndexes;
357 QString outValue;
358 const QRegularExpression cleanUp("[\n|]");
359 int index = 0;
360 for (const RecorderProfile &profile : value) {
361 const RecorderProfile &defaultProfile = ::defaultProfiles[index];
362 if (profile != defaultProfile) {
363 editedProfilesIndexes.insert(index);
364 } else {
365 editedProfilesIndexes.remove(index);
366 }
367
368 outValue += QString(profile.name).replace(cleanUp, " ") % "|"
369 % QString(profile.extension).replace(cleanUp, " ") % "|"
370 % QString(profile.arguments).replace("\n", "\\n").replace("|", " ") % "\n";
371
372 ++index;
373 }
374
375 config->writeEntry(keyProfiles, outValue);
376 if (savedEditedProfilesIndexes != editedProfilesIndexes) {
378 }
379}
380
381
383{
384 return ::defaultProfiles;
385}
386
388{
389 const QVariantList &readValue = config->readEntry(keyEditedProfiles, QVariantList());
390 QSet<int> result;
391 for (const QVariant &item : readValue) {
392 result.insert(item.toInt());
393 }
394 return result;
395}
396
398{
399 QVariantList writeValue;
400 for (int index : value) {
401 writeValue.append(index);
402 }
403 config->writeEntry(keyEditedProfiles, writeValue);
404}
405
406
408{
409 return KisConfig(true).ffmpegLocation();
410}
411
413{
414 KisConfig cfg(false);
416}
417
418
420{
421 return config->readEntry(keyVideoDirectory, QDir::homePath());
422}
423
425{
426 config->writeEntry(keyVideoDirectory, value);
427}
428
429bool operator==(const RecorderProfile &left, const RecorderProfile &right)
430{
431 return left.arguments == right.arguments
432 && left.name == right.name
433 && left.extension == right.extension;
434}
435
436bool operator!=(const RecorderProfile &left, const RecorderProfile &right)
437{
438 return !(left == right);
439}
float value(const T *src, size_t ch)
void setFFMpegLocation(const QString &value)
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
QString ffmpegLocation(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:789
void setProfiles(const QList< RecorderProfile > &value)
void setVideoDirectory(const QString &value)
void loadConfiguration(RecorderExportSettings *settings, bool loadLockFps=true) const
void setFfmpegPath(const QString &value)
QList< RecorderProfile > defaultProfiles() const
QSet< int > editedProfilesIndexes() const
QList< RecorderProfile > profiles() const
void setSize(const QSize &value)
void setEditedProfilesIndexes(const QSet< int > &value)
bool operator!=(const RecorderProfile &left, const RecorderProfile &right)
bool operator==(const RecorderProfile &left, const RecorderProfile &right)
QList< RecorderProfile > profiles
QList< RecorderProfile > defaultProfiles