无法触发 WebRTC 跟踪事件
Posted
技术标签:
【中文标题】无法触发 WebRTC 跟踪事件【英文标题】:Can not get WebRTC track event triggered 【发布时间】:2020-05-21 04:01:57 【问题描述】:我是 WebRTC 的初学者,并试图在两个浏览器窗口之间建立对等连接。我在本地运行的 nodejs 中实现了简单的 websocket 服务器。在成为候选人之前,一切似乎都很好。交换候选人后,远程流未启动。我搜索并运行了几个示例,但找不到我的工作。
我的 UI 有一些用于交流的输入。这是我正在做的调用:
-
在第一个窗口输入用户名
user1
,在第一个窗口输入远程用户名user2
在第二个窗口输入用户名user2
,在第二个窗口输入远程用户名user1
在每两个窗口中单击Connect to server
按钮。 (此后,信令服务器会知道它们)。
在第一个窗口中选择我的第一个摄像头设备,然后在第二个窗口中选择我的第二个摄像头设备。 (相机将在此之后启动)
在第一个窗口中点击调用
问题是:远程流显示在被调用者窗口(第二个窗口)上,但从调用者窗口(第一个窗口)的开发控制台我看不到 onTrack
函数运行。所以远程流不会显示在调用者身上。但我看到了候选日志。
所以调用者看不到被调用者,但被调用者看到了调用者。
index.html
我在两个浏览器窗口中打开这个文件两次(作为 file://...)。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Webrtc Test</title>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
</head>
<body>
<table border="2">
<tr>
<td>
<span>Signaling Server addres:</span>
<input type="text" id="serverAddress" value="localhost:3001" onload="window.serverAddress = this;" />
</td>
<td>
<span>Username:</span>
<input type="text" id="username" value="user1" onload="window.username = this;" />
</td>
<td>
<span>Target username:</span>
<input type="text" id="remoteUsername" value="user2" onload="window.remoteUsername = this;" />
</td>
<td>Devices</td>
</tr>
<tr>
<td>
<textarea
name="log"
id="log"
cols="50"
rows="10"
style="width: 100%; resize: vertical;"
></textarea>
</td>
<td>
<video
id="selfVideo"
autoplay
playsinline
muted
onload="window.selfVideo = this;"
></video>
</td>
<td>
<video id="remoteVideo" autoplay playsinline onload="window.remoteVideo = this;"></video deo>
</td>
<td rowspan="2">
<div>
<div>Video:<input id="videoCheck" type="checkbox" onload="window.videoCheck = this;" checked /></div>
<select
id="videoDevices"
size="5"
onload="window.videoDevices = this;"
onchange="startSelf();"
></select>
</div>
<div>
<div>Audio:<input id="audioCheck" type="checkbox" onload="window.audioCheck = this;" /></div>
<select
id="audioDevices"
size="5"
onload="window.audioDevices = this;"
onchange="startSelf();"
></select>
</div>
</td>
</tr>
<tr>
<td colspan="3">
<div
style="width: 100%; display: flex; justify-content: space-evenly;"
>
<button id="connect" onclick="connect();">Connect to server</button>
<button id="call" onclick="call();">Call</button>
<button id="hangup" onclick="hangup();">Hang up</button>
</div>
</td>
</tr>
</table>
<!-- <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> -->
<script src="adapter.js"></script>
<script src="setup.js"></script>
<script src="socket.js"></script>
<script src="rtc.js"></script>
</body>
</html>
setup.js
用于检测输入设备并在页面加载时创建选项。
const detectDevices = (deviceInfos) =>
for (let i = 0; i !== deviceInfos.length; ++i)
const deviceInfo = deviceInfos[i];
const element = document.createElement("option");
element.value = deviceInfo.deviceId;
if (deviceInfo.kind === "videoinput")
element.innerText =
deviceInfo.label || `camera $videoDevices.length + 1`;
videoDevices.add(element);
else if (deviceInfo.kind === "audioinput")
element.innerText =
deviceInfo.label || `microphone $audioDevices.length + 1`;
audioDevices.add(element);
;
navigator.mediaDevices
.getUserMedia( audio: true, video: true )
.then((stream) =>
window.localStream = stream;
return navigator.mediaDevices.enumerateDevices();
)
.then(detectDevices)
.then(() =>
localStream.getTracks().forEach((t) => t.stop());
delete localStream;
)
.catch((error) => console.log("Error detecting devices", error));
const startSelf = async () =>
if (videoCheck.checked && !videoDevices.value)
videoDevices.selectedIndex = 0;
if (audioCheck.checked && !audioDevices.value)
audioDevices.selectedIndex = 0;
const vDevId = videoDevices.value;
const aDevId = audioDevices.value;
// const constraints =
// audio: deviceId: aDevId ? exact: aDevId : undefined ,
// video: deviceId: vDevId ? exact: vDevId : undefined
// ;
const constraints = ;
if (videoCheck.checked)
constraints.video = deviceId: vDevId ? exact: vDevId : undefined ;
if (audioCheck.checked)
constraints.audio = deviceId: aDevId ? exact: aDevId : undefined ;
await navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) =>
window.localStream = stream;
selfVideo.srcObject = stream;
)
.catch((error) => console.log("Error start self", error));
;
socket.js
let ws;
const send = (obj) =>
const message =
from: username.value,
to: remoteUsername.value
;
message.data = btoa(JSON.stringify(obj));
ws.send(JSON.stringify(message));
;
const connect = () =>
ws = new WebSocket("ws://" + serverAddress.value);
registerEvents();
;
const registerEvents = () =>
ws.onopen = () =>
console.log("websocket open");
ws.send(JSON.stringify( from: username.value, data: "merheba" ));
;
ws.onmessage = (m) =>
// parse message
const message = JSON.parse(m.data);
if (message.data === "siye de merheba")
console.log("connected to server");
return;
const data = JSON.parse(atob(message.data));
message.data = data;
console.log("message: ", message);
switch (data.type)
case "offer":
onReceiveOffer(data);
break;
case "answer":
onReceiveAnswer(data);
break;
case "candidate":
console.log("received ice candidate", data);
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
break;
;
;
rtc.js
用于 webrtc 调用函数
let pc;
const initSelf = async () =>
pc = new RTCPeerConnection();
pc.onicecandidate = onIceCandidate;
pc.ontrack = onTrack;
await startSelf();
pc.addStream(localStream);
;
const call = async () =>
if (!window.localStream || !pc)
await initSelf();
// send offer
pc.createOffer().then((offer) =>
pc.setLocalDescription(offer);
console.log("sending offer");
send(offer);
);
;
const onReceiveOffer = async (receivedOffer) =>
console.log("offer receive", receivedOffer);
if (!window.localStream || !pc)
initSelf();
pc.setRemoteDescription(new RTCSessionDescription(receivedOffer));
log.value = JSON.stringify(receivedOffer);
// answer
await pc.createAnswer().then((answer) =>
pc.setLocalDescription(answer);
console.log("answer created: ", answer);
send(answer);
);
;
const onReceiveAnswer = async (receivedAnswer) =>
console.log("answer receive", receivedAnswer);
pc.setRemoteDescription(new RTCSessionDescription(receivedAnswer));
log.value = JSON.stringify(receivedAnswer);
;
const onTrack = async (event) =>
console.log("Add track");
remoteVideo.srcObject = event.streams[0];
;
const onIceCandidate = async (event) =>
if (event.candidate)
console.log("ICE candidate");
send(
type: "candidate",
candidate: event.candidate
);
;
const hangup = () =>
if (pc)
pc.close();
pc = null;
localStream.getTracks().forEach((t) => t.stop());
delete localStream;
;
信令服务器wsServer.js
与node wsServer.js
一起运行
const WebSocket = require("ws");
const wsserver = new WebSocket.Server( port: 3001 , () =>
console.log("server started");
);
let clients = [];
wsserver.on("connection", (socket) =>
socket.on("message", (message) =>
console.log("Message: %s", message);
let data;
try
data = JSON.parse(message);
catch (error)
console.log("Invalid JSON");
data = ;
return;
if (!data.from || data.from === "")
console.log("unknown sender");
return;
if (!clients[data.from])
console.log("client add: ", data.from);
clients[data.from] = socket;
if (data.data === "merheba")
socket.send(JSON.stringify( data: "siye de merheba" ));
clients[data.from] = socket;
return;
if (data.to)
const target = clients[data.to];
if (target)
console.log("forwarding to " + data.to);
// console.log(target);
target.send(message);
);
// socket.on("close", () =>
// if (socket.username)
// delete clients[socket.from];
//
// );
);
【问题讨论】:
【参考方案1】:这是语法:
RTCPeerConnection.ontrack = eventHandler;
所以看看你的代码应该是这样的:
self.ontrack = onTrack;
你为 onececandidate 做的方式
【讨论】:
感谢您的回复。我做了一些改变,包括这个。但问题部分解决了。现在远程视频显示在第二个窗口上,但仍然不显示在拨打电话的第一个窗口上。我更新了问题。收到答案时可能出了什么问题? @user12043 您需要将该部分单独包含在 localVideo 中。完美的流程是..1) 建立对等连接 2) 在应答请求视频上(仅一次)从呼叫者到被呼叫者的第一个请求视频启动协商,一旦完成将再次发回答案.. 在该应答上,被呼叫者可以请求来自第二面的视频...github.com/smitkpatel16/rsvp_vidcall/blob/master/sources/js/… 参考此文件.. 这是为多方设计的【参考方案2】:我放了一些window.localStream
并将initSelf()
的内容移动到startSelf()
中。我不知道为什么,但问题解决了。
const startSelf = async () =>
// creating pc object first
if (!pc)
pc = new RTCPeerConnection();
pc.onicecandidate = onIceCandidate;
pc.ontrack = onTrack;
// some code ...
await navigator.mediaDevices
.getUserMedia(constraints)
.then((stream) =>
window.localStream = stream;
pc.addStream(window.localStream); // adding the stream before showing
selfVideo.srcObject = window.localStream;
)
.catch((error) => console.log("Error start self", error));
;
此处的最终内容:https://github.com/user12043/webrtc-try
【讨论】:
以上是关于无法触发 WebRTC 跟踪事件的主要内容,如果未能解决你的问题,请参考以下文章