C#中如何收发RTCSessionDescription实现Unity WebRTC信令
Unity C# 实现WebRTC RTCSessionDescription 收发方案
以下流程基于Unity官方com.unity.webrtc稳定版实现,信令传输层不做限制,你可以用WebSocket、TCP、HTTP轮询任意方式传SDP内容,核心协商逻辑和传输通道无关。
前置准备
先通过Package Manager导入官方WebRTC包,不要用第三方维护的分叉版本,避免API不兼容问题。
Offer 发起方 SDP 生成与发送流程
- 初始化Peer连接,配置ICE服务器参数
// STUN服务器可自行替换,示例用公共STUN RTCConfiguration config = default; config.iceServers = new RTCIceServer[] { new RTCIceServer { urls = new string[] { "stun:stun.l.google.com:19302" } } }; RTCPeerConnection peer = new RTCPeerConnection(ref config); // 提前绑定ICE候选收集、媒体轨道、连接状态变更等回调
- 添加本地要传输的媒体轨道(音视频、数据通道都算),未添加任何轨道时生成的SDP不包含媒体协商信息,无法建立媒体连接
// 示例:添加本地摄像头视频轨道 WebCamTexture cam = new WebCamTexture(); cam.Play(); VideoStreamTrack videoTrack = new VideoStreamTrack("main_cam", cam); peer.AddTrack(videoTrack);
- 生成Offer类型SDP,设置为本地描述
// 注意所有WebRTC异步操作要等执行完成再走下一步 RTCSessionDescriptionAsyncOperation createOfferOp = peer.CreateOffer(); yield return createOfferOp; if (createOfferOp.IsError) { Debug.LogError($"Offer生成失败:{createOfferOp.Error.message}"); yield break; } RTCSessionDescription offerSdp = createOfferOp.Desc; RTCSetSessionDescriptionAsyncOperation setLocalOp = peer.SetLocalDescription(ref offerSdp); yield return setLocalOp; if (setLocalOp.IsError) { Debug.LogError($"本地SDP设置失败:{setLocalOp.Error.message}"); yield break; }
- 序列化SDP通过信令通道发送给对端
注意:必须等SetLocalDescription执行完成再发送SDP,此时ICE候选信息才会写入SDP内容,提前发送会导致连接失败
// 序列化时只需要传SDP类型和SDP内容字符串即可 SdpPayload payload = new SdpPayload { type = offerSdp.type.ToString().ToLower(), sdp = offerSdp.sdp }; string sendStr = JsonUtility.ToJson(payload); // 替换为你自己的信令发送逻辑 SendSignalingMessage(sendStr);
Answer 接收方 SDP 接收与回发流程
- 同Offer方,先初始化RTCPeerConnection,配置相同的ICE参数,添加本地需要的媒体轨道
- 监听信令通道消息,收到Offer类型SDP后反序列化,设置为远端描述
// 信令消息接收回调 void OnSignalingReceive(string rawMsg) { SdpPayload payload = JsonUtility.FromJson<SdpPayload>(rawMsg); if (payload.type == "offer") { RTCSessionDescription remoteOffer = new RTCSessionDescription { type = RTCSdpType.Offer, sdp = payload.sdp }; RTCSetSessionDescriptionAsyncOperation setRemoteOp = peer.SetRemoteDescription(ref remoteOffer); StartCoroutine(HandleRemoteOffer(setRemoteOp)); } } // SDP序列化/反序列化用结构体 [Serializable] public struct SdpPayload { public string type; public string sdp; }
- 等远端Offer设置完成后,生成Answer类型SDP,设置为本地描述后发回Offer方
IEnumerator HandleRemoteOffer(RTCSetSessionDescriptionAsyncOperation op) { yield return op; if (op.IsError) { Debug.LogError($"远端Offer设置失败:{op.Error.message}"); yield break; } // 生成Answer SDP RTCSessionDescriptionAsyncOperation createAnswerOp = peer.CreateAnswer(); yield return createAnswerOp; if (createAnswerOp.IsError) { Debug.LogError($"Answer生成失败:{createAnswerOp.Error.message}"); yield break; } RTCSessionDescription answerSdp = createAnswerOp.Desc; RTCSetSessionDescriptionAsyncOperation setLocalOp = peer.SetLocalDescription(ref answerSdp); yield return setLocalOp; if (setLocalOp.IsError) { Debug.LogError($"本地Answer设置失败:{setLocalOp.Error.message}"); yield break; } // 序列化Answer发回给Offer方 SdpPayload answerPayload = new SdpPayload { type = answerSdp.type.ToString().ToLower(), sdp = answerSdp.sdp }; SendSignalingMessage(JsonUtility.ToJson(answerPayload)); }
- Offer方收到Answer类型的SDP消息后,反序列化设置为自己的远端描述,SDP协商流程即完成
// Offer方的信令消息判断逻辑 if (payload.type == "answer") { RTCSessionDescription remoteAnswer = new RTCSessionDescription { type = RTCSdpType.Answer, sdp = payload.sdp }; RTCSetSessionDescriptionAsyncOperation setRemoteOp = peer.SetRemoteDescription(ref remoteAnswer); yield return setRemoteOp; // 后续等待ICE候选交换完成、连接状态变更为Connected即可 }
常见踩坑
- 所有
RTCPeerConnection相关API必须在Unity主线程调用,如果信令层(比如WebSocket)在子线程收消息,要先把消息投递到主线程再处理,否则会出现内存报错、状态异常 - SDP字符串自带换行符,序列化/反序列化时不要做特殊字符过滤、错误转义,否则会导致SDP解析失败
- 所有
CreateOffer/CreateAnswer/SetLocalDescription/SetRemoteDescription都是异步非阻塞操作,必须等返回的AsyncOperation执行完成再走后续逻辑,不要直接同步取结果 - WebRTC包版本尽量用2.4.0以上的正式版,2.0以下版本存在大量SDP协商、ICE收集的已知bug
内容的提问来源于stack exchange,提问作者user19404661
相关产品推荐
相关产品推荐

