大画面でのメディア プロジェクション

Android 5(API レベル 21)で導入されたメディア プロジェクション API を使用すると、デバイス ディスプレイのコンテンツをメディア ストリームとしてキャプチャし、テレビなどの他のデバイスで再生、録画、キャストできます。

メディア プロジェクションは、デバイス ディスプレイにおける次の 3 つの表現に対応しています。

仮想ディスプレイに実際のデバイス ディスプレイを投影する。仮想ディスプレイのコンテンツをアプリが提供する「サーフェス」に書き込む。
図 1. 仮想ディスプレイに実際のデバイス ディスプレイを投影する。仮想ディスプレイのコンテンツをアプリが提供する Surface に書き込む。

メディア プロジェクションでは、デバイス ディスプレイのコンテンツをキャプチャし、キャプチャした画像を仮想ディスプレイに投影して、Surface にレンダリングします。

アプリは、キャプチャされたディスプレイのコンテンツを使用する SurfaceView または ImageReader によって Surface を提供します。ImageReaderOnImageAvailableListener を使用すると、Surface でレンダリングされる画像をリアルタイムで管理できます。録画として画像を保存したり、テレビなどのデバイスにキャストしたりできます。

メディア プロジェクション

メディア プロジェクション セッションを開始するには、ディスプレイのコンテンツ、デバイス オーディオ、またはその両方をキャプチャする権限をアプリに付与するトークンを取得します。トークンは、MediaProjection クラスのインスタンスで表されます。このクラスのインスタンスは、新しいアクティビティの開始時に作成できます。

以前の方法

以前の方法でメディア プロジェクション トークンを取得するには、MediaProjectionManager システム サービスの createScreenCaptureIntent() メソッドによって返されたインテントを使用して、startActivityForResult() を呼び出します。

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

Java

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

この呼び出しによって、表示されたすべての情報(機密情報や個人を特定できる情報を含む)がメディア プロジェクションによってキャプチャされたことをユーザーに通知する、確認ダイアログ ボックスが表示されます。

ユーザーが確認すると、startActivityForResult()onActivityResult() コールバックに結果コードとデータを渡します。

次に MediaProjectionManager から getMediaProjection() メソッドにデータと結果コードを渡して、MediaProjection のインスタンスを作成できます。

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

Java

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);

メディア プロジェクション トークンを取得する推奨の方法では、Jetpack Activity ライブラリの API を使用します。

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection

val startMediaProjection = registerForActivityResult(
  StartActivityForResult()
) { result ->
  if (result.resultCode == RESULT_OK) {
    mediaProjection = mediaProjectionManager
      .getMediaProjection(result.resultCode, result.data!!)
  }
}

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
  getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];

ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult(
  new StartActivityForResult(),
  result -> {
    if (result.getResultCode() == Activity.RESULT_OK) {
      mediaProjection[0] = mediaProjectionManager
        .getMediaProjection(result.getResultCode(), result.getData());
    }
  }
);

仮想ディスプレイ

メディア プロジェクションの中心となるのは仮想ディスプレイです。仮想ディスプレイは、MediaProjection インスタンスで createVirtualDisplay() を呼び出して作成します。

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

width および height パラメータは、仮想ディスプレイの幅と高さを指定します。メディア プロジェクションの幅と高さに一致する値を取得するには、Android 11(API レベル 30)で導入された WindowMetrics API を使用します。

WindowMetrics

メディア プロジェクションを作成するアプリが全画面モードとマルチ ウィンドウ モードのどちらで実行されているかにかかわらず、メディア プロジェクションはディスプレイ全体をキャプチャします。

メディア プロジェクションの寸法を取得するには、WindowManager#getMaximumWindowMetrics() を使用します。このメソッドは、メディア プロジェクション アプリがマルチ ウィンドウ モードで実行されていてディスプレイの一部だけを占有している場合であっても、ディスプレイ全体を表す WindowMetrics オブジェクトを返します。

API レベル 14 までの互換性をさかのぼって確保するには、Jetpack WindowManager ライブラリの WindowMetricsCalculator#computeMaximumWindowMetrics() を使用します。

メディア プロジェクションの仮想ディスプレイの正しい幅と高さを取得するには、WindowMetrics#getBounds() を呼び出します(仮想ディスプレイを参照)。

メディア プロジェクション アプリは、必ずサイズ変更可能にしてください。サイズ変更が可能なアプリは、デバイス設定の変更とマルチウィンドウ モードをサポートします(マルチウィンドウのサポートをご覧ください)。

アプリがサイズ変更不可の場合は、ウィンドウ コンテキストから表示境界をクエリし、WindowManager#getMaximumWindowMetrics() を使用して、アプリで利用可能な最大表示領域の WindowMetrics を取得する必要があります。

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();

Surface

必要な解像度で出力を生成するには、メディア プロジェクション サーフェスのサイズを調整する必要があります。テレビやパソコンのモニターに画面をキャストする場合は大きいサイズ(低解像度)に、デバイスのディスプレイを録画する場合は小さいサイズ(高解像度)にします。

12L(API レベル 32)以降では、サーフェスで仮想ディスプレイをレンダリングする際に、ImageViewcenterInside オプションと同様のプロセスを使用して、仮想ディスプレイがサーフェスに合わせてスケーリングされます。

この新しいスケーリング方法により、適切なアスペクト比を維持しながら、サーフェス画像のサイズを最大化することで、テレビや他の大型ディスプレイに画面をキャストしやすくなります。

おすすめ

メディア プロジェクションで最適な結果を得るための推奨事項を示します。

  • アプリのサイズを変更できるようにします。サイズ変更が可能なアプリは、デバイス設定の変更とマルチウィンドウ モードをサポートします(マルチウィンドウのサポートをご覧ください)。アプリ マニフェストで resizeableActivity="true" を設定します。Android 7.0(API レベル 24)以降では、この設定がデフォルトで true になっています。
  • スマートフォン、タブレット、折りたたみ式フォーム ファクタは一般的に横向きと縦向きの両方に対応しているため、アプリも横向きと縦向きの両方をサポートするようにします。
  • WindowManager#getMaximumWindowMetrics() を使用して、メディア プロジェクションの境界を取得します。API レベル 14 までの互換性を確保するには、Jetpack WindowManager を使用します(WindowMetrics セクションを参照)。
  • アプリのサイズを変更できない場合は、ウィンドウ コンテキストからメディア プロジェクションの境界を取得します(WindowMetrics セクションを参照)。

参考情報

メディア プロジェクションの詳細については、動画と音声の再生のキャプチャをご覧ください。