diff --git a/public/configStore.js b/public/configStore.js index 9f9e2f7f..f0ed4654 100644 --- a/public/configStore.js +++ b/public/configStore.js @@ -14,6 +14,7 @@ const defaults = { autoSaveProjectFile: true, wheelSensitivity: 0.2, language: undefined, + ffmpegExperimental: false, }, }; diff --git a/src/App.jsx b/src/App.jsx index 92906b7c..335bfa4f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -223,6 +223,8 @@ const App = memo(() => { useEffect(() => safeSetConfig('invertTimelineScroll', invertTimelineScroll), [invertTimelineScroll]); const [language, setLanguage] = useState(configStore.get('language')); useEffect(() => safeSetConfig('language', language), [language]); + const [ffmpegExperimental, setFfmpegExperimental] = useState(configStore.get('ffmpegExperimental')); + useEffect(() => safeSetConfig('ffmpegExperimental', ffmpegExperimental), [ffmpegExperimental]); useEffect(() => { i18n.changeLanguage(language || fallbackLng).catch(console.error); @@ -633,7 +635,7 @@ const App = memo(() => { const outPath = getOutPath(newCustomOutDir, firstPath, `merged${ext}`); // console.log('merge', paths); - await ffmpegMergeFiles({ paths, outPath, allStreams, onProgress: setCutProgress }); + await ffmpegMergeFiles({ paths, outPath, allStreams, ffmpegExperimental, onProgress: setCutProgress }); openDirToast({ icon: 'success', dirPath: outputDir, text: i18n.t('Files merged!') }); } catch (err) { errorToast(i18n.t('Failed to merge files. Make sure they are all of the exact same codecs')); @@ -642,7 +644,7 @@ const App = memo(() => { setWorking(); setCutProgress(); } - }, [assureOutDirAccess, outputDir]); + }, [assureOutDirAccess, outputDir, ffmpegExperimental]); const toggleCaptureFormat = useCallback(() => setCaptureFormat(f => (f === 'png' ? 'jpeg' : 'png')), []); const toggleKeyframeCut = useCallback(() => setKeyframeCut((val) => { @@ -1009,6 +1011,7 @@ const App = memo(() => { onProgress: setCutProgress, appendFfmpegCommandLog, shortestFlag, + ffmpegExperimental, }); if (outFiles.length > 1 && autoMerge) { @@ -1021,6 +1024,7 @@ const App = memo(() => { outFormat: fileFormat, isCustomFormatSelected, segmentPaths: outFiles, + ffmpegExperimental, onProgress: setCutProgress, }); } @@ -1059,7 +1063,7 @@ const App = memo(() => { working, duration, filePath, keyframeCut, autoMerge, customOutDir, fileFormat, haveInvalidSegs, copyFileStreams, numStreamsToCopy, exportExtraStreams, nonCopiedExtraStreams, outputDir, shortestFlag, isCustomFormatSelected, - fileFormatData, mainStreams, + fileFormatData, mainStreams, ffmpegExperimental, ]); const capture = useCallback(async () => { @@ -1718,6 +1722,8 @@ const App = memo(() => { setTimecodeShowFrames={setTimecodeShowFrames} askBeforeClose={askBeforeClose} setAskBeforeClose={setAskBeforeClose} + ffmpegExperimental={ffmpegExperimental} + setFfmpegExperimental={setFfmpegExperimental} invertTimelineScroll={invertTimelineScroll} setInvertTimelineScroll={setInvertTimelineScroll} language={language} @@ -1728,7 +1734,7 @@ const App = memo(() => { renderCaptureFormatButton={renderCaptureFormatButton} onWheelTunerRequested={onWheelTunerRequested} /> - ), [AutoExportToggler, askBeforeClose, autoMerge, autoSaveProjectFile, customOutDir, invertCutSegments, keyframeCut, renderCaptureFormatButton, renderOutFmt, timecodeShowFrames, changeOutDir, onWheelTunerRequested, language, invertTimelineScroll]); + ), [AutoExportToggler, askBeforeClose, autoMerge, autoSaveProjectFile, customOutDir, invertCutSegments, keyframeCut, renderCaptureFormatButton, renderOutFmt, timecodeShowFrames, changeOutDir, onWheelTunerRequested, language, invertTimelineScroll, ffmpegExperimental, setFfmpegExperimental]); useEffect(() => { if (!isStoreBuild) loadMifiLink().then(setMifiLink); diff --git a/src/HelpSheet.jsx b/src/HelpSheet.jsx index b70ae7cf..d10059ce 100644 --- a/src/HelpSheet.jsx +++ b/src/HelpSheet.jsx @@ -37,7 +37,8 @@ const HelpSheet = memo(({
diff --git a/src/Settings.jsx b/src/Settings.jsx
index b097ea0b..0c380341 100644
--- a/src/Settings.jsx
+++ b/src/Settings.jsx
@@ -7,7 +7,7 @@ const Settings = memo(({
changeOutDir, customOutDir, autoMerge, setAutoMerge, keyframeCut, setKeyframeCut, invertCutSegments, setInvertCutSegments,
autoSaveProjectFile, setAutoSaveProjectFile, timecodeShowFrames, setTimecodeShowFrames, askBeforeClose, setAskBeforeClose,
renderOutFmt, AutoExportToggler, renderCaptureFormatButton, onWheelTunerRequested, language, setLanguage,
- invertTimelineScroll, setInvertTimelineScroll,
+ invertTimelineScroll, setInvertTimelineScroll, ffmpegExperimental, setFfmpegExperimental,
}) => {
const { t } = useTranslation();
@@ -113,6 +113,17 @@ const Settings = memo(({
+
diff --git a/src/ffmpeg.js b/src/ffmpeg.js
index 17d2e881..2d1c11f6 100644
--- a/src/ffmpeg.js
+++ b/src/ffmpeg.js
@@ -205,7 +205,7 @@ function getMovFlags(outFormat) {
}
async function cut({
- filePath, outFormat, cutFrom, cutTo, videoDuration, rotation,
+ filePath, outFormat, cutFrom, cutTo, videoDuration, rotation, ffmpegExperimental,
onProgress, copyFileStreams, keyframeCut, outPath, appendFfmpegCommandLog, shortestFlag,
}) {
const cuttingStart = isCuttingStart(cutFrom);
@@ -253,6 +253,9 @@ async function cut({
// See https://github.com/mifi/lossless-cut/issues/170
'-ignore_unknown',
+ // https://superuser.com/questions/543589/information-about-ffmpeg-command-line-options
+ ...(ffmpegExperimental ? ['-strict', 'experimental'] : []),
+
...rotationArgs,
'-f', outFormat, '-y', outPath,
@@ -279,7 +282,7 @@ function getOutFileExtension({ isCustomFormatSelected, outFormat, filePath }) {
export async function cutMultiple({
customOutDir, filePath, segments: segmentsUnsorted, videoDuration, rotation,
onProgress, keyframeCut, copyFileStreams, outFormat, isCustomFormatSelected,
- appendFfmpegCommandLog, shortestFlag,
+ appendFfmpegCommandLog, shortestFlag, ffmpegExperimental,
}) {
const segments = sortBy(segmentsUnsorted, 'cutFrom');
const singleProgresses = {};
@@ -317,6 +320,7 @@ export async function cutMultiple({
// eslint-disable-next-line no-loop-func
onProgress: progress => onSingleProgress(i, progress),
appendFfmpegCommandLog,
+ ffmpegExperimental,
});
outFiles.push(outPath);
@@ -435,7 +439,7 @@ export async function html5ifyDummy(filePath, outPath, onProgress) {
await transferTimestamps(filePath, outPath);
}
-export async function mergeFiles({ paths, outPath, allStreams, outFormat, onProgress = () => {} }) {
+export async function mergeFiles({ paths, outPath, allStreams, outFormat, ffmpegExperimental, onProgress = () => {} }) {
console.log('Merging files', { paths }, 'to', outPath);
const durations = await pMap(paths, getDuration, { concurrency: 1 });
@@ -458,6 +462,9 @@ export async function mergeFiles({ paths, outPath, allStreams, outFormat, onProg
// See https://github.com/mifi/lossless-cut/issues/170
'-ignore_unknown',
+ // https://superuser.com/questions/543589/information-about-ffmpeg-command-line-options
+ ...(ffmpegExperimental ? ['-strict', 'experimental'] : []),
+
...(outFormat ? ['-f', outFormat] : []),
'-y', outPath,
];
@@ -480,12 +487,12 @@ export async function mergeFiles({ paths, outPath, allStreams, outFormat, onProg
console.log(result.stdout);
}
-export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths, onProgress }) {
+export async function autoMergeSegments({ customOutDir, sourceFile, isCustomFormatSelected, outFormat, segmentPaths, ffmpegExperimental, onProgress }) {
const ext = getOutFileExtension({ isCustomFormatSelected, outFormat, filePath: sourceFile });
const fileName = `cut-merged-${new Date().getTime()}${ext}`;
const outPath = getOutPath(customOutDir, sourceFile, fileName);
- await mergeFiles({ paths: segmentPaths, outPath, outFormat, allStreams: true, onProgress });
+ await mergeFiles({ paths: segmentPaths, outPath, outFormat, allStreams: true, ffmpegExperimental, onProgress });
await pMap(segmentPaths, path => fs.unlink(path), { concurrency: 5 });
}