大型螢幕的媒體投影功能

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

虛擬螢幕

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

資源

詳情請參閱「擷取影片和音訊播放」。