Chrome发起首邀后Firefox共享屏幕触发WebRTC SDP冲突错误
Chrome发起首邀后Firefox发起屏幕共享出现SDP负载类型冲突错误的原因与解决方法
问题描述
我开发了一款支持视频通话+多屏共享的Web应用,多数场景运行正常,但存在特定异常:
应用主流程
- 用户P1加入空房间,无操作;
- 用户P2加入房间,P1发送Offer,P2回复Answer;
- 后续用户PN加入时,房间内已连接用户均向PN发送Offer,PN逐一回复Answer;
- 用户可多次发起屏幕共享,发起时需向所有已连接对等端发送Offer并接收Answer。
异常场景
摄像头通话始终正常,但屏幕共享仅在以下场景失效:
- P1为Chrome用户,率先加入房间;
- P2为Firefox用户加入房间,P1(Chrome)发送Offer,双方摄像头通话正常;
- P2(Firefox)发起屏幕共享时,触发错误:
Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. a=rtpmap:127 H264/90000 Duplicate payload type with conflicting codec name or clock rate
查看Offer的SDP发现a=rtpmap:127存在重复定义:
a=rtpmap:127 H264/90000 ... a=rtpmap:127 rtx/90000
触发条件
该错误仅在此场景出现:若首邀由Firefox发起,或首邀由Chrome发起但首次屏幕共享由Chrome触发,则一切正常。仅当Chrome发起首邀后,Firefox首次发起屏幕共享时触发SDP负载冲突错误。
请问该异常仅在此场景出现的原因是什么?如何规避该错误?
原因分析
这个问题的核心是Chrome和Firefox在SDP协商过程中对**负载类型(Payload Type)**的分配逻辑差异,以及Firefox复用协商缓存时的行为:
- 当Chrome作为发起方发送摄像头通话的Offer时,会为H264分配较高的Payload Type(比如127),且初始Offer通常不包含RTX(重传编码)的映射;
- Firefox接收并回复Answer后,会在本地保留这次协商的Codec-Payload映射关系;
- 当Firefox发起屏幕共享生成新Offer时,会尝试为RTX分配Payload Type,但错误复用了之前Chrome分配给H264的127号,导致同一Payload Type同时映射给H264和RTX,触发SDP解析冲突。
其他场景正常的原因:
- 若Firefox先发起首邀,它会自主分配Payload Type,后续发起屏幕共享时会主动避免重复;
- 若Chrome先发起首邀但由Chrome先触发屏幕共享,Chrome会在新Offer中为H264和RTX分配不同的Payload Type,Firefox接收后更新本地映射,后续自己发起共享时不会冲突。
规避方案
方案1:手动修正SDP中的重复Payload Type
在Firefox生成屏幕共享的Offer后、发送前,遍历SDP的a=rtpmap行,检测重复的Payload Type,将冲突条目替换为未被使用的数值(Payload Type有效范围通常是96-127)。示例代码逻辑:
async function createScreenShareOffer(peerConnection) { const offer = await peerConnection.createOffer(); const sdpLines = offer.sdp.split('\n'); const payloadMap = new Map(); const newSdpLines = []; for (const line of sdpLines) { if (line.startsWith('a=rtpmap:')) { const [payloadPart, codecInfo] = line.split(' '); const payloadType = payloadPart.split(':')[1]; if (payloadMap.has(payloadType)) { // 从127往下寻找未使用的Payload Type let newPayloadType = 127; while (payloadMap.has(newPayloadType.toString()) && newPayloadType >= 96) { newPayloadType--; } if (newPayloadType >= 96) { newSdpLines.push(`a=rtpmap:${newPayloadType} ${codecInfo}`); payloadMap.set(newPayloadType.toString(), codecInfo); // 若有对应a=fmtp行,需同步替换Payload Type(此处省略对应逻辑) } else { console.warn('无可用的Payload Type'); } } else { payloadMap.set(payloadType, codecInfo); newSdpLines.push(line); } } else { newSdpLines.push(line); } } offer.sdp = newSdpLines.join('\n'); return offer; }
方案2:限制使用无冲突的Codec
创建屏幕共享的RTCPeerConnection时,通过配置指定只使用不会引发冲突的Codec,比如禁用H264,改用VP8或VP9:
const peerConnection = new RTCPeerConnection({ codecs: [ { mimeType: 'video/VP8', clockRate: 90000 }, { mimeType: 'video/rtx', clockRate: 90000, parameters: { apt: '100' } } // 按需添加其他Codec,排除H264 ] });
注:不同浏览器对Codec配置的支持略有差异,需测试适配。
方案3:为屏幕共享使用独立的RTCPeerConnection
避免复用摄像头通话的PeerConnection,为屏幕共享单独创建新的连接实例。这样Firefox生成屏幕共享Offer时,不会受之前摄像头通话的Payload映射影响,会重新分配独立的Payload Type。
内容的提问来源于stack exchange,提问作者Alberto Fecchi
相关产品推荐
相关产品推荐

