diff --git a/src/ffmpeg.js b/src/ffmpeg.js index e421df7b..9eb4cc2f 100644 --- a/src/ffmpeg.js +++ b/src/ffmpeg.js @@ -57,7 +57,7 @@ function handleProgress(process, cutDuration, onProgress) { async function cut({ customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation, - includeAllStreams, onProgress, stripAudio, + includeAllStreams, onProgress, stripAudio, keyframeCut, }) { const ext = path.extname(filePath) || `.${format}`; const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`; @@ -72,18 +72,32 @@ async function cut({ const cutFromArgs = cutFrom === 0 ? [] : ['-ss', cutFrom]; const cutToArgs = cutTo === undefined || cutTo === videoDuration ? [] : ['-t', cutDuration]; + const inputCutArgs = keyframeCut ? [ + ...cutFromArgs, + '-i', filePath, + ...cutToArgs, + '-avoid_negative_ts', 'make_zero', + ] : [ + '-i', filePath, + ...cutFromArgs, + ...cutToArgs, + ]; + const rotationArgs = rotation !== undefined ? ['-metadata:s:v:0', `rotate=${rotation}`] : []; const ffmpegArgs = [ - '-i', filePath, '-y', + ...inputCutArgs, + ...(stripAudio ? ['-an'] : ['-acodec', 'copy']), + '-vcodec', 'copy', '-scodec', 'copy', - ...cutFromArgs, ...cutToArgs, + ...(includeAllStreams ? ['-map', '0'] : []), '-map_metadata', '0', + ...rotationArgs, - '-f', format, - outPath, + + '-f', format, '-y', outPath, ]; console.log('ffmpeg', ffmpegArgs.join(' ')); @@ -108,8 +122,7 @@ async function html5ify(filePath, outPath, encodeVideo) { const ffmpegArgs = [ '-i', filePath, ...videoArgs, '-an', - '-y', - outPath, + '-y', outPath, ]; console.log('ffmpeg', ffmpegArgs.join(' ')); diff --git a/src/renderer.jsx b/src/renderer.jsx index 6358e93a..6449654c 100644 --- a/src/renderer.jsx +++ b/src/renderer.jsx @@ -103,6 +103,7 @@ class App extends React.Component { includeAllStreams: false, captureFormat: 'jpeg', customOutDir: undefined, + keyframeCut: false, }; this.state = { @@ -316,15 +317,12 @@ class App extends React.Component { async cutClick() { if (this.state.working) return alert('I\'m busy'); - const cutStartTime = this.state.cutStartTime; - const cutEndTime = this.state.cutEndTime; - const filePath = this.state.filePath; - const outputDir = this.state.customOutDir; - const fileFormat = this.state.fileFormat; - const videoDuration = this.state.duration; + const { + cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams, + stripAudio, keyframeCut, + } = this.state; + const rotation = this.isRotationSet() ? this.getRotation() : undefined; - const includeAllStreams = this.state.includeAllStreams; - const stripAudio = this.state.stripAudio; if (!this.isCutRangeValid()) { return alert('Start time must be before end time'); @@ -333,16 +331,17 @@ class App extends React.Component { this.setState({ working: true }); try { return await ffmpeg.cut({ - customOutDir: outputDir, + customOutDir, filePath, format: fileFormat, cutFrom: cutStartTime, cutTo: cutEndTime, cutToApparent: this.getApparentCutEndTime(), - videoDuration, + videoDuration: duration, rotation, includeAllStreams, stripAudio, + keyframeCut, onProgress: progress => this.onCutProgress(progress), }); } catch (err) { @@ -547,6 +546,13 @@ class App extends React.Component {