基于本次调试会话中的 logcat 运行时日志、音频调试文件(capture.pcm / capture.aac) 以及 Bilibili 反编译代码,整理如下结论。
一、问题现象
| 项目 | 描述 |
|---|---|
| 场景 | 镜像投屏(同版本投屏 App) |
| 内容源 | Bilibili 客户端播放视频 |
| 三星手机 | 接收端 有画面、无声音 |
| 小米手机 | 接收端 有画面、有声音 |
| 对照 | 同一投屏 App 播放 抖音 时,三星、小米 均有声音 |
说明:投屏音视频传输链路本身正常,问题出在 Bilibili 音频能否被手机系统内录,而非编码或网络传输环节。
二、排查过程摘要
2.1 调试手段
-
在VideoRecordThread中增加audioDiag日志,周期性输出:
- PCM 峰值(
pcmPeak) - 当前活跃播放会话的
capturePolicy、flags - 媒体音量等
- PCM 峰值(
-
增加
MirrorAudioDebugDumper,将内录 PCM/AAC 保存到手机缓存供分析。 -
对比三星、小米、抖音 的 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 含义
| 值 | 常量 | 含义 |
|---|---|---|
| 1 | ALLOW_CAPTURE_BY_ALL | 允许第三方 App 内录 |
| 2 | ALLOW_CAPTURE_BY_SYSTEM | 仅系统可录 |
| 3 | ALLOW_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 上报 capturePolicy | 1(允许) | 3(禁止) |
| flags | 0x800 | 0x1C00(含 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 侧(可做)
- 用户提示:检测到
capturePolicy=3且 PCM 持续静音时,提示:「当前应用禁止音频内录,镜像可能无声音(部分机型如三星常见)」。 - 帮助文档:说明 Bilibili 等 App 的限制及可行替代方案。
- 无需修改内录核心逻辑:链路已验证正常。
8.2 无法通过投屏 App 解决
- 无法在三星上绕过 Bilibili 的
ALLOW_CAPTURE_BY_NONE(Android 系统级限制)。 - 需 Bilibili 主动放宽策略,或用户换播放方式。
8.3 用户侧替代方案
- 使用 允许内录 的 App(如抖音、部分浏览器网页版 Bilibili)。
- 使用 Bilibili 自带投屏(
com.bilibili.lib.projection)到电视(与第三方镜像内录路径不同)。 - 外放 + 麦克风方案(音质差,若有「无线发言」功能)。
- 有线音频输出(视硬件支持)。
九、总结
| 问题 | 答案 |
|---|---|
| 是投屏 App 的 bug 吗? | 不是 |
| 三星 Bilibili 无声音直接原因? | Bilibili 设置 capturePolicy=3,内录得到静音 PCM |
| 小米为什么可以? | 同版本 Bilibili 在小米上为 capturePolicy=1,允许内录 |
| 同一版本为何表现不同? | Bilibili 按设备/路径差异化设置音频捕获策略 |
| 能否在三星上改代码修复? | 不能(系统 + Bilibili 联合限制) |