How to deploy ffmpeg.wasm application to GitHub Pages

dannadori
6 min readOct 14, 2022

Note:
This article is also available here.(Japanese)
https://cloud.flect.co.jp/entry/2022/10/14/115344

Introduction

It has been quite a while since the announcement of the end of the Heroku free plan, and only a little over a month remains until the end of the free plan. We hope that you are preparing to move to a paid plan or are in the process of migrating to a different service. If you are not sure what to do, you may want to refer to the various comparisons that have been made by well-informed people (ref, ref).

I have always deployed experimental apps on Heroku, and I have had a lot of help from it. It may be a matter of familiarity, but I find Heroku’s development UX to be very good, and I think I may continue to use Heroku for apps that require server-side processing. On the other hand, for so-called static apps that do not require server-side processing, GitHub Pages seems like a good and easy way because we can manage the source code and apps in a same repository.

Now, I have a few apps that I deployed to Heroku that use ffmpeg.wasm, but there is actually a problem when deploying them to GitHub Pages. COOP, COEP restrictions, and the browser will not be able to process the file. In this issue, we would like to provide a brief explanation of this and how to work around it. The ffmpeg.wasm has COOP and COEP restrictions that prevent the browser from processing movies. In this article, we would like to provide a brief explanation of this and how to avoid it.

GitHub Pages

The exact description of GitHub Pages can be found on the official page, but a rough description here is as follows.

GitHub Pages is a feature that allows you to publish HTML stored in a specific folder in a public repository as a web page. Javascript can also be loaded from this HTML, so it can be published as a web application. You need to set up GitHub Pages public settings in advance, but you can easily do so in the public repository settings screen of GitHub.

ffmpeg.wasm and SharedArrayBuffer

ffmpeg, which probably needs no explanation, is an open source software mainly used to convert video and audio. ffmpeg.wasm is a module that compiles ffmpeg into WebAssembly (wasm) so that it can be used in a browser.

ffmpeg.wasm internally uses a feature called SharedArrayBuffer, which allows multiple threads (multiple execution contexts) to share memory. This explanation is a bit old, but it is easy to understand (Japanese page).

SharedArrayBufferとCOOPとCOEP

Now, here is where the problem gets a little more difficult. I am not confident that I can explain it accurately, so please refer to the links for more details. However, I will try my best to explain it here as well.

Spectre was once discovered and became a big problem. Since the discovery of Spectre, SharedArrayBuffer is also in danger. Spectre is an attack technique that infers information from the wreckage caused by speculative CPU execution. It is a hardware-level vulnerability that is difficult to counteract at the application level. SharedArrayBuffer is also at risk of being exposed to this attack method, but since it is difficult to prevent this in the browser, Chrome only allows SharedArrayBuffer to be used in environments with strict cross-origin isolation(ref).

To achieve this cross-origin isolation, COOP and COEP must be set in the HTTP header.

COOP stands for Cross Origin Opener Policy. It allows you to set up a reference relationship between Cross Origin objects that exist in the same Browsing Context Group. By setting this value to same-origin, you can prevent Cross Origin objects from referencing each other even if they exist in the same Browsing Context Group.

COEP stands for Cross Origin Embedder Policy. when loading Cross Origin resources, you can enforce that the resource being loaded has CORS set. to enforce that CORS is set, set require- CORS to enforce that CORS is set.

Specifically, the following HTTP header should be sent in the main document.

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

This video and this video are a good explanation of this.

GitHub Pages HTTP headers is not configurable

As we have explained, ffmpeg.wasm uses SharedArrayBuffer, which is only available in environments with cross-origin isolation set. cross-origin isolation is set in the HTTP header.

However, we can not configure GitHub Pages HTTP headers. This is a specification. In other words, cross-origin isolation cannot be realized with GitHub Pages. So, unfortunately, you have to find another service… No!, I would like to think of a workaround.

HTTP header rewriting with Service Worker

There is a way to add HTTP headers in the browser. Service Workers are often described as transparent proxies used when you want to run a web application offline. Service Worker allows you to hook into the browser’s fetch process and add processing to it. When offline, you can keep the web application running by adding a process to return cached data in the fetch process.

So, as you may have guessed, we can set up cross-origin isolation by using a service worker to add COOP and COEP headers to the HTTP header for the fetch process. And this can actually be done.

It is not too difficult to implement it by yourself, but there is a person who has published a library on Github, so I would like to use it this time.

The main body of the Service Worker is coi-serviceworker.js, a script of about 100 lines (as of 2022/10/14). The following part, which is described around the middle of the script, is where the header is added.

event.respondWith(
fetch(request)
.then((response) => {
if (response.status === 0) {
return response;
}

const newHeaders = new Headers(response.headers);
newHeaders.set("Cross-Origin-Embedder-Policy",
coepCredentialless ? "credentialless" : "require-corp"
);
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");

return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
})
.catch((e) => console.error(e))
);

By loading this Javascirpt from the main HTML, the cross-origin isolation can be successfully configured and ffmpeg.wasm can be used.

In this way, you can publish a web application that uses ffmpeg.wasm on GitHub Pages. Please note that cross-origin isolation is the responsibility of the user to opt-in.

Introduction of my applications published on GitHub Pages

Finally, we would like to introduce some of the applications we were able to publish on GitHub Pages.

Screen Recorder

This application captures PC screen as video. It runs in your browser, so there is no need to install a new application. It works on Chrome on Windows, Mac, and Linux, but not on Safari.

Simple Movie Editor and Ffmpeg CLI Generator

This is an application for processing videos. You can edit the start and end time of the video. It can cut out specified areas and add blurring. It also generates a CLI for ffmpeg. You can copy and paste the CLI and run it when processing is slow on the browser.

Offline Transcribe

This is an application for voice transcription. It supports multiple languages including Japanese and English.

Finally

We have introduced how to run an application using ffmpeg.wasm on GitHub Pages. We hope this will be of some help to those who are having trouble finding a place to deploy.

I need coffee!!

Disclaimer

In no event shall we be liable for any direct, indirect, consequential, or special damages arising out of the use or inability to use the software on this blog.

--

--