WebRTC 客户端
Web 实时通信 (WebRTC) 是一组标准和 API,用于在浏览器和原生应用中进行实时、点对点通信。
Ktor 中的 WebRTC 客户端支持在多平台项目中进行实时点对点通信。借助 WebRTC,你可以构建以下特性:
- 视频和语音通话
- 多人游戏
- 协作应用程序(白板、编辑器等)
- 客户端之间低延迟的数据交换
添加依赖项
要使用 WebRtcClient,你需要将 ktor-client-webrtc 构件包含在构建脚本中:
创建客户端
创建 WebRtcClient 时,请根据你的目标平台选择引擎:
- JS/Wasm:
JsWebRtc– 使用浏览器RTCPeerConnection和媒体设备。 - Android:
AndroidWebRtc– 使用PeerConnectionFactory和 Android 媒体 API。
然后,你可以提供平台特有的配置,类似于 HttpClient。STUN/TURN 服务器对于 ICE 正常工作是必需的。你可以使用现有解决方案,例如 coturn:
val jsClient = WebRtcClient(JsWebRtc) {
defaultConnectionConfig = {
iceServers = listOf(WebRtc.IceServer("stun:stun.l.google.com:19302"))
}
}val androidClient = WebRtcClient(AndroidWebRtc) {
context = appContext // 必需:提供 Android 上下文
defaultConnectionConfig = {
iceServers = listOf(WebRtc.IceServer("stun:stun.l.google.com:19302"))
}
}创建连接并协商 SDP
创建 WebRtcClient 后,下一步是创建对等连接。 对等连接是管理两个客户端之间实时通信的核心对象。
为了建立连接,WebRTC 使用会话描述协议 (SDP)。这涉及三个步骤:
- 一个对等端(调用方)创建提议。
- 另一个对等端(被调用方)响应应答。
- 两个对等端应用彼此的描述以完成设置。
// 调用方创建连接和提议
val caller = jsClient.createPeerConnection()
val offer = caller.createOffer()
caller.setLocalDescription(offer)
// 通过你的信令机制将 offer.sdp 发送给远程对等端
// 被调用方接收提议并创建应答
val callee = jsClient.createPeerConnection()
callee.setRemoteDescription(
WebRtc.SessionDescription(WebRtc.SessionDescriptionType.OFFER, remoteOfferSdp)
)
val answer = callee.createAnswer()
callee.setLocalDescription(answer)
// 通过信令将 answer.sdp 发送回调用方
// 调用方应用应答
caller.setRemoteDescription(
WebRtc.SessionDescription(WebRtc.SessionDescriptionType.ANSWER, remoteAnswerSdp)
)交换 ICE 候选者
一旦 SDP 协商完成,对等端仍需要发现如何跨网络连接。交互式连接建立 (ICE) 允许对等端查找彼此之间的网络路径。
- 每个对等端收集自己的 ICE 候选者。
- 这些候选者必须通过你选择的信令通道发送给另一个对等端。
- 一旦两个对等端都添加了彼此的候选者,连接即可成功。
// 收集并发送本地候选者
scope.launch {
caller.iceCandidates.collect { candidate ->
// 将 candidate.candidate、candidate.sdpMid、candidate.sdpMLineIndex 发送给远程对等端
}
}
// 接收并添加远程候选者
callee.addIceCandidate(WebRtc.IceCandidate(candidateString, sdpMid, sdpMLineIndex))
// (可选)等待所有候选者收集完毕
callee.awaitIceGatheringComplete()Ktor 不提供信令。请使用 WebSockets、HTTP 或其他传输方式来交换提议、应答和 ICE 候选者。
使用数据通道
WebRTC 支持数据通道,允许对等端交换任意消息。这适用于聊天、多人游戏、协作工具或客户端之间任何低延迟的消息传递。
创建通道
若要在一方创建通道,请使用 .createDataChannel() 方法:
val channel = caller.createDataChannel("chat")然后,你可以在另一方监听数据通道事件:
scope.launch {
callee.dataChannelEvents.collect { event ->
when (event) {
is DataChannelEvent.Open -> println("通道已打开: ${event.channel}")
is DataChannelEvent.Closed -> println("通道已关闭")
else -> {}
}
}
}发送和接收消息
通道使用类似 Channel 的 API,为 Kotlin 开发者所熟悉:
// 发送消息
scope.launch { channel.send("hello") }
// 接收消息
scope.launch { println("已接收: " + channel.receiveText()) }添加和观察媒体轨道
除了数据通道,WebRTC 还支持音频和视频的媒体轨道。这允许你构建应用程序,例如视频通话或屏幕共享。
创建本地轨道
你可以从本地设备(麦克风、摄像头)请求音频或视频轨道:
val audio = rtcClient.createAudioTrack {
echoCancellation = true
}
val video = rtcClient.createVideoTrack {
width = 1280
height = 720
}
val pc = jsClient.createPeerConnection()
pc.addTrack(audio)
pc.addTrack(video)在 Web 端,这使用 navigator.mediaDevices.getUserMedia。在 Android 上,它使用 Camera2 API,你必须手动请求麦克风/摄像头权限。在 iOS 上,它使用 AVFoundation API,并且你也应该手动请求任何权限。客户端将尝试根据指定的约束查找最合适的媒体设备,否则将抛出 WebRtcMedia.DeviceException。
WebRtcClient、WebRtcPeerConnection、WebRtcMedia.Track及其他接口是AutoCloseable。 确保在不再需要时调用close()方法来释放资源。
接收远程轨道
你也可以监听远程媒体轨道:
scope.launch {
pc.trackEvents.collect { event ->
when (event) {
is TrackEvent.Add -> println("已添加远程轨道: ${event.track.id}")
is TrackEvent.Remove -> println("已移除远程轨道: ${event.track.id}")
}
}
}限制
WebRTC 客户端是实验性的,并具有以下限制:
- 不包含信令。你需要实现你自己的信令(例如,使用 WebSockets 或 HTTP)。
- 支持的平台是 JavaScript/Wasm 和 Android。iOS、JVM 桌面和 Kotlin/Native 支持计划在未来版本中提供。
- 权限必须由你的应用程序处理。浏览器会提示用户授予麦克风和摄像头访问权限,而 Android 需要运行时权限请求。
- 仅支持基本的音频和视频轨道。屏幕共享、设备选择、同播和高级 RTP 特性尚未可用。
- 连接统计信息可用,但在不同平台之间有所差异,并且不遵循统一的模式。
