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

[Angular] Error: Failed to construct 'Worker': Script at 'file:///some-location/node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js'

Open gaurav10610 opened this issue 2 years ago • 6 comments

Describe the bug [Angular] Encountering the following error in an angular application while the app is locally served(npm serve) - Error: Failed to construct 'Worker': Script at 'file:///some-location/node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js'

To Reproduce

Expected behavior FFmpeg should be loaded correctly

Screenshots Screenshot 2023-08-15 at 12 52 28 PM

Desktop (please complete the following information):

  • OS: Mac OS
  • Browser chromium
  • Version: latest

Additional Information: Also, if we can have a small example added for Angular in the examples section then it will be really great. Thanks!

gaurav10610 avatar Aug 15 '23 07:08 gaurav10610

Similar issue here. It appears that @ffmpeg/ffmpeg must be imported from the same domain as the user's script. I am trying to import it from unpkg in a plain simple html+js page, not a single thing will make it work.

So it looks to me like this is not an Angular issue. Although for me the error is about dist/umd/814.ffmpeg.js rather than dist/esm/worker.js:

DOMException: Failed to construct 'Worker': Script at 'https://unpkg.com/@ffmpeg/ffmpeg/dist/umd/814.ffmpeg.js' cannot be accessed from origin 'http://localhost:8000'.

If i merely download the contents of @ffmpeg/ffmpeg and serve them myself, then it works properly, even importing util, core and core-mt from unpkg. The only issue is @ffmpeg/ffmpeg.

How simpler were the days without cors and domexception

WillyJL avatar Aug 22 '23 01:08 WillyJL

https://stackoverflow.com/a/25495206

WillyJL avatar Aug 22 '23 01:08 WillyJL

so because of this line: https://github.com/ffmpegwasm/ffmpeg.wasm/blob/1b2f5f14f5b4eeecb0c95588c0c337274eca7405/packages/ffmpeg/src/classes.ts#L166 and the above explanation on stackoverflow, you need headers or whatever other kind of bullshit on the domain hosting @ffmpeg/ffmpeg. So unpkg will NOT work AT ALL...

WillyJL avatar Aug 22 '23 01:08 WillyJL

@Willy-JL

Same issue using ffmpeg.wasm with Blazor WASM, but using ffmpeg files from unpkg does work after a simple patch (very similar to pull #562)

I am able to use unpkg as the host for all of the ffmpeg files (umd version) using Blob and URL.createObjectURL. But I do have to modify the ffmpeg.js code slightly to do it. In fact, that line you mentioned is the part I have to modify so that I can specify the URL that the worker is created with in the config passed to ffmpeg.load. The code patch can easily be done on the fly before creating the Blob using a single text replacement. If pull #562 gets merged, or at least a similar fix enabled, I won't need to modify ffmpeg.js at all and neither will others with similar issues like yours.

  • In the umd version of ffmpeg.js change
    new Worker(new URL(e.p+e.u(814),e.b),{type:void 0}) to new Worker(r.worker814URL,{type:void 0}).

  • Once it is patched you can create a BlobURL of it and then call import on that BlobURL.

  • Then create a BlobURL of 814.ffmpeg.js and add it to the config you send to ffmpeg.load as the property worker814URL.

No extra headers are needed from the unpkg site to use the ffmpeg files directly from them. Your server hosting your html file does need additional headers if you wish to use multithreading due to the requirements of SharedArrayBuffer. SharedArrayBuffer on MDN. I have tested the multithreaded version also using unpkg as the ffmpeg cdn and it works also.

If you think patch #562, which allows the path for the primary worker to be set via the config sent to ffmpeg.load may help resolve your issue also please go give it a thumbs up.

LostBeard avatar Sep 05 '23 18:09 LostBeard

Working code example that does on the fly patching of ffmpeg.js and uses unpkg for all ffmpeg.wasm files.

The below code must be served via http:// or https:// but will not work if served via file://

index.html

<html>
    <head>
        <script src="index.js"></script>
    </head>
    <body>
        <video autoplay muted id="video-result" controls></video><br/>
        <button disabled id="load-button">Load ffmpeg-core (~31 MB)</button><br/>
        <button disabled id="transcode-button">Transcode webm to mp4</button><br/>
        <p id="log-div"></p>
        <p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p>
    </body>
</html>

index.js

"use strict";

var loaded = false;
var ffmpeg = null;
var loadBtn = null;
var transcodeBtn = null;
var logDiv = null;
var videoEl = null;

const toBlobURLPatched = async (url, mimeType, patcher) => {
    var resp = await fetch(url);
    var body = await resp.text();
    if (patcher) body = patcher(body);
    var blob = new Blob([body], { type: mimeType });
    return URL.createObjectURL(blob);
};

const toBlobURL = async (url, mimeType,) => {
    var resp = await fetch(url);
    var body = await resp.blob();
    var blob = new Blob([body], { type: mimeType });
    return URL.createObjectURL(blob);
};

const fetchFile = async (url) => {
    var resp = await fetch(url);
    var buffer = await resp.arrayBuffer();
    return new Uint8Array(buffer);
};

const load = async () => {
    loadBtn.setAttribute('disabled', true);
    const baseURLFFMPEG = 'https://unpkg.com/@ffmpeg/[email protected]/dist/umd'
    const ffmpegBlobURL = await toBlobURLPatched(`${baseURLFFMPEG}/ffmpeg.js`, 'text/javascript', (js) => {
        return js.replace('new URL(e.p+e.u(814),e.b)', 'r.worker814URL');
    });
    const baseURLCore = 'https://unpkg.com/@ffmpeg/[email protected]/dist/umd'
    const config = {
        worker814URL: await toBlobURL(`${baseURLFFMPEG}/814.ffmpeg.js`, 'text/javascript'),
        coreURL: await toBlobURL(`${baseURLCore}/ffmpeg-core.js`, 'text/javascript'),
        wasmURL: await toBlobURL(`${baseURLCore}/ffmpeg-core.wasm`, 'application/wasm'),
    };
    await import(ffmpegBlobURL);
    ffmpeg = new FFmpegWASM.FFmpeg();
    ffmpeg.on('log', ({ message }) => {
        logDiv.innerHTML = message;
        console.log(message);
    });
    await ffmpeg.load(config);
    loaded = true;
    console.log('ffmpeg.load success');
    transcodeBtn.removeAttribute('disabled');
}

const transcode = async () => {
    transcodeBtn.setAttribute('disabled', true);
    await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));
    await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);
    const data = await ffmpeg.readFile('output.mp4');
    videoEl.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
}

addEventListener("load", async (event) => {
    loadBtn = document.querySelector('#load-button');
    loadBtn.addEventListener('click', async () => await load());
    loadBtn.removeAttribute('disabled');
    transcodeBtn = document.querySelector('#transcode-button');
    transcodeBtn.addEventListener('click', async () => await transcode());
    logDiv = document.querySelector('#log-div');
    videoEl = document.querySelector('#video-result');
    console.log('window loaded');
});

LostBeard avatar Sep 05 '23 20:09 LostBeard

In webpack, this issue is still exists. @jeromewu imageimage

Not sure if this has reference value: https://webpack.docschina.org/guides/asset-modules/#url-assets

superbartman avatar Feb 21 '24 08:02 superbartman

Hi. I've described the solution here)

https://www.linkedin.com/pulse/web-based-video-conversion-from-webm-mp4-ffmpegwasm-hladchenko-wfejf/?trackingId=d9wnxEdbS%2FOhd%2Fye5xtGmQ%3D%3D

vovan1710 avatar Apr 06 '24 16:04 vovan1710