Я разработал веб-приложение, в котором звонящий и получатель имеют элементы управления видео и аудио. В моем случае должна быть возможность переключить камеру для мобильных пользователей и изменить голос с динамика на внутриканальный динамик.
Я загрузил его на хостинг Firebase, и у меня возникла проблема с подключением двух пользователей в один комнату и во время потока, как я могу переключить камеру и микрофон. ?
Спасибо
Приложение. js
mdc.ripple.MDCRipple.attachTo(document.querySelector('.mdc-button'));
// DEfault configuration - Change these if you have a different STUN or TURN server.
const configuration = {
iceServers: [
{
urls: [
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
],
},
],
iceCandidatePoolSize: 10,
};
let peerConnection = null;
let localStream = null;
let remoteStream = null;
let roomDialog = null;
let roomId = null;
function init() {
document.querySelector('#cameraBtn').addEventListener('click', openUserMedia);
document.querySelector('#hangupBtn').addEventListener('click', hangUp);
document.querySelector('#createBtn').addEventListener('click', createRoom);
document.querySelector('#joinBtn').addEventListener('click', joinRoom);
roomDialog = new mdc.dialog.MDCDialog(document.querySelector('#room-dialog'));
}
async function createRoom() {
document.querySelector('#createBtn').disabled = true;
document.querySelector('#joinBtn').disabled = true;
const db = firebase.firestore();
console.log('Create PeerConnection with configuration: ', configuration);
peerConnection = new RTCPeerConnection(configuration);
registerPeerConnectionListeners();
// Add code for creating a room here
// Code for creating room above
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
// Code for creating a room below
// Code for creating a room above
// Code for collecting ICE candidates below
// Code for collecting ICE candidates above
peerConnection.addEventListener('track', event => {
console.log('Got remote track:', event.streams[0]);
event.streams[0].getTracks().forEach(track => {
console.log('Add a track to the remoteStream:', track);
remoteStream.addTrack(track);
});
});
// Listening for remote session description below
// Listening for remote session description above
// Listen for remote ICE candidates below
// Listen for remote ICE candidates above
}
function joinRoom() {
document.querySelector('#createBtn').disabled = true;
document.querySelector('#joinBtn').disabled = true;
document.querySelector('#confirmJoinBtn').
addEventListener('click', async () => {
roomId = document.querySelector('#room-id').value;
console.log('Join room: ', roomId);
document.querySelector(
'#currentRoom').innerText = `Current room is ${roomId} - You are the callee!`;
await joinRoomById(roomId);
}, {once: true});
roomDialog.open();
}
async function joinRoomById(roomId) {
const db = firebase.firestore();
const roomRef = db.collection('rooms').doc(`${roomId}`);
const roomSnapshot = await roomRef.get();
console.log('Got room:', roomSnapshot.exists);
if (roomSnapshot.exists) {
console.log('Create PeerConnection with configuration: ', configuration);
peerConnection = new RTCPeerConnection(configuration);
registerPeerConnectionListeners();
localStream.getTracks().forEach(track => {
peerConnection.addTrack(track, localStream);
});
// Code for collecting ICE candidates below
// Code for collecting ICE candidates above
peerConnection.addEventListener('track', event => {
console.log('Got remote track:', event.streams[0]);
event.streams[0].getTracks().forEach(track => {
console.log('Add a track to the remoteStream:', track);
remoteStream.addTrack(track);
});
});
// Code for creating SDP answer below
// Code for creating SDP answer above
// Listening for remote ICE candidates below
// Listening for remote ICE candidates above
}
}
async function openUserMedia(e) {
const stream = await navigator.mediaDevices.getUserMedia(
{video: true, audio: true});
document.querySelector('#localVideo').srcObject = stream;
localStream = stream;
remoteStream = new MediaStream();
document.querySelector('#remoteVideo').srcObject = remoteStream;
console.log('Stream:', document.querySelector('#localVideo').srcObject);
document.querySelector('#cameraBtn').disabled = true;
document.querySelector('#joinBtn').disabled = false;
document.querySelector('#createBtn').disabled = false;
document.querySelector('#hangupBtn').disabled = false;
}
async function hangUp(e) {
const tracks = document.querySelector('#localVideo').srcObject.getTracks();
tracks.forEach(track => {
track.stop();
});
if (remoteStream) {
remoteStream.getTracks().forEach(track => track.stop());
}
if (peerConnection) {
peerConnection.close();
}
document.querySelector('#localVideo').srcObject = null;
document.querySelector('#remoteVideo').srcObject = null;
document.querySelector('#cameraBtn').disabled = false;
document.querySelector('#joinBtn').disabled = true;
document.querySelector('#createBtn').disabled = true;
document.querySelector('#hangupBtn').disabled = true;
document.querySelector('#currentRoom').innerText = '';
// Delete room on hangup
if (roomId) {
const db = firebase.firestore();
const roomRef = db.collection('rooms').doc(roomId);
const calleeCandidates = await roomRef.collection('calleeCandidates').get();
calleeCandidates.forEach(async candidate => {
await candidate.delete();
});
const callerCandidates = await roomRef.collection('callerCandidates').get();
callerCandidates.forEach(async candidate => {
await candidate.delete();
});
await roomRef.delete();
}
document.location.reload(true);
}
function registerPeerConnectionListeners() {
peerConnection.addEventListener('icegatheringstatechange', () => {
console.log(
`ICE gathering state changed: ${peerConnection.iceGatheringState}`);
});
peerConnection.addEventListener('connectionstatechange', () => {
console.log(`Connection state change: ${peerConnection.connectionState}`);
});
peerConnection.addEventListener('signalingstatechange', () => {
console.log(`Signaling state change: ${peerConnection.signalingState}`);
});
peerConnection.addEventListener('iceconnectionstatechange ', () => {
console.log(
`ICE connection state change: ${peerConnection.iceConnectionState}`);
});
}
init();
Приложение. html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to FirebaseRTC</title>
<link href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css" rel="stylesheet">
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<!-- update the version number as needed -->
<script defer src="/__/firebase/7.13.1/firebase-app.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/7.13.1/firebase-firestore.js"></script>
<!-- initialize the SDK after all desired features are loaded -->
<script defer src="/__/firebase/init.js"></script>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h1>Welcome to FirebaseRTC!</h1>
<div id="buttons">
<button class="mdc-button mdc-button--raised" id="cameraBtn">
<i class="material-icons mdc-button__icon" aria-hidden="true">perm_camera_mic</i>
<span class="mdc-button__label">Open camera & microphone</span>
</button>
<button class="mdc-button mdc-button--raised" disabled id="createBtn">
<i class="material-icons mdc-button__icon" aria-hidden="true">group_add</i>
<span class="mdc-button__label">Create room</span>
</button>
<button class="mdc-button mdc-button--raised" disabled id="joinBtn">
<i class="material-icons mdc-button__icon" aria-hidden="true">group</i>
<span class="mdc-button__label">Join room</span>
</button>
<button class="mdc-button mdc-button--raised" disabled id="hangupBtn">
<i class="material-icons mdc-button__icon" aria-hidden="true">close</i>
<span class="mdc-button__label">Hangup</span>
</button>
</div>
<div>
<span id="currentRoom"></span>
</div>
<div id="videos">
<video id="localVideo" muted autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
</div>
<div class="mdc-dialog"
id="room-dialog"
role="alertdialog"
aria-modal="true"
aria-labelledby="my-dialog-title"
aria-describedby="my-dialog-content">
<div class="mdc-dialog__container">
<div class="mdc-dialog__surface">
<h2 class="mdc-dialog__title" id="my-dialog-title">Join room</h2>
<div class="mdc-dialog__content" id="my-dialog-content">
Enter ID for room to join:
<div class="mdc-text-field">
<input type="text" id="room-id" class="mdc-text-field__input">
<label class="mdc-floating-label" for="my-text-field">Room ID</label>
<div class="mdc-line-ripple"></div>
</div>
</div>
<footer class="mdc-dialog__actions">
<button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="no">
<span class="mdc-button__label">Cancel</span>
</button>
<button id="confirmJoinBtn" type="button" class="mdc-button mdc-dialog__button"
data-mdc-dialog-action="yes">
<span class="mdc-button__label">Join</span>
</button>
</footer>
</div>
</div>
<div class="mdc-dialog__scrim"></div>
</div>
<script src="app.js"></script>
</body>
</html>
Прил. CSS
body {
background: #ECEFF1;
color: rgba(0, 0, 0, 0.87);
font-family: Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
}
#message {
background: white;
max-width: 360px;
margin: 100px auto 16px;
padding: 32px 24px;
border-radius: 3px;
}
#message h2 {
color: #ffa100;
font-weight: bold;
font-size: 16px;
margin: 0 0 8px;
}
#message h1 {
font-size: 22px;
font-weight: 300;
color: rgba(0, 0, 0, 0.6);
margin: 0 0 16px;
}
#message p {
line-height: 140%;
margin: 16px 0 24px;
font-size: 14px;
}
#message a {
display: block;
text-align: center;
background: #039be5;
text-transform: uppercase;
text-decoration: none;
color: white;
padding: 16px;
border-radius: 4px;
}
#message, #message a {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
#load {
color: rgba(0, 0, 0, 0.4);
text-align: center;
font-size: 13px;
}
@media (max-width: 600px) {
body, #message {
margin-top: 0;
background: white;
box-shadow: none;
}
body {
border-top: 16px solid #ffa100;
}
}
body {
margin: 1em;
}
button {
margin: 0.2em 0.1em;
}
div#videos {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
div#videos > video {
background: black;
width: 640px;
height: 100%;
display: block;
margin: 1em;
}