返回

解决WebRTC 'Failed to Set Remote Description'错误:m行顺序不匹配

java

解决 “Failed to Set Remote Description” 问题

在 WebRTC 开发中,“Failed to Set Remote Description” 错误,特别是在设置远程应答 SDP(Session Description Protocol)时遇到 "m-line order mismatch"(m 行顺序不匹配)错误是很常见的。它表明接收到的应答 SDP 和原始的提供 SDP 中的媒体(m= 行)顺序不一致,从而导致连接建立失败。

理解错误原因

在 WebRTC 通信中, Offer-Answer 机制至关重要。一个 Peer 先生成 Offer SDP,自身可以发送和接收的媒体流类型(如音频、视频),然后发送给另一方; 另一方基于接收到的 Offer SDP,生成一个 Answer SDP,声明可以接受的媒体流,并回送。Offer 和 Answer 中的 m-line (媒体行)的顺序必须保持一致,才能使双方正确解析和匹配各自的媒体能力,顺利建立连接。当处理Offer和Answer时,WebRTC严格按照SDP中的媒体类型(m=)的顺序执行。 如果服务端(在本例中是应用视频灰度滤镜的服务器)在处理 Offer 之后,在Answer中重新排列了 m= 行,会导致 setRemoteDescription 错误,客户端将拒绝使用该 answer。 这正是我们现在遇到的问题的根源:服务器没有按正确的顺序返回 m-line 信息,客户端会判定为 invalid ,从而连接建立失败。

解决方案

解决 Failed to Set Remote Description 问题的主要方法是确保服务器返回的 Answer SDP 中 m= 行的顺序与 Offer SDP 中的顺序一致。 可以通过多种方式达到目的。

方法一:服务器端维护 m-line 顺序

最根本的解决方法是从服务器端着手,保证其在处理 Offer SDP 时,分析和返回时正确地维护 m-lines的原始顺序。

  • 分析问题: 首先确认服务端接收并解析offer之后, 如何修改 SDP 或者生成新的 Answer SDP。
  • 服务器代码修改: 修改服务端的代码,使其接收Offer的 m 行的顺序之后,在生成Answer的时候,同样按照这个顺序生成Answer的 m行,从而保证offer的m行顺序和answer的m行顺序一致。例如如果服务端只是增加了一些属性,并且只是在原先Offer SDP的基础上去做的更改,那就不应该发生 m 行顺序不一致的问题。 但是如果是解析offer之后重新生成sdp就容易发生这种错误,这时候应该注意m 行的生成逻辑。
  • 测试: 验证服务端返回的 Answer SDP 中的 m= 行顺序是否与 Offer SDP 的顺序一致。可以比较 Offer SDP 与服务器返回的 Answer SDP 的 m-lines,查看顺序是否发生改变。可以借助工具打印输出这两个 sdp 的具体内容。

服务器伪代码示例 (仅供理解,实际代码会依据服务端使用的语言和框架调整):

def process_offer(offer_sdp):
    # 解析 offer sdp
    offer = sdp.parse(offer_sdp)

    # 获取 offer 的 m-lines
    offer_m_lines = offer.get_m_lines()

    # 进行需要的操作,例如添加灰度滤镜相关的媒体描述 (这里省略了具体处理过程)

    # 重新构造answer的sdp , 特别需要注意  answer 中的 m 行顺序,按照 offer m 行顺序生成 answer 的 m行.
    answer_sdp = sdp.generate_answer(offer, media_lines=offer_m_lines, /*其它参数*/)

    return answer_sdp

操作步骤:

  1. 定位服务端生成Answer SDP的代码。
  2. 确保处理Offer SDP时提取m-line 信息。
  3. 使用 m-line 信息按照接收Offer的顺序,构建新的Answer SDP。
  4. 重新部署服务端并测试。

方法二:客户端 SDP 操作 (紧急情况下的备选方案,不建议)

在某些特殊情况下,如果无法直接修改服务端逻辑,可以考虑在客户端对服务器返回的 SDP 进行一定的修正,以使其 m= 行的顺序与原始 Offer SDP 的顺序匹配。不过这种方式有一定风险,可能会引入其他问题,并不推荐作为长期的解决方案,在服务端没有办法修改的场景下可以用此方案做过渡,同时尽可能督促服务端开发人员修复此问题。

具体步骤:

  1. 解析 offer 和 answer SDP: 获取本地生成的 offer sdp 和 服务器返回的 answer sdp。

  2. 提取 offer 的 m-lines: 解析 offer SDP,提取所有 m-行,包括它们的顺序。

  3. 重新排列 answer 的 m-lines: 解析 answer sdp,提取所有的 m-lines, 然后根据offer sdp中的m-lines的顺序调整answer sdp中的 m-lines的顺序, 并使用这些已经排序好的 m-lines,来重新构建新的 Answer SDP。

  4. 使用重新排序的 SDP: 使用重新排序好的 Answer SDP 设置 remoteDescription 。

    代码示例(JavaScript):

    async function fixAndSetRemoteDescription(offerSdp, answerSdp, peerConnection) {
        const offerMlines = extractMlines(offerSdp);
        const answerMlines = extractMlines(answerSdp);
    
        // 这里要处理只有音轨或者只有视频轨的情况,
        // 此处仅展示比较常见的带有音频轨道和视频轨道的情况,
       let reorderedAnswer = [];
    
        for(const offerMline of offerMlines)
         {
          for(const answerMline of answerMlines) {
                 if (answerMline.startsWith(offerMline.substring(0,offerMline.indexOf(' '))))
                 {
                      reorderedAnswer.push(answerMline)
                   }
             }
    
          }
    
    // 用新的  reorderedAnswer 来构造新的 sdp。 此处省略具体代码
    
        const reorderedAnswerSdp= reconstructSDP(answerSdp, reorderedAnswer);
    
        const sessionDescription = new RTCSessionDescription({
             type: 'answer',
             sdp: reorderedAnswerSdp
         });
    
         try{
         await peerConnection.setRemoteDescription(sessionDescription);
          console.log("Remote description set successfully");
         } catch (error){
           console.error('Failed to set remote description:', error)
         }
    
    
    }
    
    function extractMlines(sdp) {
        const mlines = [];
        const lines = sdp.split('\r\n');
        for (const line of lines) {
            if (line.startsWith('m=')) {
                mlines.push(line);
            }
        }
        return mlines;
    }
    
    
    

function reconstructSDP(sdp, newMlines) {
// 省略具体代码
}

// 例如使用
fixAndSetRemoteDescription(offer.sdp, answer.sdp, peerConnection)
.then(() => {
console.log("Remote description set complete!");
});


**说明:**  这段代码片段展示了如何使用 JavaScript 解析 SDP 并调整 `m-line` 的顺序,需要补充 `reconstructSDP` 实现。
**安全警告:**   对 SDP 进行修改需要谨慎,需要确保理解修改的内容及其可能造成的潜在影响。客户端的操作尽可能只是恢复顺序,避免引入额外风险,比如添加,删除等行为,这样更容易产生问题,应该严格限定这种操作的场景。
**操作步骤:** 

1.  在客户端 WebRTC 代码中,拦截接收到的 answer sdp 。
2.  使用代码中的逻辑提取并重新排列answer sdp的 `m-line`3.  设置重新排列 `m-line`的 Answer sdp  为remote description 。

##  总结

“Failed to Set Remote Description”  错误多数是 `m-line` 顺序不匹配造成的。理想的解决方案是在服务端确保 SDP  中的 `m-line` 顺序不被改变。如果在服务端暂时没有修改权限,可以在客户端使用临时手段处理(**仅在紧急情况下** ),不过这种手段更复杂,风险更高,要格外小心。最佳方案是和后端服务提供方进行配合,修复服务端生成 Answer SDP 时没有维护 m 行顺序的问题。始终应尽可能地遵循 WebRTC 规范,维护良好的代码实践,可以规避一些不必要的问题,保证高质量的通信。

希望这个分析能够帮助开发者更有效地解决 WebRTC 的 `Failed to Set Remote Description`  错误。