Expoでカメラを使う時ライブプレビュー画面にgl-reactでエフェクトをかけたい。
結論を書くとあまりスマートな方法で解決はできなかったです。
- ドキュメント expo camera
- ドキュメント expo gl-view
- ソース expo camera
- ネイティブソース(java)
- 参考リンク – Camera app with live preview( Medium, shoutem )
仕様の理解と方策
ドキュメントを読んだ感じではプレビュー動画へのアクセスは出来なそう。
一応ソース群を見てみますがExpoでどうこうするのは無理そう。
色々調べていると同様の試みを行っているものがありました。
Mediumとshoutemで掲載していますが内容は同じ。
これを参考にしてみます。
内容を簡潔に言うと5ms(完成ソースだと何故か8ms)ごとに写真を撮ってURIをNodeに渡して描画してます。
Expoでない(React Native Camera使用)のと古い関数を使っている部分はありますが、大体そのままで動かすことは可能です。ただし問題もあります。
問題点
書いてるときからそうなるだろうなと思ってましたが、キャッシュデータがすごい勢いで増えていきます。最低品質にしてみても無視できるものではないです。
さらにインターバル処理がまともに動きません。
Warning: Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code:
まあ保存処理が5ms以下でサクッと出来なきゃ負荷が上がってプロセスもどんどん積み重なりますよね。
こっちはsetInterval
をやめてrefreshPic()
で画像保存後にsetTimeout
で自分を呼びなおすことで一応対処できる。
1 2 3 4 5 6 7 8 9 10 11 |
const refreshPic = () => { const st = Date.now(); cameraRef.current?.takePictureAsync({ skipProcessing: true, onPictureSaved: p => { console.log((Date.now() - st) / 1000); setPath(p.uri); timer = setTimeout(() => refreshPic(), 5); } }); }; |
テスト用の貧弱スマホではありますが320×240サイズを速度重視で保存するのに1~1.7秒かかるのはつらい。平均0.8fps
ってカクカクで済むレベルじゃない。
代替手段
takeSnapshotAsync
でうまいこと出来ないかなと試す。
例によってexpoのtakeSnapshotAsync()
は使えなかったのでreact-native-view-shot
から直接使う。
1 2 3 4 5 6 7 |
import { captureRef as takeSnapshotAsync } from "react-native-view-shot"; const v = await takeSnapshotAsync(cameraRef.current, { format: "jpg", quality: 1.0, result: "tmpfile" }); |
遅くなると想定していたけど何故か倍近く早くなった(0.6秒前後)。
カメラの画像保存ってコスト高いのかな。
ここで"tmpfile"
を"data-uri"
にすることでキャッシュ問題をなくすとどうだろう。
WebGL部分でuniform t: no loader found for valueと怒られた。
この辺のIssueはちょくちょくあるけど解決策は使えそうにはない。
Node.js#L938、createSurface.js#L529、ExpoModuleTextureLoader.jsとソースを追って色々試すも以下のエラーで止まってしまった。
EXGL: Invalid pixel data argument for gl.texImage2D()!
ここで変換かますのも遅延の原因になるしそのまま使えないならこの方向はダメか。
結局どうするか
全てに妥協し、tmpfile
に保存して使い終わったらFilesystems.deleteAsync
で消す。
もしくはeject
してreact-native-webrtcを導入。
stream
をgl-react/…/examples/webcamなどを参考にgl-react
に流す。
結構探したとは思うけど今後良い方法を見つけたら追記したい。