lossless-cut/src/smartcut.js

71 wiersze
2.5 KiB
JavaScript

import { getRealVideoStreams, getVideoTimebase } from './util/streams';
import { readFrames } from './ffmpeg';
const { stat } = window.require('fs-extra');
function mapInputToOutputCodec(inputCodec) {
// if (inputCodec === 'hevc') return 'libx265';
return inputCodec;
}
// eslint-disable-next-line import/prefer-default-export
export async function getSmartCutParams({ path, videoDuration, desiredCutFrom, streams }) {
const videoStreams = getRealVideoStreams(streams);
if (videoStreams.length > 1) throw new Error('Can only smart cut video with exactly one video stream');
const videoStream = videoStreams[0];
async function readKeyframes(window) {
const frames = await readFrames({ filePath: path, aroundTime: desiredCutFrom, streamIndex: videoStream.index, window });
return frames.filter((frame) => frame.keyframe);
}
let keyframes = await readKeyframes(10);
const keyframeAtExactTime = keyframes.find((keyframe) => Math.abs(keyframe.time - desiredCutFrom) < 0.000001);
if (keyframeAtExactTime) {
console.log('Start cut is already on exact keyframe', keyframeAtExactTime.time);
return {
cutFrom: keyframeAtExactTime.time,
videoStreamIndex: videoStream.index,
needsSmartCut: false,
};
}
const findNextKeyframe = () => keyframes.find((keyframe) => keyframe.time > desiredCutFrom); // (they are already sorted)
let nextKeyframe = findNextKeyframe();
if (!nextKeyframe) {
console.log('Cannot find any keyframe after desired start cut point, trying with larger window');
keyframes = await readKeyframes(60);
nextKeyframe = findNextKeyframe();
}
if (!nextKeyframe) throw new Error('Cannot find any keyframe after desired start cut point');
console.log('Smart cut from keyframe', { keyframe: nextKeyframe.time, desiredCutFrom });
let videoBitrate = parseInt(videoStream.bit_rate, 10);
if (Number.isNaN(videoBitrate)) {
console.warn('Unable to detect input bitrate');
const stats = await stat(path);
videoBitrate = stats.size / videoDuration;
}
const videoCodec = mapInputToOutputCodec(videoStream.codec_name);
if (videoCodec == null) throw new Error('Unable to determine codec for smart cut');
const timebase = getVideoTimebase(videoStream);
if (timebase == null) console.warn('Unable to determine timebase', videoStream.time_base);
return {
cutFrom: nextKeyframe.time,
videoStreamIndex: videoStream.index,
needsSmartCut: true,
videoCodec,
videoBitrate: Math.floor(videoBitrate),
videoTimebase: timebase,
};
}