대형 화면에서의 미디어 프로젝션

Android 5(API 수준 21)에서 도입된 미디어 프로젝션 API를 사용하면 기기 디스플레이의 콘텐츠를 미디어 스트림으로 캡처하여 재생하거나 녹화하거나 TV와 같은 다른 기기로 전송할 수 있습니다.

미디어 프로젝션에서는 기기 디스플레이를 세 가지로 표시합니다.

가상 디스플레이에 프로젝션된 실제 기기 디스플레이 애플리케이션에서 제공하는 `Surface`에 기록된 가상 디스플레이의 콘텐츠

그림 1. 가상 디스플레이에 프로젝션된 실제 기기 디스플레이. 애플리케이션에서 제공하는 Surface에 기록된 가상 디스플레이의 콘텐츠

미디어 프로젝션은 기기 디스플레이의 콘텐츠를 캡처하고 캡처된 이미지를 Surface에서 이미지를 렌더링하는 가상 디스플레이에 프로젝션합니다.

애플리케이션은 SurfaceViewImageReaderSurface를 제공하고 이 둘 중 하나는 캡처된 디스플레이의 콘텐츠를 사용합니다. ImageReaderOnImageAvailableListener를 사용하면 Surface에서 렌더링된 이미지를 실시간으로 관리할 수 있습니다. 이미지를 녹화 파일로 저장하거나 TV 또는 다른 기기로 전송할 수 있습니다.

MediaProjection

앱에 디스플레이 콘텐츠나 기기 오디오 또는 둘 다를 캡처하는 기능을 부여하는 토큰을 획득하여 미디어 프로젝션 세션을 시작합니다. 토큰은 MediaProjection 클래스의 인스턴스로 표시됩니다. 새 활동을 시작할 때 이 클래스의 인스턴스를 만들 수 있습니다.

기존 방식

기존 방식을 사용하여 미디어 프로젝션 토큰을 획득하려면 MediaProjectionManager 시스템 서비스의 createScreenCaptureIntent() 메서드에서 반환된 인텐트로 startActivityForResult()를 호출합니다.

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

자바

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

호출하면 미디어 프로젝션에서 민감한 정보나 개인 식별 정보 등 표시된 모든 정보를 캡처한다고 사용자에게 알리는 확인 대화상자가 표시됩니다.

사용자가 확인하면 startActivityForResult()가 결과 코드와 데이터를 onActivityResult() 콜백에 전달합니다.

그러면 데이터와 결과 코드를 MediaProjectionManagergetMediaProjection() 메서드에 전달하여 MediaProjection의 인스턴스를 만들 수 있습니다.

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

자바

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

자바

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)

자바

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

widthheight 매개변수는 가상 디스플레이의 너비와 높이를 지정합니다. 미디어 프로젝션의 너비 및 높이와 일치하는 값을 가져오려면 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

자바

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

표면

원하는 해상도로 출력을 생성하려면 미디어 프로젝션 표면의 크기를 조절해야 합니다. TV나 컴퓨터 모니터로 화면을 전송할 때는 크기를 크게(저해상도) 설정하고 기기 디스플레이 녹화에서는 크기를 작게(고해상도) 설정합니다.

12L(API 수준 Sv2)부터 시스템이 표면에서 가상 디스플레이를 렌더링할 때 ImageViewcenterInside 옵션과 유사한 프로세스를 사용하여 표면에 맞게 가상 디스플레이의 크기를 조정합니다.

새로운 조정 방법을 통해 적절한 가로세로 비율을 보장하면서 표면 이미지 크기를 최대화하여 TV와 기타 대형 디스플레이로의 화면 전송을 개선합니다.

추천

미디어 프로젝션으로 최상의 결과를 얻으려면 다음 권장사항을 따르세요.

  • 앱을 크기 조절 가능하게 지정합니다. 크기를 조절할 수 있는 앱은 기기 설정 변경사항과 멀티 윈도우 모드를 지원합니다(멀티 윈도우 지원 참고). 앱 매니페스트에서 resizeableActivity="true"를 설정합니다. Android 7.0(API 수준 24)에서는 이 설정이 기본적으로 true입니다.
  • 앱에서 가로 모드 및 세로 모드 방향을 지원하도록 사용 설정합니다. 두 방향이 모두 스마트폰과 태블릿, 폴더블 폼 팩터에서 공통적이기 때문입니다.
  • WindowManager#getMaximumWindowMetrics()를 사용하여 미디어 프로젝션의 경계를 가져옵니다. API 수준 14까지의 호환성을 위해 Jetpack WindowManager를 사용합니다. WindowMetrics 섹션을 참고하세요.
  • 앱의 크기를 조절할 수 없으면 창 컨텍스트에서 미디어 프로젝션 경계를 가져옵니다. WindowMetrics 섹션을 참고하세요.

리소스

자세한 내용은 동영상 및 오디오 재생 캡처를 참고하세요.