動機

inf_daken_counterというツールがINFINITASの画面を画像認識するので、その映像をキャプチャしたい。この画像はゲームネイティブの無劣化FHDでないといけないが、自分は4Kモニタを利用しているのでgamescopeは4Kで出力している。

状況

  • モニタをFHDに設定するとソフトウェア的には解決するが、モニタのアップスケーラー遅延がひどく、使いたくない。
  • obs-vkcapture: 4Kしかキャプチャできない。obs-gamecapture gamescope -- game.exeで4Kに、gamescope -- obs-gamecapture game.exeでFHDになるかと期待したが、両方4Kになった。gamescopeが高度に最適化されているとして、まぁ納得できる挙動ではある。 なお、通常はこれを使うのがもっとも負荷が小さいはずだし、出来も良さそうだった。
  • PipeWireソース(ベータ): 4Kしかキャプチャできない。選択した対象ウィンドウがOBS再起動で初期化され、起動のたびに選択しないといけなくて普通でも使い物にならない。Wayland Portalを使っているっぽいのでオーバーヘッドが大きそう。
  • obs-gstreamer: gamescope自体が映像をPipeWireで出力でき、これをobs-gstreamerでキャプチャできる。ただ、標準gamescopeは4Kで出力する。

アプローチ

まず、一番可能性があるgamescope+pipewireに着目し、gamescopeをパッチして表示は4K、PipeWireはFHDで出力できるようにした。アップスケール前のフレームテクスチャは必ずあるはずで、実際gamescopeの中でアクセスでき、これは成功した。pipewireへ送るテクスチャをアップスケール後のテクスチャからその前のテクスチャに差し替えて矩形情報を調整しただけだ。

obs-gstreamer pipewiresrc target-object=gamescope ! video.で取り込める。OBS起動前にgamescopeが起動していれば上手く動く。問題はこの入力ソースが有効になったとき、まだgamescopeが動いていない場合である。WirePlumberのロジックによりtarget-objectが存在しなかったとき、勝手にフォールバックし適当な映像(自分はWebカメラ)がゲーム画面として取り込まれてしまう。正直驚き最小の法則に反していると思う。 autoconnect=falseにしておき、手動でpw-link gamescope:capture_0 obs:input_1と実行するとGStreamerパイプラインが動き出しキャプチャが始まる。 最初はOBS側のStreamは止めておき、gamescopeのSourceが作成されたことをトリガーにしてpw-linkすれば良い。これを実現するためにWirePlumberが存在している。

ということでWirePlumberを調べ始めたのだが、これがあまりにも強敵すぎて疲弊してしまった。AIは古い知識や想像で根拠無くできるとしか言わないし、ここまでソフトウェアの理解に苦しんだのは久しぶりだ。

  • autoconnect=trueにしておき、WirePlumberによるgamescope/obs間の接続確立においてはリンク先検索ロジックをフォールバックしないように上書きする。
  • autoconnect=falseにしておき、適切なタイミングでリンクをトリガーする。

前者の方が良さそう?