Next.jsのssgでffmpeg.wasmがブラウザだけで動作するサイトをcloudflareにデプロイしてみた


みなさんこんにちは、かじりです。Next.jsのssgでffmpeg.wasmがブラウザだけで動作するサイトをcloudflareにデプロイしてみた時の知見を共有します。簡単に言うとSharedArrayBufferを使えるようにする必要があり、serverがresponse headerに必要な情報を付加する必要があります。そこらへんをまとめました。
記事執筆時点のgithubのソースはこちらのコミットです。
大まかな概要
こちらがffmpeg.wasmの公式githubになります。こちらのサイトから引用すると
SharedArrayBuffer is only available to pages that are cross-origin isolated. So you need to host your own server with Cross-Origin-Embedder-Policy: require-corp and Cross-Origin-Opener-Policy: same-origin headers to use ffmpeg.wasm.
SharedArrayBufferはCross-Originで隔離されたページでしか利用できません。そのため、ffmpeg.wasmを使用するには、Cross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originのヘッダーを持つ独自のサーバーをホストする必要があります。
このように書いてあります。つまり、独自のサーバーをホストし、ヘッダーでCross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originを返す必要があります。
また、以下の引用にあるように、browserではscriptタグを使用してください。と書いてあります。
Or, using a script tag in the browser (only works in some browsers, see list below):
または、ブラウザのscriptタグを使用する方法(一部のブラウザでのみ動作します、以下のリストをご参照ください)。
なのでscriptタグを使用してffmpeg.wasmを読み込みたいのですが、サーバーからCross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originをresponse headerとして返すと、scriptタグを使用して他のoriginからソースの読み込みができません。つまり、以下のような読み込みができません。例で言うと、私のサイトはkajiri.devと言うoriginなので、unpkg.comにホストされたファイルはscriptタグで読み込むことができません。
<script src="https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js"/>
上記scriptタグをソースコードに記載すると、以下のようなエラーが出ました。
GET https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep 200
今までのところをまとめると、
- ブラウザでffmpeg.wasmを使用するにはscriptタグでffmpeg.wasmを読み込む必要がある。
- かつ、SharedArrayBufferを使用するためにresponse headerでCross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originを返す必要がある。と言うことになります。
一時的に回避する
とはいえ、いきなり上記の対応をするのが辛いこともあると思います。なので、googleのサイトで申請を出して、googleのサイトの説明に書いてあるように設定をすれば、「The exception expires in Chrome 96, and the exception only applies to Desktop Chrome」とあるので、Chrome96で期限切れになるまでは一時的に回避できるようです。私も試しに申請してみたのですが、SharedArrayBufferが使えるようになりました。
localhostで動くようにする
ではlocalhostで動くようにしましょう。
response headerを設定する
先ほど書いたようにresponse headerをサーバーから返す必要があります。Next.jsにはカスタムサーバーを設定する機能があるので、server.jsと言うファイルを作成します。
// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
handle(req, res, parsedUrl)
}).listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
上記のように設定した後に、server.jsをnodeで起動します。
node server.js
詳しくはNext.jsのサイトに記載されています。
上記のようにnode server.jsで起動することでサーバーからCross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originがresponse headerとして返ります。
scriptタグでffmpeg.wasmを読み込む
https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js
上記サイトにアクセスして、jsのファイルとして内容を保存しましょう。ここではffmpeg.jsとします。そして、scriptタグで読み込みます。
<script src="ffmpeg.js"/>
最近のNext.jsはScriptタグがあるのでそれでも良いです。その場合こうなります。
<Script strategy="beforeInteractive" src="ffmpeg.js" />
上記どちらかで設定すれば、ffmpeg.wasmが動くと思います。
私のソースコードはこちらです。
productionで動くようにする
私の場合、ssgで動かしたので、それを記載します。
scriptタグでffmpeg.wasmを読み込む
ここはlocalhostと一緒なのでこのページの上の方を参考にしてください。
response headerを設定する
response headerを設定するために、cloudflare workersを使用します。cloudflare workersの使い方は省略しますが、workerには次のコードを設定します。
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
let originalResponse = await fetch(request)
let response = new Response(originalResponse.body,originalResponse);
response.headers.set('Cross-Origin-Embedder-Policy', 'require-corp');
response.headers.set('Cross-Origin-Opener-Policy','same-origin');
return response
}
これで、response headerにCross-Origin-Embedder-Policy: require-corpとCross-Origin-Opener-Policy: Same-originが設定されるので、SharedArrayBufferが使用できるようになりました。
ちょっと前にvim.wasmを使ってみようとして、SharedArrayBufferがうまく使えなかったのですが、今回はffmpeg.wasmに挑戦してみたところうまくいきました。デプロイしましたので、動画ファイルをuploadしてgifに変換してみてください。(リンク先の変更で消えてたらごめんなさい)。サンプルの動画ファイルはffmpeg.wasmが準備しているこちらを使用できると思います。