三星手机 Bilibili 镜像投屏无声音、小米手机正常 — 原因分析报告

1 阅读5分钟

基于本次调试会话中的 logcat 运行时日志、音频调试文件(capture.pcm / capture.aac) 以及 Bilibili 反编译代码,整理如下结论。


一、问题现象

项目描述
场景镜像投屏(同版本投屏 App)
内容源Bilibili 客户端播放视频
三星手机接收端 有画面、无声音
小米手机接收端 有画面、有声音
对照同一投屏 App 播放 抖音 时,三星、小米 均有声音

说明:投屏音视频传输链路本身正常,问题出在 Bilibili 音频能否被手机系统内录,而非编码或网络传输环节。


二、排查过程摘要

2.1 调试手段

  1. 在VideoRecordThread中增加audioDiag日志,周期性输出:

    • PCM 峰值(pcmPeak
    • 当前活跃播放会话的 capturePolicyflags
    • 媒体音量等
  2. 增加 MirrorAudioDebugDumper,将内录 PCM/AAC 保存到手机缓存供分析。

  3. 对比三星、小米、抖音 的 logcat 与抓包文件。

2.2 关键数据

三星(Bilibili 播放时):

capturePolicy=3

flags=0x1C00 (FLAG_NO_MEDIA_PROJECTION + FLAG_MUTE_HAPTIC + FLAG_NO_SYSTEM_CAPTURE)

pcmPeak=0

musicVolume=9(音量已开,仍为静音)

silentRatio=99%

小米(Bilibili 播放时):

capturePolicy=1

flags=0x800 (仅 FLAG_MUTE_HAPTIC)

pcmPeak=5491 ~ 24768(持续有信号)

抖音(三星,对照组):

capturePolicy=1

pcmPeak=15357 ~ 28097

投屏有声音

本地音频文件验证:

  • capture.aac 在 VLC 播放 无声 → 与 pcmPeak≈0、99% 静音一致
  • 不是 AAC 编码损坏,而是 采集端几乎没有有效音频

三、根因结论(一句话)

三星上 Bilibili 向 Android 系统声明「禁止音频内录」(capturePolicy=3),系统内录只能得到静音 PCM;小米上 Bilibili 声明「允许内录」(capturePolicy=1),系统能采到正常音频。同一 Bilibili 版本、同一投屏 App,差异来自 Bilibili 在不同设备上的音频捕获策略不同,不是投屏 App 缺陷。


四、技术原理说明

4.1 镜像投屏音频如何获取

投屏 App 在 Android 10+ 使用 系统内录(AudioPlaybackCapture):

Bilibili 播放音频

AudioPlaybackCapture + MediaProjection

AudioRecord 读取 PCM

AAC 编码 → TCP/UDP 发送 → 接收端播放

能否采到声音,取决于 Bilibili 是否允许被内录,由 Android 系统强制执行。

4.2 capturePolicy 含义

常量含义
1ALLOW_CAPTURE_BY_ALL允许第三方 App 内录
2ALLOW_CAPTURE_BY_SYSTEM仅系统可录
3ALLOW_CAPTURE_BY_NONE完全禁止内录

另:FLAG_NO_MEDIA_PROJECTION 表示明确禁止 MediaProjection(录屏/投屏)场景下的音频捕获。

4.3 在哪里能看到「Bilibili 自己报了什么」

通过 adb 查看投屏 App 打印的 audioDiag 日志(数据来自 AudioManager.getActivePlaybackConfigurations()):

adb logcat -d | findstr "capturePolicy pcmPeak audioDiag"

Bilibili 播放时出现的那条 capturePolicy=,即 Bilibili 当前播放会话向系统上报的策略。


五、Bilibili 侧证据(反编译)

在 Bilibili 反编译代码中发现:

if (Build.VERSION.SDK_INT >= 29) {

// log: set AudioAttributes.ALLOW_CAPTURE_BY_NONE

contentType.setAllowedCapturePolicy(3);

}

含义:Android 10+ 上,Bilibili 在部分音频路径会主动设置「禁止内录」。

但小米 log 为 capturePolicy=1,说明:

  • 并非所有播放路径、所有机型都会执行上述最严策略;
  • 很可能存在 按厂商/机型/播放模块/远程配置 的分支;
  • 三星主播放链路走了 policy=3,小米主播放链路走了 policy=1

Bilibili Manifest 中 未 声明 android:allowAudioPlaybackCapture="false",应用级 XML 未全局禁止;运行时 AudioAttributes 策略 才是三星上实际生效的限制层。


六、为何同一 Bilibili 版本、小米可以、三星不行

对比项小米三星
Bilibili 版本相同相同
投屏 App相同相同
内录 API相同(AudioPlaybackCapture)相同
Bilibili 上报 capturePolicy1(允许)3(禁止)
flags0x8000x1C00(含 NO_MEDIA_PROJECTION)
内录 PCM有波形(pcmPeak 高)全 0(pcmPeak=0)
投屏结果有声音无声音

排除的结论:

假设结论
投屏传输/接收端故障❌ 抖音、小米 Bilibili 均正常
AAC 编码异常❌ 静音 PCM 编码后自然无声
三星媒体音量为 0❌ musicVolume=9 仍 pcmPeak=0
投屏 App 机型适配 bug❌ 各品牌共用同一套内录代码

确认的原因:

Bilibili 在三星上启用 ALLOW_CAPTURE_BY_NONE + NO_MEDIA_PROJECTION,在小米上仍为 ALLOW_CAPTURE_BY_ALL,属于 Bilibili 的差异化防录屏/版权策略,投屏 App 无法在系统层合法绕过。

可能动机包括:版权保护、防盗版、对三星等国际机型更严、远程配置/灰度等(具体分支需进一步反编译 MANUFACTURER 相关逻辑确认)。


七、数据流对比图

【小米 — 正常】

Bilibili 播放

→ capturePolicy=1

→ 系统允许内录

→ pcmPeak 5000~25000

→ AAC 编码有数据

→ 接收端有声音

【三星 — 异常】

Bilibili 播放

→ capturePolicy=3 + NO_MEDIA_PROJECTION

→ 系统拒绝混入 Bilibili 音频

→ pcmPeak=0(99% 静音)

→ AAC 编码静音数据

→ 接收端无声音(画面仍正常)


八、处理建议

8.1 投屏 App 侧(可做)

  1. 用户提示:检测到 capturePolicy=3 且 PCM 持续静音时,提示:「当前应用禁止音频内录,镜像可能无声音(部分机型如三星常见)」。
  2. 帮助文档:说明 Bilibili 等 App 的限制及可行替代方案。
  3. 无需修改内录核心逻辑:链路已验证正常。

8.2 无法通过投屏 App 解决

  • 无法在三星上绕过 Bilibili 的 ALLOW_CAPTURE_BY_NONE(Android 系统级限制)。
  • 需 Bilibili 主动放宽策略,或用户换播放方式。

8.3 用户侧替代方案

  1. 使用 允许内录 的 App(如抖音、部分浏览器网页版 Bilibili)。
  2. 使用 Bilibili 自带投屏(com.bilibili.lib.projection)到电视(与第三方镜像内录路径不同)。
  3. 外放 + 麦克风方案(音质差,若有「无线发言」功能)。
  4. 有线音频输出(视硬件支持)。

九、总结

问题答案
是投屏 App 的 bug 吗?不是
三星 Bilibili 无声音直接原因?Bilibili 设置 capturePolicy=3,内录得到静音 PCM
小米为什么可以?同版本 Bilibili 在小米上为 capturePolicy=1,允许内录
同一版本为何表现不同?Bilibili 按设备/路径差异化设置音频捕获策略
能否在三星上改代码修复?不能(系统 + Bilibili 联合限制)