gsy-cast-and-loopback
顶层架构
CastCapability (单例) ← 主线程 API 入口
├─ registerProvider(CastProvider)
├─ startDiscovery(Context) / stopDiscovery()
├─ getAvailableDevices(): List<CastDevice>
├─ connect(device, ConnectCallback) : CastSession(异步)
├─ addListener(CastListener)
└─ disconnect()
对齐 Media3 Player 语义:CastCapability ≈ Player 池、activeSession ≈ 当前 Player、CastProvider ≈ 渲染后端、CastListener/SessionListener ≈ Player.Listener。见 CastCapability 与 CastProvider 的头 JavaDoc。
核心 API
CastCapability
| 方法 | 位置 | 说明 |
|---|---|---|
getInstance() |
#L38-L47 | DCL 单例 |
registerProvider(CastProvider) |
#L85-L90 | 注册;protocol 相同则覆盖 |
unregisterProvider(String) |
#L93-L103 | 反注册并停止其发现 |
startDiscovery(Context) |
#L106-L125 | 所有已注册 provider 同时开始发现 |
stopDiscovery() |
#L128-L135 | 全部停止 |
getAvailableDevices() |
#L138-L146 | 快照,跨 provider 聚合 |
connect(CastDevice, ConnectCallback) |
#L154-L183 | 会自动 disconnect 旧会话 |
disconnect() |
#L186-L196 | release 当前 session |
getActiveSession() |
#L199-L201 | 拿当前会话(可能 null) |
addListener / removeListener |
#L203-L209 | 主线程 listener |
CastProvider(SPI 契约)
| 方法 | 说明 |
|---|---|
getProtocol(): String |
全小写协议 id(dlna / chromecast / airplay) |
startDiscovery(Context, DiscoveryListener) |
后台线程订阅,回调 postMain |
stopDiscovery() |
释放订阅、解绑 Service |
connect(CastDevice, ConnectCallback): CastSession |
建立会话;同步返回 session,异步回调结果 |
线程约定:所有 network IO 由 provider 自己后台跑,回调都 postMain。
CastSession(媒体控制)
见 CastSession:
| 方法 | 说明 |
|---|---|
load(CastMediaInfo) |
推送要投的 URL |
play() / pause() / stop() |
播控 |
seekTo(long ms) |
定位 |
setVolume(int 0-100) / setMute(boolean) |
音量 |
getState(): CastState |
IDLE / CONNECTING / CONNECTED / LOADING / PLAYING / PAUSED / STOPPED / ERROR,见 CastState |
addListener(SessionListener) |
位置/状态回调 |
release() |
归还 |
CastMediaInfo(要投的媒体)
见 CastMediaInfo。字段:url、title、durationMs、mimeType、headers、metadata(Map)。
DLNA 参考实现
模块 gsyvideoplayer-cast 提供 JupnpDlnaProvider 与 JupnpDlnaSession。业务代码调用:
CastCapability cap = CastCapability.getInstance();
cap.registerProvider(new JupnpDlnaProvider());
cap.addListener(myListener);
cap.startDiscovery(context);
发现事件:CastListener#onDeviceListChanged(List<CastDevice>);连接:cap.connect(devices.get(0), connectCallback)。
本地 Loopback(Demo 测试用)
app 模块下的 cast/ 提供可发现的接收端,方便在同一台设备上完成集成测试:
- DevReceiverService —— foreground service,通过 jUPnP 注册"回环设备"。
- LoopbackDeviceFactory —— 构造 UPnP MediaRenderer。
- LoopbackAvTransportService / LoopbackRenderingControlService —— AVTransport / RenderingControl 实现。
- CastReceiverFloatingWindow + CastReceiverPlayer —— 可视化播放。
Demo 对照
- 一体化:CastDemoActivity
- Cast 相关控制页:SampleCastControlVideo
- 设计与测试:doc/CAST_FEATURE_PLAN.md、doc/CAST_RECEIVER_DESIGN.md、doc/CAST_TEST_PLAYBOOK.md
常见坑
- 不同 provider 上报的
CastDevice需要用getProtocol()分组;直接把陌生 protocol 传给connect会拿到IllegalStateException。 - Provider 网络 IO 必须在自己线程,
CastCapability不做 main 切换。 - Wi-Fi 多播锁没拿 → DLNA 发现不到设备:Provider 需要
WifiManager.MulticastLock。 - Loopback 与真实设备同网段时会同时被发现,Demo 里有过滤示例。