1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-25 19:52:44 +01:00

Allow for testing keyframe cut

This means a button for toggling whether -ss should be before -i or after (default)
See discussion #13 and #79
Also some text/button enchancements
This commit is contained in:
Mikael Finstad 2018-09-09 20:50:21 +02:00
parent 197c2f9418
commit 31c8b86e50
2 changed files with 38 additions and 19 deletions

View File

@ -57,7 +57,7 @@ function handleProgress(process, cutDuration, onProgress) {
async function cut({ async function cut({
customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation, customOutDir, filePath, format, cutFrom, cutTo, cutToApparent, videoDuration, rotation,
includeAllStreams, onProgress, stripAudio, includeAllStreams, onProgress, stripAudio, keyframeCut,
}) { }) {
const ext = path.extname(filePath) || `.${format}`; const ext = path.extname(filePath) || `.${format}`;
const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`; const cutSpecification = `${util.formatDuration(cutFrom, true)}-${util.formatDuration(cutToApparent, true)}`;
@ -72,18 +72,32 @@ async function cut({
const cutFromArgs = cutFrom === 0 ? [] : ['-ss', cutFrom]; const cutFromArgs = cutFrom === 0 ? [] : ['-ss', cutFrom];
const cutToArgs = cutTo === undefined || cutTo === videoDuration ? [] : ['-t', cutDuration]; 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 rotationArgs = rotation !== undefined ? ['-metadata:s:v:0', `rotate=${rotation}`] : [];
const ffmpegArgs = [ const ffmpegArgs = [
'-i', filePath, '-y', ...inputCutArgs,
...(stripAudio ? ['-an'] : ['-acodec', 'copy']), ...(stripAudio ? ['-an'] : ['-acodec', 'copy']),
'-vcodec', 'copy', '-vcodec', 'copy',
'-scodec', 'copy', '-scodec', 'copy',
...cutFromArgs, ...cutToArgs,
...(includeAllStreams ? ['-map', '0'] : []), ...(includeAllStreams ? ['-map', '0'] : []),
'-map_metadata', '0', '-map_metadata', '0',
...rotationArgs, ...rotationArgs,
'-f', format,
outPath, '-f', format, '-y', outPath,
]; ];
console.log('ffmpeg', ffmpegArgs.join(' ')); console.log('ffmpeg', ffmpegArgs.join(' '));
@ -108,8 +122,7 @@ async function html5ify(filePath, outPath, encodeVideo) {
const ffmpegArgs = [ const ffmpegArgs = [
'-i', filePath, ...videoArgs, '-an', '-i', filePath, ...videoArgs, '-an',
'-y', '-y', outPath,
outPath,
]; ];
console.log('ffmpeg', ffmpegArgs.join(' ')); console.log('ffmpeg', ffmpegArgs.join(' '));

View File

@ -103,6 +103,7 @@ class App extends React.Component {
includeAllStreams: false, includeAllStreams: false,
captureFormat: 'jpeg', captureFormat: 'jpeg',
customOutDir: undefined, customOutDir: undefined,
keyframeCut: false,
}; };
this.state = { this.state = {
@ -316,15 +317,12 @@ class App extends React.Component {
async cutClick() { async cutClick() {
if (this.state.working) return alert('I\'m busy'); if (this.state.working) return alert('I\'m busy');
const cutStartTime = this.state.cutStartTime; const {
const cutEndTime = this.state.cutEndTime; cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
const filePath = this.state.filePath; stripAudio, keyframeCut,
const outputDir = this.state.customOutDir; } = this.state;
const fileFormat = this.state.fileFormat;
const videoDuration = this.state.duration;
const rotation = this.isRotationSet() ? this.getRotation() : undefined; const rotation = this.isRotationSet() ? this.getRotation() : undefined;
const includeAllStreams = this.state.includeAllStreams;
const stripAudio = this.state.stripAudio;
if (!this.isCutRangeValid()) { if (!this.isCutRangeValid()) {
return alert('Start time must be before end time'); return alert('Start time must be before end time');
@ -333,16 +331,17 @@ class App extends React.Component {
this.setState({ working: true }); this.setState({ working: true });
try { try {
return await ffmpeg.cut({ return await ffmpeg.cut({
customOutDir: outputDir, customOutDir,
filePath, filePath,
format: fileFormat, format: fileFormat,
cutFrom: cutStartTime, cutFrom: cutStartTime,
cutTo: cutEndTime, cutTo: cutEndTime,
cutToApparent: this.getApparentCutEndTime(), cutToApparent: this.getApparentCutEndTime(),
videoDuration, videoDuration: duration,
rotation, rotation,
includeAllStreams, includeAllStreams,
stripAudio, stripAudio,
keyframeCut,
onProgress: progress => this.onCutProgress(progress), onProgress: progress => this.onCutProgress(progress),
}); });
} catch (err) { } catch (err) {
@ -547,6 +546,13 @@ class App extends React.Component {
</div> </div>
<div className="right-menu"> <div className="right-menu">
<button
title={`Cut mode ${this.state.keyframeCut ? 'nearest keyframe cut' : 'normal cut'}`}
onClick={withBlur(() => this.setState({ keyframeCut: !this.state.keyframeCut }))}
>
{this.state.keyframeCut ? 'kc' : 'nc'}
</button>
<button <button
title={`Set output streams. Current: ${this.state.includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`} title={`Set output streams. Current: ${this.state.includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`}
onClick={withBlur(() => this.toggleIncludeAllStreams())} onClick={withBlur(() => this.toggleIncludeAllStreams())}
@ -555,7 +561,7 @@ class App extends React.Component {
</button> </button>
<button <button
title={`Delete audio? Current: ${this.state.stripAudio ? 'delete audio tracks' : "don't delete audio tracks"}`} title={`Delete audio? Current: ${this.state.stripAudio ? 'delete audio tracks' : 'keep audio tracks'}`}
onClick={withBlur(() => this.setState({ stripAudio: !this.state.stripAudio }))} onClick={withBlur(() => this.setState({ stripAudio: !this.state.stripAudio }))}
> >
{this.state.stripAudio ? 'da' : 'ka'} {this.state.stripAudio ? 'da' : 'ka'}
@ -572,7 +578,7 @@ class App extends React.Component {
title={`Custom output dir (cancel to restore default). Current: ${this.getOutputDir() || 'Not set (use input dir)'}`} title={`Custom output dir (cancel to restore default). Current: ${this.getOutputDir() || 'Not set (use input dir)'}`}
onClick={withBlur(() => this.setOutputDir())} onClick={withBlur(() => this.setOutputDir())}
> >
{this.getOutputDir() ? `...${this.getOutputDir().substr(-10)}` : 'OUTDIR'} {this.getOutputDir() ? 'cd' : 'id'}
</button> </button>
<i <i