Region Capture を使用したタブ共有の改善

François Beaufort
François Beaufort

ウェブ プラットフォームでは、ウェブアプリが現在のタブの動画トラックをキャプチャできます。このバージョンでは、これらの動画トラックを切り抜くメカニズムである Region Capture が搭載されています。ウェブアプリは現在のタブの一部を対象領域として指定し、ブラウザはその領域外のすべてのピクセルを切り抜きます。

以前は、ウェブアプリで動画トラックを「手動」で切り抜くことができました。つまり、ウェブアプリでフレームごとに直接操作できたのです。これは堅牢性もパフォーマンスもありませんでした。リージョン キャプチャは、これらの欠点を解決します。ウェブアプリは、ブラウザに代わって処理を行うよう指示できるようになりました。

リージョン キャプチャについて

Dynamic Content™ を使用してウェブサイトを作成しました。これはこれまでで最高のウェブアプリであり、ユーザーは頻繁に共同で使用しています。次のステップとして、仮想会議機能を埋め込むことも考えられます。あなたはそれを採用することにしました。既存のビデオ会議サービス プロバイダと提携し、そのウェブアプリをクロスオリジン iframe として埋め込みます。ビデオ会議ウェブアプリは、現在のタブを動画トラックとしてキャプチャし、リモート参加者に送信します。

メイン コンテンツ領域とクロスオリジン iframe がハイライト表示されたウェブアプリを表示するブラウザ ウィンドウのスクリーンショット。
メイン コンテンツ エリアは青色で、クロスオリジン iframe は赤色です。

ちょっと待ってください… ユーザー自身の動画をユーザーに送り返すわけにはいきませんよね。その部分は切り抜いた方がよいでしょう。これには、埋め込まれた iframe は、表示するコンテンツとその場所を認識できないため、なんらかの支援なしに切り抜くことはできません。理論的には、目的の座標を渡すことができます。しかし、ユーザーがウィンドウのサイズを変更した場合はどうなりますか?ビューポートをスクロールしますか?ズームインまたはズームアウトしますか?レイアウトが変更されるような方法でページを操作しますか?新しい座標をキャプチャ iframe に送信しても、タイミングの問題により、一部のフレームが切り抜かれる可能性があります。

では、一部をキャプチャする方法を使ってみましょう。ページに Element<div> の場合もあります)があり、メイン コンテンツが含まれています。これを mainContentArea とします。ビデオ会議ウェブアプリで、この要素の境界ボックスで定義された領域をリモートでキャプチャして共有したいとします。mainContentArea から CropTarget を派生させます。この CropTarget をビデオ会議ウェブアプリに渡します。この CropTarget を使用して動画トラックを切り抜くと、そのトラックのフレームは mainContentArea の境界ボックス内に収まるピクセルのみで構成されます。mainContentArea のサイズ、形状、位置を変更すると、どちらのウェブアプリからも追加の入力を必要とせずに、動画トラックがそれに従って変化します。

手順をもう一度確認しましょう。

ウェブアプリで CropTarget を定義するには、任意の要素を入力として CropTarget.fromElement() を呼び出します。

// In the main web app, associate mainContentArea with a new CropTarget
const mainContentArea = document.querySelector("#mainContentArea");
const cropTarget = await CropTarget.fromElement(mainContentArea);

CropTarget をビデオ会議ウェブアプリに渡します。

// Send the CropTarget to the video conferencing web app.
const iframe = document.querySelector("#videoConferenceIframe");
iframe.contentWindow.postMessage(cropTarget);

ビデオ会議ウェブアプリは、メインウェブアプリから受信した切り抜きターゲットを使用してセルフキャプチャ動画トラックで cropTo() を呼び出し、CropTarget で定義された領域にトラックを切り抜くようブラウザにリクエストします。

// In the embedded video conferencing web app, ask the user for permission
// to start capturing the current tab.
const stream = await navigator.mediaDevices.getDisplayMedia({
  preferCurrentTab: true,
});
const [track] = stream.getVideoTracks();

// Start cropping the self-capture video track using the CropTarget
// received over window.onmessage.
await track.cropTo(cropTarget);

// Enjoy! Transmit remotely the cropped video track with RTCPeerConnection.

Et voilà! これで完了です。

詳細

特徴検出

CropTarget.fromElement() がサポートされているかどうかを確認するには、次のコマンドを使用します。

if ("CropTarget" in self && "fromElement" in CropTarget) {
  // Deriving a target is supported.
}

CropTarget の導出

mainContentArea という要素に注目しましょう。そこから CropTarget を導出するには、CropTarget.fromElement(mainContentArea) を呼び出します。成功すると、返された Promise は新しい CropTarget オブジェクトで解決されます。CropTarget オブジェクトを不当な数で作成した場合は、拒否されます。

const mainContentArea = document.querySelector("#mainContentArea");
const cropTarget = await CropTarget.fromElement(mainContentArea);

Element とは異なり、CropTarget オブジェクトはシリアル化可能です。たとえば、Window.postMessage() を使用して別のドキュメントに渡すことができます。

切り抜き

タブキャプチャでは、動画トラックは MediaStreamTrack のサブクラスである BrowserCaptureMediaStreamTrack としてインスタンス化されます。このサブクラスは cropTo() を公開します。track.cropTo(cropTarget) を呼び出して、mainContentArea(cropTarget が派生した要素)の輪郭への切り抜きを開始します。

成功すると、その後のすべての動画フレームが mainContentArea の境界ボックス内に収まるピクセルで構成されることが保証されると、Promise が解決されます。

失敗した場合、Promise は拒否されます。これは次のような場合に発生します。

  • CropTarget が別のタブで作成された。(現時点では - 最新情報をお待ちください)。
  • CropTarget は、存在しない Element から派生しています。
  • トラックにクローンがある。(問題 1509418 を参照)。
  • 現在のトラックは自己撮影動画トラックではありません。下記をご覧ください。

cropTo() メソッドは、セルフキャプチャだけでなく、タブキャプチャの動画トラックでも公開されます。そのため、トラックを切り抜く前に、ユーザーが現在のタブを選択したかどうかを確認することをおすすめします。これは、キャプチャ ハンドルを使用して実現できます。preferCurrentTab を使用して、ユーザーにセルフキャプチャを促すようにブラウザに指示することもできます。

// Start cropping the self-capture video track using the CropTarget.
await track.cropTo(cropTarget);

切り抜かれていない状態に戻すには、null を指定して cropTo() を呼び出します。

// Stop cropping.
await track.cropTo(null);

遮蔽するコンテンツと遮蔽されるコンテンツ

リージョン キャプチャでは、z インデックスではなく、ターゲットの位置とサイズのみが重要です。ターゲットを遮っているピクセルはキャプチャされます。ターゲットの遮蔽された部分はキャプチャされません。

これは、領域キャプチャが基本的に切り抜きであるという帰結です。別の方法として、今後独自の API となる要素レベルのキャプチャがあります。これは、遮蔽に関係なく、ターゲットに関連付けられたピクセルのみをキャプチャします。このような API には、単純な切り抜きとは異なるセキュリティとプライバシーの要件があります。

領域キャプチャ API と要素レベルのキャプチャ API の異なる結果の画像。
遮蔽コンテンツがある場合の領域キャプチャの動作。

セキュリティとプライバシー

リージョン キャプチャを使用すると、タブ内のすべてのピクセルをすでに監視しているウェブアプリが、その一部のピクセルを自発的に削除できます。新しい情報を得ることができないため、明らかに安全です。

地域キャプチャを使用すると、リモート参加者に送信される情報を制限できます。たとえば、スライドは共有したいが、スピーカー ノートは共有したくないという場合などです。

スライドとスピーカー ノートが表示されているブラウザ ウィンドウのスクリーンショット。
スライドとスピーカー ノートが含まれるウェブアプリ。
ノートをリモートで共有することは非常に望ましくありません。キュー リージョン キャプチャ。

ローカルでは、リージョン キャプチャによってセキュリティが強化されることはありません。トラックを別のドキュメントに渡しても、受け取ったドキュメントではトラックの切り抜きを解除し、キャプチャされたタブのすべてのピクセルにアクセスできます。

Chrome では、キャプチャされたタブの周囲に青い枠線が描画されます。通常、切り抜きを行うと、切り抜かれた対象の周囲に青い枠線が表示されます。

デモ

リージョン キャプチャを試すには、Glitch でデモを実行します。ソースコードをチェックアウトしてください。

ブラウザ サポート

対応ブラウザ

  • Chrome: 104。
  • Edge: 104。
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

領域キャプチャは、パソコン版 Chrome 104 以降でのみご利用いただけます。

次のステップ

今後、ウェブでの画面共有を改善する予定の機能のプレビューをご紹介します。

  • リージョン キャプチャは、他のタブのキャプチャをサポートします。
  • 条件付きフォーカスを使用すると、キャプチャ ウェブアプリは、キャプチャされたディスプレイ サーフェスにフォーカスを切り替えるか、フォーカスの変更を回避するようブラウザに指示できます。
  • 要素レベルの Capture API が提供される場合があります。

フィードバック

Chrome チームとウェブ標準コミュニティは、リージョン キャプチャに関する皆様のご意見をお待ちしております。

デザインについて

領域キャプチャが想定どおりに機能しない点はありますか?または、アイデアを実装するために必要なメソッドやプロパティが不足している場合は、セキュリティ モデルについてご質問やご意見がございましたら、

  • GitHub リポジトリで仕様に関する問題を報告するか、既存の問題にコメントを追加してください。

実装に関する問題

Chrome の実装にバグが見つかりましたか?それとも、実装が仕様と異なるのでしょうか?

  • https://new.crbug.com でバグを報告します。できるだけ詳細な情報を含め、再現手順を簡単に記載してください。Glitch は、簡単な再現手順をすばやく共有するのに適しています。

クリエイターを応援する

リージョン キャプチャを使用する予定はありますか?公開されている機能のサポートは、Chrome チームが機能の優先順位を決める際に役立ちます。また、他のブラウザ ベンダーに、その機能のサポートがどれほど重要であるかを示します。

@ChromiumDev にツイートして、どこでどのように使用しているかをお知らせください。

謝辞

この記事を確認していただいた Joe Medley に感謝いたします。