欢迎光临
我们一直在努力

【攻克 Android 14 录屏限制:手把手带你落地工业级 WebRTC 高清投屏】

秒级同步!基于 WebRTC 与 WebSocket 实现 Android 局域网高清投屏系统

前言

在局域网环境下,如何实现手机屏幕的高清、超低延迟投屏?很多人第一反应是直接通过 WebSocket 传输 Bitmap 帧,但实测发现这种方式在 1080P 下带宽占用巨大且容易出现严重的积压延迟。

本文将带你落地一套工业级的 WebRTC 投屏方案。通过 WebRTC 的 P2P 传输与硬件编码加速,结合 WebSocket 信令服务器,实现 延迟 < 100ms、支持多端同时播放 的高性能投屏系统。


一、 系统架构设计

本系统由三个核心组件组成:

  • 录屏端 (Sender):运行于 Android 手机,负责 MediaProjection 屏幕采集、H264 硬件编码及 WebRTC 推流。同时内置 WebSocket Server 作为信令中心。
  • 播放端 (Receiver – Android):独立 App,负责接收 WebRTC 视频流并使用 SurfaceViewRenderer 渲染画面。
  • 播放端 (Receiver – HTML5):网页端,通过浏览器原生 WebRTC API 实现跨平台接收。

  • 二、 核心技术落地

    1. 依赖配置

    在 libs.versions.toml 中引入核心库:

    [libraries]
    google-webrtc = { module = "org.webrtc:google-webrtc", version = "1.0.32006" }
    java-websocket = { module = "org.java-websocket:Java-WebSocket", version = "1.6.0" }

    2. 挑战一:Android 14+ 录屏权限与前台服务时序

    难点:Android 14 对录屏安全管控极其严格。如果先请求录屏权限再启动服务,或者重复使用 Token,会导致 SecurityException。

    解决方案:
    必须确保“服务先行,延迟采集”的策略。

    // 在 Activity 中请求录屏
    private val screenCaptureLauncher = registerForActivityResult(...) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
    // 1. 必须先启动前台服务并绑定 mediaProjection 类型
    val serviceIntent = Intent(this, ScreenCaptureService::class.java)
    ContextCompat.startForegroundService(this, serviceIntent)

    // 2. 给予系统足够的同步时间(约 1000ms),再初始化 MediaProjection 采集
    Handler(Looper.getMainLooper()).postDelayed({
    initScreenCapture(result.data!!)
    }, 1000)
    }
    }

    3. 挑战二:实现“一发多收”连接池管理

    难点:默认的 WebRTC 是一对一的。当 H5 加入时,如果直接重置连接,会导致 Android 接收端断开。

    解决方案:
    在发送端维护 ConcurrentHashMap,为每个 ClientId 创建独立的 PeerConnection。

    private val senderConnections = ConcurrentHashMap<String, PeerConnection>()

    private fun handleSignalingMessage(message: String?, isSenderMode: Boolean) {
    val json = JSONObject(message ?: return)
    val type = json.optString("type")
    val fromId = json.optString("from") // 获取接收端唯一 ID

    when (type) {
    "join" -> if (isSenderMode && isStreaming) {
    // 为每个新加入的 ID 独立创建 PeerConnection,并复用同一个 localVideoTrack
    runOnUiThread { createSenderPeerConnection(fromId) }
    }
    // … 分发 Candidate 和 Answer 至对应的 senderConnections[fromId]
    }
    }


    三、 超低延迟(<100ms)终极优化

    这是本项目最核心的价值所在。WebRTC 默认会为了应对弱网而开启 Jitter Buffer,这在局域网下是延迟的主因。

    1. 强制“粉碎”缓冲区 (SDP 注入)

    在协商 SDP 时,强制注入 googJitterBufferMaxMs=0。

    private fun optimizeSdp(sdp: String): String {
    return sdp.replace("a=mid:video\\r\\n",
    "a=mid:video\\r\\na=fmtp:96 x-google-max-bitrate=25000;" +
    "x-google-min-bitrate=10000;x-google-start-bitrate=15000;" +
    "googJitterBufferMaxMs=0;googJitterBufferFastRefresh=true;googLeakyBucket=true\\r\\n")
    }

    2. 锁定分辨率优先 (RtpSender 参数)

    禁止 WebRTC 因 CPU 负载而主动降低分辨率(这会导致画面模糊并产生缩放延迟)。

    parameters.degradationPreference = RtpParameters.DegradationPreference.MAINTAIN_RESOLUTION
    for (encoding in parameters.encodings) {
    encoding.maxBitrateBps = 15000 * 1000 // 锁定 15Mbps
    encoding.scaleResolutionDownBy = 1.0 // 禁止缩放
    encoding.networkPriority = 1 // 提高网络优先级
    }

    3. 解决后台录制“降频”卡顿

    难点:App 切到后台后,系统会冻结 CPU 和网络。

    解决方案:
    在 Service 中申请电池优化白名单,并持有 PartialWakeLock。

    // ScreenCaptureService.kt
    val powerManager = getSystemService(POWER_SERVICE) as PowerManager
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RTC:CaptureLock")
    wakeLock?.acquire() // 强制 CPU 在后台保持高性能运行


    四、 搭建与运行流程

  • 编译 Sender:运行 app 模块。
    • 点击“启动信令服务器”。
    • 选择 1080P 分辨率。
    • 点击“开始投屏”,在系统弹窗中勾选“允许忽略电池优化”。
  • 启动播放端:
    • Android 端:运行 play 模块,输入发送端显示的 IP,点击“播放”。
    • H5 端:打开桌面上的 index.html,输入 IP 连接。
  • 体验:此时你会发现,即便手机切到后台玩游戏,接收端的延迟依然稳稳控制在 100ms 以内。

  • 五、 结语

    WebRTC 在局域网投屏场景下拥有巨大的潜力。通过本文介绍的 SDP 注入 和 后台保活策略,你可以轻松搭建出一套性能媲美商业软件的投屏方案。

    希望这篇实战分享能帮你少走弯路,早日实现顺滑的无线投屏体验!

    项目地址

    https://gitee.com/tangjie666/rtc.git
    请添加图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 【攻克 Android 14 录屏限制:手把手带你落地工业级 WebRTC 高清投屏】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址