Lumped tweets

Just marks

twilio-video-processors.js を Next.js で使う

この記事でわかること

twilio-video-processors.js を Next.js で利用する方法

twilio.github.io

背景

副業先では Twilio を使ってサービスを提供している。

サービスは Next.js + Graphql + React な今どきっぽい構成。

で、今どきのウェブ会議ツールにあるようなVirtualBackgroundとか、BluredBackgroundとか使えたら良いよねって話があった。

調べてみると↓のように、ライブラリが提供されていたのでこれを使えばいいじゃん、となった。

しかし、一向に動作しなかったがこれを解決したのでその方法を記す.

www.twilio.com

結論

執筆時点のバージョン(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 について

GaussianBlurBackgroundProcessorassetsPath@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が全てといえばそう。