# 模式与使用场景
## 架构
客户端 (WebRTC) CF 边缘节点 后端 (HTTP)
|
CF 骨干网 (310+ 数据中心)
|
其他边缘节点 其他客户端
Anycast:最后一公里 边缘节点 A -> 边缘节点 B -> 订阅者1
-> 边缘节点 C -> 订阅者2,3
## 使用场景
**1:1:** A 创建会话并发布,B 创建并订阅 A 的流并发布,A 订阅 B 的流
**N:N:** 所有参与者创建会话并发布,后端广播轨道 ID,所有人订阅其他人的流
**1:N:** 发布者创建并发布,观看者各自创建并订阅(无扇出限制)
**分组讨论:** 同一个 PeerConnection!后端关闭/添加轨道,无需重新创建
## PartyTracks (推荐)
基于 Observable 的客户端,具有自动设备/网络处理功能:
typescript
import {PartyTracks} from 'partytracks';
// 创建客户端
const pt = new PartyTracks({
apiUrl: '/api/calls',
sessionId: 'my-session',
onTrack: (track, peer) => {
const video = document.getElementById(`video-${peer.id}`) as HTMLVideoElement;
video.srcObject = new MediaStream([track]);
}
});
// 发布摄像头 (push API)
const camera = await pt.getCamera(); // 自动请求权限,处理设备变更
await pt.publishTrack(camera, {trackName: 'my-camera'});
// 订阅远程轨道 (pull API)
await pt.subscribeToTrack({trackName: 'remote-camera', sessionId: 'other-session'});
// React Hook 示例
import {useObservableAsValue} from 'observable-hooks';
function VideoCall() {
const localTracks = useObservableAsValue(pt.localTracks$);
const remoteTracks = useObservableAsValue(pt.remoteTracks$);
return
{/* 渲染轨道 */}
;
}
// 屏幕共享
const screen = await pt.getScreenshare();
await pt.publishTrack(screen, {trackName: 'my-screen'});
// 处理设备变更 (自动)
// PartyTracks 检测设备变更(例如蓝牙耳机)并重新协商
## 后端
Express:
js
app.post('/api/new-session', async (req, res) => {
const r = await fetch(`${CALLS_API}/apps/${process.env.CALLS_APP_ID}/sessions/new`,
{method: 'POST', headers: {'Authorization': `Bearer ${process.env.CALLS_APP_SECRET}`}});
res.json(await r.json());
});
Workers:相同模式,使用 `env.CALLS_APP_ID` 和 `env.CALLS_APP_SECRET`
DO Presence:参见 configuration.md 获取样板代码
## 音频级别 De