ffmpegwasm / ffmpeg.wasm

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

Help and explanation please: `Uncaught (in promise) RuntimeError: null function or function signature mismatch` + Code #665

Open piscopancer opened 6 months ago

piscopancer commented 6 months ago

I do not actually know what this error means on the language of this package, but I can provide you with a simple example where it appears and breaks the worker (meaning worker becomes unusable until a page reload).

image

Here is the video.

https://github.com/ffmpegwasm/ffmpeg.wasm/assets/109352196/0e88899b-d14a-4fc4-8937-ef68672d2d50

See the code below.

Next 14.0.4

A simple mockup project I bootstrapped just to show the error in your package guys

'use client'

import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
import { downloadZip } from 'client-zip'
import { useRef, useState } from 'react'

export default function HomePage() {
  const [loaded, setLoaded] = useState(false)
  const [isLoading, setIsLoading] = useState(false)
  const ffmpegRef = useRef(new FFmpeg())

  const load = async () => {
    setIsLoading(true)
    const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd'
    const ffmpeg = ffmpegRef.current
    await ffmpeg.load({
      coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
      wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
    })
    setLoaded(true)
    setIsLoading(false)
  }

  const convertAndDownload = async (files: File[]) => {
    const ffmpeg = ffmpegRef.current
    const convertedFiles = await Promise.all(
      files.map(async (song) => {
        const key = song.size
        const iName = key + '.mp3'
        const oName = key + '.ogg'
        await ffmpeg.writeFile(iName, await fetchFile(song))
        await ffmpeg.exec(['-i', iName, oName])
        const o = (await ffmpeg.readFile(oName)) as Uint8Array
        return new File([o.buffer], song.name, { type: 'audio/ogg' })
      })
    )

    const blob = await downloadZip(convertedFiles).blob()

    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'songs.zip'
    a.click()
  }

  return (
    <main>
      {loaded ? (
        <input
          type='file'
          multiple
          name=''
          id=''
          accept='audio/mpeg'
          onChange={async (e) => {
            const mp3Files = Array.from(e.target.files!)
            convertAndDownload(mp3Files)
          }}
        />
      ) : (
        <button onClick={load}>{isLoading ? 'loading...' : 'load'}</button>
      )}
    </main>
  )
}

If you ever encountered this error, please give your feedback and solutions. Thanks

piscopancer commented 5 months ago

I also tried recreating Ffmpeg instance for every new file I wanted to convert following the logic "If one worker can do one file, I need a new instance for each file" which took the shape of this code:

- it did not help btw, same error 😞
async function onSongsInput(e: React.ChangeEvent<HTMLInputElement>) {
    const files = Array.from(e.target.files!)
    for (let i = 0; i < files.length; i++) {
      const ffmpeg = new FFmpeg()
      ffmpeg.on('progress', ({ progress }) => {
        setProgress(progress)
      })
      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'),
      })

      console.log('starting ' + i)
      console.log(files[i])
      await convertAndDownload(ffmpeg, files[i])
      ffmpeg.terminate()
    }
  }

    const convertAndDownload = async (ffmpeg: FFmpeg, song: File) => {
    const key = song.size
    const iName = key + '.mp3'
    const oName = key + '.ogg'
    await ffmpeg.writeFile(iName, await fetchFile(song))
    await ffmpeg.exec(['-i', iName, oName])
    const o = (await ffmpeg.readFile(oName)) as Uint8Array
    const convertedSong = new File([o.buffer], oName, { type: 'audio/ogg' })

    const blob = await downloadZip([convertedSong]).blob()

    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'songs.zip'
    a.click()
  }