ffmpegwasm / ffmpeg.wasm

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

Any plans to have a version without SharedArrayBuffer? #137

Closed PaTiToMaSteR closed 3 years ago

PaTiToMaSteR commented 3 years ago

Is your feature request related to a problem? Please describe. It's a known problem that SharedArrayBuffer was removed due to Spectre and Meldown hacks.

Describe the solution you'd like A safe version without SharedArrayBuffer

Describe alternatives you've considered I don't have the knowledge in Emcripten nor how you transpile the code to know if it's possible.

Additional context To be able to run this amazing project everywhere we need a version without SharedArrayBuffer!

brunoluiz commented 2 years ago

After seeing so many replies with the same subject, I am going to publish here how to deal with this and how you can compile ffmpeg yourself (some might be afraid or lazy).

Because of the nature of ffmpeg, it is better to have multi-threading to have a better performance. This requires SharedArrayBuffer though, which is the reason many complain it doesn't work / it is not production ready.

@jeromewu already added support to single-thread in the main branch of ffmpeg.wasm-core. The trick is that it is not being published to npm, but it is ready for use basically (trust me). Saying that, to use it you have to do two things:

Build ffmpeg.wasm-core

  1. You can build manually by heading to https://github.com/ffmpegwasm/ffmpeg.wasm-core and following the instructions
  2. You can:

Fix ffmpeg.wasm

In your application, you will realise quite quickly that just using the new compiled ffmpeg will not work (some error message around main or proxy_main).

When you build without multi-threading, proxy_main is not published anymore, if i understood correctly. It is because this is used as a proxy entry point by ECMScripten when dealing with multi-threading + workers to not block the main thread (more details here). With the single-threaded version you want to use main as an entry point.

The easiest way to deal with this is (again) forking this repository, applying this patch and build a dist: https://github.com/ffmpegwasm/ffmpeg.wasm/pull/235. This one is quite straight forward to do locally (npm ci && npm build basically).

Now, in theory your application should work with the following options:

const ffmpeg = createFFmpeg({
  corePath: './<path-to-ffmpeg-core-dist>/ffmpeg-core.js',
  mainName: 'main'
});

Conclusion

While someone doesn't push these changes in a nicer packaged way, there are still work arounds.

About Chrome/Firefox extensions

It seems Chrome manifest v2 extensions have support to SharedArrayBuffer, but Firefox doesn't (as of Jan 2021). So, if you are planning to use the instructions above for browser extensions, bear in mind you will need two versions of the same thing.

brunoluiz commented 2 years ago

I usually vendor the libs used on my Chrome Extensions, so if anyone is looking for compiled versions of the ffmpegwasm-core single-threaded, you are welcome to copy the files at src/vendor/ffmpeg-st*: https://github.com/brunoluiz/gifsane-extension. The ffmpegwasm in there as well is patched to fix the proxy aforementioned issues.

I've written a post with the tricks and caveats around implementing FFmpeg in an extension. Mainly a bit of pain on Firefox, due to the missing SharedArrayBuffer, but I had my way: https://brunoluiz.net/blog/2022/jan/gif-sane-playback-control-ffmpegwasm/

merri-ment commented 2 years ago

Hi @brunoluiz

Thanks for putting together the post.

I have followed it step-by-step (and also tried your vendor files), but i keep coming up against this error when running the single threaded version (Chrome emulator, v98);

Uncaught (in promise) RuntimeError: abort(CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 0a 76 61 72 @+0). Build with -s ASSERTIONS=1 for more info.

and similar, yet differently worded error on Safari v15.3 ;

Unhandled Promise Rejection: RuntimeError: abort(CompileError: WebAssembly.Module doesn't parse at byte 0: module doesn't start with '\0asm'). Build with -s ASSERTIONS=1 for more info.

My implementation;

import { createFFmpeg } from '@/vendor/ffmpeg.min'

load = () => {
    return new Promise(async resolve => {
      this.ffmpeg = createFFmpeg({
        log: true,
        corePath: './ffmpeg/ffmpeg-core.st.js',
        mainName: 'main',
      })
      await this.ffmpeg.load()
      resolve()
   })
}

Have you encountered this?

Any help appreciated, Thanks Cam

merri-ment commented 2 years ago

Ok, managed to resolve it. It was to do with the 'st' / 'mt' - id's within the file name.
I didn't following your symbolic link approach, therefore the ffmpeg-core.js could never locate ffmpeg-core.wasm.

web86 commented 2 years ago

Hello @brunoluiz Thanks for the guidence, I have followed all steps, but stacked with the last one:

The easiest way to deal with this is (again) forking this repository, applying this patch and build a dist: #235. This one is quite straight forward to do locally (npm ci && npm build basically).

Sorry for maybe noob question. After changing createFFmpeg.js acording to #235 I should build a package, from this point could you explane more detaily? How to build the package? Can I build it via workflows/main.yml? If so could you share your yml script?

brunoluiz commented 2 years ago

Sorry for maybe noob question. After changing createFFmpeg.js acording to https://github.com/ffmpegwasm/ffmpeg.wasm/pull/235 I should build a package, from this point could you explane more detaily? How to build the package? Can I build it via workflows/main.yml? If so could you share your yml script?

To build the package, you need to:

  1. Clone the ffmpeg.wasm locally
  2. Apply the patch
  3. Call npm ci (installs dependencies)
  4. Call npm build
  5. (Optional) If you want to have your own patched version saved somewhere, you might want to push to your own fork

This will generate a new JS package. The caveat is that, if you want to install in other projects through npm, you might need to publish in npm. You can publish perhaps under a personal scope, such as @USER/ffmpegwasm or something, but PLEASE leave a README note saying that this is a fork etc. As I used it in a Chrome extension, I didn't need to go through this (copied to a local lib folder).

jimbojw commented 1 year ago

What's the conclusion for this? Are we still patching and building ourselves, or is there a simple way to disable the dependency on SharedArrayBuffer?

jimbojw commented 1 year ago

I added mainName: 'main' to the options to my createFfmpeg() call, but it still throws a ReferenceError about SharedArrayBuffer not being defined.

jimbojw commented 1 year ago

Looks like the problem is that this fix is in 0.11.1, but the highest version on npm is 0.11.0.

jimbojw commented 1 year ago

Update: Yes, if you fetch the 0.11.1 version, which is not yet available on npm, you can use the mainName option.

FrenchGithubUser commented 1 year ago

Any news about a potential easier way instead of having to build ourselves ?

jimbojw commented 1 year ago

@FrenchGithubUser The unreleased v0.12.0-alpha works in non-SharedArrayBuffer mode. Code: https://github.com/ffmpegwasm/ffmpeg.wasm/tree/v0.12.0-alpha.1

You can install it from npm: npm install @ffmpeg/ffmpeg@0.12.0-alpha.1

However, note that the API is pretty different, and there aren't good docs yet.

FrenchGithubUser commented 1 year ago

I'm having an issue trying to run the latest beta version. Could someone help me figuring out what is wrong with what I did ?

Versions used :

    "@ffmpeg/core": "^0.12.0-alpha.2"
    "@ffmpeg/ffmpeg": "^0.12.0-alpha.1"

Here is my code :

import { FFmpeg } from '@ffmpeg/ffmpeg'

      let load = () => {
        return new Promise(async (resolve) => {
          const ffmpeg = new FFmpeg({
            log: true,
            corePath: './ffmpeg/ffmpeg-core.js',
            mainName: 'main',
          })
          ffmpeg.load()
          resolve()
        })
      }
      load()

And I'm getting these errors :

failed to send download progress event:  RangeError: Invalid typed array length: -1
    at new Uint8Array (<anonymous>)
    at downloadWithProgress (utils.js?0649:25:1)
    at async toBlobURL (utils.js?0649:61:1)
    at async load (worker.js?ab3b:16:1)
    at async self.onmessage (worker.js?ab3b:80:1)
1 Uncaught (in promise) TypeError: Failed to execute 'arrayBuffer' on 'Response': body stream is locked
    at downloadWithProgress (utils.js?0649:45:1)
    at async toBlobURL (utils.js?0649:61:1)
    at async load (webpack-internal:///./node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js:20:19)
    at async self.onmessage (webpack-internal:///./node_modules/@ffmpeg/ffmpeg/dist/esm/worker.js:84:24)
yagudaev commented 1 year ago

@FrenchGithubUser, you might want to file a different bug report to make sure the team sees it and clearly indicate the alpha version you are using

@jimbojw we are excited to give the new alpha a spin today 😁

Boyyoka commented 1 year ago

It seems that you no longer need to build any of the libraries or to rely on the alpha version as there is a hosted single threaded version on unkpg.

  const ffmpeg = createFFmpeg({
          corePath: "https://unpkg.com/@ffmpeg/core-st/dist/ffmpeg-core.js",
          mainName: "main"
  })

I'm still looking for ways to have the corePath take a local path, but it seems to raise issues that are for another thread.

So for now I'll just rely on the above solution. I hope this can help someone out there.

Rizzist commented 11 months ago

It seems that you no longer need to build any of the libraries or to rely on the alpha version as there is a hosted single threaded version on unkpg.

  const ffmpeg = createFFmpeg({
          corePath: "https://unpkg.com/@ffmpeg/core-st/dist/ffmpeg-core.js",
          mainName: "main"
  })

I'm still looking for ways to have the corePath take a local path, but it seems to raise issues that are for another thread.

So for now I'll just rely on the above solution. I hope this can help someone out there.

I can confirm this is THE Solution saved me from devleoper hell ty dude

SpaceSaver commented 10 months ago

It seems that you no longer need to build any of the libraries or to rely on the alpha version as there is a hosted single threaded version on unkpg.

  const ffmpeg = createFFmpeg({
          corePath: "https://unpkg.com/@ffmpeg/core-st/dist/ffmpeg-core.js",
          mainName: "main"
  })

I'm still looking for ways to have the corePath take a local path, but it seems to raise issues that are for another thread.

So for now I'll just rely on the above solution. I hope this can help someone out there.

Correct me if I'm wrong, but I believe it's now just https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.4/ without the "-st." Could be something else though.

Evengard commented 10 months ago

Correct me if I'm wrong, but I believe it's now just https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.4/ without the "-st." Could be something else though.

Wouldn't that be the multithreading version? Aka st means "single thread".

SpaceSaver commented 10 months ago

Correct me if I'm wrong, but I believe it's now just https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.4/ without the "-st." Could be something else though.

Wouldn't that be the multithreading version? Aka st means "single thread".

But this is the multithreading version: https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.4/