[mirotalksfu] - refactoring

هذا الالتزام موجود في:
Miroslav Pejic
2024-04-27 18:39:14 +02:00
الأصل dbfe4349e5
التزام 37d6aa5501
6 ملفات معدلة مع 192 إضافات و260 حذوفات

عرض الملف

@@ -106,8 +106,8 @@ module.exports = class Peer {
close() {
this.transports.forEach((transport, transport_id) => {
log.debug('Close and delete peer transports', {
transport_id: transport_id,
//transportInternal: transport.internal,
//transport_id: transport_id,
transportInternal: transport.internal,
});
transport.close();
this.delTransport(transport_id);
@@ -131,55 +131,40 @@ module.exports = class Peer {
}
async createProducer(producerTransportId, producer_rtpParameters, producer_kind, producer_type) {
try {
if (!producerTransportId) {
return 'Invalid producer transport ID';
}
if (!this.transports.has(producerTransportId)) return;
const producerTransport = this.transports.get(producerTransportId);
const producerTransport = this.transports.get(producerTransportId);
if (!producerTransport) {
return `Producer transport with ID ${producerTransportId} not found`;
}
const producer = await producerTransport.produce({
kind: producer_kind,
rtpParameters: producer_rtpParameters,
});
const producer = await producerTransport.produce({
kind: producer_kind,
rtpParameters: producer_rtpParameters,
const { id, appData, type, kind, rtpParameters } = producer;
appData.mediaType = producer_type;
this.producers.set(id, producer);
if (['simulcast', 'svc'].includes(type)) {
const { scalabilityMode } = rtpParameters.encodings[0];
const spatialLayer = parseInt(scalabilityMode.substring(1, 2)); // 1/2/3
const temporalLayer = parseInt(scalabilityMode.substring(3, 4)); // 1/2/3
log.debug(`Producer [${type}-${kind}] ----->`, {
scalabilityMode,
spatialLayer,
temporalLayer,
});
if (!producer) {
return `Producer type: ${producer_type} kind: ${producer_kind} not found`;
}
const { id, appData, type, kind, rtpParameters } = producer;
appData.mediaType = producer_type;
this.producers.set(id, producer);
if (['simulcast', 'svc'].includes(type)) {
const { scalabilityMode } = rtpParameters.encodings[0];
const spatialLayer = parseInt(scalabilityMode.substring(1, 2)); // 1/2/3
const temporalLayer = parseInt(scalabilityMode.substring(3, 4)); // 1/2/3
log.debug(`Producer [${type}-${kind}] ----->`, {
scalabilityMode,
spatialLayer,
temporalLayer,
});
} else {
log.debug('Producer ----->', { type: type, kind: kind });
}
producer.on('transportclose', () => {
log.debug('Producer "transportclose" event');
this.closeProducer(id);
});
return producer;
} catch (error) {
log.error('Error creating producer', error.message);
return error.message;
} else {
log.debug('Producer ----->', { type: type, kind: kind });
}
producer.on('transportclose', () => {
log.debug('Producer "transportclose" event');
this.closeProducer(id);
});
return producer;
}
closeProducer(producer_id) {
@@ -222,74 +207,57 @@ module.exports = class Peer {
}
async createConsumer(consumer_transport_id, producer_id, rtpCapabilities) {
try {
if (!consumer_transport_id) {
return 'Invalid consumer transport ID';
}
if (!this.transports.has(consumer_transport_id)) return;
const consumerTransport = this.transports.get(consumer_transport_id);
const consumerTransport = this.transports.get(consumer_transport_id);
if (!consumerTransport) {
return `Consumer transport with id ${consumer_transport_id} not found`;
}
const consumer = await consumerTransport.consume({
producerId: producer_id,
rtpCapabilities,
enableRtx: true, // Enable NACK for OPUS.
paused: false,
});
const consumer = await consumerTransport.consume({
producerId: producer_id,
rtpCapabilities,
enableRtx: true, // Enable NACK for OPUS.
paused: false,
});
const { id, type, kind, rtpParameters, producerPaused } = consumer;
if (!consumer) {
return `Consumer for producer ID ${producer_id} not found`;
}
this.consumers.set(id, consumer);
const { id, type, kind, rtpParameters, producerPaused } = consumer;
this.consumers.set(id, consumer);
if (['simulcast', 'svc'].includes(type)) {
// simulcast - L1T3/L2T3/L3T3 | svc - L3T3
const { scalabilityMode } = rtpParameters.encodings[0];
const spatialLayer = parseInt(scalabilityMode.substring(1, 2)); // 1/2/3
const temporalLayer = parseInt(scalabilityMode.substring(3, 4)); // 1/2/3
try {
await consumer.setPreferredLayers({
spatialLayer: spatialLayer,
temporalLayer: temporalLayer,
});
log.debug(`Consumer [${type}-${kind}] ----->`, {
scalabilityMode,
spatialLayer,
temporalLayer,
});
} catch (error) {
return `Error to set Consumer preferred layers: ${error.message}`;
}
} else {
log.debug('Consumer ----->', { type: type, kind: kind });
}
consumer.on('transportclose', () => {
log.debug('Consumer "transportclose" event');
this.removeConsumer(id);
});
return {
consumer: consumer,
params: {
producerId: producer_id,
id: id,
kind: kind,
rtpParameters: rtpParameters,
type: type,
producerPaused: producerPaused,
},
};
} catch (error) {
log.error('Error creating consumer', error.message);
return error.message;
if (['simulcast', 'svc'].includes(type)) {
// simulcast - L1T3/L2T3/L3T3 | svc - L3T3
const { scalabilityMode } = rtpParameters.encodings[0];
const spatialLayer = parseInt(scalabilityMode.substring(1, 2)); // 1/2/3
const temporalLayer = parseInt(scalabilityMode.substring(3, 4)); // 1/2/3
try {
await consumer.setPreferredLayers({
spatialLayer: spatialLayer,
temporalLayer: temporalLayer,
});
log.debug(`Consumer [${type}-${kind}] ----->`, {
scalabilityMode,
spatialLayer,
temporalLayer,
});
} catch (error) {}
} else {
log.debug('Consumer ----->', { type: type, kind: kind });
}
consumer.on('transportclose', () => {
log.debug('Consumer "transportclose" event');
this.removeConsumer(id);
});
return {
consumer: consumer,
params: {
producerId: producer_id,
id: id,
kind: kind,
rtpParameters: rtpParameters,
type: type,
producerPaused: producerPaused,
},
};
}
removeConsumer(consumer_id) {

عرض الملف

@@ -88,6 +88,7 @@ module.exports = class Room {
}
closeRouter() {
log.debug('Close Room router id', this.router.id);
this.router.close();
}
@@ -204,11 +205,7 @@ module.exports = class Room {
}
getPeer(socket_id) {
//
if (!this.peers.has(socket_id)) {
log.error('---> Peer not found for socket ID', socket_id);
return null;
}
if (!this.peers.has(socket_id)) return;
const peer = this.peers.get(socket_id);
@@ -240,11 +237,9 @@ module.exports = class Room {
}
async removePeer(socket_id) {
const peer = this.getPeer(socket_id);
if (!this.peers.has(socket_id)) return;
if (!peer || typeof peer !== 'object') {
return;
}
const peer = this.getPeer(socket_id);
const { id, peer_name } = peer;
@@ -274,6 +269,8 @@ module.exports = class Room {
// ####################################################
async createWebRtcTransport(socket_id) {
if (!this.peers.has(socket_id)) return;
const { maxIncomingBitrate, initialAvailableOutgoingBitrate, listenInfos } = this.webRtcTransport;
const webRtcTransportOptions = {
@@ -290,7 +287,7 @@ module.exports = class Room {
const transport = await this.router.createWebRtcTransport(webRtcTransportOptions);
if (!transport) {
return this.callback('[Room|createWebRtcTransport] Failed to create WebRTC transport');
throw new Error('Create WebRtc Transport failed!');
}
const { id, iceParameters, iceCandidates, dtlsParameters } = transport;
@@ -298,16 +295,14 @@ module.exports = class Room {
if (maxIncomingBitrate) {
try {
await transport.setMaxIncomingBitrate(maxIncomingBitrate);
} catch (error) {
log.debug('Transport setMaxIncomingBitrate error', error.message);
}
} catch (error) {}
}
const peer = this.getPeer(socket_id);
if (!peer || typeof peer !== 'object') {
return this.callback(`[Room|createWebRtcTransport] Peer object not found for socket ID: ${socket_id}`);
}
peer.addTransport(transport);
log.debug('Transport created', { transportId: id });
const { peer_name } = peer;
@@ -342,13 +337,12 @@ module.exports = class Room {
});
transport.on('close', () => {
log.debug('Transport closed', { peer_name: peer_name, transport_id: transport.id });
log.debug('Transport closed', {
peer_name: peer_name,
transport_id: transport.id,
});
});
peer.addTransport(transport);
log.debug('Transport created', { transportId: id });
return {
id: id,
iceParameters: iceParameters,
@@ -358,28 +352,13 @@ module.exports = class Room {
}
async connectPeerTransport(socket_id, transport_id, dtlsParameters) {
try {
if (!socket_id || !transport_id || !dtlsParameters) {
return this.callback('[Room|connectPeerTransport] Invalid input parameters');
}
if (!this.peers.has(socket_id)) return;
const peer = this.getPeer(socket_id);
const peer = this.getPeer(socket_id);
if (!peer || typeof peer !== 'object') {
return this.callback(`[Room|connectPeerTransport] Peer object not found for socket ID: ${socket_id}`);
}
await peer.connectTransport(transport_id, dtlsParameters);
const connectTransport = await peer.connectTransport(transport_id, dtlsParameters);
if (!connectTransport) {
return this.callback(`[Room|connectPeerTransport] error: Transport with ID ${transport_id} not found`);
}
return '[Room|connectPeerTransport] done';
} catch (error) {
log.error('Error connecting peer transport', error.message);
return this.callback(`[Room|connectPeerTransport] error: ${error.message}`);
}
return '[Room|connectPeerTransport] done';
}
// ####################################################
@@ -387,21 +366,14 @@ module.exports = class Room {
// ####################################################
async produce(socket_id, producerTransportId, rtpParameters, kind, type) {
//
if (!socket_id || !producerTransportId || !rtpParameters || !kind || !type) {
return this.callback('[Room|produce] Invalid input parameters');
}
if (!this.peers.has(socket_id)) return;
const peer = this.getPeer(socket_id);
if (!peer || typeof peer !== 'object') {
return this.callback(`[Room|produce] Peer object not found for socket ID: ${socket_id}`);
}
const peerProducer = await peer.createProducer(producerTransportId, rtpParameters, kind, type);
if (!peerProducer || !peerProducer.id) {
return this.callback(`[Room|produce] Peer producer error: '${peerProducer}'`);
if (!peerProducer) {
throw new Error(`Peer producer kind ${kind} with id ${producerTransportId} not found`);
}
const { id } = peerProducer;
@@ -422,14 +394,10 @@ module.exports = class Room {
}
closeProducer(socket_id, producer_id) {
if (!socket_id || !producer_id) return;
if (!this.peers.has(socket_id)) return;
const peer = this.getPeer(socket_id);
if (!peer || typeof peer !== 'object') {
return;
}
peer.closeProducer(producer_id);
}
@@ -438,53 +406,47 @@ module.exports = class Room {
// ####################################################
async consume(socket_id, consumer_transport_id, producer_id, rtpCapabilities) {
try {
if (!socket_id || !consumer_transport_id || !producer_id || !rtpCapabilities) {
return this.callback('[Room|consume] Invalid input parameters');
}
if (!this.peers.has(socket_id)) return;
if (!this.router.canConsume({ producerId: producer_id, rtpCapabilities })) {
log.warn('Cannot consume', {
socket_id,
consumer_transport_id,
producer_id,
});
return this.callback(`[Room|consume] Room router cannot consume producer_id: '${producer_id}'`);
}
const peer = this.getPeer(socket_id);
if (!peer || typeof peer !== 'object') {
return this.callback(`[Room|consume] Peer object not found for socket ID: ${socket_id}`);
}
const peerConsumer = await peer.createConsumer(consumer_transport_id, producer_id, rtpCapabilities);
if (!peerConsumer || !peerConsumer.consumer || !peerConsumer.params) {
log.debug('peerConsumer or params are not defined');
return this.callback(`[Room|consume] peerConsumer error: '${peerConsumer}'`);
}
const { consumer, params } = peerConsumer;
const { id, kind } = consumer;
consumer.on('producerclose', () => {
log.debug('Consumer closed due to "producerclose" event');
peer.removeConsumer(id);
// Notify the client that consumer is closed
this.send(socket_id, 'consumerClosed', {
consumer_id: id,
consumer_kind: kind,
});
if (
!this.router.canConsume({
producerId: producer_id,
rtpCapabilities,
})
) {
log.warn('Cannot consume', {
socket_id,
consumer_transport_id,
producer_id,
});
return params;
} catch (error) {
log.error('Error occurred during consumption', error.message);
return this.callback(`[Room|consume] ${error.message}`);
return;
}
const peer = this.getPeer(socket_id);
const peerConsumer = await peer.createConsumer(consumer_transport_id, producer_id, rtpCapabilities);
if (!peerConsumer) {
throw new Error(`Peer consumer kind ${kind} with id ${consumer_transport_id} not found`);
}
const { consumer, params } = peerConsumer;
const { id, kind } = consumer;
consumer.on('producerclose', () => {
log.debug('Consumer closed due to "producerclose" event');
peer.removeConsumer(id);
// Notify the client that consumer is closed
this.send(socket_id, 'consumerClosed', {
consumer_id: id,
consumer_kind: kind,
});
});
return params;
}
// ####################################################
@@ -543,14 +505,6 @@ module.exports = class Room {
this._hostOnlyRecording = status;
}
// ####################################################
// ERRORS
// ####################################################
callback(message) {
return { error: message };
}
// ####################################################
// SENDER
// ####################################################

عرض الملف

@@ -41,7 +41,7 @@ dependencies: {
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.4.23
* @version 1.4.24
*
*/
@@ -907,11 +907,9 @@ function startServer() {
log.info('User joined', data);
const { peer_token } = data.peer_info;
const room = roomList.get(socket.room_id);
const { peer_name, peer_id, peer_uuid, os_name, os_version, browser_name, browser_version } =
const { peer_name, peer_id, peer_uuid, peer_token, os_name, os_version, browser_name, browser_version } =
data.peer_info;
let is_presenter = true;
@@ -1063,7 +1061,7 @@ function startServer() {
callback(getRouterRtpCapabilities);
} catch (err) {
log.error('Get RouterRtpCapabilities error', err.message);
log.error('Get RouterRtpCapabilities error', err);
callback({
error: err.message,
});
@@ -1086,7 +1084,7 @@ function startServer() {
callback(createWebRtcTransport);
} catch (err) {
log.error('Create WebRtc Transport error', err.message);
log.error('Create WebRtc Transport error', err);
callback({
error: err.message,
});
@@ -1111,7 +1109,7 @@ function startServer() {
callback(connectTransport);
} catch (err) {
log.error('Connect transport error', err.message);
log.error('Connect transport error', err);
callback({
error: err.message,
});
@@ -1134,7 +1132,9 @@ function startServer() {
try {
const transport = peer.getTransport(transport_id);
if (!transport) throw new Error(`Restart ICE, transport with id "${transport_id}" not found`);
if (!transport) {
throw new Error(`Restart ICE, transport with id "${transport_id}" not found`);
}
const iceParameters = await transport.restartIce();
@@ -1142,7 +1142,7 @@ function startServer() {
callback(iceParameters);
} catch (err) {
log.error('Restart ICE error', err.message);
log.error('Restart ICE error', err);
callback({
error: err.message,
});
@@ -1200,7 +1200,7 @@ function startServer() {
producer_id,
});
} catch (err) {
log.error('Producer transport error', err.message);
log.error('Producer transport error', err);
callback({
error: err.message,
});
@@ -1229,7 +1229,7 @@ function startServer() {
callback(params);
} catch (err) {
log.error('Consumer transport error', err.message);
log.error('Consumer transport error', err);
callback({
error: err.message,
});

عرض الملف

@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
"version": "1.4.23",
"version": "1.4.24",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {
@@ -41,8 +41,8 @@
"node": ">=18"
},
"dependencies": {
"@sentry/integrations": "7.112.1",
"@sentry/node": "7.112.1",
"@sentry/integrations": "7.112.2",
"@sentry/node": "7.112.2",
"axios": "^1.6.8",
"body-parser": "1.20.2",
"colors": "1.4.0",

عرض الملف

@@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.4.23
* @version 1.4.24
*
*/
@@ -1842,6 +1842,7 @@ function handleMediaError(mediaType, err) {
sound('alert');
let errMessage = err;
let getUserMediaError = true;
switch (err.name) {
case 'NotFoundError':
@@ -1864,24 +1865,30 @@ function handleMediaError(mediaType, err) {
errMessage = 'Empty constraints object';
break;
default:
getUserMediaError = false;
break;
}
const $html = `
<ul style="text-align: left">
<li>Media type: ${mediaType}</li>
<li>Error name: ${err.name}</li>
<li>
<p>Error message:</p>
<p style="color: red">${errMessage}</p>
</li>
<li>Common: <a href="https://blog.addpipe.com/common-getusermedia-errors" target="_blank">getUserMedia errors</a></li>
let html = `
<ul style="text-align: left">
<li>Media type: ${mediaType}</li>
<li>Error name: ${err.name}</li>
<li>
<p>Error message:</p>
<p style="color: red">${errMessage}</p>
</li>`;
if (getUserMediaError) {
html += `
<li>Common: <a href="https://blog.addpipe.com/common-getusermedia-errors" target="_blank">getUserMedia errors</a></li>`;
}
html += `
</ul>
`;
const redirectURL = ['screen', 'screenType'].includes(mediaType) ? false : '/';
const redirectURL = ['screen', 'screenType'].includes(mediaType) || !getUserMediaError ? false : '/';
popupHtmlMessage(null, image.forbidden, 'Access denied', $html, 'center', redirectURL);
popupHtmlMessage(null, image.forbidden, 'Access denied', html, 'center', redirectURL);
throw new Error(
`Access denied for ${mediaType} device [${err.name}]: ${errMessage} check the common getUserMedia errors: https://blog.addpipe.com/common-getusermedia-errors/`,

عرض الملف

@@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.4.23
* @version 1.4.24
*
*/
@@ -592,11 +592,9 @@ class RoomClient {
appData,
rtpParameters,
});
if (producer_id.error) {
errback(producer_id.error);
} else {
callback({ id: producer_id });
}
callback({
id: producer_id,
});
} catch (err) {
errback(err);
}
@@ -750,27 +748,27 @@ class RoomClient {
console.log('Restart ICE...');
try {
if (this.producerTransport) {
console.log('Restarting producer transport ICE');
const iceParameters = await this.socket.request('restartIce', {
transport_id: this.producerTransport.id,
});
console.log('Restarting producer transport ICE', iceParameters);
await this.producerTransport.restartIce({ iceParameters });
}
if (this.consumerTransport) {
console.log('Restarting consumer transport ICE');
const iceParameters = await this.socket.request('restartIce', {
transport_id: this.consumerTransport.id,
});
console.log('Restarting consumer transport ICE', iceParameters);
await this.consumerTransport.restartIce({ iceParameters });
}
console.log('Restart ICE done');
} catch (error) {
console.error('Restart ICE error', error.message);
console.error('Restart ICE error', error);
}
}
@@ -976,6 +974,7 @@ class RoomClient {
}
refreshBrowser() {
this.exit(true);
getPeerName() ? location.reload() : openURL(this.getReconnectDirectJoinURL());
}
@@ -1143,11 +1142,9 @@ class RoomClient {
break;
case mediaType.video:
this.isVideoAllowed = true;
if (swapCamera) {
mediaConstraints = this.getCameraConstraints();
} else {
mediaConstraints = this.getVideoConstraints(deviceId);
}
swapCamera
? (mediaConstraints = this.getCameraConstraints())
: (mediaConstraints = this.getVideoConstraints(deviceId));
break;
case mediaType.screen:
mediaConstraints = this.getScreenConstraints();
@@ -1227,7 +1224,10 @@ class RoomClient {
};
}
console.log('PRODUCER PARAMS', params);
console.log('PRODUCER TYPE AND PARAMS', {
type: type,
params: params,
});
const producer = await this.producerTransport.produce(params);
@@ -1235,8 +1235,6 @@ class RoomClient {
throw new Error('Producer not found!');
}
console.log('PRODUCER', producer);
this.producers.set(producer.id, producer);
this.producerLabel.set(type, producer.id);
@@ -1329,6 +1327,7 @@ class RoomClient {
default:
break;
}
this.sound('joined');
return producer;
} catch (err) {
@@ -6742,4 +6741,8 @@ class RoomClient {
}
});
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
} // End