Proyección de contenido multimedia en pantallas grandes

Las API de proyección de contenido multimedia que se introdujeron en Android 5 (nivel de API 21) te permiten capturar el contenido de la pantalla de un dispositivo como una transmisión de contenido multimedia que puedes reproducir, grabar o transmitir a otros dispositivos, como TVs.

Una proyección de contenido multimedia implica tres representaciones de la pantalla de un dispositivo:

Pantalla real de un dispositivo proyectada en una pantalla virtual. Contenido de la pantalla virtual escrito en "Surface" que brinda la aplicación.

Figura 1: Pantalla real de un dispositivo proyectada en una pantalla virtual. Contenido de la pantalla virtual escrito en Surface que brinda la aplicación.

Una proyección de contenido multimedia captura el contenido de la pantalla de un dispositivo y, luego, proyecta la imagen capturada en una pantalla virtual que renderiza la imagen en Surface.

La aplicación brinda Surface por medio de SurfaceView o ImageReader, que consumen el contenido de la pantalla capturada. OnImageAvailableListener de ImageReader te permite administrar las imágenes renderizadas en Surface, en tiempo real. Puedes guardar las imágenes como una grabación, o transmitirlas a una TV o a otro dispositivo.

MediaProjection

Para iniciar una sesión de proyección de contenido multimedia, obtén un token que le otorgue a la app la capacidad de capturar el contenido de la pantalla, el audio del dispositivo o ambos. El token es representado por una instancia de la clase MediaProjection. Puedes crear una instancia de esta clase cuando inicies una actividad nueva.

Enfoque heredado

Para obtener un token de proyección de contenido multimedia con el enfoque heredado, llama a startActivityForResult() con un intent que muestre el método createScreenCaptureIntent() del servicio del sistema MediaProjectionManager:

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

Java

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

La llamada muestra un cuadro de diálogo de confirmación que le informa al usuario que la proyección de contenido multimedia captura todos los datos que se muestran, incluida cualquier información sensible o de identificación personal.

Si el usuario confirma, startActivityForResult() pasa datos y un código de resultado a la devolución de llamada onActivityResult().

Luego, puedes pasar los datos y el código de resultado al método getMediaProjection() desde MediaProjectionManager a fin de crear una instancia de MediaProjection:

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

Java

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);

Como enfoque recomendado para obtener un token de proyección de contenido multimedia, se usan las API de la biblioteca de Activity de Jetpack:

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

Pantalla virtual

La pieza central de una proyección de contenido multimedia es la pantalla virtual, que creas cuando llamas a createVirtualDisplay() en una instancia de MediaProjection:

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

Los parámetros width y height especifican el ancho y el alto de la pantalla virtual. Para obtener valores que coincidan con el ancho y la altura de la proyección de contenido multimedia, usa las API de WindowMetrics que se introdujeron en Android 11 (nivel de API 30).

WindowMetrics

Una proyección de contenido multimedia captura la pantalla completa, independientemente de si la app que crea la proyección de contenido multimedia se ejecuta en pantalla completa o en el modo multiventana.

WindowManager#getMaximumWindowMetrics() es el método correcto para obtener las dimensiones de una proyección de contenido multimedia. Este método muestra un objeto WindowMetrics para la pantalla completa, incluso si la app de proyección de contenido multimedia está en el modo multiventana y ocupa solo una parte de la pantalla.

Para brindar compatibilidad hasta el nivel de API 14, usa WindowMetricsCalculator#computeMaximumWindowMetrics() de la biblioteca de WindowManager de Jetpack.

Llama a WindowMetrics#getBounds() a fin de obtener el ancho y la altura correctos para la pantalla virtual de proyección de contenido multimedia (consulta Pantalla virtual).

Siempre configura las apps de proyección de contenido multimedia para que puedan cambiar de tamaño. Las apps que pueden cambiar de tamaño admiten cambios en la configuración del dispositivo y del modo multiventana (consulta Compatibilidad con el modo Multiventana).

Si no se puede cambiar el tamaño de la app, esta debe consultar los límites de la pantalla desde un contexto de ventana y recuperar la clase WindowMetrics del área de visualización máxima disponible para la app mediante 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();

Superficie

Debes dimensionar la superficie de proyección de contenido multimedia para producir resultados en la resolución deseada. Configura un tamaño grande (baja resolución) para transmitir la pantalla a televisiones o monitores de computadora, y un tamaño pequeño (alta resolución) para grabar la pantalla de dispositivos.

A partir de 12L (nivel de API Sv2), cuando el sistema renderiza una pantalla virtual en la superficie, ajusta esa pantalla para que se adapte a la superficie mediante un proceso similar a la opción centerInside desde ImageView.

El enfoque nuevo de ajuste mejora la transmisión de la pantalla a televisiones y otras pantallas grandes, ya que se maximiza el tamaño de la imagen de la superficie, a la vez que se garantiza la relación de aspecto correcta.

Recomendaciones

Para obtener los mejores resultados con la proyección de contenido multimedia, sigue estas recomendaciones:

  • Crea una app que pueda cambiar de tamaño. Las apps que pueden cambiar de tamaño admiten cambios en la configuración del dispositivo y del modo multiventana (consulta Compatibilidad con el modo Multiventana). En el manifiesto de la app, establece resizeableActivity="true". En Android 7.0 (nivel de API 24), esta configuración es verdadera de forma predeterminada.
  • Permite que las apps admitan la orientación horizontal y vertical, ya que ambas son comunes en factores de forma de dispositivos plegables, teléfonos y tablets.
  • Usa WindowManager#getMaximumWindowMetrics() para obtener los límites de una proyección de contenido multimedia. Para obtener compatibilidad hasta el nivel de API 14, usa WindowManager de Jetpack. (Consulta la sección WindowMetrics).
  • Si no se puede cambiar el tamaño de la app, obtén los límites de la proyección de contenido multimedia desde un contexto de ventana. (Consulta la sección WindowMetrics).

Recursos

Para obtener más información, consulta Cómo capturar la reproducción de audio y video.