巧妙的 webrtc 群组通话 无法在 Javascript 和简单对等中工作
Posted
技术标签:
【中文标题】巧妙的 webrtc 群组通话 无法在 Javascript 和简单对等中工作【英文标题】:ably webrtc group call Not working in Javascript and simple peer 【发布时间】:2020-08-11 01:33:22 【问题描述】:我们正在使用 Ably 和 simple peer 制作一个简单的群组通话网络应用程序。按照循序渐进的教程,一对一通话可以工作(在公共 ip 上使用时也有问题,但这是另一天的话题)
但是,当修改相同的代码以用于群组通话时,它似乎不起作用。列出了成员,用户可以加入群组(频道),控制台甚至显示信号已发送/接收。但音频视频流不起作用。以下是一段js代码(从巧妙的例子修改)
var membersList = []
var connections =
var currentCall
var localStream
var CurrentGroup = ""
var constraints = video: true, audio: true
var apiKey = 'oZ_NdA.xyzabc' // a new app key for Group Call
//var clientId = GetUserName() +'_'+ Math.random().toString(36).substr(2, 6) || 'client-' + Math.random().toString(36).substr(2, 16)
var clientId = GetUserName() || 'client-' + Math.random().toString(36).substr(2, 16)
let GroupMembers = null
let RealtimeApi = new Ably.Realtime( key: apiKey, clientId: clientId )
var RealtimeChannel;
function JoinGroup(groupName)
if (CurrentGroup !== "")
LeaveCurrentGroup()
if (CurrentGroup == groupName)
// user is already in this group
return
CurrentGroup = groupName
InitializeRealtimeChannel()
RealtimeChannel.presence.enter()
StartLocalCamera()
//RegisterSignalReceivingEvent()
RealtimeChannel.presence.subscribe('enter', function(member)
RealtimeChannel.presence.get((err, members) =>
GroupMembers = members
RenderGroupMemberList(groupName, GroupMembers)
GenerateGroupMembersVideoTags(GroupMembers)
)
if (member.clientId === clientId)
document.querySelector("#GroupStatus").innerhtml = "You have Joined the Group"
InitiateConnectionsWithGroupMembers(groupName)
RealtimeChannel.subscribe(`rtc-signal/$clientId`, msg =>
if (localStream === undefined)
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream)
/* use the stream */
localStream = stream
var video = document.getElementById('local')
//video.src = window.URL.createObjectURL(stream)
video.srcObject = stream
video.play()
connect(msg.data, stream)
)
.catch(function(err)
alert('error occurred while trying to get stream')
)
else
connect(msg.data, localStream)
)
else
document.querySelector("#GroupStatus").innerHTML = member.clientId + " has Joined"
)
RealtimeChannel.presence.subscribe('leave', member =>
if (member.clientId === clientId)
document.querySelector("#GroupStatus").innerHTML = "You have Left the Group"
document.querySelector("#GroupMemberVideos").innerHTML = ""
connections = []
else
document.querySelector("#GroupStatus").innerHTML = member.clientId + " has Left"
ReleaseLeavingMemberResources(member.clientId)
RealtimeChannel.presence.get((err, members) =>
GroupMembers = members
RenderGroupMemberList(groupName, GroupMembers)
)
)
function LeaveCurrentGroup()
if (CurrentGroup !== "")
InitializeRealtimeChannel()
RealtimeChannel.presence.leave()
CurrentGroup = ""
function GetAvailableMembersInGroup(groupName)
let Channel = RealtimeApi.channels.get(groupName)
let GroupMembers = new Array()
Channel.presence.get((err, members) =>
GroupMembers = members
RenderGroupMemberList(groupName, GroupMembers)
// if (CurrentGroup == groupName)
// GenerateGroupMembersVideoTags(members)
//
)
function RenderGroupMemberList(groupName, memberNames)
let Element = document.querySelector("#" + groupName + " .people-list")
let html = "<ul class='MemberList'>";
if (!memberNames || memberNames.length < 1)
html += "<li style='font-weight: bold;'>No Member Yet..</li>"
if (memberNames)
for (var i = 0; i < memberNames.length; i++)
if (memberNames[i].clientId !== clientId)
html += "<li>" + memberNames[i].clientId + "</li>"
else
html += "<li style='font-weight: bold;'>" + memberNames[i].clientId + " (You)</li>"
html += "</ul>"
Element.innerHTML = html;
function GenerateGroupMembersVideoTags(memberNames)
let VideoHtml = "";
if (memberNames)
for (var i = 0; i < memberNames.length; i++)
if (memberNames[i].clientId === clientId || document.querySelector("#VideoContainer_" + memberNames[i].clientId) != null)
// do nothing
else
let VideoTag = '<div class = "col-lg-3 col-sm-6 col-xs-12" id="VideoContainer_' + memberNames[i].clientId + '" >\n';
VideoTag += '<video controls style = "width: 100%; height: 100%; min-height: 200px;" id="Video_' + memberNames[i].clientId + '" ></video> \n';
VideoTag += '</div >';
VideoHtml += VideoTag;
// document.querySelector("#GroupMemberVideos").innerHTML += VideoHtml;
document.querySelector("#GroupMemberVideos").insertAdjacentHTML("beforeend", VideoHtml)
function ReleaseLeavingMemberResources(client_Id)
if (document.querySelector("#VideoContainer_" + client_Id))
document.querySelector("#VideoContainer_" + client_Id).remove();
if (connections[client_Id])
connections[client_Id] = null
delete connections[client_Id]
function InitiateConnectionsWithGroupMembers(groupName)
if (GroupMembers)
for (let index = 0; index < GroupMembers.length; index++)
if (GroupMembers[index].clientId != clientId && connections[GroupMembers[index].clientId] == null)
initiateCall(GroupMembers[index].clientId)
function StartLocalCamera()
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream)
/* use the stream */
localStream = stream
var video = document.getElementById('local')
if (video.srcObject == null)
video.srcObject = stream
video.play()
//video.src = window.URL.createObjectURL(stream)
)
.catch(function(err)
console.error(err)
alert('Could not get video stream from source')
)
function InitializeRealtimeChannel()
if (!RealtimeChannel)
RealtimeChannel = RealtimeApi.channels.get(CurrentGroup)
function initiateCall(client_id)
console.info(`Initialting call with $client_id`)
// Create a new connection
currentCall = client_id
if (!connections[client_id])
connections[client_id] = new Connection(client_id, RealtimeChannel, true, localStream)
function connect(data, stream)
if (!connections[data.user])
connections[data.user] = new Connection(data.user, RealtimeChannel, false, stream)
if (!connections[data.user].isConnected)
connections[data.user].handleSignal(data.signal)
function receiveStream(client_id, stream)
let video = document.getElementById('Video_' + client_id)
//video.src = window.URL.createObjectURL(stream)
video.srcObject = stream
video.play()
以及我们下面的简单对等助手:
class Connection
constructor(remoteClient, AblyRealtime, initiator, stream)
console.log(`Opening connection to $remoteClient`)
this._remoteClient = remoteClient
this.isConnected = false
this._p2pConnection = new SimplePeer(
initiator: initiator,
stream: stream
)
this._p2pConnection.on('signal', this._onSignal.bind(this))
this._p2pConnection.on('error', this._onError.bind(this))
this._p2pConnection.on('connect', this._onConnect.bind(this))
this._p2pConnection.on('close', this._onClose.bind(this))
this._p2pConnection.on('stream', this._onStream.bind(this))
handleSignal(signal)
this._p2pConnection.signal(signal)
send(msg)
this._p2pConnection.send(msg)
destroy()
this._p2pConnection.destroy()
_onSignal(signal)
InitializeRealtimeChannel()
try
console.info("Sending Signal to :" + `$this._remoteClient`)
RealtimeChannel.publish(`rtc-signal/$this._remoteClient`,
user: clientId,
signal: signal
)
catch (error)
console.error("Signal error: " + error)
_onConnect()
this.isConnected = true
console.log('connected to ' + this._remoteClient)
_onClose()
console.log(`connection to $this._remoteClient closed`)
_onStream(data)
console.info("Attempting to receive stream from " + this._remoteClient)
receiveStream(this._remoteClient, data)
_onError(error)
console.log(`an error occurred $error.toString()`)
HTML 如下
<!--https://www.ably.io/tutorials/web-rtc-video-calling#testing-our-app-->
<!DOCTYPE html>
<html>
<head>
<title>Group Calls</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
</head>
<body class="bodybg">
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.2.1/adapter.min.js"></script>
<script src="https://cdn.ably.io/lib/ably.min-1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/simple-peer/9.1.2/simplepeer.min.js"></script>
<!-- <script src="js/simple-peer/simplepeer.min.js" type="javascript"></script> -->
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- <script src="js/simple-peer/index.js"></script> -->
<script src="js/helper.js"></script>
<script src="js/group-call.js"></script>
<script src="js/ably-groupcall.js"></script>
<!-- <script src="js/connection-helper.js"></script> -->
<script src="js/group-connectionhelper.js"></script>
<form action="groups.html" method="GET" id="DisplayNameForm">
<div class="row" style="background-color: rgba(136, 26, 32, 0.53);">
<div class="col-sm-4">
<div class="form-group">
<label style="color: #fff;">Display Name</label>
<input type="text" name="Name" class="form-control" />
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label> </label>
<button type="submit" class="btn btn-primary btn-block">Update</button>
</div>
</div>
</div>
</form>
<div class="conatainer-fluid" style="margin-top: 3em;">
<div class="row">
<!-- Group-1 -->
<div class="col-sm-3 col-xs-6">
<div class="panel panel-primary" id="Group_1">
<div class="panel-heading" id="Group_1_Heading">
Group-1
</div>
<div class="panel-body">
<h4>Members:</h4>
<div class="people-list"></div>
</div>
<div class="panel-footer">
<button onclick="JoinGroup('Group_1')" class="btn btn-info btn-block">Join</button>
</div>
</div>
</div>
<!-- Group-2 -->
<div class="col-sm-3 col-xs-6">
<div class="panel panel-primary" id="Group_2">
<div class="panel-heading" id="Group_2_Heading">
Group-2
</div>
<div class="panel-body">
<h4>Members:</h4>
<div class="people-list"></div>
</div>
<div class="panel-footer">
<button onclick="JoinGroup('Group_2')" class="btn btn-info btn-block">Join</button>
</div>
</div>
</div>
<!-- Group-3 -->
<div class="col-sm-3 col-xs-6">
<div class="panel panel-primary" id="Group_3">
<div class="panel-heading" id="Group_3_Heading">
Group-3
</div>
<div class="panel-body">
<h4>Members:</h4>
<div class="people-list"></div>
</div>
<div class="panel-footer">
<button onclick="JoinGroup('Group_3')" class="btn btn-info btn-block">Join</button>
</div>
</div>
</div>
<!-- Group-4 -->
<div class="col-sm-3 col-xs-6">
<div class="panel panel-primary" id="Group_4">
<div class="panel-heading" id="Group_4_Heading">
Group-4
</div>
<div class="panel-body">
<h4>Members:</h4>
<div class="people-list"></div>
</div>
<div class="panel-footer">
<button onclick="JoinGroup('Group_4')" class="btn btn-info btn-block">Join</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-3 col-xs-12">
<div class="panel panel-default">
<div class="panel-body">
<video id="local" muted style="z-index: 2; width: 100%; min-height: 200px;"></video>
<div class="row">
<div class="col-sm-12">
<div class="col-xs-6">
<h3>Duration:</h3>
<label id="minutes"></label> : <label id="seconds"></label>
</div>
<div class="col-xs-6">
<button class="btn btn-default" onclick="LeaveCurrentGroup()" title="Leave Group">
<img src="asset/hang-up.svg" style="width: 50px; height: 50px; color: #fff;">
</button>
</div>
</div>
<div class="col-sm-12">
<h3 id="CallStatus"></h3>
</div>
<div class="col-sm-12">
<h3 id="GroupStatus"></h3>
</div>
</div>
</div>
</div>
Icons made by <a href="https://www.flaticon.com/authors/dmitri13" title="dmitri13">dmitri13</a> from <a href="https://www.flaticon.com/" title="Flaticon"> www.flaticon.com</a>
</div>
<div class="col-sm-9 col-xs-12" style="margin-bottom: 50px;">
<div id="call" style="position: relative;">
<div class="row" id="GroupMemberVideos">
</div>
</div>
</div>
</div>
</div>
</body>
<style>
.MemberList
list-style: decimal;
</style>
</html>
<script type="text/javascript">
window.onload = function()
var Name = GetUrlParameter('Name');
if (Name != null && Name != '')
document.getElementById('DisplayNameForm').style.display = 'none'
setTimeout(() =>
UpdateGroupMemeberLists()
, 5000);
function UpdateGroupMemeberLists()
for (let index = 1; index <= 4; index++)
GetAvailableMembersInGroup("Group_" + index)
</script>
通过一些基本的调试,显示连接建立但没有流
任何帮助表示感谢,因为我完全不知道 webrtc 是如何工作的。
【问题讨论】:
嘿阿卜杜勒!这是 Srushtika - Ably 的开发倡导者。你也可以分享你的HTML文件吗?这将有助于调试问题可能是什么。我们可以尝试复制您遇到的确切问题。 @SrushtikaNeelakantam 。感谢您的答复。 HTML 已添加到问题中。任何帮助表示赞赏。 【参考方案1】:发现问题。 :)
正在初始化加入组函数中的本地摄像机流。该问题已通过在文档加载时初始化本地视频流得到解决。
希望这对其他人有所帮助。
【讨论】:
以上是关于巧妙的 webrtc 群组通话 无法在 Javascript 和简单对等中工作的主要内容,如果未能解决你的问题,请参考以下文章
React 原生 firebase+webrtc 进行视频通话