媒体投影

android.media.projection Android 5(API 级别 21)中引入的 API 可让您捕获内容 作为可播放、录制或投屏到的媒体流 例如电视

Android 14(API 级别 34)引入了应用屏幕共享功能,让用户能够 共享单个应用窗口,而不是整个设备屏幕, 窗口模式。除状态栏、导航栏 通知和其他系统界面元素,甚至可以 当用户使用应用屏幕共享功能截取应用全屏时触发。系统只会分享所选应用的内容。

应用屏幕共享功能可以确保用户隐私,提高用户工作效率, 让用户能够运行多个应用,但限制用户 将内容分享到单个应用。

三种显示屏

媒体投影捕获了设备显示屏或应用窗口的内容,并且 然后将捕获的图像投影到虚拟屏幕上, 一个 Surface

投影到虚拟屏幕上的真实设备屏幕。写入应用提供的 `Surface` 的虚拟屏幕内容。
图 1. 投影到上的真实设备屏幕或应用窗口 虚拟显示屏。写入应用提供的虚拟屏幕 Surface.

应用通过Surface MediaRecorderSurfaceTextureImageReader:消耗 捕获的显示内容,并使您能够管理呈现的图片 Surface上提供的实时报告。您可以将图片保存为录音或投放 投放到电视或其他设备上

真实展示广告

获取向您的应用授予 捕获设备显示屏或应用窗口的内容。令牌 由 MediaProjection 类。

使用 getMediaProjection() 方法的 MediaProjectionManager 系统服务,用于创建 MediaProjection 实例 。使用 intent 从 createScreenCaptureIntent() 方法,用于指定屏幕 捕获操作:

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 参数用于指定虚拟 。要获取宽度和高度的值,请使用 引入了 WindowMetrics API (Android 11(API 级别 30)中)。(有关详情,请参阅 媒体投影大小部分)。

Surface

调整媒体投影 Surface 的大小,以便在适当的 分辨率。使界面尺寸较大(低分辨率),以便投射到电视或 计算机显示器,以及小型(高分辨率)设备显示屏录制功能。

从 Android 12L(API 级别 32)开始,在 系统会在保持宽高比不变的前提下均匀缩放内容 确保内容的两个尺寸(宽度和高度)均等于或小于 尺寸大于表面相应的尺寸。然后,捕获的内容将 使它们以表面为中心

Android 12L 扩缩方法改进了将屏幕投放到电视的功能,以及 可最大限度地增加 Surface 图像的大小,同时确保 合适的宽高比

前台服务权限

如果您的应用以 Android 14 或更高版本为目标平台,应用清单必须包含 权限声明 mediaProjection 前台服务类型:

<manifest ...>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
    <application ...>
        <service
            android:name=".MyMediaProjectionService"
            android:foregroundServiceType="mediaProjection"
            android:exported="false">
        </service>
    </application>
</manifest>

通过调用 startForeground() 启动媒体投影服务。

如果您未在调用中指定前台服务类型,则默认为类型 清单中定义的前台服务类型的按位整数。如果 清单未指定任何服务类型,则系统会抛出 MissingForegroundServiceTypeException.

您的应用必须在每个媒体投影会话之前请求用户同意。答 是对 createVirtualDisplay() 的单次调用。一个 MediaProjection 令牌 只能使用一次进行调用。

在 Android 14 或更高版本中,createVirtualDisplay() 方法会抛出 SecurityException(如果您的 应用会执行以下任一操作:

  • 将从 createScreenCaptureIntent() 返回的 Intent 实例多次传递到 getMediaProjection()
  • 对同一个 MediaProjection 多次调用 createVirtualDisplay() 实例

媒体投影大小

媒体投影可以拍摄整个设备显示屏或应用窗口 而无论窗口模式如何

初始大小

对于全屏媒体投影,您的应用必须确定 设备屏幕。在应用屏幕共享中,您的应用将无法确定 截取的屏幕大小,直到用户选择捕获区域。 因此,任何媒体投影的初始尺寸都是设备屏幕的尺寸。

使用平台 WindowManager getMaximumWindowMetrics() 方法返回 WindowMetrics 对象 设备屏幕(即使媒体投影托管应用处于多窗口模式) 模式,只占据部分屏幕。

如需向下兼容 API 级别 14,请使用 WindowMetricsCalculator computeMaximumWindowMetrics() 方法。WindowManager

调用 WindowMetrics getBounds() 方法可获取设备显示屏的宽度和高度。

大小变化

设备旋转时,媒体投影的大小可能会发生变化 或者用户选择某个应用窗口作为应用屏幕共享时的拍摄区域。 如果捕获的内容是 与媒体广告播放时获取的最大窗口指标不同, 预测已设置完毕。

为了确保媒体投影与拍摄的视频大小精确对齐 针对任何捕获区域和跨设备旋转获取的内容,请使用 onCapturedContentResize() 回调,用于调整捕获大小。(有关 相关信息,请参阅后面的自定义部分)。

自定义

您的应用可以通过以下元素自定义媒体投影用户体验 MediaProjection.Callback API:

  • onCapturedContentVisibilityChanged(): 允许托管应用(启动媒体投影的应用)显示或 隐藏共享的内容。

    使用此回调,根据捕获的 所有区域例如,如果您的应用对 并在应用界面中显示捕获的内容,以及 捕获的应用也对用户可见(如此处所示 回调),用户将看到相同的内容两次。使用回调进行更新 隐藏捕获的内容并释放 显示其他内容。

  • onCapturedContentResize(): 允许托管应用更改虚拟服务器上的媒体投影大小 显示和媒体投影Surface(取决于拍摄的图片的大小) 显示区域。

    每当捕获的内容(一个应用窗口或整个应用窗口)时触发 设备显示屏 - 更改尺寸(由于设备旋转或拍摄的 应用进入不同的窗口模式)。使用此 API 可调整 虚拟屏幕和 Surface,以确保宽高比与捕获的 并且捕获的内容不采用信箱模式。

资源恢复

您的应用应注册 MediaProjection onStop() 回调,以便在媒体投影会话停止并变为 无效。会话停止后,您的应用应释放 例如虚拟显示和投影表面A 已停止 媒体投影会话无法再创建新的虚拟屏幕,即使 您的应用之前未为该媒体投影创建虚拟屏幕。

该回调将在媒体投影终止时调用,原因可能是: 用户手动停止会话,也可能是因为系统针对以下时间停止会话: 出于某种原因

如果您的应用未注册回调,则对 createVirtualDisplay() 的任何调用 抛出 IllegalStateException.

停止

Android 14 或更高版本默认启用应用屏幕共享。每种媒体 投影会话让用户能够选择共享应用窗口或 整个显示屏

您的应用可以通过调用 createScreenCaptureIntent(MediaProjectionConfig) 方法 ,其中包含调用以下函数返回的 MediaProjectionConfig 参数: createConfigForDefaultDisplay()

使用createScreenCaptureIntent(MediaProjectionConfig) 调用 后返回的 MediaProjectionConfig 参数 createConfigForUserChoice()保持不变 视为默认行为,也就是说,调用 createScreenCaptureIntent()

可调整大小的应用

请始终将媒体投影应用设为可调整大小 (resizeableActivity="true")。可调整大小 应用支持设备配置更改和多窗口模式(请参阅 多窗口支持)。

如果您的应用不可调整大小,则必须从窗口查询显示边界 上下文并使用 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();

其他资源

如需详细了解媒体投影,请参阅录制视频和音频播放内容