ffmpegwasm / ffmpegwasm/ffmpeg.wasm

Ffmpeg on NextJS do not work!?

Open
#875 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
17.8k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

Description

**Describe the bug**
I am trying to get those 5 params: width: number; height: number; fps: number; totalFrame: number; sizeMb: number; durationSecond: number;
1. The user uploads the video using upload from antd (package version "^5.25.4")
2. next@15.3.3, react@19.1.0, ffmpeg/ffmpeg@0.12.15", ffmpeg/util@^0.12.2".
3. I don't have the user's full file path because of a security issue in the browser.
4. The method "getVideoDetail" is working, but when the video is cropped, it gives me the wrong fps
5. The method "getVideoDetailFfmpeg" is giving me this error: "FFmpeg error: ErrnoError: FS error"

PS: I'm new to Javascript and not sure what I'm doing wrong, or is it possible to do this on Javascript, please help me

**To Reproduce**
`// --- test-page-view.tsx ---

import React, { useRef, useState } from 'react';
import { Button, Card, Flex, Upload, Modal } from 'antd';
import { CloudUploadOutlined } from '@ant-design/icons';

import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile, toBlobURL } from '@ffmpeg/util';

const ffmpeg = new FFmpeg();

export const getVideoDetailFfmpeg = async (
file: File
): Promise<{
width: number;
height: number;
fps: number;
totalFrame: number;
sizeMb: number;
durationSecond: number;
}> => {

await ffmpeg.load();

const fileName = file.name;
// 2) write file into MEMFS
await ffmpeg.writeFile(fileName, await fetchFile(file));

try {
// 3) exec ffprobe‐style JSON dump
await ffmpeg.exec([
'-i', fileName,
'-hide_banner',
'-loglevel', 'error',
'-select_streams', 'v:0',
'-show_entries', 'stream=width,height,r_frame_rate,nb_frames',
'-show_entries', 'format=duration,size',
'-of', 'json',
'metadata.json',
]);

// 4) read + parse
const rawData = await ffmpeg.readFile('metadata.json');
const rawBytes = typeof rawData === 'string'
? new TextEncoder().encode(rawData)
: rawData;
const { streams, format } = JSON.parse(new TextDecoder().decode(rawBytes));
const stream = streams[0];

// 5) compute
const safeDiv = (ratio: string) => {
const [n, d] = ratio.split('/').map(Number);
return n / (d || 1);
};
const fps = safeDiv(stream.r_frame_rate);
const durationSecond = Math.floor(Number(format.duration));
const sizeMb = Number(format.size) / 1_000_000;
const totalFrame = stream.nb_frames
? Number(stream.nb_frames)
: Math.floor(durationSecond * fps);

return {
width: stream.width,
height: stream.height,
fps: parseFloat(fps.toFixed(2)),
totalFrame,
sizeMb: parseFloat(sizeMb.toFixed(2)),
durationSecond,
};
} catch (err) {
console.error('FFmpeg error:', err);
throw err;
}
};

const getVideoDetail = (file: File): Promise<{
width: number;
height: number;
fps: number;
totalFrame: number;
sizeMb: number;
durationSecond: number;
}> => {

return new Promise((resolve, reject) => {
const video = document.createElement('video');
const url = URL.createObjectURL(file);

console.log('Video URL:', url);

video.src = url;
video.muted = true;
video.preload = 'metadata';

video.addEventListener('loadedmetadata', () => {
const width = video.videoWidth;
const height = video.videoHeight;
const durationSecond = Math.ceil(video.duration);
const sizeMb = parseFloat((file.size / (1024 * 1024)).toFixed(2));

const frameTimes: number[] = [];
const maxFrames = 60;

const onFrame = (_: number, metadata: VideoFrameCallbackMetadata) => {
frameTimes.push(metadata.mediaTime);

if (frameTimes.length < maxFrames) {
video.requestVideoFrameCallback(onFrame);
return;
}

const intervals = frameTimes.slice(1).map((t, i) => t - frameTimes[i]);
const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
const fps = parseFloat((1 / avgInterval).toFixed(2));
const totalFrame = Math.round(durationSecond * fps);
console.log('FPS frame interval:', fps, intervals.length, durationSecond);

resolve({
width,
height,
fps,
totalFrame,
sizeMb: Number(sizeMb),
durationSecond: parseFloat(durationSecond.toFixed(2))
});

URL.revokeObjectURL(url);
};

video.play().then(() => video.requestVideoFrameCallback(onFrame)).catch(reject);
});

video.addEventListener('error', reject);
});
};

export const TestPageView: React.FC = () => {
const [open, setOpen] = useState(false);
const [fileList, setFileList] = useState([]);
const [videoDetails, setVideoDetails] = useState<{
width: number;
height: number;
fps: number;
totalFrame: number;
sizeMb: number;
durationSecond: number;
} | null>(null);

const fileInputRef = useRef(null);
const uploadRef = useRef(null);

const handleChange = async (info: any) => {
const file = info.file;
if (!file?.type?.startsWith('video/')) {
setFileList([file]);
setOpen(true);
return;
}

getVideoDetailFfmpeg(file)
.then(setVideoDetails)
.catch(console.error)
.finally(() => {
setFileList([file]);
setOpen(true);
});
};

const closeModal = () => {
setOpen(false);
setFileList([]);
setVideoDetails(null);
if (fileInputRef.current) fileInputRef.current.value = '';
if (uploadRef.current) uploadRef.current.upload.uploader.fileInput.value = '';
};

return (






{/* Fixed by adding rev prop */}





fileInputRef.current?.click()}>Browse File
e.target.files?.[0] && handleChange({ file: e.target.files[0] })}
/>


{
console.log('Uploading:', fileList);
closeModal();
}}
>
{fileList[0] ? (


Selected file: {fileList[0].name}


{videoDetails ? (
<>

Dimensions: {videoDetails.width} × {videoDetails.height}


FPS: {videoDetails.fps}


Total frames: {videoDetails.totalFrame}


Size: {videoDetails.sizeMb} MB


Duration: {videoDetails.durationSecond} seconds



) : (

Loading video details...


)}

) : (

No file selected


)}


);
};`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.