 
							- 
							
							ScreenCaptureKitの紹介ScreenCaptureKitが、macOSの画面共有・ビデオ会議・ゲームストリーミングサービスなどのAppに、高性能のスクリーンキャプチャを提供する仕組みについて解説します。このAPIの構成要素について解説し、ストリームを設定して画面上の動画およびオーディオコンテンツをキャプチャする方法や、それを既存のAppに統合するためのヒントを紹介します。 リソース関連ビデオWWDC23WWDC22
- 
							このビデオを検索(音楽) こんにちは 僕はErnest ScreenCaptureKitチームの ソフトウェアエンジニアです この2年間 リモートでの共同作業で しばしば画面の共有を行います それに加え OBS Studioのような録画アプリケーションを 使ったゲームプレイのストリーミングや コンテンツ制作そのものが 教育や娯楽において成長し続けています このような背景から開発者のニーズに応え パフォーマンスと堅牢性に優れた スクリーンキャプチャーの フレームワークを作りました ScreenCaptureKitです これはmacOSの新しいフレームワークで Appにおける画面共有を 実現します ScreenCaptureKit は アプリケーションのニーズに合わせて デベロッパのコントロールやトグルを使って キャプチャしたいコンテンツを 選択可能な API を提供します フィルターやコントロールは 即座に更新できます このフレームワークはディスプレイの ネイティブの解像度や フレームレートに対応する 最高の品質と性能を実現し プライバシーにも重点を置いています このセッションでは ScreenCaptureKit を 使い始めるためのお手伝いをします 基本を押さえた上で 「Take ScreenCaptureKit to the next level」で より高度なトピックをご覧ください まずフレームワークの主な機能を説明します 次に ScreenCaptureKit の主な構成要素について API の概要を説明します 次にフィルタと設定を使ってストリームを セットアップする方法を説明します そして最後にビデオとオーディオのサンプルを アプリケーションに ストリーミングする方法を説明します では 主な特徴からです ScreenCaptureKitは共有したいコンテンツや 除去したいコンテンツの種類を指定できます ディスプレイ アプリケーション ウィンドウの あらゆる組み合わせから 画面のコンテンツをキャプチャでき それに付随する音声もキャプチャできます ScreenCaptureKitは ピクセルフォーマット 色空間 フレームレート 解像度 そしてオーディオ側ではサンプルレートや チャンネル数などのコントロールなど さまざまな開発者向けコントロールを サポートしています これらのフィルターや設定は オンザフライで調節でき より柔軟なAppデザインが可能です 最大48kHzステレオの音声と ディスプレイのネイティブ解像度 フレームレートの映像を取り込むために 既存のキャプチャ方法よりも低い CPU オーバーヘッドで Mac GPU のパワーを活用します もちろんScreenCaptureKit はプライバシーを 考慮して構築されており すべてのアプリケーションに対して グローバルなプライバシー保護機能を提供します 映像と音声を取り込む前に承諾が必要で システム環境設定の Screen Recordingのプライバシー設定に 保存されます ScreenCaptureKitが何かわかったところで APIの最も重要な概念をお見せしましょう ScreenCaptureKitの中心はSCStreamです SCStreamはstartやstopなどの 制御メソッドを扱い SCShareableContentとSCContentFilter SCStreamConfigurationと共に作られます これらは何のコンテンツをどう取り込むか 決定します いざ作成されスタートすると メディアサンプルは SCStreamOutputプロトコルで Appに配信されます 後ほど説明します Appでのストリーム設定のAPIについてです ストリームをセットアップする際に 知っておきたいオブジェクトを紹介します これらのオブジェクトが キャプチャーの内容や品質 パフォーマンスを決定します SCShareableContentから始めましょう このデスクトップ上には ウィンドウ アプリケーション ディスプレイがあります ScreenCaptureKitには 共有コンテンツ作成のため それぞれに対応するクラスがあります まずはSCDisplayです ディスプレイをSCDisplaysと分類し リードオンリーのDisplay identifierと サイズのWidthとHeightのプロパティがあります ディスプレイ内で開いているAppにはそれぞれ SCRunningApplicationがあります SCRunningApplicationsにはApp情報の リードオンリープロパティがあり それらはBundle identifierや Application nameとProcess identifierです この例ではKeynoteとSafariの SCRunningApplicationがあります そしてこれらのAppにはウィンドウがあります これらのウィンドウにはSCWindowがあり それぞれを定義する リードオンリープロパティの Window IDやFrameとTitle そしてOn screenか最小化されてるかです またSCWindowにはOwning applicationがあり この場合 両方のSafariのSCWindowsに 同じowning applicationのSafariがあります SCWindows SCRunningApplications SCDisplays が SCShareableContentにまとめられて 共有できるコンテンツを提供します デバイスの共有可能なコンテンツの リストを得るか パラメーターで指定できます 共有するものを選ぶために スクリーン上のAppや ウィンドウのリストが欲しい場合 ScreenCaptureKitにはシンプルなAPIがあります このコードはdeveloper.apple.comにある サンプルコードの一部です 画面上にあるウィンドウだけが SCShareableContent で返され 関連する SCWindows SCApplications SCDisplays が含まれます 共有可能なコンテンツが得られたので フィルターを作ります SCContentFiltersには2種類あります ディスプレイを指定しないフィルタは 複数のディスプレイのウィンドウを取得し ディスプレイ指定のフィルタは 特定のウィンドウやアプリケーションを 含めたり除外したりするオプションがあります 音声の取り込みはAppレベルでのみ フィルター可能です ではフィルターのデモをお見せしましょう Keynoteのウィンドウだけを共有したいとします ディスプレイを指定しないフィルタを使って ディスプレイ間を移動する ウィンドウを取得します ディスプレイ内のすべてを共有したい場合も 共有したくないものがあるかもしれません 例えばキャプチャを行う自身のAppを除外して 鏡の回廊効果を回避したいはずです また機密情報が画面上に出ている場合も キャプチャの対象から外すべきです SCContentFilterがこれらをすべて処理します コードを見てみましょう これは先ほどのコードです 共有可能なコンテンツを検索したら このサンプルコードと同じ Bundle IDのAppを探し ディスプレイ指定のフィルターで そのAppを除外します ScreenCaptureKitはストリーム毎に 品質と性能を調整できます SCStreamConfigurationで設定します 映像の調整には出力解像度やフレームレート カーソルの有無などがあります オーディオの方は オーディオでの有無やサンプルレート チャンネル数があります これらを調整するシナリオを見てみましょう メモやスプレッドシートなど 文字の鮮明度が重要なコンテンツの場合 出力解像度を4K フレームを10fpsにします このコンテンツには音声がないので 音声をオフにしておきます 休暇のビデオの共有など 動きの多いコンテンツの場合 解像度よりフレームレートを優先し 出力解像度を1080pに下げ フレームレートを60fpsに増やします カーソルは邪魔になるので カーソルを隠します また音声を取り込めば没入感が高まります これらの調整はSCStreamConfigurationで 行うことができます 動きの多いコンテンツの設定例です ここでは出力解像度が1080pになっています minimum Frame Interval は 60fpsで取り込むために 1/60の設定です そしてstreamConfigでカーソルを隠しています 音声では captures Audio を true にして音声を取り込み サンプルレートは48kHz チャンネル数は2です SCContentFilter と SCStreamConfiguration で Appのニーズに応えた 取り込み設定に必要な情報が 得られました これでSCStreamが作成できます オーバービューに戻りましょう フィルターとコンフィグで ストリームを初期化します エラーの対応に デリゲートを追加できます それからStart captureしScreenCaptureKitが サンプルをSCStreamで提供します フィルタとコンフィグが作成されたので コードでストリームを開始するのは簡単です お見せしましょう 再びフィルターとコンフィグで SCStreamを初期化します エラー処理のデリゲートとして self を指定 SCStreamを作成しStart captureを呼びます ストリームの初期化と起動が完了したら 次のステップはメディアサンプルを アプリケーションに取り込むことです 音声と映像サンプルはCMSampleBuffersとして Appに送られます それらのサンプルを得るためには SCStreamOutput プロトコルを 実装したオブジェクトを ストリームに追加する必要があります ハンドラーキューも指定します これは余分な dispatch なしに 特定のキューにサンプルを送るのに最適です 指定しなければデフォルトキューです ストリームが始まるとサンプルができた時 コールバックを行います では コードでメディアサンプルを得る方法です SCStreamOutputプロトコルの 実装を示しています 新しいサンプルができると呼び出されます CMSampleBuffersとしてサンプルが送られ ストリームとサンプルタイプを提供します サンプルバッファのハンドラーを実装し StreamOutputを追加するだけです これでストリームからのサンプルが 希望するコンテンツとフォーマットで Appに送られます CMSampleBuffersとしてサンプルを配信するので 使い方について話しましょう 映像の方では CMSampleBuffer は IOSurface-backed です SCStreamFrameInfo で CMSampleBufferにアタッチメントを 提供しています これにはビデオサンプルの情報が含まれています Frame Status で現在の ストリーム状況を確認します completeは新しいビデオフレームが あることを意味し idleはサンプルに変わりがないことを意味し 新しいIOSurfaceはありません それ以外の場合提供されるサンプルは 他の CMSampleBuffer と同様なので 既存の CMSampleBuffer ユーティリティを 使用することができます ScreenCaptureKit には画面上の 音声と映像を選択するAPIがあります さらにフレームワークは Appのニーズに応えるコントロールを提供します キャプチャの方法の基本も ここでカバーしました ScreenCaptureKitの登場でCGDisplayStreamや CGWindowListは近い将来非推奨となります ScreenCaptureKitの導入が 皆様の助けとなることを願っています さらに高度な情報は "ScreenCaptureKitを次のレベルに" をご覧ください ありがとうございました (音楽) 
- 
							- 
										
										6:53 - Creating a SCShareableContent object // Creating a SCShareableContent object // Get the content that's available to capture. let content = try await SCShareableContent.excludingDesktopWindows( false, onScreenWindowsOnly: true )
- 
										
										8:32 - Creating a SCContentFilter object // Creating a SCContentFilter object // Get the content that's available to capture. let content = try await SCShareableContent.excludingDesktopWindows( false, onScreenWindowsOnly: true ) // Exclude the sample app by matching the bundle identifier. let excludedApps = content.applications.filter { app in Bundle.main.bundleIdentifier == app.bundleIdentifier } // Create a content filter that excludes the sample app. filter = SCContentFilter(display: display, excludingApplications: excludedApps, exceptingWindows: [])
- 
										
										10:23 - Creating a SCStreamConfiguration object // Creating a SCStreamConfiguration object let streamConfig = SCStreamConfiguration() // Set output resolution to 1080p streamConfig.width = 1920 streamConfig.height = 1080 // Set the capture interval at 60 fps streamConfig.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(60)) // Hides cursor streamConfig.showsCursor = false // Enable audio capture streamConfig.capturesAudio = true // Set sample rate to 48000 kHz stereo streamConfig.sampleRate = 48000 streamConfig.channelCount = 2
- 
										
										11:46 - Creating and starting a SCStream object // Creating and starting a SCStream object // Create a capture stream with the filter and stream configuration stream = SCStream(filter: filter, configuration: streamConfig, delegate: self) // Start the capture session try await stream?.startCapture() // ... // Error handling delegate func stream(_ stream: SCStream, didStopWithError error: Error) { DispatchQueue.main.async { self.logger.error("Stream stopped with error: \(error.localizedDescription)") self.error = error self.isRecording = false } }
- 
										
										13:07 - Getting media samples // SCStreamOutput protocol implementation func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) { switch type { case .screen: handleLatestScreenSample(sampleBuffer) case .audio: handleLatestAudioSample(sampleBuffer) } } // ... // Create a capture stream with the filter and stream configuration stream = SCStream(filter: filter, configuration: streamConfig, delegate: self) // Add a stream output to capture screen and audio content try stream?.addStreamOutput(self, type: .screen, sampleHandlerQueue: screenFrameOutputQueue) try stream?.addStreamOutput(self, type: .audio, sampleHandlerQueue: audioFrameOutputQueue) // Start the capture session try await stream?.startCapture()
 
-