ffmpeg.wasm icon indicating copy to clipboard operation
ffmpeg.wasm copied to clipboard

Nextjs & Vercel support

Open nikorainto opened this issue 2 years ago • 18 comments
trafficstars

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"

nikorainto avatar Nov 04 '23 07:11 nikorainto

@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

CrypticSignal avatar Nov 05 '23 23:11 CrypticSignal

Thanks for the tip! Deploy working now 👏

nikorainto avatar Nov 06 '23 17:11 nikorainto

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

jonathanmv avatar Nov 21 '23 12:11 jonathanmv

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

image

davidtranjs avatar Jan 10 '24 03:01 davidtranjs

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

davidtranjs avatar Jan 10 '24 03:01 davidtranjs

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/[email protected]/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);
        }
    };

iAmIlluminati avatar Jan 19 '24 03:01 iAmIlluminati

i downgraded and it still doesnt work

LyghtCode avatar Mar 22 '24 05:03 LyghtCode

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

LyghtCode avatar Mar 22 '24 05:03 LyghtCode

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 avatar Mar 22 '24 21:03 iAmIlluminati

Calling this function if DOM window is defined.

const initializeFFmpegInstance = async (logs = false) => { const ffmpeg = new FFmpeg(); const baseURL = 'https://unpkg.com/@ffmpeg/[email protected]/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; };

iAmIlluminati avatar Mar 22 '24 21:03 iAmIlluminati

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;

MertHaddad avatar May 11 '24 10:05 MertHaddad