📸

【8th Wall Web】写真・動画撮影機能の追加

2021/08/13に公開

8thwallの開発情報で、なぜかThree.js版のキャプチャ機能の情報が(ドキュメントの内容が古かったりとかで)あんまりなかったので備忘録にまとめます。

※2021年8月現在の情報です

A-Frame版

A-Frame版はすみませんが本記事では割愛…。
こちらのドキュメントで実装方法が確認できます。

▼ドキュメント
https://www.8thwall.com/docs/web/#customize-video-recording

▼参考にできるデモ
https://www.8thwall.com/8thwall/capturephoto-aframe/code/

Three.js版

Three.js版では、 【8thwallが用意したUIで簡単に撮影機能を入れる方法(簡単な方法)】 と、 【より柔軟にカスタムできる方法(カスタムできる方法)】 の2通りがあります。

とにかく簡単に撮影機能をつけたい方は前者を、自前のUIで任意のタイミングで撮影させたい場合は後者を参照するのがおすすめです。

簡単な方法

「簡単な方法」ではシャッターボタンや、撮影したものを確認できるプレビュー画面までを、自分でDOMを用意する必要なくインポートできます。

▼8thwallのシャッターボタン例

画像引用: 8thwall

シャッターボタンの追加

まず onxrloaded 関数内に以下を追加
XRExtras.MediaRecorder.initRecordButton();

プレビューモーダルの追加

同じくonxrloaded 関数内で以下を追加
XRExtras.MediaRecorder.initMediaPreview();

専用pipelineModuleの追加

XR8.addCameraPipelineModules 内に以下を追加
XR8.CanvasScreenshot.pipelineModule(),
(これは写真撮影用に必要っぽい)

ウォーターマークの追加(任意)

何と撮影した画像やビデオにウォーターマーク(ロゴ)を追加できる…

  XRExtras.MediaRecorder.configure({
    watermarkImageUrl: require('./assets/Logos/8logo.png'), // Adds watermark to photo/video
    watermarkMaxWidth: 100,
    watermarkMaxHeight: 10,
  })

基本のカメラをインカメにする(任意)

フェイストラッキングなど、起動時インカメ状態にしたい場合はXR8.run にcameraConfigの設定を追加する必要がある

XR8.run({
    canvas: document.getElementById("camerafeed"),
    cameraConfig: { direction: XR8.XrConfig.camera().FRONT },
    allowedDevices: XR8.XrConfig.device().ANY,
  });

参考

こちらのデモの実装がそのまま参考になります
https://www.8thwall.com/8thwall/face-effects-threejs/code/run-face-pipeline.js

カスタムできる方法

写真撮影

XR8.addCameraPipelineModules 内に XR8.CanvasScreenshot.pipelineModule() を追加

例:

 XR8.addCameraPipelineModules([
    XR8.GlTextureRenderer.pipelineModule(),
    XR8.Threejs.pipelineModule(),
    XR8.XrController.pipelineModule(),
    // ↓これ
    XR8.CanvasScreenshot.pipelineModule(),
...

② 自前でシャッターボタンとか、シャッターボタン押した時のプレビューモーダルとかをhtml要素で用意しておく

③ シャッターボタンを押した時のイベントで以下をいれる

XR8.CanvasScreenshot.takeScreenshot().then((data)=>{}, (error)=>{})

例:

const capturePhoto = () => {
  XR8.CanvasScreenshot.takeScreenshot().then(
    (data) => {
      const captureImage = document.getElementById("image");
      captureImage.src = "data:image/jpeg;base64," + data;
      showPreview();
    },
    (error) => {
      console.log(error);
    }
  );
};

参考

ドキュメント
(ドキュメントの例がちょっと古いという引っかけがある… | 2021年8月現在)
https://www.8thwall.com/docs/web/#xr8canvasscreenshottakescreenshot

参考記事
(こちらも2019年ので古くて少し書き方違う)

動画撮影

XR8.addCameraPipelineModules 内に XR8.MediaRecorder.pipelineModule() を追加

例:

 XR8.addCameraPipelineModules([
    XR8.GlTextureRenderer.pipelineModule(),
    XR8.Threejs.pipelineModule(),
    XR8.XrController.pipelineModule(),
    XR8.CanvasScreenshot.pipelineModule(),
    // ↓これ
    XR8.MediaRecorder.pipelineModule(),
...

② 自前でシャッターボタンとか、シャッターボタン押した時のプレビューモーダルとかをhtml要素で用意しておく

③ 録画開始のタイミングに以下の処理を入れる

XR8.MediaRecorder.recordVideo({ onError, onProcessFrame, onStart, onStop, onVideoReady })

例:

XR8.MediaRecorder.recordVideo({
  onVideoReady: (result) => {
    // 録画完了時の処理
    // 録画された動画は result で返される
  },
  onStart: () => {
    // 録画が開始した時の処理
  },
  onStop: () => {
    // 録画が停止した時の処理
  },
  onError: () => {
    // エラーが発生した時の処理
  },
  onPreviewReady: () => {
    // android/desktop限定 プレビュー可能だが、最適化の作業前の状態
    // 最適化できると onVideoReady が呼ばれる
  },
  onProcessFrame: ({ elapsedTimeMs, maxRecordingMs, ctx }) => {
    // 録画中毎秒呼ばれる処理
    // 録画にテキストや画像を描画したい時はここで処理を追加する
  }
});

④ 録画終了のタイミングに以下の処理を入れる

XR8.MediaRecorder.stopRecording();

参考

ドキュメント
https://www.8thwall.com/docs/web/#xr8mediarecorderrecordvideo

補足

AndroidはonVideoReadyが呼ばれるまでに時間がかかるのでonPreviewReadyで別途ローディング画面を表示して、onVideoReadyでローディング非表示をやるのがいいかもしれない

XR8.MediaRecorder.recordVideo({
  onVideoReady: (result) => {
    androidVideoLoading.classList.add("is-hidden");
    showPreview(result.videoBlob);
  },
  onPreviewReady: () => {
    androidVideoLoading.classList.remove("is-hidden");
  },
});

Discussion