File indexing completed on 2024-12-22 04:14:55
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dmitrii Utkin <loentar@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-only 0005 */ 0006 0007 #include "recorder_export_config.h" 0008 0009 #include <kis_config.h> 0010 0011 #include <QString> 0012 #include <QDir> 0013 0014 namespace 0015 { 0016 const QString keyAnimationExport = "ANIMATION_EXPORT"; 0017 const QString keyFfmpegPath = "ffmpeg_path"; 0018 const QString keyVideoDirectory = "recorder_export/videodirectory"; 0019 const QString keyInputFps = "recorder_export/inputfps"; 0020 const QString keyFps = "recorder_export/fps"; 0021 const QString keyResultPreview="recorder_export/resultpreview"; 0022 const QString keyFirstFrameSec = "recorder_export/firstframesec"; 0023 const QString keyExtendResult = "recorder_export/extendresult"; 0024 const QString keyLastFrameSec = "recorder_export/lastframesec"; 0025 const QString keyResize = "recorder_export/resize"; 0026 const QString keySize = "recorder_export/size"; 0027 const QString keyLockRatio = "recorder_export/lockratio"; 0028 const QString keyLockFps = "recorder_export/lockfps"; 0029 const QString keyProfileIndex = "recorder_export/profileIndex"; 0030 const QString keyProfiles = "recorder_export/profiles"; 0031 const QString keyEditedProfiles = "recorder_export/editedprofiles"; 0032 0033 const QString profilePrefix = "-framerate $IN_FPS\n-i \"$INPUT_DIR%07d.$EXT\"\n-framerate $IN_FPS\n-start_number $FRAMES-1\n-i \"$INPUT_DIR%07d.$EXT\"\n"; 0034 0035 const QList<RecorderProfile> defaultProfiles = { 0036 { "MP4 x264", "mp4", profilePrefix % "-filter_complex \"\n" 0037 /* Filter documentation https://trac.ffmpeg.org/wiki/FilteringGuide 0038 * Inside the complex filter command each line is a different filter that is applied to an input stream. 0039 * The input stream to transform is specified at the start of each line in the format '[$STREAM_NAME]' (ex. [p1], [v1], [v2]). 0040 * Immediatly following the input stream name will be the filter to apply (ex: trim, scale, loop) 0041 * Depending on the filter it may or may have various options set 0042 * 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. 0043 */ 0044 " [0]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=$FRAMES[main1];\n" 0045 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0046 " [main2]loop=1:size=1:start=0[main3];\n" 0047 " [main3]setpts=PTS-STARTPTS[main4];\n" 0048 0049 " [1]split [first1][transition1];\n" 0050 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0051 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0052 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0053 0054 " [transition4][main4]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0055 " [v1]setpts=PTS-STARTPTS[v2];\n" 0056 " [v2]trim=start_frame=1[v3];\n" 0057 0058 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0059 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0060 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0061 " [preview3][v3] concat [final1];\n" 0062 " [final1] setpts=PTS-STARTPTS[final2];\n" 0063 " [final2] trim=start_frame=1\n" 0064 0065 "\"\n-c:v libx264\n-r $OUT_FPS\n-pix_fmt yuv420p" }, 0066 { "MP4 openh264", "mp4", profilePrefix % "-filter_complex \"\n" 0067 /* Filter documentation https://trac.ffmpeg.org/wiki/FilteringGuide 0068 * Inside the complex filter command each line is a different filter that is applied to an input stream. 0069 * The input stream to transform is specified at the start of each line in the format '[$STREAM_NAME]' (ex. [p1], [v1], [v2]). 0070 * Immediatly following the input stream name will be the filter to apply (ex: trim, scale, loop) 0071 * Depending on the filter it may or may have various options set 0072 * 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. 0073 */ 0074 " [0]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=$FRAMES[main1];\n" 0075 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0076 " [main2]loop=1:size=1:start=0[main3];\n" 0077 " [main3]setpts=PTS-STARTPTS[main4];\n" 0078 0079 " [1]split [first1][transition1];\n" 0080 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0081 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0082 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0083 0084 " [transition4][main4]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0085 " [v1]setpts=PTS-STARTPTS[v2];\n" 0086 " [v2]trim=start_frame=1[v3];\n" 0087 0088 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0089 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0090 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0091 " [preview3][v3] concat [final1];\n" 0092 " [final1] setpts=PTS-STARTPTS[final2];\n" 0093 " [final2] trim=start_frame=1\n" 0094 0095 "\"\n-c:v libopenh264\n-r $OUT_FPS\n-pix_fmt yuv420p" }, 0096 { "GIF", "gif", profilePrefix % "-filter_complex \"\n" 0097 " [0]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=$FRAMES[main1];\n" 0098 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0099 " [main2]loop=1:size=1:start=0[main3];\n" 0100 " [main3]setpts=PTS-STARTPTS[main4];\n" 0101 " [1]split [first1][transition1];\n" 0102 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0103 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0104 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0105 " [transition4][main4]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0106 " [v1]setpts=PTS-STARTPTS[v2];\n" 0107 " [v2]trim=start_frame=1[v3];\n" 0108 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0109 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0110 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0111 " [preview3][v3] concat [final1];\n" 0112 " [final1] setpts=PTS-STARTPTS[final2];\n" 0113 " [final2] trim=start_frame=1[final3];\n" 0114 " [final3]split[final4][final5];\n" 0115 " [final4]palettegen[palettegen];\n" 0116 " [final5][palettegen]paletteuse\"\n-loop -1" }, 0117 { "Matroska", "mkv", profilePrefix % "-filter_complex \"\n" 0118 " [0]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=$FRAMES[main1];\n" 0119 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0120 " [main2]loop=1:size=1:start=0[main3];\n" 0121 " [main3]setpts=PTS-STARTPTS[main4];\n" 0122 0123 " [1]split [first1][transition1];\n" 0124 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0125 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0126 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0127 0128 " [transition4][main4]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0129 " [v1]setpts=PTS-STARTPTS[v2];\n" 0130 " [v2]trim=start_frame=1[v3];\n" 0131 0132 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0133 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0134 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0135 " [preview3][v3] concat [final1];\n" 0136 " [final1] setpts=PTS-STARTPTS[final2];\n" 0137 " [final2] trim=start_frame=1\n" 0138 "\"\n-r $OUT_FPS" }, 0139 { "WebM", "webm", profilePrefix % "-filter_complex \"\n" 0140 " [0]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=$FRAMES[main1];\n" 0141 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0142 " [main2]loop=1:size=1:start=0[main3];\n" 0143 " [main3]setpts=PTS-STARTPTS[main4];\n" 0144 0145 " [1]split [first1][transition1];\n" 0146 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0147 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0148 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0149 0150 " [transition4][main4]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0151 " [v1]setpts=PTS-STARTPTS[v2];\n" 0152 " [v2]trim=start_frame=1[v3];\n" 0153 0154 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0155 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0156 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0157 " [preview3][v3] concat [final1];\n" 0158 " [final1] setpts=PTS-STARTPTS[final2];\n" 0159 " [final2] trim=start_frame=1\n" 0160 "\"\n-r $OUT_FPS" }, 0161 { "MP4 x264 (Flash Effect)", "mp4", profilePrefix % "-filter_complex \"\n" 0162 " [1]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=0[fade1];\n" 0163 " [fade1]fps=$OUT_FPS[fade2];\n" 0164 " [fade2]fade=type=in:color=white:start_time=0.7:duration=0.7[fade3];\n" 0165 " [fade3]setsar=1[fade4];\n" 0166 " [0]setsar=1[main0];\n" 0167 " [main0][fade4] concat=n=2:v=1[main1];\n" 0168 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0169 " [main2]loop=1:size=1:start=0[main3];\n" 0170 " [main3]setpts=PTS-STARTPTS[main4];\n" 0171 " [main4]fps=fps=$IN_FPS[main5];\n" 0172 " [1]split [first1][transition1];\n" 0173 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0174 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0175 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0176 " [transition4][main5]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0177 " [v1]setpts=PTS-STARTPTS[v2];\n" 0178 " [v2]trim=start_frame=1[v3];\n" 0179 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0180 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0181 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0182 " [preview3][v3] concat [final1];\n" 0183 " [final1] setpts=PTS-STARTPTS[final2];\n" 0184 " [final2] trim=start_frame=1\n" 0185 "\"\n-c:v libx264\n-r $OUT_FPS\n-pix_fmt yuv420p" }, 0186 { "MP4 openh264 (Flash Effect)", "mp4", profilePrefix % "-filter_complex \"\n" 0187 " [1]loop=$LAST_FRAME_SEC*$IN_FPS:size=1:start=0[fade1];\n" 0188 " [fade1]fps=$OUT_FPS[fade2];\n" 0189 " [fade2]fade=type=in:color=white:start_time=0.7:duration=0.7[fade3];\n" 0190 " [fade3]setsar=1[fade4];\n" 0191 " [0]setsar=1[main0];\n" 0192 " [main0][fade4] concat=n=2:v=1[main1];\n" 0193 " [main1]scale=$WIDTH:$HEIGHT[main2];\n" 0194 " [main2]loop=1:size=1:start=0[main3];\n" 0195 " [main3]setpts=PTS-STARTPTS[main4];\n" 0196 " [main4]fps=fps=$IN_FPS[main5];\n" 0197 " [1]split [first1][transition1];\n" 0198 " [transition1]scale=$WIDTH:$HEIGHT [transition2];\n" 0199 " [transition2]loop='if(gte($FIRST_FRAME_SEC, 1), 1*$IN_FPS, 0)':size=1:start=1[transition3];\n" 0200 " [transition3]setpts=PTS-STARTPTS[transition4];\n" 0201 " [transition4][main5]xfade=transition=smoothright:duration=0.5:offset=0[v1];\n" 0202 " [v1]setpts=PTS-STARTPTS[v2];\n" 0203 " [v2]trim=start_frame=1[v3];\n" 0204 " [first1]loop='if(gte($FIRST_FRAME_SEC, 1), ($FIRST_FRAME_SEC*$IN_FPS) - 0.5*$IN_FPS, $FIRST_FRAME_SEC*$IN_FPS)':size=1:start=1[preview1];\n" 0205 " [preview1]scale=$WIDTH:$HEIGHT[preview2];\n" 0206 " [preview2]setpts=PTS-STARTPTS[preview3];\n" 0207 " [preview3][v3] concat [final1];\n" 0208 " [final1] setpts=PTS-STARTPTS[final2];\n" 0209 " [final2] trim=start_frame=1\n" 0210 "\"\n-c:v libopenh264\n-r $OUT_FPS\n-pix_fmt yuv420p" }, 0211 { "Custom1", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" }, 0212 { "Custom2", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" }, 0213 { "Custom3", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" }, 0214 { "Custom4", "editme", profilePrefix % "-filter_complex \"loop=$LAST_FRAME_SEC:size=1:start=$FRAMES,scale=$WIDTH:$HEIGHT\"\n-r $OUT_FPS" } 0215 }; 0216 } 0217 0218 RecorderExportConfig::RecorderExportConfig(bool readOnly) 0219 : config(new KisConfig(readOnly)) 0220 { 0221 } 0222 0223 RecorderExportConfig::~RecorderExportConfig() 0224 { 0225 delete config; 0226 } 0227 0228 void RecorderExportConfig::loadConfiguration(RecorderExportSettings *settings, bool loadLockFps) const 0229 { 0230 settings->inputFps = inputFps(); 0231 settings->fps = fps(); 0232 settings->resultPreview = resultPreview(); 0233 settings->firstFrameSec = firstFrameSec(); 0234 settings->extendResult = extendResult(); 0235 settings->lastFrameSec = lastFrameSec(); 0236 settings->resize = resize(); 0237 settings->size = size(); 0238 settings->lockRatio = lockRatio(); 0239 settings->ffmpegPath = ffmpegPath(); 0240 settings->profiles = profiles(); 0241 settings->defaultProfiles = defaultProfiles(); 0242 settings->profileIndex = profileIndex(); 0243 settings->videoDirectory = videoDirectory(); 0244 if (loadLockFps) 0245 settings->lockFps = lockFps(); 0246 } 0247 0248 int RecorderExportConfig::inputFps() const 0249 { 0250 return config->readEntry(keyInputFps, 30); 0251 } 0252 0253 void RecorderExportConfig::setInputFps(int value) 0254 { 0255 config->writeEntry(keyInputFps, value); 0256 } 0257 0258 0259 int RecorderExportConfig::fps() const 0260 { 0261 return config->readEntry(keyFps, 30); 0262 } 0263 0264 void RecorderExportConfig::setFps(int value) 0265 { 0266 config->writeEntry(keyFps, value); 0267 } 0268 0269 bool RecorderExportConfig::resultPreview() const 0270 { 0271 return config->readEntry(keyResultPreview, true); 0272 } 0273 0274 void RecorderExportConfig::setResultPreview(bool value) 0275 { 0276 config->writeEntry(keyResultPreview, value); 0277 } 0278 0279 int RecorderExportConfig::firstFrameSec() const 0280 { 0281 return config->readEntry(keyFirstFrameSec, 2); 0282 } 0283 0284 void RecorderExportConfig::setFirstFrameSec(int value) 0285 { 0286 config->writeEntry(keyFirstFrameSec, value); 0287 } 0288 0289 0290 bool RecorderExportConfig::extendResult() const 0291 { 0292 return config->readEntry(keyExtendResult, true); 0293 } 0294 0295 void RecorderExportConfig::setExtendResult(bool value) 0296 { 0297 config->writeEntry(keyExtendResult, value); 0298 } 0299 0300 int RecorderExportConfig::lastFrameSec() const 0301 { 0302 return config->readEntry(keyLastFrameSec, 5); 0303 } 0304 0305 void RecorderExportConfig::setLastFrameSec(int value) 0306 { 0307 config->writeEntry(keyLastFrameSec, value); 0308 } 0309 0310 bool RecorderExportConfig::resize() const 0311 { 0312 return config->readEntry(keyResize, false); 0313 } 0314 0315 void RecorderExportConfig::setResize(bool value) 0316 { 0317 config->writeEntry(keyResize, value); 0318 } 0319 0320 0321 QSize RecorderExportConfig::size() const 0322 { 0323 return config->readEntry(keySize, QSize(1024, 1024)); 0324 } 0325 0326 void RecorderExportConfig::setSize(const QSize &value) 0327 { 0328 config->writeEntry(keySize, value); 0329 } 0330 0331 0332 bool RecorderExportConfig::lockRatio() const 0333 { 0334 return config->readEntry(keyLockRatio, false); 0335 } 0336 0337 void RecorderExportConfig::setLockRatio(bool value) 0338 { 0339 config->writeEntry(keyLockRatio, value); 0340 } 0341 0342 bool RecorderExportConfig::lockFps() const 0343 { 0344 return config->readEntry(keyLockFps, false); 0345 } 0346 0347 void RecorderExportConfig::setLockFps(bool value) 0348 { 0349 config->writeEntry(keyLockFps, value); 0350 } 0351 0352 int RecorderExportConfig::profileIndex() const 0353 { 0354 return config->readEntry(keyProfileIndex, 0); 0355 } 0356 0357 void RecorderExportConfig::setProfileIndex(int value) 0358 { 0359 config->writeEntry(keyProfileIndex, value); 0360 } 0361 0362 QList<RecorderProfile> RecorderExportConfig::profiles() const 0363 { 0364 const QString &profilesStr = config->readEntry(keyProfiles, QString()); 0365 if (profilesStr.isEmpty()) 0366 return ::defaultProfiles; 0367 0368 const QSet<int> &editedIndexes = editedProfilesIndexes(); 0369 QList<RecorderProfile> profiles; 0370 const QStringList &profilesList = profilesStr.split("\n"); 0371 int index = 0; 0372 for (const QString &profileStr : profilesList) { 0373 // take saved profile in case it's edited.. or something bad happen 0374 if (editedIndexes.contains(index) || index >= ::defaultProfiles.size()) { 0375 const QStringList& profileList = profileStr.split("|"); 0376 if (profileList.size() == 3) { 0377 profiles.append({profileList[0], profileList[1], QString(profileList[2]).replace("\\n", "\n")}); 0378 } 0379 } else { 0380 // use default profile to make it possible to upgrade 0381 profiles.append(::defaultProfiles[index]); 0382 } 0383 0384 ++index; 0385 } 0386 return profiles; 0387 } 0388 0389 void RecorderExportConfig::setProfiles(const QList<RecorderProfile> &value) 0390 { 0391 Q_ASSERT(value.size() == ::defaultProfiles.size()); 0392 0393 const QSet<int> &savedEditedProfilesIndexes = editedProfilesIndexes(); 0394 QSet<int> editedProfilesIndexes = savedEditedProfilesIndexes; 0395 QString outValue; 0396 const QRegExp cleanUp("[\n|]"); 0397 int index = 0; 0398 for (const RecorderProfile &profile : value) { 0399 const RecorderProfile &defaultProfile = ::defaultProfiles[index]; 0400 if (profile != defaultProfile) { 0401 editedProfilesIndexes.insert(index); 0402 } else { 0403 editedProfilesIndexes.remove(index); 0404 } 0405 0406 outValue += QString(profile.name).replace(cleanUp, " ") % "|" 0407 % QString(profile.extension).replace(cleanUp, " ") % "|" 0408 % QString(profile.arguments).replace("\n", "\\n").replace("|", " ") % "\n"; 0409 0410 ++index; 0411 } 0412 0413 config->writeEntry(keyProfiles, outValue); 0414 if (savedEditedProfilesIndexes != editedProfilesIndexes) { 0415 setEditedProfilesIndexes(editedProfilesIndexes); 0416 } 0417 } 0418 0419 0420 QList<RecorderProfile> RecorderExportConfig::defaultProfiles() const 0421 { 0422 return ::defaultProfiles; 0423 } 0424 0425 QSet<int> RecorderExportConfig::editedProfilesIndexes() const 0426 { 0427 const QVariantList &readValue = config->readEntry(keyEditedProfiles, QVariantList()); 0428 QSet<int> result; 0429 for (const QVariant &item : readValue) { 0430 result.insert(item.toInt()); 0431 } 0432 return result; 0433 } 0434 0435 void RecorderExportConfig::setEditedProfilesIndexes(const QSet<int> &value) 0436 { 0437 QVariantList writeValue; 0438 for (int index : value) { 0439 writeValue.append(index); 0440 } 0441 config->writeEntry(keyEditedProfiles, writeValue); 0442 } 0443 0444 0445 QString RecorderExportConfig::ffmpegPath() const 0446 { 0447 return KisConfig(true).ffmpegLocation(); 0448 } 0449 0450 void RecorderExportConfig::setFfmpegPath(const QString &value) 0451 { 0452 KisConfig cfg(false); 0453 cfg.setFFMpegLocation(value); 0454 } 0455 0456 0457 QString RecorderExportConfig::videoDirectory() const 0458 { 0459 return config->readEntry(keyVideoDirectory, QDir::homePath()); 0460 } 0461 0462 void RecorderExportConfig::setVideoDirectory(const QString &value) 0463 { 0464 config->writeEntry(keyVideoDirectory, value); 0465 } 0466 0467 bool operator==(const RecorderProfile &left, const RecorderProfile &right) 0468 { 0469 return left.arguments == right.arguments 0470 && left.name == right.name 0471 && left.extension == right.extension; 0472 } 0473 0474 bool operator!=(const RecorderProfile &left, const RecorderProfile &right) 0475 { 0476 return !(left == right); 0477 }