You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome发起首邀后Firefox共享屏幕触发WebRTC SDP冲突错误

Chrome发起首邀后Firefox发起屏幕共享出现SDP负载类型冲突错误的原因与解决方法

问题描述

我开发了一款支持视频通话+多屏共享的Web应用,多数场景运行正常,但存在特定异常:

应用主流程

  • 用户P1加入空房间,无操作;
  • 用户P2加入房间,P1发送Offer,P2回复Answer;
  • 后续用户PN加入时,房间内已连接用户均向PN发送Offer,PN逐一回复Answer;
  • 用户可多次发起屏幕共享,发起时需向所有已连接对等端发送Offer并接收Answer。

异常场景

摄像头通话始终正常,但屏幕共享仅在以下场景失效:

  1. P1为Chrome用户,率先加入房间;
  2. P2为Firefox用户加入房间,P1(Chrome)发送Offer,双方摄像头通话正常;
  3. 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复用协商缓存时的行为:

  1. 当Chrome作为发起方发送摄像头通话的Offer时,会为H264分配较高的Payload Type(比如127),且初始Offer通常不包含RTX(重传编码)的映射;
  2. Firefox接收并回复Answer后,会在本地保留这次协商的Codec-Payload映射关系;
  3. 当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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:14:54