この記事でわかること
twilio-video-processors.js を Next.js で利用する方法
背景
副業先では Twilio を使ってサービスを提供している。
サービスは Next.js + Graphql + React な今どきっぽい構成。
で、今どきのウェブ会議ツールにあるようなVirtualBackgroundとか、BluredBackgroundとか使えたら良いよねって話があった。
調べてみると↓のように、ライブラリが提供されていたのでこれを使えばいいじゃん、となった。
しかし、一向に動作しなかったがこれを解決したのでその方法を記す.
結論
執筆時点のバージョン(1.0.1)では、 以下のコマンドを実行しファイルをコピーし、 以下のように設定しなければならない。
また、 Component は dynamic import する必要がある。
$ mkdir -p public/twilio-video-processors $ cp -a node_modules/@twilio/video-processors/dist/build public/twilio-video-processors
const blurBackground = new VideoProcessors.GaussianBlurBackgroundProcessor({ assetsPath: `/twilio-video-processors/`, blurFilterRadius: 20, maskBlurRadius: 5, });
それぞれの理由
mv と cp と assetsPath について
GaussianBlurBackgroundProcessor
の assetsPath
は @twilio/video-processors/dist/build
を指し示す必要がある。
このディレクトリには tensolflow lite のファイルや、それを用いて学習させた結果のモデルデータのバイナリが入っている。
これを読み込み、ブラウザがWASMで処理することで初めて実現される。
普通のReactなどであれば、Bundle時に適当にExposeしたりすればいいが、NextJSだとそもそもSSRされた時点でもちろんそんなディレクトリは参照できないし、バンドルできない。
これを回避するために、 Next.js における /public
以下にファイルを配置すると Next.js から /
で参照できることを利用する。
dist/build
以下はファイルが複数あるので、適当にディレクトリを切って、そこに展開する。
このコマンドをBuild時に行うようにすれば良い。
dynamic import
Next.js には Dynamic import という機能がある.
Advanced Features: Dynamic Import | Next.js
これを使えばSSRを回避できる。(本来の用途とは違うけども).
なぜSSRを回避したいかというと、 この @twilio/video-processors
は動作環境がChromeとEdgeのみとなっている(1.0.1時点).
この判定のために、 window
を利用している箇所がある。しかしSSR時点では window
が存在しないため、 undefined variable なエラーがでてBuildできない。
よって、 Dynamic import を使い、 ssr: false
のオプションを渡してSSRを回避する必要がある。例えば以下のような感じに。
// VideoComponent.tsx // import 省略 import * as VideoProcessors from "@twilio/video-processors"; const VideoComponent: () => { // tracks は `LocalVideoTrack[]` const enableBlurOnTrack = React.useCallback(async () => { if (!VideoProcessors.isSupported) return const blurBackground = new VideoProcessors.GaussianBlurBackgroundProcessor({ assetsPath: `/twilio-video-processors/`, blurFilterRadius: 20, maskBlurRadius: 5, }); await blurBackground.loadModel(); tracks.forEach((tr) => { if (!(tr instanceof LocalVideoTrack)) { return; } tr.addProcessor(blurBackground); setEnabledBlur(true); }); }, [tracks]) }
// Room.tsx const Video = dynamic(() => import("./VideoComponent"), { ssr: false });
まとめ
一番最後のCode exampleが全てといえばそう。