ffmpegwasm / ffmpeg.wasm

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

Bundle worker.js into inline code #617

Open promise96319 opened 11 months ago

promise96319 commented 11 months ago

Problem

As mentioned in #594,#560,#548,there a lot issues about woker.js,I also encountered the same problem when developing chrome extension,and finally find the solution.

Now there are two ways to use @ffmpeg/ffmpeg

umd

We could load ffmpeg via cdn like https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.7/dist/umd/ffmpeg.min.js. But it will report an error like https://github.com/ffmpegwasm/ffmpeg.wasm/issues/560#issuecomment-1703065141 when we execute ffmpeg.load()

The reason is that Webpack will build the new Worker(xxx) into a seperate file,so there will be two file in umd,see here

When we load ffmpeg.js, it will load 814.ffmpeg.js, but in a wrong path.

esm

In esm module, new Worker('./worker.js') will remain in production mode. Due to different bundling tools presenting this relative path differently, it can sometimes lead to worker.js is not found.

Solution

My proposed solution is to bundle worker.js as inline code, eliminating the need to worry about the path to worker.js.

There are two potential solutions:

If necessary, I can submit a PR.

grigorebudac commented 10 months ago

I'm currently developing an npm package that utilizes ffmpeg.wasm, and I'm encountering an issue where the worker.js file isn't loading from the correct path.

To address this, I forked the contents of ffmpeg.wasm/packages/ffmpeg into my project and modified how worker.ts is loaded in classes.ts. I'm now importing worker.ts like this: import FFmpegWorker from './worker?worker&inline'; and using it in the load function as follows:

this.#worker = new FFmpegWorker();
this.#registerHandlers();

However, this change has led to other issues, primarily due to the way _coreURL is being handled lazily:

    (self as WorkerGlobalScope).createFFmpegCore = (
      (await import(
        /* webpackIgnore: true *//* @vite-ignore */ _coreURL
      )) as ImportedFFmpegCoreModuleFactory
    ).default;

I also attempted to directly import ffmpeg-core.js here, without using dynamic import, but this approach caused the build process to stall and resulted in errors.

PS: I have to mention that everything is being compiled through my package's vite configuration.

hxhxhx88 commented 10 months ago

@promise96319 Thanks! Could you post your code or start a PR? Really need this.

swissspidy commented 8 months ago

When we load ffmpeg.js, it will load 814.ffmpeg.js, but in a wrong path.

Came here to report the same. import.meta.url is resolved to file:///home/jeromewu/ffmpeg.wasm/packages/ffmpeg/dist/esm/classes.js 🙃

Issues like this currently prevent me from upgrading to 0.12 unfortunately.

superbartman commented 8 months ago

@promise96319 Thanks! Could you post your code or start a PR? Really need this.

@promise96319 +1

thebongy commented 7 months ago

Came here to report the same. import.meta.url is resolved to file:///home/jeromewu/ffmpeg.wasm/packages/ffmpeg/dist/esm/classes.js 🙃

Yea, I had to end up writing a manual patch script which runs post npm i and fixes up the paths to my own self-hosted versions. It all works fine after that....

ICEDLEE337 commented 7 months ago

@thebongy would you mind sharing your script as a gist? please and thank you!

thebongy commented 7 months ago

@ICEDLEE337 here's the script: Here, /static/814.ffmpeg.js should be whatever relative path where you actually host the 814.ffmpeg.js file in your case under the same domain.

const fs = require('fs');

fs.readFile('node_modules/@ffmpeg/ffmpeg/dist/umd/ffmpeg.js', 'utf8', (err, data) => {
    if (err) {
        console.error(err);
        return;
    }
    const result = data.replace('new URL(e.p+e.u(814),e.b)', `'/static/814.ffmpeg.js'`);
    fs.writeFile('node_modules/@ffmpeg/ffmpeg/dist/umd/ffmpeg.js', result, 'utf8', (err) => {
        if (err) {
            console.error(err);
            return;
        }
    });
});
EnixCoda commented 5 months ago

Thanks, my project, which was setup with Vite, finally works in this way, for both dev and prod usages. No need to configure vite config with exclude or worker fields.

import { FFmpeg } from "@ffmpeg/ffmpeg";
import workerUrl from "@ffmpeg/ffmpeg/dist/esm/worker.js?worker&url";
import { toBlobURL } from "@ffmpeg/util";

const ffmpeg = new FFmpeg();
const baseURL = `https://unpkg.com/@ffmpeg/core-mt@0.12.6/dist/esm`;
await ffmpeg.load({
  coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
  wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"),
  workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"),
  classWorkerURL: new URL(workerUrl, import.meta.url).toString(),
});
ghnp5 commented 2 months ago

I'm interested in this.

I've also noticed that in the production's bundle, my local path is also leaking:

(this.#t=r?new Worker(new URL(r,"file:///F:/repos/myrepo/node_modules/@ffmpeg/ffmpeg/dist/esm/classes.js"),{type:"module"}):new Worker(new URL(t.p+t.u(138),t.b),{type:void 0})

The second new Worker(...) is what is used, and loads 138.bundle.js correctly.

However, it would be much cleaner if we could have everything in just 1 bundle, also to handle cache well, as 138.bundle.js could be updated, but users will be using the cached version, and potentially cause problems, maybe.

ghnp5 commented 2 months ago

Alright... it was very hard to get this working when you want to host all the files, but I was able in the end.

There are indeed issues with the way the worker loads and gets bundled, so I had to create some patches in the gulpfile.

And it's not easy at all to debug errors happening in the ffmpeg modules, especially due to a catch that ignores the exception completely, making it impossible to know why it failed!

Here's how I have it, for now...

const loadFFmpeg = async () => {
    const coreURL = await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript');
    const workerURL = await toBlobURL(`${baseURL}/ffmpeg-worker.js`, 'text/javascript');
    const wasmURL = await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm');

    const ffmpeg = new FFmpeg();
    await ffmpeg.load({
        coreURL,
        wasmURL,
        workerURL
    });

    return ffmpeg;
};

Then these replaces in the gulpfile, against the main bundle...

.replace('r?new Worker(new URL(r,"."),{type:"module"}):new Worker(new URL(t.p+t.u(138),t.b),{type:void 0})', 'new Worker(r,{type:void 0})')
.replace('classWorkerURL:r', 'workerURL:r')
MileanCo commented 1 month ago

So im using Angular and I was only able to get it to work like this using 'http://localhost' hahah.... Otherwise it keeps trying to load from file://// and I get errors like:

ERROR DOMException: Failed to construct 'Worker': Script at 'file:///Users/jstenkvi/workspace/dev_projects/MediaGenerator/media-generator-app/node_modules/@ffmpeg/ffmpeg/dist/esm/assets/ffmpeg/worker.js' cannot be accessed from origin 'http://localhost:4200'.

        const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
        await this.ffmpeg.load({
            coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
            wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
            classWorkerURL :  'http://localhost:4200/assets/ffmpeg/worker.js',
        });

This is annoying and I wasted like 3h trying to just load this dang module today. No wonder nobody is really using this lib!

ghnp5 commented 1 month ago

@MileanCo - I also spent several hours trying to figure this out, but I didn't come here to insult the developers. I'm actually very grateful they provided this as a solution I can use. Being polite isn't too hard.

MileanCo commented 1 month ago

Yes this is a really elegant lib and I'm grateful for that! Just trying to make a statement that something so simple as trying to install/use a lib should be easy, that way it grows in popularity :) If it's such a hassle to install, most people are just going to give up and move on

sp90 commented 1 month ago

Make a PR ?