第27章 车载多媒体场景:CarMediaService、多屏互动、音频分区
车载多媒体,说实话,是Android多媒体体系里最“拧巴”的一块。为什么?因为手机上的那套逻辑——一个屏幕、一对喇叭、一个用户——在车里完全不够用。你想想看,前排司机在导航,后排孩子在动画片,副驾在打电话,这怎么搞?
我最早接触车载项目时,以为就是把MediaSession和AudioManager搬过来就行。结果呢?被CarService的架构按在地上摩擦。今天我们就来聊聊CarMediaService、多屏互动和音频分区这三个核心话题。
27.1 CarMediaService:车载多媒体的“总管”
CarMediaService不是普通的MediaSessionService。它是CarService体系里专门管理媒体播放的组件。说白了,它要协调多个MediaSession、多个显示区域、多个音频焦点。
我习惯把CarMediaService理解成一个“媒体路由器”。它不直接播放,而是把媒体流路由到正确的屏幕和音频通道上。
核心职责:
- 管理多个MediaSession(导航、音乐、视频、通话)
- 处理音频焦点冲突(比如导航播报时压低音乐)
- 分发媒体元数据到各个显示区域
- 支持多用户场景(司机和乘客的媒体隔离)
来看一个典型的CarMediaService初始化代码:
public class CarMediaServiceImpl extends CarMediaService {
private static final String TAG = "CarMediaService";
@Override
public void onCreate() {
super.onCreate();
// 注册多个MediaSession
registerMediaSession("navigation", new NavigationMediaSession());
registerMediaSession("music", new MusicMediaSession());
registerMediaSession("video", new VideoMediaSession());
// 设置音频分区策略
AudioZoneManager zoneManager = getCar().getCarManager(AudioZoneManager.class);
zoneManager.setZonePolicy(AudioZone.ZONE_DRIVER, AudioZone.POLICY_NAVIGATION_ONLY);
zoneManager.setZonePolicy(AudioZone.ZONE_PASSENGER, AudioZone.POLICY_MEDIA_ONLY);
}
}
嗯,这里要注意:registerMediaSession的key不能重复。我曾经踩过坑,两个MediaSession用了同一个key,结果后注册的把前一个覆盖了,导航声音死活出不来。
27.2 多屏互动:不是简单的“投屏”
车载多屏,不是手机投屏到车机那么简单。它涉及的是多个物理屏幕之间的媒体状态同步、控制权切换、以及内容隔离。
我参与过一个项目,后排屏幕想看视频,前排屏幕在导航。这时候如果后排点“播放”,前排的导航语音不能被打断。怎么办?
解决方案是:每个屏幕绑定一个独立的MediaSession,但共享同一个MediaController。CarMediaService负责仲裁哪个Session拥有“前台焦点”。
多屏互动设计要点:
- 每个屏幕有独立的MediaSession实例
- MediaController可以跨屏幕共享,但播放命令需要经过CarMediaService仲裁
- 元数据更新要广播到所有注册的屏幕
- 控制权切换需要用户确认(防止后排抢方向盘)
来看一个多屏状态同步的代码片段:
// 当后排屏幕开始播放视频时
public void onPlayRequested(int screenId, MediaSession session) {
// 检查当前是否有更高优先级的播放
if (currentActiveSession != null &&
currentActiveSession.getPriority() > session.getPriority()) {
// 拒绝请求,通知用户
notifyUser(screenId, "当前导航播报中,无法播放视频");
return;
}
// 暂停当前播放
if (currentActiveSession != null) {
currentActiveSession.getController().getTransportControls().pause();
}
// 切换到新会话
currentActiveSession = session;
broadcastMediaState(screenId, session);
}
我个人习惯在广播状态时加上一个序列号,防止旧状态覆盖新状态。这个坑我踩过两次,第一次是后排暂停了,但前排收到的是旧的状态,以为还在播放。
27.3 音频分区:让每个座位“各听各的”
音频分区是车载多媒体最硬核的部分。它要求系统能同时输出多路音频流到不同的扬声器区域。比如:导航声音只从驾驶位喇叭出,音乐从全车喇叭出,后排耳机独立一路。
Android原生的AudioManager不支持分区。CarService引入了AudioZone的概念。每个Zone对应一个物理区域,有自己的音频策略和音量控制。
音频分区注意事项:
- 硬件必须支持多路DAC输出,否则分区是空谈
- 每个Zone需要独立的AudioTrack实例
- 音量调节要按Zone独立,不能全局联动
- 蓝牙电话必须路由到所有Zone(法规要求)
来看一个音频分区的配置示例:
// 创建三个音频分区
AudioZone driverZone = new AudioZone.Builder(AudioZone.ZONE_DRIVER)
.setOutputDevice(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
.setVolumeGroup(VolumeGroup.GROUP_DRIVER)
.setAllowedUsages(AudioAttributes.USAGE_NAVIGATION,
AudioAttributes.USAGE_VOICE_COMMUNICATION)
.build();
AudioZone passengerZone = new AudioZone.Builder(AudioZone.ZONE_PASSENGER)
.setOutputDevice(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
.setVolumeGroup(VolumeGroup.GROUP_PASSENGER)
.setAllowedUsages(AudioAttributes.USAGE_MEDIA,
AudioAttributes.USAGE_GAME)
.build();
AudioZone rearZone = new AudioZone.Builder(AudioZone.ZONE_REAR)
.setOutputDevice(AudioDeviceInfo.TYPE_WIRED_HEADSET)
.setVolumeGroup(VolumeGroup.GROUP_REAR)
.setAllowedUsages(AudioAttributes.USAGE_MEDIA)
.build();
// 应用分区配置
AudioZoneManager zoneManager = getCar().getCarManager(AudioZoneManager.class);
zoneManager.setZones(driverZone, passengerZone, rearZone);
我曾经在调试分区时遇到一个问题:导航声音明明配置了只走驾驶位,但副驾也能听到。查了半天,发现是硬件混音器没配置好,DSP把两路音频混到一起了。嗯,软件配好了,硬件没跟上,白搭。
27.4 整体架构图
下面这张图展示了CarMediaService、多屏互动和音频分区之间的关系:
27.5 实战中的坑与经验
做车载多媒体开发,有几个坑我不得不提:
- 音频焦点冲突:导航播报时,音乐要自动降低音量(不是暂停)。我见过有些方案直接暂停音乐,结果导航播完音乐不恢复了,用户投诉。
- 多屏延迟:后排屏幕的播放状态更新到前排,延迟不能超过200ms。否则用户会感觉“卡”。我建议用Binder直连,不要走广播。
- 音量同步:不同Zone的音量曲线要一致。我曾经遇到驾驶位音量调到10很响,副驾调到10几乎听不见。原因是两个Zone的DAC增益不同。
- 蓝牙电话:法规要求电话必须全车播放。你不能把电话只路由到驾驶位。这个在AudioZone配置里要特殊处理。
避坑指南:
我曾经在调试多屏互动时,发现后排屏幕的MediaSession状态更新总是慢半拍。后来定位到是用了Handler.postDelayed做同步。换成Binder直调后,延迟从500ms降到了50ms。所以,能直调就别用消息队列。
27.6 总结
车载多媒体场景,说白了就是解决“多”的问题:多屏幕、多音频流、多用户。CarMediaService是核心调度器,多屏互动是状态同步的艺术,音频分区是硬件和软件的联调。
我个人觉得,做车载开发最考验的是“边界意识”。什么时候该让,什么时候该抢,什么时候该隔离,这些策略比代码本身更重要。你想想看,如果导航和音乐抢起来了,用户会骂谁?肯定是车厂。所以,策略定好了,代码只是实现而已。
嗯,今天就聊到这里。如果你正在做车载项目,建议先把AudioZone的硬件能力摸清楚,再动手写代码。否则,软件写得再好,硬件不支持也是白搭。