Media projection on large screens

The media projection APIs introduced in Android 5 (API level 21) enable you to capture the contents of a device display as a media stream that you can play back, record, or cast to other devices such as TVs.

A media projection involves three representations of the device display:

Real device display projected onto virtual display. Contents of
          virtual display written to application-provided `Surface`.

Figure 1. Real device display projected onto virtual display. Contents of virtual display written to application-provided Surface.

A media projection captures the contents of a device display and then projects the captured image to a virtual display that renders the image on a Surface.

The application provides the Surface by means of a SurfaceView or ImageReader, either of which consumes the contents of the captured display. The OnImageAvailableListener of an ImageReader enables you to manage images rendered on the Surface in real time. You can save the images as a recording or cast them to a TV or other device.

MediaProjection

Begin a media projection session by obtaining a token that grants the app the ability to capture the display contents, device audio, or both. The token is represented by an instance of the MediaProjection class. You can create an instance of this class when you start a new activity.

Legacy approach

To acquire a media projection token using the legacy approach, call startActivityForResult() with an intent returned from the createScreenCaptureIntent() method of the MediaProjectionManager system service:

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

Java

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

The call displays a confirmation dialog box informing the user that the media projection captures all displayed information, including any sensitive or personally identifiable information.

If the user provides confirmation, startActivityForResult() passes a result code and data to the onActivityResult() callback.

You can then pass the data and result code to the getMediaProjection() method from MediaProjectionManager to create an instance of MediaProjection:

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

Java

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);

The recommended approach for obtaining a media projection token uses APIs from the Jetpack Activity library:

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());
    }
  }
);

Virtual display

The centerpiece of a media projection is the virtual display, which you create by calling createVirtualDisplay() on a MediaProjection instance:

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);

The width and height parameters specify the width and height of the virtual display. To obtain values that match the width and height of the media projection, use the WindowMetrics APIs introduced in Android 11 (API level 30).

WindowMetrics

A media projection captures the entire display, regardless of whether the app creating the media projection is running in full screen or in multi-window mode.

The correct method for obtaining the dimensions of a media projection is WindowManager#getMaximumWindowMetrics(). This method returns a WindowMetrics object for the full display even if the media projection app is in multi-window mode, occupying only part of the display.

For compatibility down to API level 14, use WindowMetricsCalculator#computeMaximumWindowMetrics() from the Jetpack WindowManager library.

Call WindowMetrics#getBounds() to get the correct width and height for the media projection’s virtual display (see Virtual display).

Always make your media projection apps resizable. Resizable apps support device configuration changes and multi‑window mode (see Multi-window support).

If your app is not resizable, it must query the display bounds from a window context and retrieve the WindowMetrics of the maximum display area available to the app using WindowManager#getMaximumWindowMetrics():

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

You should size the media projection surface to produce output in the desired resolution. Make the size large (low resolution) for screen casting to TVs or computer monitors and small (high resolution) for device display recording.

As of 12L (API level Sv2), when the system renders a virtual display on the surface, it scales the virtual display to fit the surface using a process similar to the centerInside option from ImageView.

The new scaling approach improves screen casting to televisions and other large displays by maximizing the size of the surface image while ensuring the proper aspect ratio.

Recommendations

To get the best results with media projection, follow these recommendations:

  • Make your app resizable. Resizable apps support device configuration changes and multi-window mode (see Multi‑window support). In your app manifest, set resizeableActivity="true". On Android 7.0 (API level 24), this setting is true by default.
  • Enable your apps to support landscape and portrait orientations because both orientations are common across phone, tablet, and foldable form factors.
  • Use WindowManager#getMaximumWindowMetrics() to get the bounds of a media projection. For compatibility down to API level 14, use Jetpack WindowManager. (See the WindowMetrics section.)
  • If your app is not resizable, obtain the media projection bounds from a window context. (See the WindowMetrics section.)

Resources

For more information, see Capture video and audio playback.