はじめに
本エントリでは先ほど公開した uWindowCapture という Windows において個別のウィンドウをキャプチャできるアセットの解説を行います。本アセットで実現できることは以下のようなものです。
- ウィンドウを個別にキャプチャ
- 背面やデスクトップ画面の範囲外にある部分もキャプチャします
- デスクトップをキャプチャ
- uDesktopDuplication よりは低速ですが古い Windows でも動くはず
- ウィンドウマネージャ
- ウィンドウの追加・削除や検索
- タイトルや位置、大きさなどの情報が取れます
デモ
個別画面キャプチャ
ウィンドウマネージャ
VR 内ウィンドウ自由配置
作った経緯
VR の中ではディスプレイという概念を取っ払った環境が作れるはず!というコンセプトは色々な人が考えていると思います。それを考える上での題材の一つとして、デスクトップをどうするか、もう少し正確には従来の(非VR)アプリケーション群を VR の中でどう使うか、という問題が挙げられます。これに答える案として、例えば Virtual Desktop や、Bigscreen Beta は、従来の物理的なディスプレイのコンテキストを利用して VR の中でそれらを自由に配置できるようにしています(Bigscreen の方は更にネットワーク越しに共有もできます)。
私も開発をお手伝いした現バージョンでの Mikulus もここに並びます(※コンセプトはもっと先を見据えています)。
これに対して、今年頭の Oculus の Rift Core 2.0 から追加された Oculus Dash は個別のウィンドウを空間上に好きに配置できるものとなっており、ディスプレイの制約を取り払ったものになっています:
私もこの発表よりも前の時点で、拙作の uDesktopDuplication と uTouchInjection、そして今回紹介する uWindowCapture を組み合わせた N-Windows という実動作するデモ(前項「VR 内ウィンドウ自由配置」の動画を参照)を公開していました(アプリは配布しておらず動画だけでしたが...)。
配布に至らなかったのは、コンセプトは実現できたもののパフォーマンスの関係で実用にはまだ遠い形だったからです。しかしながら、単に画面をキャプチャするアセットとしてもかなり使い途があるのではと思いまして、使いやすいように整理し、またパフォーマンスが出来る限り出るよう修正して公開することにしました。
ダウンロード
ライセンスは MIT です。
基本的な使い方
いくつかサンプルシーンを利用して出来ることを解説を交えて見ていきたいと思います。Examples 以下にシーンがあるのでそれぞれ開いてみてみてください。
Single Window
これは単体のウィンドウキャプチャを行うシンプルなシーンで、実行すると次のように Unity の画面が表示されると思います。
Window オブジェクトを見てみると次のような UwcWindowTexture
コンポーネントがアタッチされています。このコンポーネントをつけたオブジェクトのメインテクスチャが対象のウィンドウになる仕組みになっています。
対象のウィンドウは Partial Window Titleでタイトルを部分指定します。書き換えると次のように別のウィンドウに切り替わります。
コンポーネントの詳細については後述します。
Desktop
コンポーネントの Type を Window から Desktop へ変更するとウィンドウだけでなくデスクトップ画面もキャプチャできるようになります。Desktop を選択すると、ウィンドウ単体ではなく指定したインデックスのモニタ画面全体をキャプチャします。コンポーネントの Target の UI は以下のように変わります。
キャプチャした様子は次のとおりです。
Window Object
単体のウィンドウキャプチャだとコンテキストメニューや子ウィンドウがそこに含まれていないため見えません。そこで Create Child Windows にチェックを入れると、子ウィンドウの追加・削除を自動でハンドルして、指定した Child Window Prefab で子ウィンドウを生成するようになります。チェックを入れると次のように UI が変わります。
Child Window Z Distance は子ウィンドウとして追加したゲームオブジェクトを奥行き方向にどれだけ動かすかというものです。実行すると次のようになります。
現状だと完全な子ウィンドウの識別はできておらず、表示されないケースや DPI スケーリングが子側でうまく働かないなどのバグ(例. Visual Studio)もあります。。見つけた際はアプリケーション名やケースについてご一報いただけると助かります。
Icon
UwcIconTexture
コンポーネントを使うとウィンドウのアイコンのテクスチャも取得可能です。
Cursor Object
UwcCursorTexture
を使うとカーソルのテクスチャも個別に取得可能です。マスクも含めてキャプチャするので文字列カーソル等のデスクトップ背面の色によって変化するカーソルも正しく取得可能です。
コンポーネント解説
前項でいくつか UwcWindowTexture
の Target 部分のコンポーネントの解説をしました。ここでは残りも含めてコンポーネント全体について解説を行います。
Target
ここではキャプチャする対象となるウィンドウやデスクトップの設定を行います。
- Type
- Window、Desktop、Child と 3 つのタイプがあります。Window と Desktop は前項の通りで選択すると Target 内に表示される項目が増減します。Child だけは特殊で uWindowCapture が内部で子ウィンドウをハンドルするために使われます。手動で設定するとエラーが表示されますので設定しないようにしてください。
- Partial Window Title
- 部分一致でターゲットとなるウィンドウを指定します。変更があったタイミングで新しいウィンドウを探しに行きます。
- 部分一致は先頭にマッチするものが優先的に選ばれます。
- Update Title
- チェックすると常にタイトルを更新します。
- タイトル更新は比較的負荷が高いためデフォルトでは初回のみ取得がデフォルトの動作になっています。
- Desktop Index
- 指定した ID のモニタをキャプチャ対象とします。現在の仕様ではモニタのインデックス番号は実行のたびに不定で入れ替わることもあります。
- Alt Tab Window
- 検索する対象を Alt-Tab で表示されるウィンドウに限定します(子ウィンドウやバックグラウンドでスリープしているウィンドウは含まれません)
- Create Child Windows
- チェックすると
UwcWindowTextureChildrenManager
が自動的にアタッチされ、前項のように子ウィンドウが自動で追加されるようになります。
- チェックすると
- Child Window Prefab
- Child Window Z Distance
- 生成する子と親の Z 間距離です。
Capture Settings
ここではキャプチャの方法に対する設定を行います。
- Capture Mode
- Capture Priority
- キャプチャの優先度です*1。
- High
- 最も優先度の高いキューに追加して追加順に処理されていきます。
- Middle
- このキューに追加されると更新の度に先頭のものを 1 つだけ High キューの末尾へ移動します。High で埋まっていてもたまにキャプチャされる形になります。High にない場合はここのものがキャプチャされます。
- Low
- High にも Middle にもない場合にキャプチャされます。High と Middle が埋まり続ける状況ではキャプチャされません。
- Auto(デフォルト)
- ウィンドウがカーソル下にある場合は High に、ウィンドウが前の方にある場合に Middle に設定されます(どこまでを Middle にするかはパラメタ)。
- Capture Request Timing
- キャプチャするタイミングです。
- Every Frame
- 画面外にあっても毎フレームキャプチャします。
- Only When Visible(デフォルト)
- カメラに映っているとき(
OnWillRenderObject()
タイミング)にのみキャプチャのリクエストを行います。 - Scene ビューのみ見ていると更新されなくなるので注意!
- カメラに映っているとき(
- Manual
RequestCapture()
を手動で呼ぶとキャプチャを行います。
- Capture Frame Rate
- キャプチャを行う目標フレームレート値です。
- Draw Cursor
- チェックすると画面にカーソルを描画します。
Scale Settings
- Scale Control Type
- ゲームオブジェクトのスケールをどう調整するかの種類です。
- Base Scale
- Scale Per 1000 Pixel で指定したスケールにオブジェクトを自動的に設定します。例えば 2 が設定されていた場合にウィンドウのサイズが 1920px - 1080px であれば、Unity のスケールで 3.84 - 2.16 となります。
- Fixed Width
- 横幅を
Transform
の x で固定し、y を可変にしてアスペクト比をあわせます。
- 横幅を
- Fixed Height
- 縦幅を
Transform
の y で固定し、x を可変にしてアスペクト比をあわせます。
- 縦幅を
- Manual
- コンポーネント側では何もせずに、手動で
Transform
を更新する必要があります。
- コンポーネント側では何もせずに、手動で
- Scale Per 1000 Pixel
- Base Scale が選択されているときの 1000px 辺りの大きさ(m)です。
Window Information
ウィンドウの情報の一部が表示されます。
実践的な機能解説
次に少しスクリプトも交えて実践的なサンプルシーンを見ていきましょう。
Horizontal Layout
Alt-Tab で表示されるウィンドウを横一列に並べるシーンです。Windows オブジェクトに UwcAltTabWindowTextureManager
と UwcHorizontalLayouter
という 2 つのコンポーネントが追加されています。
- UwcAltTabWindowTextureManager
- Alt-Tab ウィンドウを Window Preafab で指定したオブジェクトで生成します。
- UwcHorizontalLayouter
UwcAltTabWindowTextureManager
に登録されたウィンドウを横に並べるシンプルなスクリプトです。
UwcHorizontalLayouter
は次のようなスクリプトです。
using UnityEngine; [RequireComponent(typeof(UwcWindowTextureManager))] public class UwcHorizontalLayouter : MonoBehaviour { UwcWindowTextureManager manager_; void Awake() { manager_ = GetComponent<UwcWindowTextureManager>(); } void Update() { var pos = Vector3.zero; foreach (var kv in manager_.windows) { var windowTexture = kv.Value; var width = windowTexture.transform.localScale.x; pos += new Vector3(width * 0.5f, 0f, 0f); windowTexture.transform.localPosition = pos; pos += new Vector3(width * 0.5f, 0f, 0f); } } }
最初は大量のウィンドウをキャプチャリクエストが走るので、しばらくテクスチャが表示されないウィンドウもあります。全てキャプチャした後は Capture Priority が Auto になっているため、カーソル下にあるウィンドウが優先的にキャプチャされるようになります。
Desktop Layout
こちらは Horizontal Layout よりも少しだけ複雑な UwcDesktopLayouter
を通じて、デスクトップ上の配置を再現するサンプルになります。それぞれのウィンドウには UwcTextureWindowChildrenManager
がつくので、Alt-Tab なウィンドウ以外の子ウィンドウも表示するのがこれだけ短いコードで書けます。
using UnityEngine; [RequireComponent(typeof(UwcWindowTextureManager))] public class UwcDesktopLayouter : MonoBehaviour { public float scale = 1f; public float zMargin = 0.1f; UwcWindowTextureManager manager_; void Awake() { manager_ = GetComponent<UwcWindowTextureManager>(); manager_.onWindowTextureAdded.AddListener(InitWindow); } void InitWindow(UwcWindowTexture windowTexture) { MoveWindow(windowTexture, false); ScaleWindow(windowTexture, false); } void Update() { foreach (var kv in manager_.windows) { var windowTexture = kv.Value; CheckWindow(windowTexture); MoveWindow(windowTexture); ScaleWindow(windowTexture); } } void CheckWindow(UwcWindowTexture windowTexture) { windowTexture.enabled = !windowTexture.window.isIconic; } void MoveWindow(UwcWindowTexture windowTexture) { var window = windowTexture.window; var pos = UwcWindowUtil.ConvertDesktopCoordToUnityPosition(window, 1000f / scale); pos.z = window.zOrder * zMargin; var targetPos = transform.localToWorldMatrix.MultiplyPoint3x4(pos); windowTexture.transform.position = Vector3.Slerp(windowTexture.transform.position, targetPos, filter); } void ScaleWindow(UwcWindowTexture windowTexture) { windowTexture.scaleControlType = WindowTextureScaleControlType.BaseScale; windowTexture.scalePer1000Pixel = scale; } }
Window List
最後はリスト表示です。これは先程の UwcHorizontalLayouter
か UwcDesktopLayouter
の上にリストを表示し、選択したものだけ表示させる、というものになっています。
より詳細は UwcWindowList.cs
を見てみてください。
スクリプトからの利用
ここからはスクリプトからの利用を見ていきましょう。スクリプトは基本的に利用しなくても使いやすいようになっていると思いますが、凝ったことをしたい場合は必要になってきます。基本的にはサンプルで基本的な使い方は網羅していると思いますので、サンプルのコンポーネントを直接見ていただくとより理解が深まると思います。
UwcManager
実行すると UwcManager
がアタッチされた uWindowCapture
と名前のついたゲームオブジェクトが 1 つだけ作成されます(予め配置しておいても構いません)。ここでは DLL との橋渡しを行っており、次のようなスクリプトになっています。
public class UwcManager : MonoBehaviour { public static UwcManager instance { get; } public static UwcWindowEvent onWindowAdded { get; } public static UwcWindowEvent onWindowRemoved { get; } public static UwcEvent onCursorCaptured { get; } public static Dictionary<int, UwcWindow> windows { get; } public static UwcWindow cursorWindow { get; } public static UwcCursor cursor { get; } public static int desktopCount { get; } public static UwcWindow Find(int id); public static UwcWindow Find(string partialTitle, bool isAltTabWindow = true); public static UwcWindow Find(System.IntPtr handle); public static UwcWindow Find(System.Func<UwcWindow, bool> func); public static List<UwcWindow> FindAll(string title); public static UwcWindow FindParent(int id); public static UwcWindow FindDesktop(int index); public static void UpdateAllWindowTitles(); public static void UpdateAltTabWindowTitles(); }
それぞれ次のような機能になっています。
ウィンドウの検索
ウィンドウは UwcManager
を経由して検索します。検索方法はいくつかあります。
タイトルの部分一致
ウィンドウのタイトルで検索する方法は 2 つあります。
// Unity と名前のついたウィンドウを 1 つだけ返す UwcWindow unity = UwcManager.Find("Unity"); // Chrome と名前のついたウィンドウ全てを返す List<UwcWindow> chromes = UwcManager.FindAll("Chrome");
なお、UwcManager.Find()
の第 2 引数は、Alt-Tab ウィンドウだけを探す場合は true(デフォルト)、そうでない場合は false を与えることで子ウィンドウも検索できます。
ハンドル
何らかの方法でウィンドウハンドルを取得しているのであればそれを使って UwcWindow
を取得できます。
System.IntPtr hWnd = ...; var window = UwcManager.Find(hWnd);
条件で検索
UwcWindow
を逐次見つつ当てはまるものを検索することも出来ます。
var window = UwcManager.Find( window => window.isAltTabWindow && window.title.IndexOf("Unity") != -1);
タイトルの一括更新
タイトルの更新はコストがかかるので、updateTitle
というフィールドを UwcWindowTexture
に追加しているのですが、この関係でデフォルトではウィンドウのタイトルが更新されず、タイトルでの検索時に困ることがあります。具体的には例えばブラウザを開いていて、開始時は Google 検索画面を開いていて、途中で Twitter を開き、「Twitter」というキーワードで対象のブラウザを見つけたかったとしても、起動時の「Google」で該当の UwcWindow
の title
が固定されてしまっています。そこでマネージャには全てのタイトルを更新する UpdateAllWindowTitles()
と Alt-Tab ウィンドウのタイトルだけを更新する UpdateAltTabWindowTitles()
が用意されています。呼び出し直後に更新されるわけではなく、あくまで更新リクエストをして非同期で更新されるものなので注意してください。
コールバック
ウィンドウの追加・削除は以下のコールバックから取得できます。
UwcManager.onWindowAdded
UwcManager.onWindowRemoved
デスクトップを表示するサンプルではこれを使用しています。
ウィンドウ一覧の取得
UwcManager.windows
で現在管理しているウィンドウを全て取得することができます。リストのサンプルではこれを使っています。
UwcWindow
さて、上記のように得られた UwcWindow
はどういったものなのか見ていきます。細かい実装は省いてメンバのみ見ていきましょう。
public class UwcWindow { // プロパティ public int id { get; set; } public UwcWindow parentWindow { get; set; } public System.IntPtr handle { get; set; } public System.IntPtr ownerHandle { get; set; } public System.IntPtr parentHandle { get; set; } public System.IntPtr instance { get; set; } public int processId { get; set; } public int threadId { get; set; } public bool isValid { get; set; } public bool isAlive { get; set; } public bool isRoot { get; set; } public bool isChild { get; set; } public bool isVisible { get; set; } public bool isAltTabWindow { get; set; } public bool isDesktop { get; set; } public bool isEnabled { get; set; } public bool isUnicode { get; set; } public bool isZoomed { get; set; } public bool isMaximized { get; set; } public bool isIconic { get; set; } public bool isMinimized { get; set; } public bool isHungup { get; set; } public bool isTouchable { get; set; } public bool isStoreApp { get; set; } public bool isBackground { get; set; } public string title { get; set; } public string className { get; set; } public int x { get; set; } public int y { get; set; } public int width { get; set; } public int height { get; set; } public int zOrder { get; set; } public int bufferWidth { get; set; } public int bufferHeight { get; set; } public int iconWidth { get; set; } public int iconHeight { get; set; } public Texture2D texture { get; set; } public bool hasIconTexture { get; set; } public Texture2D iconTexture { get; set; } public CaptureMode captureMode { get; set; } public bool cursorDraw { get; set; } // コールバック public class ChildAddedEvent : UnityEvent<UwcWindow> {} public class ChildRemovedEvent : UnityEvent<UwcWindow> {} public UnityEvent onCaptured { get; set; } public UnityEvent onSizeChanged { get; set; } public UnityEvent onIconCaptured { get; set; } public ChildAddedEvent onChildAdded { get; set; } public ChildRemovedEvent onChildRemoved { get; set; } // 関数 public void RequestCaptureIcon() public void RequestCapture(CapturePriority priority = CapturePriority.High) }
これは DLL を通じて C++ 側で管理されているウィンドウの情報とやりとりをするラッパークラスになっています。ほとんどのメンバは Win32API を叩いて得られる結果と同じです。ほとんどの情報は別スレッドで非同期に取得しているので、プロパティを呼び出してもそのタイミングで Win32API がコールされるわけではありませんので付加的に大きなインパクトはありません。UwcWindowTexture
や UwcIconTexture
を利用せずとも、この UwcWindow
を管理すれば自前で更新タイミングやテクスチャの管理をコントロールすることも出来ます。
プロパティ
ウィンドウの属性を取得するためのプロパティが一通り用意されています。これ以外にも欲しいものがあれば GitHub の issue 等でリクエストをください。
テクスチャ
テクスチャはウィンドウのテクスチャである texture
と、アイコンのテクスチャである iconTexture
があります。これらはデフォルトではキャプチャされていないので、手動で RequestCapture()
や RequestCaptureIcon()
を呼ぶ必要があります。
コールバック
キャプチャ完了時や画面のサイズが変更されたとき、子が追加・削除されたときなどにフックして処理を行えるよういくつかコールバックが用意されています。
UwcWindowTexture
そしてこの UwcWindow
を MonoBehaviour
経由で使いやすくしたものが UwcWindowTexture
です。インスペクタに出ているものは一通り public なプロパティまたは変数を通じてアクセス可能です。
public class UwcWindowTexture : MonoBehaviour { // プロパティ / 変数 public WindowTextureType type { get; set; } public bool altTabWindow { get; set; } public bool createChildWindows { get; set; } public GameObject childWindowPrefab; public string partialWindowTitle { get; set; } public float childWindowZDistance; public int desktopIndex { get; set; } public CaptureMode captureMode = CaptureMode.PrintWindow; public CapturePriority capturePriority = CapturePriority.Auto; public WindowTextureCaptureTiming captureRequestTiming = WindowTextureCaptureTiming.OnlyWhenVisible; public int captureFrameRate = 30; public bool drawCursor = true; public bool updateTitle = true; public WindowTextureScaleControlType scaleControlType = WindowTextureScaleControlType.BaseScale; public float scalePer1000Pixel = 1f; public UwcWindow window { get; set; } public UwcWindowTexture parent { get; set; } public UwcWindowChangeEvent onWindowChanged { get; set; } public bool isValid { get; } // メソッド public void RequestCapture(); }
ウィンドウの手動セット
UwcWindow
を検索した後、直接 UwcWindowTexture.window
に代入すれば、そのウィンドウを UwcWindowTexture
がハンドルするようになります。
UwcWindowTextureManager / UwcAltTabWindowTextureManager
ウィンドウマネージャを作成したい場合はこれらを参考にしてください。UwcWindowTextureManager
がウィンドウの追加・削除・リストといった基本的な機能を持っているので、UwcAltTabWindowTextureManager
はそれを継承する形になっています。
using UnityEngine; public class UwcAltTabWindowTextureManager : UwcWindowTextureManager { void Start() { // マネージャのコールバックに登録 UwcManager.onWindowAdded.AddListener(OnWindowAdded); UwcManager.onWindowRemoved.AddListener(OnWindowRemoved); // 現時点ですでにあるウィンドウを処理 foreach (var pair in UwcManager.windows) { OnWindowAdded(pair.Value); } } void OnWindowAdded(UwcWindow window) { // 子ウィンドウは UwcWindowTextureChildrenManager で管理 if (window.parentWindow != null) return; // 可視、Alt-Tab 表示でないもの、かつスリープしているストアアプリを除外 if (!window.isVisible || !window.isAltTabWindow || window.isBackground) return; // 初回だけ高優先度でキャプチャ window.RequestCapture(); // 管理対象へ追加(UwcWindowTextureManager のリストに追加) AddWindowTexture(window); } void OnWindowRemoved(UwcWindow window) { // 管理対象から除外(UwcWindowTextureManager のリストから削除) RemoveWindowTexture(window); } }
UwcCursor
UwcWindow
のカーソル版が UwcCursor
で、マネージャの中に 1 つだけ存在しています。
using UnityEngine; public class UwcCursor { public int x { get; } public int y { get; } public int width { get; } public int height { get; } public Texture2D texture { get; } public UwcEvent onCaptured { get; } public UwcEvent onTextureChanged { get; } public void RequestCapture(); }
UwcWindow
と同じく必要なときに RequestCapture()
してテクスチャを更新する必要があります。こういったことを自動でやってくれるように MonoBehaviour
でラップしたものが UwcCursorTexture
で、アタッチしたオブジェクトのマテリアルのメインテクスチャがカーソルのテクスチャになります。
UwcIconTexture
アイコン表示の基本的な使い方は Window Texture へ表示したいアイコンの UwcWindowTexture
コンポーネントをインスペクタから設定する形ですが、直接 windowTexture
をスクリプトからセットしても構いません。また、UwcWindowTexture
同様に、直接 window
プロパティに UwcWindow
をセットしても大丈夫です。UwcWindowTexture
が不要でアイコンのみ必要な時にこういった利用も可能です。
仕組み
Unity のメインスレッドおよびレンダリングスレッドへ極力負荷を掛けない形で設計しています。まず、専用のウィンドウの情報を集めてくるスレッドで EnumWindows()
し、得られたウィンドウハンドルからそのウィンドウの情報を回収しておきます。このウィンドウの追加・削除・属性といった情報を Unity 側に伝え、Unity 側では先程のスレッドでキャッシュされた情報を利用して必要なウィンドウを選別、キャプチャのリクエストを行います。キャプチャのリクエストは専用のキャプチャスレッドへ伝えられ、この中で PrintWindow()
や BitBlt()
が行われます。キャプチャが終わったら以降は順に処理されていきます。キャプチャが終わるとアップロードスレッド内で Shared Texture へ UpdateSubresource()
して書き込みを行い、その書き込みが終わると最後に Unity のレンダリングスレッドでそれを CopyResource()
する、という流れになっています。
PrintWindow()
を並列化しても速くならないため、これ以上速度を上げるには、更新のあった領域のみ処理する方法や直接バッファをもらってくる方法を見つける以外難しそうです。
トラブルシューティング
画像の反転
デフォルトで使用している Prefab についているメッシュの UV はいじってあるので問題ないのですが、テクスチャの向きが合わない場合は Tiling
をマイナスに設定して調整してください。
UwcWindow の取得タイミング
ゲームを開始した後に DLL の内部で状態を初期化し、ウィンドウを探しに行くため、場合によっては最初の数フレームはウィンドウが見つからないことがあります。Start()
だけでウィンドウを探しに行く処理を書いている場合はこれでハマることがありますので、Update()
で見つかるまで探す処理を書いてください。
クラッシュ
まだ完全にクラッシュが防げていないケースもあります。何か変な挙動を見つけた際は、実行場所(Editor 上ではプロジェクトのルート、実行ファイルでは .exe の隣)に uWindowCapture.log
が出力されているので、そのログを送っていただけると参考になります。
ログファイルを出力したくない
また、ログファイルを出力したくない場合は UwcManager.instance.debugMode
を DebugMode.None
に設定してください。予めシーンにマネージャを配置して、インスペクタから設定しても大丈夫です。
現状の問題点や将来の改善点
テクスチャの向き
現状、自動スケールが XY 方向にしか対応していません(標準のオブジェクトなら Quad は OK、Plane は NG)。将来的に XZ でも可能にしようと思いますが、手動でスケーリングしたい場合は Scale Control Type
を Manual
に設定してください。
パフォーマンス
現状のボトルネックは PrintWindow()
の速度です。環境にも依存しますが自宅の環境では 4K のテクスチャのキャプチャの 50 ~ 60 ms 程度の時間を要します(= 15 fps 程度が限界)。小さいウィンドウほど速いので 60 fps 出るかもしれませんが、ゲーム実況と行った用途にはまだ難しいです。。もしどなたか別のキャプチャ方法について詳しい資料などご存知でしたら教えていただけますと幸いです。
おわりに
uDesktopDuplication も VR 内作業ツールや VTuber 向けツールなど色々な場所で利用していただいているので、ぜひこちらも利用してフィードバックなど貰えると嬉しいです。