手动完成WebRTC对等SDP协商后ontrack函数始终未被触发
WebRTC手动SDP协商阶段
ontrack回调不触发问题排查 当前处于WebRTC视频聊天应用开发的原理验证阶段,未搭建专用信令服务,参照公开教程手动完成全流程SDP协商:
- 基础流程:通过
navigator.mediaDevices.getUserMedia获取本地音视频流 → 为本地RTCPeerConnection实例绑定媒体轨道 → 生成SDP Offer手动拷贝到对端 → 对端生成SDP Answer手动回本端完成双向连接协商 - 异常现象:调用
addTrack方法无控制台报错,但对端绑定的ontrack回调始终不执行,即便遍历本地流所有轨道逐个调用addTrack也无法解决
现有核心代码片段
发起端(主对等端)代码
navigator.mediaDevices.getUserMedia( { video: true, audio: true } ).then(stream => { document.getElementById('video').srcObject = stream; document.getElementById('video').play() localConnection.addTrack(stream.getTracks()[0]) return localConnection.createOffer(); }).then(() => { offer => applicationCache.setLocalDescription(new RTCSessionDescription(offer)); console.log(JSON.stringify(localConnection.localDescription)) })
应答端(接收视频的对等端)代码
remoteConnection.ontrack = e => { document.getElementById('video1').srcObject = e.streams[0]; document.getElementById('video1').play(); console.log('i'm triggered'); };
问题根因定位与修复方案
对照WebRTC标准协商流程,现有代码存在3个会直接阻断ontrack触发的核心问题:
- 发起端Promise链逻辑完全断裂,从未完成有效的SDP Offer本地绑定
两个执行缺陷直接导致生成的SDP完全不具备协商有效性:createOffer()的返回值根本没有被承接:第二个.then()回调没有声明参数接收上一步返回的Offer对象,反而直接定义了一个从未被调用的匿名箭头函数,写在箭头函数内部的逻辑全程没有被执行- 代码中提到的
applicationCache是浏览器原生的离线缓存API,和RTCPeerConnection实例无任何关联,即便箭头函数被执行,也不会对PeerConnection的状态产生任何修改
修复后的发起端核心逻辑:
.then(offer => { // 承接createOffer返回的Offer对象,直接调用PeerConnection实例的setLocalDescription方法 return localConnection.setLocalDescription(offer); }).then(() => { // 此处打印的localDescription才是包含所有媒体轨道信息、可正常用于协商的有效SDP console.log(JSON.stringify(localConnection.localDescription)) }) addTrack调用缺少MediaStream上下文参数
现有代码调用addTrack时仅传入单个轨道对象,没有把轨道所属的MediaStream实例作为第二个参数传入:- 这种写法下,SDP虽能标记存在媒体轨道,但浏览器内部不会将轨道与流对象做关联,对端触发
ontrack事件时无法拿到完整的流信息,部分浏览器版本会直接因为上下文缺失不触发ontrack回调
正确的轨道绑定写法:
stream.getTracks().forEach(track => { // 第二个参数传入轨道所属的流对象,保证对端ontrack事件能拿到完整流引用 localConnection.addTrack(track, stream); })- 这种写法下,SDP虽能标记存在媒体轨道,但浏览器内部不会将轨道与流对象做关联,对端触发
- 手动协商流程缺漏强制顺序的步骤,未完成双向Description绑定
手动拷贝SDP的场景下必须严格按WebRTC协商的固定顺序执行,缺任意一步都无法建立媒体通道:- 对端收到拷贝的SDP Offer后,先执行
remoteConnection.setRemoteDescription(拿到的Offer字符串)完成远端Offer绑定 - 绑定完成后调用
remoteConnection.createAnswer()生成应答对象,紧接着执行remoteConnection.setLocalDescription(answer)完成本地Answer绑定 - 将此时打印出的Answer字符串拷贝回发起端,由发起端执行
localConnection.setRemoteDescription(拷贝的Answer字符串)完成远端Answer绑定 - 等待两端连接状态变为
connected之后,媒体通道才会正式激活,此时才会触发对端的ontrack回调
- 对端收到拷贝的SDP Offer后,先执行
内容的提问来源于stack exchange,提问作者Skin_phil
相关产品推荐
相关产品推荐

