ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ScreenCaptureKitを次のレベルへ引き上げる
ScreenCaptureKitで、Appユーザに高度なスクリーンキャプチャ機能を提供する方法をご覧ください。コンテンツフィルタの微調整、フレームメタデータの解釈、ウィンドウピッカーなど、組み込むことができる数多くの高度なオプションを紹介します。また、最適なパフォーマンスを実現するストリームを構成する方法についても解説します。
リソース
関連ビデオ
WWDC23
WWDC22
-
ダウンロード
♪ メロウなヒップホップ音楽 ♪ ♪ Meng Yang:こんにちは Meng Yangです Apple GPU Softwareエンジニアです 今日は ScreenCaptureKitの高度な内容を いくつか取り上げAppの画面共有体験を 次のレベルに引き上げる 方法について説明します 後ほど同僚のDrewが この新しいAPIの使い方をデモでご紹介します スクリーンキャプチャはZoom・Google Meet SharePlayなどでの画面共有に さらにはTwitchなどゲームストリーミングの サービスでも中心的な役割を果たし ここ数年で仕事や勉強 共同作業や 人とつながるための 新たな方法になりました ScreenCaptureKitはまったく新しい 高性能なスクリーンキャプチャ フレームワークで 新規に開発された強力な機能を備えています 豊富な機能の中には高度にカスタマイズ可能な コンテンツコントロールが含まれ キャプチャしたい ウィンドウやAppディスプレイなどを 自由に組み合わせて簡単に選択できます 画面コンテンツのネイティブ解像度や フレームレートまでキャプチャする機能 解像度 フレームレート ピクセルフォーマットなどの ストリームプロパティの動的な制御 ストリームを新たに作成することなく これらをオンザフライで制御可能です キャプチャバッファはGPUメモリに バックアップされメモリコピーを削減します ハードウェアアクセラレーションによる コンテンツのキャプチャとスケーリング ピクセルとカラーフォーマット 変換により CPU使用率を削減します ビデオとオーディオの両方の キャプチャをサポートしています それでは始めますが この動画はフレームワークの 動作に関する基本的な概念や構成要素 ワークフローの理解を前提としています 詳しくはイントロセッション 「ScreenCaptureKitの紹介」 をご覧ください このセッションでは1つのウィンドウを キャプチャして表示する 方法についてお話します 次にフルティスプレイキャプチャに 画面コンテンツを追加する方法 ディスプレイキャプチャから コンテンツを削除する方法 様々な使い方に合わせてストリームを 設定する方法をいくつかご紹介します 最後にScreenCaptureKitが 人気のオープンソース画面キャプチャAppの OBS Studioの 画面・音声キャプチャ体験にもたらす 変化についてデモをご覧いただきます では最初の例から始めましょう おそらく最も一般的な使用例である 1つのウィンドウをキャプチャします この例では シングルウィンドウフィルタの設定方法 キャプチャしたウィンドウの大きさを変えたり ウィンドウが隠れたり 画面外に出たり 最小化された時 ストリーム出力に与える 影響について説明します フレーム単位のメタデータの 使い方や キャプチャした ウィンドウの適切な 表示方法についても説明します では始めましょう 表示されているディスプレイに関係なく 1つのウィンドウをキャプチャするには まずシングルウィンドウフィルタを使い 1つウィンドウだけでフィルタを初期化します この例では 1つのSafariウィンドウを 含むよう設定されています ビデオ出力には その ウィンドウだけが含まれます Safariの子ウィンドウやポップアップ 他のウィンドウは含まれません 一方 ScreenCaptureKitの 音声キャプチャポリシーは 常にAppレベルで動作しています シングルウィンドウフィルタが 使われている場合 そのウィンドウを含むAppからのすべての オーディオコンテンツがキャプチャされ 映像出力には含まれない ウィンドウの音声もキャプチャします コードのサンプルを見てみましょう 1つのウィンドウでストリームを作成するには SCShareableContentを介して 共有可能なすべてのコンテンツを取得します 次に SCShareableContentから windowIDを照合して 共有したいウィンドウを取得します 次に 指定されたSCWindowで desktopIndependentWindowタイプの SCContentFilterを作成します ストリーム出力に音声を含めるよう ストリームを設定する事も可能です これでcontentFilterとstreamConfigを 使って ストリームを作成する準備が出来ました StreamOutputを追加すると ストリームが始まります 次に ストリーム出力を見てみましょう この例ではソースディスプレイが左に表示され ストリーム出力が右に表示されます ストリームフィルタには Safariウィンドウが1つあります キャプチャされているSafariウィンドウを スクロールしてみましょう ストリーム出力には単一のSafariウィンドウの ライブコンテンツが出力され ソースウィンドウのフレームレートと 同じテンポで更新しています 例えばソースウィンドウが常に120Hzで 更新されていればストリーム出力も 最大120fpsでの更新が実現可能です ウィンドウサイズを 変更したらどうなるでしょう メモリの追加割り当てが発生しますので ウィンドウサイズを 頻繁に変更することは 推奨される行為ではありません ストリームの出力サイズは ほぼ固定となっており ソースウィンドウに合わせて 変わることはありません ではソースウィンドウのサイズを変更して ストリーム出力の変化を見てみましょう ScreenCaptureKitは キャプチャしたウィンドウに対して 常にハードウェアスケーリングを 行うため ソースウィンドウの サイズが変わっても フレームを はみ出すことはありません 他のウィンドウで隠された ウィンドウはどうでしょう? ソースウィンドウの全部や一部が隠れていても ストリームには常にウィンドウ 全体のコンテンツが出力されます これはウィンドウが完全に 画面外に出てしまったり 他のディスプレイに移動しても変わりません また ソースウィンドウが最小化されると ストリーム出力が一時停止し ウィンドウの最小化が 解除されると再開されます では オーディオ出力の話に移りましょう こちらの例では オーディオトラックのあるSafariウィンドウが 2つあり 左のウィンドウを キャプチャしています ビデオ出力には最初のウィンドウが表示され 両方のSafariウィンドウの オーディオトラックが出力されています では視聴してみましょう ♪ 電子的なダンス・ミュージック♪ シェフ:お気に入りのワカモレの レシピを書きました アボカドを4つ使います Meng:ストリームの最中に 新しいフレームが利用可能になると Appが更新されたフレームを受け取ります フレーム出力には キャプチャ されたフレームを表す IOSurfaceと フレームごとの メタデータが含まれます 少しメタデータについて説明したいと思います これから Appにとても役立つ メタデータの例をお見せします メタデータにはdirty rectやcontent rect コンテンツスケールや スケールファクタなどがあります Dirty rectから始めましょう Dirty rectは前のフレームから新たに コンテンツが更新された場所を示します この例ではDirty rectがハイライトされ フレームの更新された領域を示しています 常にフレーム全体をエンコードしたり エンコーダで2フレーム間の差分を計算せずに Dirty rectを使って更新がある 領域だけをエンコードし送信して 受信側で更新を前のフレームに コピーすれば新しいフレームを作成できます Dirty rectは 出力されたCMSampleBufferの メタデータディクショナリから マッチングキーを使って取得できます 次はコンテンツRectとコンテンツスケールです キャプチャするソースウィンドウは左 出力されるストリームは右のウィンドウです ウィンドウはサイズ変更可能なため ソースウィンドウの元のサーフェスサイズが ストリーム出力の サイズに合わない事があります この例ではキャプチャしたウィンドウは フレーム出力とアスペクト比が 異なり 大きくなっています キャプチャしたウィンドウは 出力に合うよう縮小されます コンテンツRectは緑でハイライトされ ストリーム出力のキャプチャコンテンツ内の 意味のある領域を示しています コンテンツスケールは コンテンツがフィットする 拡大縮小の度合いを示しています ここではキャプチャしたSafariウィンドウは フレームに収めるために 0.77倍縮小されています 先ほど説明したメタデータを使って キャプチャしたウィンドウをできるだけ 本来の姿に近づけ正しく表示できます まずコンテンツRectを使って 出力からコンテンツを切りとりましょう 次にコンテンツスケールの 倍率でコンテンツを拡大します これでキャプチャしたコンテンツは ソースと ピクセル比が1対1になるよう 拡大縮小されました しかしキャプチャしたウィンドウは ターゲットディスプレイに どう表示されるでしょう? その質問の答としてまずスケールファクタの 仕組みから説明したいと思います ディスプレイのスケール ファクタとは ディスプレイや ウィンドウの論理的なポイントサイズと バックサーフェスのピクセル サイズとの比率を示します スケールファクタが2つまり2xモードでは 画面上の1ピクセルが4ピクセルに相当します この例のようにスケールファクタ2の Retinaディスプレイからスケールファクタ1の Retinaではないディスプレイに キャプチャしたウィンドウを移動します スケールファクタ1では 画面上の論理的な1ポイントが バックサーフェスの1ピクセルに対応します また ソースディスプレイは キャプチャしたコンテンツが表示される ターゲットディスプレイと スケールファクタが異なる場合があります この例では 左のスケールファクタ2の Retinaディスプレイから ウィンドウをキャプチャし 右のRetinaでは無いディスプレイに表示します キャプチャしたウィンドウを 1ポイント1ピクセルで ターゲットのRetinaでは無い ディスプレイにそのまま表示すると ウィンドウは4倍の大きさに見えます これを解決するにはフレームのメタデータの スケールファクタとターゲットディスプレイの スケールファクタを確認する必要があります 異なる場合は キャプチャした コンテンツのサイズを 表示前にスケールファクタで スケーリングします スケーリング後 ターゲットディスプレイのウィンドウは ソースウィンドウと同じ サイズに見えるようになります ではコードを見てみましょう 非常にシンプルです コンテンツRect コンテンツスケール スケールファクタは 出力されたCMSampleBufferの 添付メタデータから取得も可能です このメタデータを使いキャプチャした コンテンツを正しく表示するため トリミングやスケーリングを行います まとめると シングルウィンドウフィルタは ソースウィンドウが 画面外だったり 隠れていても 常にフルウィンドウの コンテンツをキャプチャ可能です ディスプレイやスペースには依存しません 出力は常に左上にオフセットされています ポップアップや子ウィンドウは含まれません コンテンツを最適に表示するため メタデータを使いましょう App全体のオーディオトラックが含まれます 1つのウィンドウをキャプチャして 表示する方法を学んだところで 表示に基づいたコンテンツ フィルタの話に移りましょう 次の例では ウィンドウやAppを使った ディスプレイベースの フィルタの作成方法を学び ビデオとオーディオフィルタの ルールの違いを紹介します ディスプレイベースの インクルージョンフィルタは コンテンツをキャプチャする ディスプレイを指定します デフォルトではウィンドウは キャプチャされません ウィンドウ単位でキャプチャする コンテンツを選択できます この例では SafariとKeynoteのウィンドウが ディスプレイフィルタに追加されています ビデオ出力には ディスプレイ スペースに配置された この2つのウィンドウだけが含まれ オーディオ出力にはKeynoteと Safari Appからの音声がすべて含まれます このコードのサンプルは 追加されたウィンドウを使って ディスプレイベースのフィルタの 作成方法を示しています まずSCShareableContentとwindowIDを使って SCWindowsのリストを作成します 次に ディスプレイと追加されたウィンドウの リストを含む ディスプレイベースの SCContentFilterを作成します その後デスクトップから独立した ウィンドウと同じように フィルタと設定を使用して ストリームを作成し 開始します ストリームが始まったので ストリーム出力を見てみましょう フィルタは 2つの Safariウィンドウ メニューバー 壁紙ウィンドウを含むよう設定されています
ウィンドウが画面外に移動すると ストリーム出力から削除されます 新しいSafariウィンドウを作っても フィルタに含まれていないため ストリーム出力には表示されません 子ウィンドウやポップアップにも 同じルールが適用され ストリームに表示されません ストリーム出力に子ウィンドウが 自動的に含まれるようにしたい場合は 追加するAppでディスプレイ ベースフィルタを使用します この例では SafariとKeynote Appをフィルタに追加することで この2つの Appのすべてのウィンドウと サウンドトラックからの オーディオとビデオ出力が 出力に含まれるようになります ウィンドウ例外フィルタは追加したAppで ディスプレイに指定すると特定のウィンドウを 出力から取り除くパワフルな方法です 例えば 1つのSafariウィンドウを 出力から削除できます ScreenCaptureKitはAppレベルで オーディオキャプチャを可能にするため 1つのSafariウィンドウから オーディオを除外すると すべてのSafari Appからオーディオトラックを 除外するのと同じことになります ストリームのビデオ出力にはまだ Safariウィンドウは含まれていますが Safari Appからのオーディオ トラックはすべて削除され オーディオ出力にはKeynoteのオーディオ トラックだけが含まれます このコードの例ではSCWindowsの代わりに SCContentFilterを変更して SCRunningApplicationsのリストを含めます さらに除外したい個別のウィンドウがあれば SCWindowsのリストを作成し 除外するウィンドウのリストと SCApplicationsのリストを使用して SCContentFilterを作成します では 含めるAppを指定して 新規または子ウィンドウを 作成した場合にストリーム出力が どのようになるかを見てみましょう 今回はSafari Appと システムウィンドウをフィルタに追加します 新しいSafariウィンドウは自動的に ストリーム出力に含まれ子ウィンドウと ポップアップにも同じ ルールが適用されています これはチュートリアルを行う際に ポップアップや新しいウィンドウを開く 作業を含め すべてを実演したい場合に便利です 複数の方法でストリーム出力に コンテンツを追加する方法をお見せしました 次はストリーム出力から コンテンツを削除する方法をお見せします この例では共有画面の プレビュー機能を有する ビデオ会議Appを模したテストAppを紹介します テストAppのプレビューが 繰り返し表示されるため 合わせ鏡のような効果が発生しています 全画面共有の場合でも画面共有Appでは 合わせ鏡効果を回避するために 自身のウィンドウや キャプチャプレビュー参加者のカメラビュー 通知ウィンドウなど 他のシステム UIを除外するのが一般的です ScreenCaptureKitはディスプレイの キャプチャからコンテンツを素早く除外できる 除外用のフィルタセットを提供しています 除外用のフィルタはデフォルトで 指定されたディスプレイから すべてのウィンドウをキャプチャします その後 個々のウィンドウやAppを 除外フィルタに追加して削除を開始できます 例えばコンテンツをキャプチャするテストAppや 通知センターを除外Appリストに追加できます 一連のAppを除外する ディスプレイベースのフィルタを作成するには バンドルIDの一致によって 除外するSCApplicationsを 取得することから始めます ストリーム出力に個別に戻したい ウィンドウがあれば例外にするSCWindowsの リストをオプションで作成することも可能です そして使用するディスプレイ 除外するAppと例外ウィンドウの リストを使用して コンテンツフィルタを作成します 結果を見てみましょう 合わせ鏡状態を引き起こした コンテンツキャプチャ テストAppと 通知ウィンドウが ストリーム出力から削除されました これらAppの新しいウィンドウや 子ウィンドウも自動的に削除されます 削除されたAppに音声が含まれる場合 その音声もオーディオ出力から削除されます ここまで単一のウィンドウをキャプチャする方法 ディスプレイフィルタから ウィンドウを追加・削除する方法を見てきました 次はストリームの設定に移ります 次の例では設定可能な様々なストリームの プロパティや スクリーンキャプチャと ストリーミングの設定 ライブプレビュー機能を備えた ウィンドウピッカーを作成する 方法について学びます まずプロパティの設定から説明します こちらは一般的なストリームのプロパティです ストリーム出力のサイズソースや送信先のRect カラースペースカラーマトリックス ピクセルフォーマットカーソルを含むか否か フレームレート制御などを設定できます 次に各プロパティの詳細を見ていきます 出力のサイズから始めましょう ここでは幅と高さを ピクセル単位で指定できます ソースディスプレイのサイズとアスペクト比は 常に出力サイズと一致するとは限りません 全画面キャプチャの時にここが異なると ストリーム出力にピラーや レターボックスが表れます キャプチャする部分を定義する ソースRectを指定すると その結果がフレーム出力上の送信先Rectに レンダリングされスケーリングされます ScreenCaptureKitは ハードウェアアクセラレーションによる カラースペース カラーマトリックス ピクセルフォーマットの変換をサポートしています 一般的なBGRAとYUV フォーマットに対応しています 完全な一覧については 開発者向けページをご覧ください カーソルの表示が有効な場合 ストリーム出力には プリレンダリングされたカーソルが含まれます これはすべてのシステムカーソルに適用され このようなカメラ型の カスタムカーソルにも適用されます 最小フレームインターバルを使って 希望の出力フレームレートを制御できます 例えば 60 fpsを要求する場合 最小インターバルを1/60に設定します すると最大で60 fpsまたはコンテンツの 元のフレームレートでフレームが更新されます キューの深さは サーバー側のサーフェスプールの サーフェス数を決定するために指定します プール内のサーフェス数を増やすと フレームレートと パフォーマンスが向上しますが システムメモリの使用量が増え レイテンシとのトレードオフに なる可能性があります これは後ほど詳しくお話します ScreenCaptureKitはキューの深さを 3~8の間で設定できますが デフォルトは3となります この例ではScreenCaptureKitが レンダリングできる 4つのサーフェスを含むように サーフェスプールが 設定されています 現在アクティブなサーフェスはサーフェス1で ScreenCaptureKitは 次の フレームをレンダリングしています サーフェス1が完了すると ScreenCaptureKitは サーフェス1をAppに送信します Appがサーフェス1を処理し保持する間 ScreenCaptureKitは サーフェス2をレンダリングしています サーフェス1はAppがまだ使用中なため プール内で使用不可とマークされます サーフェス2が完了するとAppに送信され ScreenCaptureKitは サーフェス3をレンダリングします Appがまだサーフェス1を処理している場合 フレームが処理可能な速度より速く提供され 遅延が発生し始めます サーフェスプールに大量の サーフェスがある場合 新しいサーフェスが蓄積され始め 追いつくためにフレームを落とすことを 検討する必要があります この場合 プール内のサーフェス数が 多いほど レイテンシが 高くなる可能性があります ScreenCaptureKitが使用できる プールに残っている サーフェスの数は キューの深さから Appが保持するサーフェス数を 引いた数になります この例では サーフェス1と2の両方が まだAppに保持されています サーフェスプールには サーフェスが2つ残っています サーフェス3が完了してAppに送信されると プール内に残った 利用可能な サーフェスは4だけです Appがサーフェス1~3を保持し続けると ScreenCaptureKitがレンダリングできる サーフェスが無くなり フレームロスやグリッチが発生し始めます フレームロスを防ぐためScreenCaptureKitが サーフェス4の次のフレームの レンダリングを開始する前に Appがサーフェス1を完了して リリースする必要があります Appがサーフェス1をリリースしたので 再びScreenCaptureKitで使用可能になりました まとめると フレームの遅延や ロスを回避するために Appが従うべきルールが2つあります フレームの遅延を防ぐには MinimumFrameInterval内に フレームを処理できるように する必要があります フレームロスを防ぐにはAppがサーフェスを リリースしてプールに戻すまでの時間が MinimumFrameIntervalに キューの深さを掛けた値から1を 引いた数より短い必要があります その後はScreenCaptureKitが使える サーフェスが無くなり 新しいフレームを生成できません さまざまなプロパティ設定を見たところで スクリーンキャプチャおよび ストリーミングのために ストリームを設定する例をいくつか紹介します スクリーンコンテンツには 常に更新されるビデオ ゲームアニメーションなどがあり 高いフレームレートを要求されます また Keynoteウィンドウのように 静止したテキストが多く フレームレートより高解像度を 優先するものもあります 共有するコンテンツやネットワークの 状況に応じて ストリーム設定を その場で調整できます このコードの例では4K 64fpsで ゲームをストリームするために キャプチャを設定します まずストリーム出力のサイズを ピクセルサイズで4Kに設定します そして出力フレームを60fpsにするために 最小フレームインターバルを1/60に設定します エンコードとストリーミングに ピクセルフォーマットYUV420を使用します オプションのソースRectを設定して 画面の一部のみをキャプチャします 次に背景のフィルカラーを黒に変更し フレーム出力にカーソルを含めます 最適なフレームレートと パフォーマンスのために サーフェスキューの深さを5に設定します 最後に 出力ストリームの オーディオを有効にします 先ほどの例で見たストリーム設定はすべて ストリームを再度作成せずにオンザフライで 動的に変更できます 例えば出力サイズなどの プロパティをライブで調整したり フレームレートを動的に変更したり ストリームフィルタを更新したりできます ここでは4Kから720pに出力サイズを 下げる例を示しています フレームレートも60fpsから 15fpsに落としています その後 updateConfigurationを呼び出すだけで ストリームを中断することなく オンザフライで新しい設定を適用できます 最後の例ではライブビュー機能を備えた ウィンドウピッカーの作成方法を説明します ここでは典型的な ウィンドウピッカーの例を示します ウェブ会議の画面共有Appでは 共有するウィンドウの正確なサイズを 選択できるオプションを ユーザに提供するのが一般的です ScreenCaptureKitはライブコンテンツの 更新の際に多数のサムネイルサイズの ストリームを作成する効率的で高性能な ソリューションを簡単に実装可能です ScreenCaptureKitを使って このようなウィンドウピッカーを 作る作業を見てみましょう ピッカーを設定するにはまずAppがユーザに ピックを許可する対象となるウィンドウごとに デスクトップの独立したウィンドウを フィルタタイプとする シングルウィンドウフィルタを作成します 次にストリームを設定します サムネイルサイズ 5fps BGRAピクセルフォーマット キューの深さはデフォルト カーソルとオーディオは無しにします シングルウィンドウフィルタと このストリーム設定を使って 各ウィンドウに 1つのストリームを作成します これをコード化するにはまず デスクトップとシステムウィンドウを除外し SCShareableContentを取得します 次に対象の各ウィンドウに デスクトップの独立した ウィンドウタイプの コンテンツフィルタを作成します 次に ストリーム設定部分に移ります 適切なサムネイルサイズを選択します この例では284x182とし 最小フレームインターバルを1/5に設定します 画面表示のピクセルフォーマットをBGRAに オーディオとカーソルはプレビューでは 必要ないため無効にします 頻繁な更新はないと思うのでキューの深さは 3に設定します ストリームコンテンツフィルタと設定を作成し ストリームを作成する準備ができました ウィンドウごとにストリームを1つ作成し ストリームごとにストリーム出力を追加 ストリームを開始します 最後にストリームリストに加えます これが先ほどのサンプル コードを使って作成した ライブプレビュ-付きの ウィンドウピッカーです 各サムネイルはライブで更新され シングルウィンドウフィルタで 個々にストリームされます ScreenCaptureKitを使うと このようなライブプレビューピッカーを 簡単に作成できシステムに負担を掛けずに 多くのライブスクリーンを 同時にキャプチャできます では 同僚のDrewに交代して ScreenCaptureKitのOBSへの応用について 素晴らしいデモを披露してもらいます Drew Nills:ありがとう Meng Apple Partner EngineerのDrewです OBS Studioはコンピュータからの ストリーミングコンテンツを録画 管理できる オープンソースのAppです この春に取り組んだプロジェクトで ScreenCaptureKitが統合されました ScreenCaptureKitは OBSの既存のCGDisplayStream ベースのキャプチャと同様の コードを利用し実装されました ScreenCaptureKitの実装については 「ScreenCaptureKitの紹介」のセッションで 多くの機能のデモを行っています デスクトップ全体 および Appのすべてのウィンドウ 特定の1つのウィンドウの キャプチャなどを紹介しています ScreenCaptureKitは OBSのCGWindowListCreateImage ベースのキャプチャよりも オーバーヘッドが低くなっています これは 画面の一部をキャプチャする場合 コンテンツ制作に使えるリソースが 増えることを意味しています ではこれまで説明した 内容を実際にデモで見てみましょう 左はOBSのウィンドウ キャプチャの最悪の例です CGWindowListCreateImage API でキャプチャを行い コマ飛びが発生しています テストではフレームレートが 7fpsまで落ち込んでいました 一方 右のScreenCaptureKitを 使用するとより滑らかな結果となり スムーズな映像が出力されています このケースでは60fpsを実現しています その間 OBSではWindow Captureより RAMの使用量が最大15%下がっています またWindow Captureの代わりに ScreenCaptureKitを 使用した場合 CPU使用率は 最大で半分に削減できます ScreenCaptureKitが OBSユーザに提供する 他の改善点を見てみましょう 私はまだ「Sayonara Wild Hearts」の 全部のGold Rankを達成していないのですが 自分のベストなプレーを見せたいので ゲームプレイを録画しています ScreenCaptureKitのおかげでゲームから オーディオストリームを 直接キャプチャできます Macに通知が来ても 録画の音声や映像には影響しません オーディオルーティングソフトを 別にインストールする必要はありません ♪ AppleシリコンがScreenCaptureKitに 提供する機能拡張をすべて利用して Macで「太鼓の達人 Pop Tap Beat」 のようなゲームを 人気のストリーミングサービスに配信できます Appleシリコンのハードウェアエンコーダに 新たに固定ビットレートオプションが加わり ゲームパフォーマンスに大きな影響を与えずに 固定ビットレートを必要とするサービス向けに ストリーミングコンテンツをエンコードできます ScreenCaptureKitの低いリソース占有率と エンコードの負担軽減により 重要なコンテンツでさらに多くの パフォーマンスを利用できるのです Mengにお返しします Meng:ありがとう Drew デモや例を通して 高度なスクリーンコンテンツ フィルタについて学びました さまざまな使い方に対応する ストリーム設定の方法 フレーム単位のメタデータを 使用し キャプチャした コンテンツを正しく表示する方法 最高のパフォーマンスを 実現するベストプラクティス 最後に DrewがScreenCaptureKitが OBSに提供する機能や パフォーマンスの改善について紹介しました ScreenCaptureKitを使ったAppで画面共有 ストリーミング 共同制作体験の新たな可能性を 見るのが待ち遠しいです ご視聴ありがとうございました! ♪
-
-
4:36 - Create a single window filter
// Get all available content to share via SCShareableContent let shareableContent = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) // Get window you want to share from SCShareableContent guard let window : [SCWindow] = shareableContent.windows.first( where: { $0.windowID == windowID }) else { return } // Create SCContentFilter for Independent Window let contentFilter = SCContentFilter(desktopIndependentWindow: window) // Create SCStreamConfiguration object and enable audio capture let streamConfig = SCStreamConfiguration() streamConfig.capturesAudio = true // Create stream with config and filter stream = SCStream(filter: contentFilter, configuration: streamConfig, delegate: self) stream.addStreamOutput(self, type: .screen, sampleHandlerQueue: serialQueue) stream.startCapture()
-
9:38 - Get dirty rects
// Get dirty rects from CMSampleBuffer dictionary metadata func streamUpdateHandler(_ stream: SCStream, sampleBuffer: CMSampleBuffer) { guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: false) as? [[SCStreamFrameInfo, Any]], let attachments = attachmentsArray.first else { return } let dirtyRects = attachments[.dirtyRects] } } // Only encode and transmit the content within dirty rects
-
13:34 - Get content rect, content scale and scale factor
/* Get and use contentRect, contentScale and scaleFactor (pixel density) to convert the captured window back to its native size and pixel density */ func streamUpdateHandler(_ stream: SCStream, sampleBuffer: CMSampleBuffer) { guard let attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: false) as? [[SCStreamFrameInfo, Any]], let attachments = attachmentsArray.first else { return } let contentRect = attachments[.contentRect] let contentScale = attachments[.contentScale] let scaleFactor = attachments[.scaleFactor] /* Use contentRect to crop the frame, and then contentScale and scaleFactor to scale it */ } }
-
15:37 - Create display filter with included windows
// Get all available content to share via SCShareableContent let shareableContent = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) // Create SCWindow list using SCShareableContent and the window IDs to capture let includingWindows = shareableContent.windows.filter { windowIDs.contains($0.windowID)} // Create SCContentFilter for Full Display Including Windows let contentFilter = SCContentFilter(display: display, including: includingWindows) // Create SCStreamConfiguration object and enable audio let streamConfig = SCStreamConfiguration() streamConfig.capturesAudio = true // Create stream stream = SCStream(filter: contentFilter, configuration: streamConfig, delegate: self) stream.addStreamOutput(self, type: .screen, sampleHandlerQueue: serialQueue) stream.startCapture()
-
18:13 - Create display filter with included apps
// Get all available content to share via SCShareableContent let shareableContent = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) /* Create list of SCRunningApplications using SCShareableContent and the application IDs you’d like to capture */ let includingApplications = shareableContent.applications.filter { appBundleIDs.contains($0.bundleIdentifier) } // Create SCWindow list using SCShareableContent and the window IDs to except let exceptingWindows = shareableContent.windows.filter { windowIDs.contains($0.windowID) } // Create SCContentFilter for Full Display Including Apps, Excepting Windows let contentFilter = SCContentFilter(display: display, including: includingApplications, exceptingWindows: exceptingWindows)
-
20:46 - Create display filter with excluded apps
// Get all available content to share via SCShareableContent let shareableContent = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: false) /* Create list of SCRunningApplications using SCShareableContent and the app IDs you’d like to exclude */ let excludingApplications = shareableContent.applications.filter { appBundleIDs.contains($0.bundleIdentifier) } // Create SCWindow list using SCShareableContent and the window IDs to except let exceptingWindows = shareableContent.windows.filter { windowIDs.contains($0.windowID) } // Create SCContentFilter for Full Display Excluding Windows let contentFilter = SCContentFilter(display: display, excludingApplications: excludingApplications, exceptingWindows: exceptingWindows)
-
28:46 - Configure 4k 60FPS capture for streaming
let streamConfiguration = SCStreamConfiguration() // 4K output size streamConfiguration.width = 3840 streamConfiguration.height = 2160 // 60 FPS streamConfiguration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(60)) // 420v output pixel format for encoding streamConfiguration.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange // Source rect(optional) streamConfiguration.sourceRect = CGRectMake(100, 200, 3940, 2360) // Set background fill color to black streamConfiguration.backgroundColor = CGColor.black // Include cursor in capture streamConfiguration.showsCursor = true // Valid queue depth is between 3 to 8 streamConfiguration.queueDepth = 5 // Include audio in capture streamConfiguration.capturesAudio = true
-
30:08 - Live downgrade 4k 60FPS to 720p 15FPS
// Update output dimension down to 720p streamConfiguration.width = 1280 streamConfiguration.height = 720 // 15FPS streamConfiguration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(15)) // Update the configuration try await stream.updateConfiguration(streamConfiguration)
-
31:57 - Build a window picker with live preview
// Get all available content to share via SCShareableContent let shareableContent = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true) // Create a SCContentFilter for each shareable SCWindows let contentFilters = shareableContent.windows.map { SCContentFilter(desktopIndependentWindow: $0) } // Stream configuration let streamConfiguration = SCStreamConfiguration() // 284x182 frame output streamConfiguration.width = 284 streamConfiguration.height = 182 // 5 FPS streamConfiguration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(5)) // BGRA pixel format for on screen display streamConfiguration.pixelFormat = kCVPixelFormatType_32BGRA // No audio streamConfiguration.capturesAudio = false // Does not include cursor in capture streamConfiguration.showsCursor = false // Valid queue depth is between 3 to 8 // Create a SCStream with each SCContentFilter var streams: [SCStream] = [] for contentFilter in contentFilters { let stream = SCStream(filter: contentFilter, streamConfiguration: streamConfig, delegate: self) try stream.addStreamOutput(self, type: .screen, sampleHandlerQueue: serialQueue) try await stream.startCapture() streams.append(stream) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。