媒體投影

Android 5 (API 級別 21) 中引入的媒體投影 API 可讓您將裝置螢幕的內容擷取為媒體串流,以便播放、錄製或投放到電視等其他裝置。

媒體投影包含裝置螢幕的三種表示法:

投影到虛擬螢幕的真實裝置螢幕。將虛擬螢幕的內容寫入應用程式提供的「途徑」。
圖 1. 投影到虛擬螢幕的真實裝置螢幕。將虛擬螢幕的內容寫入應用程式提供的 Surface

媒體投影功能會擷取裝置螢幕的內容,然後將擷取的圖片投影到虛擬螢幕上,以在 Surface 上呈現圖片。

應用程式會透過 SurfaceViewImageReader 提供 Surface,兩種方式都會取用擷取的螢幕內容。ImageReaderOnImageAvailableListener 可讓您即時管理顯示在 Surface 上的圖片。您可以將圖片儲存為錄製內容,或是將圖片投放到電視或其他裝置。

MediaProjection

如要啟動媒體投影工作階段,請先取得權杖,該權杖可授予應用程式擷取螢幕內容和/或裝置音訊的權限。權杖由 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());
    }
  }
);

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent());

虛擬螢幕

媒體投影的核心部分是虛擬螢幕,您可以透過 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);

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

Java

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

途徑

您需要調整媒體投影途徑的大小,以所需的解析度產生輸出內容。建議您在電視或電腦顯示器上投放畫面時,將螢幕調整為大尺寸 (低解析度);在進行裝置螢幕錄製時調整為小尺寸 (高解析度)。

自 12L (API 級別 32) 起,當系統在途徑上呈現虛擬螢幕時,會使用類似於 ImageViewcenterInside 選項的流程來縮放虛擬螢幕,使其適合途徑尺寸。

新的縮放方法能夠透過盡量放大途徑影像大小,同時確保適當的顯示比例,來改善電視和其他大螢幕的畫面投放功能。

建議

如想獲得媒體投影的最佳效果,請遵循下列建議做法:

  • 讓您的應用程式可調整大小。可調整大小的應用程式支援裝置設定變更和多視窗模式 (請參閱「多視窗模式支援」)。在應用程式資訊清單中,設定 resizeableActivity="true"。在 Android 7.0 (API 級別 24) 以上版本中,這項設定預設為 true。
  • 請讓您的應用程式支援橫向和直向模式,因為這兩種螢幕方向在手機、平板電腦和折疊式裝置板型規格中都很常見。
  • 使用 WindowManager#getMaximumWindowMetrics() 取得媒體投影的邊界。為了往下與 API 級別 14 相容,請使用 Jetpack WindowManager (請參閱 WindowMetrics 一節)。
  • 如果您的應用程式無法調整大小,請透過視窗內容取得媒體投影邊界 (請參閱「WindowMetrics」一節)。

其他資源

如要進一步瞭解媒體投影,請參閱「擷取影片和音訊播放」。