diff --git a/public/ffmpeg.js b/public/ffmpeg.js index 2a4dca2f..55a5fa71 100644 --- a/public/ffmpeg.js +++ b/public/ffmpeg.js @@ -284,18 +284,29 @@ function getFffmpegJpegQuality(quality) { return Math.min(Math.max(qMin, quality, Math.round((1 - quality) * (qMax - qMin) + qMin)), qMax); } -async function captureFrames({ from, to, videoPath, outPathTemplate, quality, filter, framePts, onProgress }) { - const ffmpegQuality = getFffmpegJpegQuality(quality); +function getQualityOpts({ captureFormat, quality }) { + if (captureFormat === 'jpeg') return ['-q:v', getFffmpegJpegQuality(quality)]; + if (captureFormat === 'webp') return ['-q:v', Math.max(0, Math.min(100, Math.round(quality * 100)))]; + return []; +} +function getCodecOpts(captureFormat) { + if (captureFormat === 'webp') return ['-c:v', 'libwebp']; // else we get only a single file for webp https://github.com/mifi/lossless-cut/issues/1693 + return []; +} + +async function captureFrames({ from, to, videoPath, outPathTemplate, quality, filter, framePts, onProgress, captureFormat }) { const args = [ '-ss', from, '-i', videoPath, '-t', Math.max(0, to - from), - '-q:v', ffmpegQuality, + ...getQualityOpts({ captureFormat, quality }), ...(filter != null ? ['-vf', filter] : []), // https://superuser.com/questions/1336285/use-ffmpeg-for-thumbnail-selections ...(framePts ? ['-frame_pts', '1'] : []), '-vsync', '0', // else we get a ton of duplicates (thumbnail filter) + ...getCodecOpts(captureFormat), + '-f', 'image2', '-y', outPathTemplate, ]; diff --git a/src/hooks/useFrameCapture.js b/src/hooks/useFrameCapture.js index d302f687..2ed81288 100644 --- a/src/hooks/useFrameCapture.js +++ b/src/hooks/useFrameCapture.js @@ -34,7 +34,7 @@ export default ({ formatTimecode, treatOutputFileModifiedTimeAsStart }) => { const outPathTemplate = getSuffixedOutPath({ customOutDir, filePath, nameSuffix: nameTemplateSuffix }); const firstFileOutPath = getSuffixedOutPath({ customOutDir, filePath, nameSuffix }); - await ffmpegCaptureFrames({ from: fromTime, to: toTime, videoPath: filePath, outPathTemplate, quality, filter, onProgress }); + await ffmpegCaptureFrames({ from: fromTime, to: toTime, videoPath: filePath, outPathTemplate, captureFormat, quality, filter, onProgress }); return firstFileOutPath; } @@ -42,7 +42,7 @@ export default ({ formatTimecode, treatOutputFileModifiedTimeAsStart }) => { // see https://github.com/mifi/lossless-cut/issues/1139 const tmpSuffix = 'llc-tmp-frame-capture-'; const outPathTemplate = getSuffixedOutPath({ customOutDir, filePath, nameSuffix: getSuffix(`${tmpSuffix}%d`) }); - await ffmpegCaptureFrames({ from: fromTime, to: toTime, videoPath: filePath, outPathTemplate, quality, filter, framePts: true, onProgress }); + await ffmpegCaptureFrames({ from: fromTime, to: toTime, videoPath: filePath, outPathTemplate, captureFormat, quality, filter, framePts: true, onProgress }); const outDir = getOutDir(customOutDir, filePath); const files = await readdir(outDir);