support for grabbing a frame at 4k/hevc to jpg even though the preview is not native #88

pull/285/head
Mikael Finstad 2020-03-28 22:50:21 +08:00
rodzic bde25d2265
commit d683fbbb69
3 zmienionych plików z 41 dodań i 10 usunięć

Wyświetl plik

@ -35,7 +35,7 @@ import { loadMifiLink } from './mifi';
import { primaryColor, controlsBackground, waveformColor } from './colors';
import { showMergeDialog, showOpenAndMergeDialog } from './merge/merge';
import allOutFormats from './outFormats';
import captureFrame from './capture-frame';
import { captureFrameFromTag, captureFrameFfmpeg } from './capture-frame';
import {
defaultProcessedCodecTypes, getStreamFps, isCuttingStart, isCuttingEnd,
getDefaultOutFormat, getFormatData, renderFrame, mergeAnyFiles, renderThumbnails as ffmpegRenderThumbnails,
@ -920,15 +920,16 @@ const App = memo(() => {
exportExtraStreams, nonCopiedExtraStreams, outputDir, shortestFlag,
]);
// TODO use ffmpeg to capture frame
const capture = useCallback(async () => {
if (!filePath) return;
if (html5FriendlyPath || dummyVideoPath) {
errorToast(i18n.t('Capture frame from this video not yet implemented'));
return;
}
try {
const outPath = await captureFrame(customOutDir, filePath, videoRef.current, currentTimeRef.current, captureFormat);
const mustCaptureFfmpeg = html5FriendlyPath || dummyVideoPath;
const currentTime = currentTimeRef.current;
const video = videoRef.current;
const outPath = mustCaptureFfmpeg
? await captureFrameFfmpeg({ customOutDir, videoPath: filePath, currentTime, captureFormat, duration: video.duration })
: await captureFrameFromTag({ customOutDir, filePath, video, currentTime, captureFormat });
toast.fire({ icon: 'success', title: `${i18n.t('Screenshot captured to:')} ${outPath}` });
} catch (err) {
console.error(err);

Wyświetl plik

@ -2,6 +2,8 @@ import strongDataUri from 'strong-data-uri';
import { formatDuration, getOutPath, transferTimestampsWithOffset } from './util';
import { captureFrame as ffmpegCaptureFrame } from './ffmpeg';
const fs = window.require('fs-extra');
const mime = window.require('mime-types');
@ -17,7 +19,21 @@ function getFrameFromVideo(video, format) {
return strongDataUri.decode(dataUri);
}
export default async function captureFrame(customOutDir, filePath, video, currentTime, captureFormat) {
async function transferTimestamps({ duration, currentTime, fromPath, toPath }) {
const offset = -duration + currentTime;
await transferTimestampsWithOffset(fromPath, toPath, offset);
}
export async function captureFrameFfmpeg({ customOutDir, videoPath, currentTime, captureFormat, duration }) {
const time = formatDuration({ seconds: currentTime, fileNameFriendly: true });
const outPath = getOutPath(customOutDir, videoPath, `${time}.${captureFormat}`);
await ffmpegCaptureFrame({ timestamp: currentTime, videoPath, outPath });
await transferTimestamps({ duration, currentTime, fromPath: videoPath, toPath: outPath });
return outPath;
}
export async function captureFrameFromTag({ customOutDir, filePath, video, currentTime, captureFormat }) {
const buf = getFrameFromVideo(video, captureFormat);
const ext = mime.extension(buf.mimetype);
@ -25,7 +41,7 @@ export default async function captureFrame(customOutDir, filePath, video, curren
const outPath = getOutPath(customOutDir, filePath, `${time}.${ext}`);
await fs.writeFile(outPath, buf);
const offset = -video.duration + currentTime;
await transferTimestampsWithOffset(filePath, outPath, offset);
await transferTimestamps({ duration: video.duration, currentTime, fromPath: filePath, toPath: outPath });
return outPath;
}

Wyświetl plik

@ -631,6 +631,20 @@ export async function renderFrame(timestamp, filePath, rotation) {
return URL.createObjectURL(blob);
}
// see capture-frame.js
export async function captureFrame({ timestamp, videoPath, outPath }) {
const args = [
'-ss', timestamp,
'-i', videoPath,
'-vframes', '1',
'-q:v', '3',
'-y', outPath,
];
const ffmpegPath = getFfmpegPath();
await execa(ffmpegPath, args, { encoding: null });
}
// https://www.ffmpeg.org/doxygen/3.2/libavutil_2utils_8c_source.html#l00079
export const defaultProcessedCodecTypes = [
'video',