227 أسطر
7.6 KiB
JavaScript
227 أسطر
7.6 KiB
JavaScript
'use strict';
|
|
|
|
const Logger = require('./Logger');
|
|
const log = new Logger('Peer');
|
|
|
|
module.exports = class Peer {
|
|
constructor(socket_id, data) {
|
|
this.id = socket_id;
|
|
this.peer_info = data.peer_info;
|
|
this.peer_name = data.peer_info.peer_name;
|
|
this.peer_presenter = data.peer_info.peer_presenter;
|
|
this.peer_audio = data.peer_info.peer_audio;
|
|
this.peer_video = data.peer_info.peer_video;
|
|
this.peer_video_privacy = data.peer_video_privacy;
|
|
this.peer_recording = data.peer_info.peer_recording;
|
|
this.peer_hand = data.peer_info.peer_hand;
|
|
this.transports = new Map();
|
|
this.consumers = new Map();
|
|
this.producers = new Map();
|
|
}
|
|
|
|
// ####################################################
|
|
// UPDATE PEER INFO
|
|
// ####################################################
|
|
|
|
updatePeerInfo(data) {
|
|
log.debug('Update peer info', data);
|
|
switch (data.type) {
|
|
case 'audio':
|
|
case 'audioType':
|
|
this.peer_info.peer_audio = data.status;
|
|
this.peer_audio = data.status;
|
|
break;
|
|
case 'video':
|
|
case 'videoType':
|
|
this.peer_info.peer_video = data.status;
|
|
this.peer_video = data.status;
|
|
if (data.status == false) {
|
|
this.peer_info.peer_video_privacy = data.status;
|
|
this.peer_video_privacy = data.status;
|
|
}
|
|
break;
|
|
case 'screen':
|
|
case 'screenType':
|
|
this.peer_info.peer_screen = data.status;
|
|
break;
|
|
case 'hand':
|
|
this.peer_info.peer_hand = data.status;
|
|
this.peer_hand = data.status;
|
|
break;
|
|
case 'privacy':
|
|
this.peer_info.peer_video_privacy = data.status;
|
|
this.peer_video_privacy = data.status;
|
|
break;
|
|
case 'presenter':
|
|
this.peer_info.peer_presenter = data.status;
|
|
this.peer_presenter = data.status;
|
|
break;
|
|
case 'recording':
|
|
this.peer_info.peer_recording = data.status;
|
|
this.peer_recording = data.status;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ####################################################
|
|
// TRANSPORT
|
|
// ####################################################
|
|
|
|
addTransport(transport) {
|
|
this.transports.set(transport.id, transport);
|
|
}
|
|
|
|
async connectTransport(transport_id, dtlsParameters) {
|
|
if (!this.transports.has(transport_id)) return;
|
|
|
|
await this.transports.get(transport_id).connect({
|
|
dtlsParameters: dtlsParameters,
|
|
});
|
|
}
|
|
|
|
close() {
|
|
this.transports.forEach((transport) => transport.close());
|
|
}
|
|
|
|
// ####################################################
|
|
// PRODUCER
|
|
// ####################################################
|
|
|
|
getProducer(producer_id) {
|
|
return this.producers.get(producer_id);
|
|
}
|
|
|
|
async createProducer(producerTransportId, producer_rtpParameters, producer_kind, producer_type) {
|
|
try {
|
|
if (!producerTransportId) {
|
|
throw new Error('Invalid producer transport ID');
|
|
}
|
|
|
|
const producerTransport = this.transports.get(producerTransportId);
|
|
if (!producerTransport) {
|
|
throw new Error(`Producer transport with ID ${producerTransportId} not found`);
|
|
}
|
|
|
|
const producer = await producerTransport.produce({
|
|
kind: producer_kind,
|
|
rtpParameters: producer_rtpParameters,
|
|
});
|
|
|
|
const { id, appData, type, 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}] created with ID ${id}`, {
|
|
scalabilityMode,
|
|
spatialLayer,
|
|
temporalLayer,
|
|
});
|
|
} else {
|
|
log.debug(`Producer of type ${type} created with ID ${id}`);
|
|
}
|
|
|
|
producer.on('transportclose', () => {
|
|
log.debug('Producer transport closed', {
|
|
peer_name: this.peer_info?.peer_name,
|
|
producer_id: id,
|
|
});
|
|
this.closeProducer(id);
|
|
});
|
|
|
|
return producer;
|
|
} catch (error) {
|
|
log.error('Error creating producer', error.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
closeProducer(producer_id) {
|
|
if (!this.producers.has(producer_id)) return;
|
|
try {
|
|
this.producers.get(producer_id).close();
|
|
} catch (ex) {
|
|
log.warn('Close Producer', ex);
|
|
}
|
|
this.producers.delete(producer_id);
|
|
}
|
|
|
|
// ####################################################
|
|
// CONSUMER
|
|
// ####################################################
|
|
|
|
async createConsumer(consumer_transport_id, producer_id, rtpCapabilities) {
|
|
try {
|
|
const consumerTransport = this.transports.get(consumer_transport_id);
|
|
|
|
if (!consumerTransport) {
|
|
throw new Error(`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 { id, type, kind, rtpParameters, producerPaused } = consumer;
|
|
|
|
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
|
|
await consumer.setPreferredLayers({
|
|
spatialLayer: spatialLayer,
|
|
temporalLayer: temporalLayer,
|
|
});
|
|
log.debug(`Consumer [${type}] ----->`, {
|
|
scalabilityMode,
|
|
spatialLayer,
|
|
temporalLayer,
|
|
});
|
|
} else {
|
|
log.debug('Consumer ----->', { type: type });
|
|
}
|
|
|
|
consumer.on('transportclose', () => {
|
|
log.debug('Consumer transport close', {
|
|
peer_name: this.peer_info?.peer_name,
|
|
consumer_id: id,
|
|
});
|
|
this.removeConsumer(id);
|
|
});
|
|
|
|
this.consumers.set(id, consumer);
|
|
|
|
return {
|
|
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 null;
|
|
}
|
|
}
|
|
|
|
removeConsumer(consumer_id) {
|
|
if (this.consumers.has(consumer_id)) {
|
|
this.consumers.delete(consumer_id);
|
|
}
|
|
}
|
|
};
|