ffmpegwasm / ffmpeg.wasm

FFmpeg for browser, powered by WebAssembly
https://ffmpegwasm.netlify.app
MIT License
13.49k stars 775 forks source link

Nextjs & Vercel support #622

Open nikorainto opened 8 months ago

nikorainto commented 8 months ago

I know you mentioned in the FAQ section that Nodejs is no longer supported by default in v0.12 but is there any way to run this in Vercel using Nextjs? I got it working locally with warnings but when deploying to Vercel it fails with message:

Error: ffmpeg.wasm does not support nodejs

I don't need to run it in server-side or serverless. Client side would be enough with "use client"

CrypticSignal commented 8 months ago

@ffmpeg/ffmpeg 0.12.7 is incompatible for some reason. Use @ffmpeg/ffmpeg 0.12.6 instead.

My Next.js app deployed using Vercel works after downgrading to 0.12.6. https://av-converter.vercel.app

Source code: https://github.com/CrypticSignal/av-converter-vercel

nikorainto commented 8 months ago

Thanks for the tip! Deploy working now 👏

jonathanmv commented 7 months ago

Thank you! Downgrading from 0.12.7 to 0.12.6 worked for me as well. I am able to build and deploy now

davidtranjs commented 6 months ago

When using 0.12.6, I still have error self is not defined

image
davidtranjs commented 6 months ago

I convert my component to CSR with next/dynamic and it work now

iAmIlluminati commented 5 months ago

I am still having error, can you take a look at my code ? I am having the self is not defined error

import dynamic from 'next/dynamic';
import { fetchFile, toBlobURL } from '@ffmpeg/util';
import { useRef, useState, useEffect } from 'react';
import { FFmpeg } from '@ffmpeg/ffmpeg';
const DynamicFFmpeg = dynamic(
    () => import('@ffmpeg/ffmpeg').then(mod => mod.FFmpeg),
    { ssr: false }
);

export default function Home() {
    const [isFFmpegLoaded, setIsFFmpegLoaded] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const ffmpegRef = useRef(null);
    const videoRef = useRef(null);
    const messageRef = useRef(null);

    useEffect(() => {
        // Check if DynamicFFmpeg is loaded in the browser
        if (typeof DynamicFFmpeg !== 'undefined') {
            ffmpegRef.current = new FFmpeg();
            setIsFFmpegLoaded(true);
        }
    }, []);

    const load = async () => {
        setIsLoading(true);
        const ffmpeg = ffmpegRef.current;
        ffmpeg.on('log', ({ message }) => {
            if (messageRef.current) messageRef.current.innerHTML = message;
        });
        try {
            const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd';
            await ffmpeg.load({
                coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
                wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm')
            });
        } catch (error) {
            console.error('Failed to load FFmpeg:', error);
            setIsFFmpegLoaded(false);
        } finally {
            setIsLoading(false);
        }
    };
LyghtCode commented 3 months ago

i downgraded and it still doesnt work

LyghtCode commented 3 months ago

I see there's an App Router example but not a Page Router one

iAmIlluminati commented 3 months ago

I moved the ffmpeg logic outside of component and added it as a util function which loads only when the window is defined. It works for me.

iAmIlluminati commented 3 months ago

Calling this function if DOM window is defined.

const initializeFFmpegInstance = async (logs = false) => { const ffmpeg = new FFmpeg(); const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd'; await ffmpeg.load({ coreURL: ${baseURL}/ffmpeg-core.js, wasmURL: ${baseURL}/ffmpeg-core.wasm }); if (logs) ffmpeg.on('log', ({ message }) => { console.log(message); }); return ffmpeg; };

MertHaddad commented 1 month ago

I recommend 2 working solutions for this :

First one :

use NoSSRWrapper as in this example : https://github.com/ffmpegwasm/ffmpeg.wasm/tree/main/apps/nextjs-app/app

Second one:

dynamically import ffmpeg directly before using it :

    // Dynamically import loadFFMPEG function
    const { default: loadFFMPEG } = await import('./utils/loadFFMPEG');
    // Load FFmpeg
    const loadedFFmpeg = await loadFFMPEG();
    // Store FFmpeg instance in the ref
    ffmpegRef.current = loadedFFmpeg;