WebRTC 未将视频流式传输到其他客户端
我是新来的,两周前我开始学习java脚本。 我正在尝试建立一个视频会议私人网站,问题是: 视频流在用户 A 和用户 B 上显示,但他们看不到对方的流 这是 javascript 应用程序代码
var AppProcess = (function(){
var peers_connection_ids = [];
var peers_connection = [];
var remote_vid_stream = [];
var remote_aud_stream = [];
var videoCamTrack;
var local_div;
var serverProcess;
var audio;
var isAudioMute = true;
var rtp_aud_senders = [];
var video_state = {
None:0,
Camera: 1,
ScreenShare:2,
};
var video_st = video_state.None;
var rtp_vid_senders = [];
async function _init(SDP_function, my_connid){
serverProcess = SDP_function;
my_connection_id = my_connid;
eventProcess();
local_div = document.getElementById("locaVideoPlayer");
}
function eventProcess(){
$("#micMuteUnmute").on("click", async function(){
if(!audio){
await loadAudio();
}
if(!audio){
alert("Audio Permission Is Not Granted");
return;
}
if(isAudioMute){
audio.enabled = true;
$(this).html("<span class='material-icons'>mic</span>");
updateMediaSenders(audio, rtp_aud_senders);
}else{
audio.enabled = false;
$(this).html("<span class='material-icons'>mic_off</span>");
removeMediaSenders(rtp_aud_senders);
}
isAudioMute = !isAudioMute;
});
$("#videoCamOnOff").on("click", async function(){
if(video_st == video_state.Camera){
await videoProcess(video_state.None);
}else{
await videoProcess(video_state.Camera);
}
});
$("#btnScreenShareOnOff").on("click", async function(){
if(video_st == video_state.ScreenShare){
await videoProcess(video_state.None);
}else{
await videoProcess(video_state.ScreenShare);
}
});
}
async function videoProcess(newVideoState){
if(newVideoState == video_state.None){
$("videoCamOnOff").html("<span class='material-icons'>videocam_off</span>");
}
if(newVideoState == video_state.Camera){
$("videoCamOnOff").html("<span class='material-icons'>videocam_on</span>");
}
try{
var vstream = null;
if(newVideoState == video_state.Camera){
vstream = await navigator.mediaDevices.getUserMedia({
video:{
width:1920,
height:1080,
},
audio:false,
});
}else if(newVideoState == video_state.ScreenShare){
vstream = await navigator.mediaDevices.getDisplayMedia({
video:{
width:1920,
height:1080,
},
audio:false,
});
}
if(vstream && vstream.getVideoTracks().length > 0){
videoCamTrack = vstream.getVideoTracks()[0];
if(videoCamTrack){
local_div.srcObject = new MediaStream([videoCamTrack]);
await updateMediaSenders(videoCamTrack, rtp_vid_senders);
}
}
} catch(e){
console.log(e);
return;
}
video_st = newVideoState;
}
var iceConfiguration = {
iceServers:[
{
urls:'stun:stun.l.google.com:19302',
},
{
urls:'stun:stun.l.google.com:19302',
},
],
};
async function setConnection(connid){
var connection = new RTCPeerConnection(iceConfiguration);
connection.onnegotiationneeded = async function(event) {
await setOffer(connid);
};
connection.onicecandidate = function(event) {
if(event.candidate){
serverProcess(JSON.stringify({icecandidate: event.candidate}),
connid);
}
};
connection.ontrack = function(event){
if(!remote_vid_stream[connid]){
remote_vid_stream[connid] = new MediaStream();
}
if(!remote_aud_stream[connid]){
remote_aud_stream[connid] = new MediaStream();
}
if(event.track.kind == "video"){
remote_vid_stream[connid]
.getVideoTracks()
.forEach((t)=>remote_vid_stream[connid].removeTrack(t));
remote_vid_stream[connid].addTrack(event.track);
var remoteVideoPlayer = document.getElementById("v_" + connid);
remoteVideoPlayer.srcObject = null;
remoteVideoPlayer.srcObject = remote_vid_stream[connid];
remoteVideoPlayer.load();
} else if(event.track.kind == "audio"){
remote_aud_stream[connid]
.getAudioTracks()
.forEach((t)=>remote_aud_stream[connid].removeTrack(t));
remote_aud_stream[connid].addTrack(event.track);
var remoteAudioPlayer = document.getElementById("a_" + connid);
remoteAudioPlayer.srcObject = null;
remoteAudioPlayer.srcObject = remote_aud_stream[connid];
remoteAudioPlayer.load();
}
};
peers_connection_ids[connid] = connid;
peers_connection[connid] = connection;
if(video_st == video_state.Camera ||
video_st == video_state.ScreenShare
){
if(videoCamTrack){
updateMediaSenders(videoCamTrack, rtp_vid_senders);
}
}
return connection;
}
function connection_status(connection){
if((connection && connection.connectionState == "new" ||
connection.connectionState == "connecting" ||
connection.connectionState == "connected")){
return true;
}else {
return false;
}
}
async function updateMediaSenders(track, rtp_senders){
for(var con_id in peers_connection_ids){
if(connection_status(peers_connection[con_id])){
if(rtp_senders[con_id] && rtp_senders[con_id].track){
rtp_senders[con_id].replaceTrack(track);
}else {
rtp_senders[con_id] = peers_connection[con_id].addTrack(track);
}
}
}
}
async function setOffer(connid){
var connection = peers_connection[connid];
var offer = await connection.createOffer();
await connection.setLocalDescription(offer);
serverProcess(JSON.stringify({
offer : connection.LocalDescription,
}), connid);
}
async function SDPProcess(message, from_connid){
message = JSON.parse(message);
if(message.answer){
await peers_connection[from_connid]
.setRemoteDescription(new RTCSessionDescription(message.answer));
}else if(message.offer){
if(!peers_connection[from_connid]){
await setConnection(from_connid);
}
await peers_connection[from_connid]
.setRemoteDescription(new RTCSessionDescription(message.offer));
var answer = await peers_connection[from_connid].createAnswer();
await peers_connection[from_connid].setLocalDescription(answer);
serverProcess(JSON.stringify({
answer : answer,
}), from_connid);
}else if (message.icecandidate){
if(!peers_connection[from_connid]){
await setConnection(from_connid);
}
try{
await peers_connection[from_connid].addIceCandidate(message.
icecandidate);
}catch(e){
console.log(e);
}
}
}
return {
setNewConnection: async function(connid){
await setConnection(connid);
},
init : async function(SDP_function, my_connid){
await _init(SDP_function, my_connid);
},
processClientFunc : async function(data, from_connid){
await SDPProcess(data, from_connid);
},
};
})();
var MyApp = (function(){
var socket = null;
var user_id = "";
var meeting_id = "";
function init(uid, mid){
user_id = uid;
meeting_id = mid;
$("#meetingContainer").show();
$("#me h2").text(user_id + "(ME");
document.title = user_id;
event_process_for_signaling_server();
}
function event_process_for_signaling_server(){
socket = io.connect();
var SDP_function = function(data, to_connid){
socket.emit("SDPProcess", {
message: data,
to_connid: to_connid,
});
}
socket.on("connect", () =>{
if(socket.connected){
AppProcess.init(SDP_function, socket.id);
if(user_id != "" && meeting_id != ""){
socket.emit("userconnect", {
displayName: user_id,
meetingid: meeting_id,
});
}
}
});
socket.on("inform_others_about_me", function(data){
addUser(data.other_user_id, data.connId);
AppProcess.setNewConnection(data.connId);
});
socket.on("inform_me_about_other_user", function(other_users){
if(other_users){
for(var i = 0; i<other_users.length; i++){
addUser(other_users[i].user_id,
other_users[i].connectionId);
AppProcess.setNewConnection(other_users[i].connectionId);
}
}
});
socket.on("SDPProcess", async function(data){
await AppProcess.processClientFunc(data.message, data.from_connid);
});
}
function addUser(other_user_id, connId){
var newDivId = $("#otherTamplate").clone();
newDivId = newDivId.attr("id", connId).addClass("other");
newDivId.find("h2").text(other_user_id);
newDivId.find("video").attr("id", "v_id"+connId);
newDivId.find("audio").attr("id", "a_"+connId);
newDivId.show();
$("#divUsers").append(newDivId);
}
return{
_init: function(uid, mid){
init(uid, mid);
},
};
})();
这也是 server.js 文件
const express = require("express");
const path = require("path");
var app = express();
var server = app.listen(3000, function(){
console.log("Listening On Port 3000");
});
const io = require("socket.io")(server,{
allowEIO3: true,
});
app.use(express.static(path.join(__dirname,"")));
var userConnections = [];
io.on("connection",(socket)=>{
console.log("socket id is", socket.id);
socket.on("userconnect", (data)=>{
console.log("userconnect", data.displayName, data.meetingid);
var other_users = userConnections.filter(
(p) => p.meeting_id == data.meetingid);
userConnections.push({
connectionId: socket.id,
user_id: data.displayName,
meeting_id: data.meetingid,
});
other_users.forEach((v) =>{
socket.to(v.connectionId).emit("inform_others_about_me", {
other_user_id: data.displayName,
connId: socket.id,
})
});
socket.emit("inform_me_about_other_user", other_users);
});
socket.on("SDPProcess", (data)=>{
socket.to(data.to_connid).emit("SDPProcess",{
message: data.message,
from_connid: socket.id,
})
})
});
Example index html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kira Meet</title>
<link rel="stylesheet" href="kira_public/k_assets/css/bootstrap.min.css">
<link rel="stylesheet" href="kira_public/k_assets/css/k_style.css">
<link rel="stylesheet" href="kira_public/k_assets/css/index_style.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="kira_public/k_assets/js/k_meet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script>
$(function(){
/*const urlParams = new URLSearchParams(window.location.search);
var meeting_id = urlParams.get('meetingID');*/
meeting_id = window.prompt('EnterRoomID (8-d number)')
user_id = window.prompt('Enter Your User ID');
if(!user_id || !meeting_id){
alert('User ID Or Meeting ID Is Missing');
window.location.href = '/kira_action.html';
return;
}
$("#meetingContainer").show();
MyApp._init(user_id, meeting_id);
})
</script>
</head>
<body>
<main class="d-flex flex-column home-wrap">
<div class="g-top text-light">
<div class="top-remote-video-show-wrap d-flex">
<div id="meetingContainer" class="w-75" style="display:none;">
<div class="call-wrap" style="background-color: black;">
<div class="video-wrap" id="divUsers" style="display:flex; flex-wrap:wrap">
<div id="me" class="userbox display-center flex-column">
<h2 class="display-center" style="font-size: 14px;"></h2>
<div class="display-center">
<video autoplay muted id="locaVideoPlayer"></video>
</div>
</div>
<div id="otherTamplate" class="userbox display-center flex-column" style="display:none">
<h2 class="display-center" style="font-size: 14px;"></h2>
<div class="display-center">
<video autoplay muted></video>
<audio autoplay controls muted style="display:none"></audio>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="g-top-left bg-light text-secondary w-25 d-flex align-items-center justify-content-between ps-2 pe-2">
<div class="top-left-participant-wrap pt-2 cursor-pointer">
<div class="top-left-participant-icon">
<span class="material-icons">people</span>
</div>
<div class="top-left-participant-count">2</div>
</div>
<div class="top-left-chat-wrap pt-2 cursor-pointer">
<span class="material-icons">message</span>
</div>
</div>
<div class="top-left-time-wrap"></div>
</div>
<div class="g-bottom bg-light m-0 d-flex justify-content-between align-items-center">
<div class="bottom-left d-flex" style="height: 10vh;">
<div class="display-center cursor-pointer meeting-details-button">
Meeting Details<span class="material-icons">keyboard_arrow_down</span>
</div>
</div>
<div class="bottom-middle d-flex justify-content-center align-items-center" style="height: 10vh;">
<div class="mic-toggle-wrap action-icon-style display-center me-2 cursor-pointer" id="micMuteUnmute">
<span class="material-icons">mic_off</span>
</div>
<div class="end-call-wrap action-icon-style display-center me-2 cursor-pointer">
<span class="material-icons text-danger">call</span>
</div>
<div class="video-toggle-wrap action-icon-style display-center cursor-pointer" id="videoCamOnOff">
<span class="material-icons">videocam_off</span></div>
</div>
<div class="bottom-right d-flex justify-content-center align-items-center me-3" style="height: 10vh;">
<div class="present-now-wrap d-flex justify-content-center flex-column align-items-center me-5 cursor-pointer" id="btnScreenShareOnOff">
<span class="material-icons">present_to_all</span>
<div>Present Now</div>
</div>
<div class="option-wrap cursor-pointer d-flex display-center" style="height: 10vh; position: relative;">
<div class="option-icon">
<span class="material-icons">more_vert</span>
</div>
</div>
</div>
</div>
</main>
</body>
</html>
我不知道我到底在哪里搞砸了,但我确信我搞砸了。
i'm new here and i started learning java script like 2 weeks ago.
i'm trying to build a video conference private website and the problem is :
video stream is showing on user A and User B but they cant see each others' stream
here is the javascript app code
var AppProcess = (function(){
var peers_connection_ids = [];
var peers_connection = [];
var remote_vid_stream = [];
var remote_aud_stream = [];
var videoCamTrack;
var local_div;
var serverProcess;
var audio;
var isAudioMute = true;
var rtp_aud_senders = [];
var video_state = {
None:0,
Camera: 1,
ScreenShare:2,
};
var video_st = video_state.None;
var rtp_vid_senders = [];
async function _init(SDP_function, my_connid){
serverProcess = SDP_function;
my_connection_id = my_connid;
eventProcess();
local_div = document.getElementById("locaVideoPlayer");
}
function eventProcess(){
$("#micMuteUnmute").on("click", async function(){
if(!audio){
await loadAudio();
}
if(!audio){
alert("Audio Permission Is Not Granted");
return;
}
if(isAudioMute){
audio.enabled = true;
$(this).html("<span class='material-icons'>mic</span>");
updateMediaSenders(audio, rtp_aud_senders);
}else{
audio.enabled = false;
$(this).html("<span class='material-icons'>mic_off</span>");
removeMediaSenders(rtp_aud_senders);
}
isAudioMute = !isAudioMute;
});
$("#videoCamOnOff").on("click", async function(){
if(video_st == video_state.Camera){
await videoProcess(video_state.None);
}else{
await videoProcess(video_state.Camera);
}
});
$("#btnScreenShareOnOff").on("click", async function(){
if(video_st == video_state.ScreenShare){
await videoProcess(video_state.None);
}else{
await videoProcess(video_state.ScreenShare);
}
});
}
async function videoProcess(newVideoState){
if(newVideoState == video_state.None){
$("videoCamOnOff").html("<span class='material-icons'>videocam_off</span>");
}
if(newVideoState == video_state.Camera){
$("videoCamOnOff").html("<span class='material-icons'>videocam_on</span>");
}
try{
var vstream = null;
if(newVideoState == video_state.Camera){
vstream = await navigator.mediaDevices.getUserMedia({
video:{
width:1920,
height:1080,
},
audio:false,
});
}else if(newVideoState == video_state.ScreenShare){
vstream = await navigator.mediaDevices.getDisplayMedia({
video:{
width:1920,
height:1080,
},
audio:false,
});
}
if(vstream && vstream.getVideoTracks().length > 0){
videoCamTrack = vstream.getVideoTracks()[0];
if(videoCamTrack){
local_div.srcObject = new MediaStream([videoCamTrack]);
await updateMediaSenders(videoCamTrack, rtp_vid_senders);
}
}
} catch(e){
console.log(e);
return;
}
video_st = newVideoState;
}
var iceConfiguration = {
iceServers:[
{
urls:'stun:stun.l.google.com:19302',
},
{
urls:'stun:stun.l.google.com:19302',
},
],
};
async function setConnection(connid){
var connection = new RTCPeerConnection(iceConfiguration);
connection.onnegotiationneeded = async function(event) {
await setOffer(connid);
};
connection.onicecandidate = function(event) {
if(event.candidate){
serverProcess(JSON.stringify({icecandidate: event.candidate}),
connid);
}
};
connection.ontrack = function(event){
if(!remote_vid_stream[connid]){
remote_vid_stream[connid] = new MediaStream();
}
if(!remote_aud_stream[connid]){
remote_aud_stream[connid] = new MediaStream();
}
if(event.track.kind == "video"){
remote_vid_stream[connid]
.getVideoTracks()
.forEach((t)=>remote_vid_stream[connid].removeTrack(t));
remote_vid_stream[connid].addTrack(event.track);
var remoteVideoPlayer = document.getElementById("v_" + connid);
remoteVideoPlayer.srcObject = null;
remoteVideoPlayer.srcObject = remote_vid_stream[connid];
remoteVideoPlayer.load();
} else if(event.track.kind == "audio"){
remote_aud_stream[connid]
.getAudioTracks()
.forEach((t)=>remote_aud_stream[connid].removeTrack(t));
remote_aud_stream[connid].addTrack(event.track);
var remoteAudioPlayer = document.getElementById("a_" + connid);
remoteAudioPlayer.srcObject = null;
remoteAudioPlayer.srcObject = remote_aud_stream[connid];
remoteAudioPlayer.load();
}
};
peers_connection_ids[connid] = connid;
peers_connection[connid] = connection;
if(video_st == video_state.Camera ||
video_st == video_state.ScreenShare
){
if(videoCamTrack){
updateMediaSenders(videoCamTrack, rtp_vid_senders);
}
}
return connection;
}
function connection_status(connection){
if((connection && connection.connectionState == "new" ||
connection.connectionState == "connecting" ||
connection.connectionState == "connected")){
return true;
}else {
return false;
}
}
async function updateMediaSenders(track, rtp_senders){
for(var con_id in peers_connection_ids){
if(connection_status(peers_connection[con_id])){
if(rtp_senders[con_id] && rtp_senders[con_id].track){
rtp_senders[con_id].replaceTrack(track);
}else {
rtp_senders[con_id] = peers_connection[con_id].addTrack(track);
}
}
}
}
async function setOffer(connid){
var connection = peers_connection[connid];
var offer = await connection.createOffer();
await connection.setLocalDescription(offer);
serverProcess(JSON.stringify({
offer : connection.LocalDescription,
}), connid);
}
async function SDPProcess(message, from_connid){
message = JSON.parse(message);
if(message.answer){
await peers_connection[from_connid]
.setRemoteDescription(new RTCSessionDescription(message.answer));
}else if(message.offer){
if(!peers_connection[from_connid]){
await setConnection(from_connid);
}
await peers_connection[from_connid]
.setRemoteDescription(new RTCSessionDescription(message.offer));
var answer = await peers_connection[from_connid].createAnswer();
await peers_connection[from_connid].setLocalDescription(answer);
serverProcess(JSON.stringify({
answer : answer,
}), from_connid);
}else if (message.icecandidate){
if(!peers_connection[from_connid]){
await setConnection(from_connid);
}
try{
await peers_connection[from_connid].addIceCandidate(message.
icecandidate);
}catch(e){
console.log(e);
}
}
}
return {
setNewConnection: async function(connid){
await setConnection(connid);
},
init : async function(SDP_function, my_connid){
await _init(SDP_function, my_connid);
},
processClientFunc : async function(data, from_connid){
await SDPProcess(data, from_connid);
},
};
})();
var MyApp = (function(){
var socket = null;
var user_id = "";
var meeting_id = "";
function init(uid, mid){
user_id = uid;
meeting_id = mid;
$("#meetingContainer").show();
$("#me h2").text(user_id + "(ME");
document.title = user_id;
event_process_for_signaling_server();
}
function event_process_for_signaling_server(){
socket = io.connect();
var SDP_function = function(data, to_connid){
socket.emit("SDPProcess", {
message: data,
to_connid: to_connid,
});
}
socket.on("connect", () =>{
if(socket.connected){
AppProcess.init(SDP_function, socket.id);
if(user_id != "" && meeting_id != ""){
socket.emit("userconnect", {
displayName: user_id,
meetingid: meeting_id,
});
}
}
});
socket.on("inform_others_about_me", function(data){
addUser(data.other_user_id, data.connId);
AppProcess.setNewConnection(data.connId);
});
socket.on("inform_me_about_other_user", function(other_users){
if(other_users){
for(var i = 0; i<other_users.length; i++){
addUser(other_users[i].user_id,
other_users[i].connectionId);
AppProcess.setNewConnection(other_users[i].connectionId);
}
}
});
socket.on("SDPProcess", async function(data){
await AppProcess.processClientFunc(data.message, data.from_connid);
});
}
function addUser(other_user_id, connId){
var newDivId = $("#otherTamplate").clone();
newDivId = newDivId.attr("id", connId).addClass("other");
newDivId.find("h2").text(other_user_id);
newDivId.find("video").attr("id", "v_id"+connId);
newDivId.find("audio").attr("id", "a_"+connId);
newDivId.show();
$("#divUsers").append(newDivId);
}
return{
_init: function(uid, mid){
init(uid, mid);
},
};
})();
here is also the server.js file
const express = require("express");
const path = require("path");
var app = express();
var server = app.listen(3000, function(){
console.log("Listening On Port 3000");
});
const io = require("socket.io")(server,{
allowEIO3: true,
});
app.use(express.static(path.join(__dirname,"")));
var userConnections = [];
io.on("connection",(socket)=>{
console.log("socket id is", socket.id);
socket.on("userconnect", (data)=>{
console.log("userconnect", data.displayName, data.meetingid);
var other_users = userConnections.filter(
(p) => p.meeting_id == data.meetingid);
userConnections.push({
connectionId: socket.id,
user_id: data.displayName,
meeting_id: data.meetingid,
});
other_users.forEach((v) =>{
socket.to(v.connectionId).emit("inform_others_about_me", {
other_user_id: data.displayName,
connId: socket.id,
})
});
socket.emit("inform_me_about_other_user", other_users);
});
socket.on("SDPProcess", (data)=>{
socket.to(data.to_connid).emit("SDPProcess",{
message: data.message,
from_connid: socket.id,
})
})
});
Example index html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kira Meet</title>
<link rel="stylesheet" href="kira_public/k_assets/css/bootstrap.min.css">
<link rel="stylesheet" href="kira_public/k_assets/css/k_style.css">
<link rel="stylesheet" href="kira_public/k_assets/css/index_style.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="kira_public/k_assets/js/k_meet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
<script>
$(function(){
/*const urlParams = new URLSearchParams(window.location.search);
var meeting_id = urlParams.get('meetingID');*/
meeting_id = window.prompt('EnterRoomID (8-d number)')
user_id = window.prompt('Enter Your User ID');
if(!user_id || !meeting_id){
alert('User ID Or Meeting ID Is Missing');
window.location.href = '/kira_action.html';
return;
}
$("#meetingContainer").show();
MyApp._init(user_id, meeting_id);
})
</script>
</head>
<body>
<main class="d-flex flex-column home-wrap">
<div class="g-top text-light">
<div class="top-remote-video-show-wrap d-flex">
<div id="meetingContainer" class="w-75" style="display:none;">
<div class="call-wrap" style="background-color: black;">
<div class="video-wrap" id="divUsers" style="display:flex; flex-wrap:wrap">
<div id="me" class="userbox display-center flex-column">
<h2 class="display-center" style="font-size: 14px;"></h2>
<div class="display-center">
<video autoplay muted id="locaVideoPlayer"></video>
</div>
</div>
<div id="otherTamplate" class="userbox display-center flex-column" style="display:none">
<h2 class="display-center" style="font-size: 14px;"></h2>
<div class="display-center">
<video autoplay muted></video>
<audio autoplay controls muted style="display:none"></audio>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="g-top-left bg-light text-secondary w-25 d-flex align-items-center justify-content-between ps-2 pe-2">
<div class="top-left-participant-wrap pt-2 cursor-pointer">
<div class="top-left-participant-icon">
<span class="material-icons">people</span>
</div>
<div class="top-left-participant-count">2</div>
</div>
<div class="top-left-chat-wrap pt-2 cursor-pointer">
<span class="material-icons">message</span>
</div>
</div>
<div class="top-left-time-wrap"></div>
</div>
<div class="g-bottom bg-light m-0 d-flex justify-content-between align-items-center">
<div class="bottom-left d-flex" style="height: 10vh;">
<div class="display-center cursor-pointer meeting-details-button">
Meeting Details<span class="material-icons">keyboard_arrow_down</span>
</div>
</div>
<div class="bottom-middle d-flex justify-content-center align-items-center" style="height: 10vh;">
<div class="mic-toggle-wrap action-icon-style display-center me-2 cursor-pointer" id="micMuteUnmute">
<span class="material-icons">mic_off</span>
</div>
<div class="end-call-wrap action-icon-style display-center me-2 cursor-pointer">
<span class="material-icons text-danger">call</span>
</div>
<div class="video-toggle-wrap action-icon-style display-center cursor-pointer" id="videoCamOnOff">
<span class="material-icons">videocam_off</span></div>
</div>
<div class="bottom-right d-flex justify-content-center align-items-center me-3" style="height: 10vh;">
<div class="present-now-wrap d-flex justify-content-center flex-column align-items-center me-5 cursor-pointer" id="btnScreenShareOnOff">
<span class="material-icons">present_to_all</span>
<div>Present Now</div>
</div>
<div class="option-wrap cursor-pointer d-flex display-center" style="height: 10vh; position: relative;">
<div class="option-icon">
<span class="material-icons">more_vert</span>
</div>
</div>
</div>
</div>
</main>
</body>
</html>
i don't know exactly where did i mess it up but i'm sure i did.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论