@huddle01/web-core
Advanced tools
Comparing version 1.0.0-alpha.1 to 1.0.0-alpha.2
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
import { Consumer as mediasoupConsumer } from "mediasoup-client/lib/types"; | ||
export type ConsumerEvents = { | ||
playable: [ | ||
data: { | ||
label: string; | ||
consumer: Consumer; | ||
producerPeerId: string; | ||
} | ||
]; | ||
closed: []; | ||
}; | ||
/** | ||
* Consumer class, this class is responsible for consuming the media produced by the remote peers. | ||
* | ||
* Just likes Producer is responsible of producing a media stream in a room, similarly Consumer is responsible for consuming the media stream in a room. | ||
* | ||
* A Peer creates a Producer which takes the stream and produces that stream to all the Joined Remote Peer. | ||
* Remote Peers then make Consumers for that Producer to be able to consume the media stream. | ||
*/ | ||
declare class Consumer extends EnhancedEventEmitter<ConsumerEvents> { | ||
#private; | ||
/** | ||
* ProducerId of the Consumer, this is the id of the Media Entity which is responsible for producing the media in the room. | ||
*/ | ||
readonly producerId: string; | ||
/** | ||
* PeerId of the Producer, this is the peerId of the Peer which is responsible for producing the media in the room. | ||
*/ | ||
readonly producerPeerId: string; | ||
/** | ||
* Label of the Consumer, this is the label of the Media Entity which is responsible for producing the media in the room. | ||
*/ | ||
readonly label: string; | ||
/** | ||
* Flag to check if the Consumer is consuming a media, if `true` then the Consumer is consuming a media. | ||
* | ||
* @default false | ||
*/ | ||
private __consuming; | ||
/** | ||
* Flag to check if the Consumer is consuming a media, if `true` then the Consumer is consuming a media. | ||
* | ||
* @default false | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get consuming(): boolean; | ||
/** | ||
* Getter for the id for the mediaSoupConsumer, which is also the id of the Consumer for the RemotePeer. | ||
*/ | ||
get id(): string | undefined; | ||
/** | ||
* | ||
* @param consumer Sets the mediasoupConsumer for the Consumer | ||
*/ | ||
setMediaSoupConsumer(consumer: mediasoupConsumer): void; | ||
get consuming(): boolean; | ||
/** | ||
* Getter for the mediasoupConsumer id, which is also the id of the Consumer for the RemotePeer. it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get consumerId(): string | undefined; | ||
/** | ||
* Get the Track of the Consumer, it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get track(): MediaStreamTrack | undefined; | ||
/** | ||
* Get the kind of the Consumer, it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get kind(): import("mediasoup-client/lib/RtpParameters").MediaKind | undefined; | ||
/** | ||
* If the Consumer is paused, it is only available when the Consumer is consuming a media. | ||
* | ||
* if paused the user is not consuming any media for the given producerId. | ||
*/ | ||
get paused(): boolean | undefined; | ||
/** | ||
* AppData of the Consumer, it is only available when the Consumer is consuming a media. | ||
*/ | ||
get appData(): import("mediasoup-client/lib/types").AppData | undefined; | ||
/** | ||
* State of a Consumer is defined by the following: | ||
* - `playable` - The Consumer is ready to play the media. | ||
* - `unavailable` - The Consumer is not available to play the media. This can happen when the Consumer is closed or paused. | ||
* - `paused` - The Consumer is paused and is not playing the media. | ||
* - `available` - The Consumer is available to play the media. Peer can consume the media by using `localPeer.consume({ peerId, label: "video", appData: {} });` after which the state will change to `playable`. | ||
*/ | ||
get state(): "playable" | "unavailable" | "paused" | "available"; | ||
/** | ||
* Get the stats of the Consumer, it is only available when the Consumer is consuming a media. | ||
* It generates the stats for the Consumer using the `getStats` method of the mediasoupConsumer. | ||
* @returns - RTCStatsReport | null | ||
*/ | ||
getStats: () => Promise<RTCStatsReport | undefined>; | ||
/** | ||
* Resume the consumer, if the state of the consumer is `paused`. | ||
*/ | ||
resume: () => void; | ||
/** | ||
* Removes all the eventListeners attached to the Consumer. | ||
*/ | ||
removeListeners: () => void; | ||
/** | ||
* Creates a Consumer instance. This is a static method and should be called using `Consumer.create({ producerPeerId, producerId, label })`. | ||
*/ | ||
static create: (data: { | ||
@@ -36,2 +121,3 @@ producerPeerId: string; | ||
}) => Consumer; | ||
close: () => void; | ||
private constructor(); | ||
@@ -38,0 +124,0 @@ } |
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
import { mainLogger } from "./logger"; | ||
const logger = mainLogger.createSubLogger("Consumer"); | ||
/** | ||
* Consumer class, this class is responsible for consuming the media produced by the remote peers. | ||
* | ||
* Just likes Producer is responsible of producing a media stream in a room, similarly Consumer is responsible for consuming the media stream in a room. | ||
* | ||
* A Peer creates a Producer which takes the stream and produces that stream to all the Joined Remote Peer. | ||
* Remote Peers then make Consumers for that Producer to be able to consume the media stream. | ||
*/ | ||
class Consumer extends EnhancedEventEmitter { | ||
/** | ||
* ProducerId of the Consumer, this is the id of the Media Entity which is responsible for producing the media in the room. | ||
*/ | ||
producerId; | ||
/** | ||
* PeerId of the Producer, this is the peerId of the Peer which is responsible for producing the media in the room. | ||
*/ | ||
producerPeerId; | ||
/** | ||
* Label of the Consumer, this is the label of the Media Entity which is responsible for producing the media in the room. | ||
*/ | ||
label; | ||
/** | ||
* Flag to check if the Consumer is consuming a media, if `true` then the Consumer is consuming a media. | ||
* | ||
* @default false | ||
*/ | ||
__consuming = false; | ||
/** | ||
* Flag to check if the Consumer is consuming a media, if `true` then the Consumer is consuming a media. | ||
* | ||
* @default false | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get consuming() { | ||
return this.__consuming; | ||
} | ||
/** | ||
* mediasoupConsumer instance, this is the instance of the mediasoupConsumer which is responsible for consuming the media in the room. | ||
* @remarks This is a private property and should not be accessed directly. | ||
* | ||
* Every Consumer is created without a mediasoupConsumer, when the peer starts to consume the media, the mediasoupConsumer is set. | ||
*/ | ||
#mediasoupConsumer = null; | ||
/** | ||
* Getter for the id for the mediaSoupConsumer, which is also the id of the Consumer for the RemotePeer. | ||
*/ | ||
get id() { | ||
return this.#mediasoupConsumer?.id; | ||
} | ||
/** | ||
* | ||
* @param consumer Sets the mediasoupConsumer for the Consumer | ||
*/ | ||
setMediaSoupConsumer(consumer) { | ||
@@ -23,20 +71,56 @@ if (this.consuming) { | ||
} | ||
get consuming() { | ||
return this.__consuming; | ||
} | ||
/** | ||
* Getter for the mediasoupConsumer id, which is also the id of the Consumer for the RemotePeer. it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get consumerId() { | ||
return this.#mediasoupConsumer?.id; | ||
} | ||
/** | ||
* Get the Track of the Consumer, it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get track() { | ||
return this.#mediasoupConsumer?.track; | ||
} | ||
/** | ||
* Get the kind of the Consumer, it is only available when the Consumer is consuming a media. | ||
* | ||
* @remarks | ||
* There are two ways a Peer can Consume any media produced in the room. | ||
* - Automatically consume the media streams of the remote peers by setting the `autoConsume` flag to `true` in the `Room`. | ||
* - Using the `consume` function inside the `LocalPeer` instance. `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
get kind() { | ||
return this.#mediasoupConsumer?.kind; | ||
} | ||
/** | ||
* If the Consumer is paused, it is only available when the Consumer is consuming a media. | ||
* | ||
* if paused the user is not consuming any media for the given producerId. | ||
*/ | ||
get paused() { | ||
return this.#mediasoupConsumer?.paused; | ||
} | ||
/** | ||
* AppData of the Consumer, it is only available when the Consumer is consuming a media. | ||
*/ | ||
get appData() { | ||
return this.#mediasoupConsumer?.appData; | ||
} | ||
/** | ||
* State of a Consumer is defined by the following: | ||
* - `playable` - The Consumer is ready to play the media. | ||
* - `unavailable` - The Consumer is not available to play the media. This can happen when the Consumer is closed or paused. | ||
* - `paused` - The Consumer is paused and is not playing the media. | ||
* - `available` - The Consumer is available to play the media. Peer can consume the media by using `localPeer.consume({ peerId, label: "video", appData: {} });` after which the state will change to `playable`. | ||
*/ | ||
get state() { | ||
@@ -49,2 +133,7 @@ if (this?.consuming) | ||
} | ||
/** | ||
* Get the stats of the Consumer, it is only available when the Consumer is consuming a media. | ||
* It generates the stats for the Consumer using the `getStats` method of the mediasoupConsumer. | ||
* @returns - RTCStatsReport | null | ||
*/ | ||
getStats = async () => { | ||
@@ -54,8 +143,17 @@ const stats = await this.#mediasoupConsumer?.getStats(); | ||
}; | ||
/** | ||
* Resume the consumer, if the state of the consumer is `paused`. | ||
*/ | ||
resume = () => { | ||
this.#mediasoupConsumer?.resume(); | ||
}; | ||
/** | ||
* Removes all the eventListeners attached to the Consumer. | ||
*/ | ||
removeListeners = () => { | ||
this.removeAllListeners(); | ||
}; | ||
/** | ||
* Creates a Consumer instance. This is a static method and should be called using `Consumer.create({ producerPeerId, producerId, label })`. | ||
*/ | ||
static create = (data) => { | ||
@@ -71,2 +169,17 @@ try { | ||
}; | ||
close = () => { | ||
try { | ||
this.#mediasoupConsumer?.close(); | ||
this.__consuming = false; | ||
this.emit("closed"); | ||
this.removeAllListeners(); | ||
} | ||
catch (error) { | ||
logger.error("❌ Error Closing Consumer"); | ||
logger.error({ | ||
consumerId: this.consumerId, | ||
producerId: this.producerId, | ||
}); | ||
} | ||
}; | ||
constructor(data) { | ||
@@ -73,0 +186,0 @@ super(); |
@@ -28,16 +28,20 @@ import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
"device-change": []; | ||
"stream-added": [data: { | ||
"stream-fetched": [ | ||
data: { | ||
mediaKind: TCustomMediaKind; | ||
stream: MediaStream; | ||
} | ||
]; | ||
"stream-stopped": [data: { | ||
mediaKind: TCustomMediaKind; | ||
stream: MediaStream; | ||
}]; | ||
"stream-closed": [data: { | ||
"screen-fetched": [ | ||
data: { | ||
mediaKind: TCustomMediaKind; | ||
stream: MediaStream; | ||
} | ||
]; | ||
"screen-stopped": [data: { | ||
mediaKind: TCustomMediaKind; | ||
}]; | ||
"screen-added": [data: { | ||
mediaKind: TCustomMediaKind; | ||
stream: MediaStream; | ||
}]; | ||
"screen-closed": [data: { | ||
mediaKind: TCustomMediaKind; | ||
}]; | ||
"permission-granted": [data: { | ||
@@ -44,0 +48,0 @@ deviceKind: TCustomMediaDevice; |
@@ -176,3 +176,3 @@ import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
} | ||
this.emit("screen-added", { | ||
this.emit("screen-fetched", { | ||
mediaKind: "screen", | ||
@@ -258,3 +258,3 @@ stream, | ||
} | ||
this.emit("stream-added", { | ||
this.emit("stream-fetched", { | ||
mediaKind: data.mediaDeviceKind === "mic" ? "mic" : "cam", | ||
@@ -476,3 +476,3 @@ stream, | ||
} | ||
this.emit("stream-closed", { | ||
this.emit("stream-stopped", { | ||
mediaKind: data.mediaDeviceKind, | ||
@@ -487,3 +487,3 @@ }); | ||
} | ||
this.emit("screen-closed", { | ||
this.emit("screen-stopped", { | ||
mediaKind: "screen", | ||
@@ -490,0 +490,0 @@ }); |
@@ -1,7 +0,5 @@ | ||
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
import LocalPeer from "./LocalPeer"; | ||
import Socket from "./Socket"; | ||
import Room from "./Room"; | ||
export type HuddleClientEvents = {}; | ||
declare class HuddleClient extends EnhancedEventEmitter<HuddleClientEvents> { | ||
declare class HuddleClient { | ||
/** | ||
@@ -8,0 +6,0 @@ * Connection Manager Instance, Hanlder socket connection and stores information about the connection |
@@ -1,2 +0,1 @@ | ||
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
import LocalPeer from "./LocalPeer"; | ||
@@ -8,3 +7,3 @@ import { mainLogger } from "./logger"; | ||
const logger = mainLogger.createSubLogger("NezukoClient"); | ||
class HuddleClient extends EnhancedEventEmitter { | ||
class HuddleClient { | ||
/** | ||
@@ -61,3 +60,2 @@ * Connection Manager Instance, Hanlder socket connection and stores information about the connection | ||
logger.info("✅ Initializing HuddleClient"); | ||
super(); | ||
this.projectId = data.projectId; | ||
@@ -64,0 +62,0 @@ const isLocalDev = this.projectId === "development"; |
@@ -1,2 +0,2 @@ | ||
import { AppData, Producer } from "mediasoup-client/lib/types"; | ||
import { AppData, Device, Producer } from "mediasoup-client/lib/types"; | ||
import DeviceHandler from "./DeviceHandler"; | ||
@@ -10,28 +10,11 @@ import Transport from "./Transport"; | ||
export type LocalPeerEvents = { | ||
"join-error": [data: { | ||
message: string; | ||
status: string; | ||
error?: Error; | ||
}]; | ||
"new-send-transport": [data: { | ||
transport: Transport; | ||
}]; | ||
"new-recv-transport": [data: { | ||
transport: Transport; | ||
}]; | ||
"new-producer": [data: { | ||
"stream-playabale": [data: { | ||
label: string; | ||
producer: Producer; | ||
}]; | ||
"produce-error": [data: { | ||
label: string; | ||
error?: unknown; | ||
}]; | ||
"new-consumer": [data: { | ||
label: string; | ||
consumer: Consumer; | ||
}]; | ||
"consume-error": [data: { | ||
label: string; | ||
}]; | ||
"stream-closed": [ | ||
data: { | ||
label: string; | ||
} | ||
]; | ||
"data-message": [data: { | ||
@@ -43,7 +26,2 @@ peerId: string; | ||
"receive-data": [data: TDataMessage]; | ||
"producer-close": [ | ||
data: { | ||
label: string; | ||
} | ||
]; | ||
reconnected: [data: Socket]; | ||
@@ -53,2 +31,11 @@ "metadata-updated": [data: { | ||
}]; | ||
"device-created": [data: { | ||
device: Device; | ||
}]; | ||
"new-send-transport": [data: { | ||
transport: Transport; | ||
}]; | ||
"new-recv-transport": [data: { | ||
transport: Transport; | ||
}]; | ||
}; | ||
@@ -148,3 +135,5 @@ declare class LocalPeer extends EnhancedEventEmitter<LocalPeerEvents> { | ||
admin: boolean; | ||
canConsume: boolean; | ||
canConsume: boolean; /** | ||
* Turn Server used for this client | ||
*/ | ||
canProduce: boolean; | ||
@@ -342,3 +331,3 @@ canProduceSources: ("cam" | "mic" | "screen")[]; | ||
stopConsuming: (data: { | ||
remotePeerId: string; | ||
peerId: string; | ||
label: string; | ||
@@ -345,0 +334,0 @@ }) => void; |
@@ -10,3 +10,2 @@ import { Device, detectDevice, } from "mediasoup-client/lib/types"; | ||
import { EnhancedMap } from "common-js/EnhancedMap"; | ||
import { EnhancedSet } from "common-js/EnhancedSet"; | ||
import Permissions, { checkPermissions } from "./Permissions"; | ||
@@ -90,4 +89,4 @@ const logger = mainLogger.createSubLogger("LocalPeer"); | ||
// Consumer creation tasks awaiting to be processed. | ||
// Stores the lables of the pending consumers { label: string, Promise<Consumer> } | ||
__pendingConsumerTasks = new EnhancedSet({}); | ||
// Stores the lables of the pending consumers { producerId: string ==> Promise<Consumer> } | ||
__pendingConsumerTasks = new Map(); | ||
// !important | ||
@@ -636,3 +635,3 @@ // Consumer creation tasks awaiting to be processed. | ||
}); | ||
this.emit("producer-close", { | ||
this.emit("stream-closed", { | ||
label: data.label, | ||
@@ -752,13 +751,10 @@ }); | ||
} | ||
const consumer = remotePeer._getConsumerWithLabel(data.label); | ||
if (!consumer) { | ||
throw new Error(`Consumer Not Found with Label ${data.label}`); | ||
const labelData = remotePeer.getLabelData(data.label); | ||
if (!labelData) { | ||
throw new Error(`Remote Peer is not producing with Label ${data.label}`); | ||
} | ||
if (consumer.consuming) { | ||
logger.warn(`🔔 Consumer Already Consuming with label ${data.label}, Returning Consumer`); | ||
return consumer; | ||
} | ||
const pendingTask = this.__pendingConsumerTasks.has(data.peerId, data.label); | ||
if (pendingTask) { | ||
const pendingPromise = this.__pendingConsumerTasks.get(labelData?.producerId); | ||
if (pendingPromise) { | ||
logger.warn(`🔔 Consumer Task Pending to be Consumed with label ${data.label}, Returning`); | ||
const consumer = await pendingPromise; | ||
return consumer; | ||
@@ -773,9 +769,28 @@ } | ||
} | ||
this.socket.publish("consume", { | ||
createConsumerData: { | ||
appData: data.appData, | ||
producerId: consumer.producerId, | ||
producerPeerId: consumer.producerPeerId, | ||
}, | ||
const consumerPromise = new Promise((resolve) => { | ||
const handleStreamPlayable = (streamData) => { | ||
if (streamData.label === data.label) { | ||
remotePeer.off("stream-playable", handleStreamPlayable); | ||
resolve(streamData.consumer); | ||
} | ||
}; | ||
remotePeer.on("stream-playable", handleStreamPlayable); | ||
this.socket.publish("consume", { | ||
createConsumerData: { | ||
appData: data.appData, | ||
producerId: labelData.producerId, | ||
producerPeerId: data.peerId, | ||
}, | ||
}); | ||
}); | ||
this.__pendingConsumerTasks.set(labelData.producerId, consumerPromise); | ||
const consumer = await consumerPromise | ||
.catch((error) => { | ||
logger.error("❌ Error Consuming Stream"); | ||
logger.error(error); | ||
throw error; | ||
}) | ||
.finally(() => { | ||
this.__pendingConsumerTasks.delete(labelData.producerId); | ||
}); | ||
return consumer; | ||
@@ -791,5 +806,13 @@ }); | ||
stopConsuming = (data) => { | ||
const remotePeer = this.room.getRemotePeerById(data.remotePeerId); | ||
const consumer = remotePeer._getConsumerWithLabel(data.label); | ||
if (!consumer || !consumer.consuming) { | ||
const remotePeer = this.room.getRemotePeerById(data.peerId); | ||
if (!remotePeer.hasLabel(data.label)) { | ||
logger.error(`❌ You are not Consuming any Stream with Label: ${data.label}`); | ||
return; | ||
} | ||
const consumer = this.recvTransport.getConsumer(data); | ||
if (!consumer) { | ||
logger.error("❌ Consumer Not Found", data); | ||
return; | ||
} | ||
if (!consumer.consuming) { | ||
logger.error("❌ You are not Consuming any Stream, Consumer Not Found"); | ||
@@ -806,4 +829,3 @@ return; | ||
}); | ||
remotePeer._removeConsumer({ producerId: consumer.producerId }); | ||
this.recvTransport.closeConsumer({ producerId: consumer.producerId }); | ||
this.recvTransport.closeConsumer(data); | ||
}; | ||
@@ -883,2 +905,3 @@ sendData = checkPermissions({ | ||
} | ||
this.emit("device-created", { device: this.__device }); | ||
this.joined = true; | ||
@@ -891,6 +914,5 @@ this.__setRemotePeers(roomInfo); | ||
logger.error(error); | ||
this.emit("join-error", { | ||
message: "Room Joined Error", | ||
status: "Room Joined Failed", | ||
error: error, | ||
this.room.emit("room-joined-failed", { | ||
message: "❌ Error Joining Room", | ||
status: "ROOM_ERRORED", | ||
}); | ||
@@ -932,9 +954,9 @@ } | ||
// Handle peer removal | ||
peer.consumers | ||
.filter((consumer) => consumer.consuming) | ||
.forEach((consumer) => { | ||
this.__recvTransport?.closeConsumer({ | ||
producerId: consumer.producerId, | ||
}); | ||
}); | ||
// peer.consumers | ||
// .filter((consumer) => consumer.consuming) | ||
// .forEach((consumer) => { | ||
// this.__recvTransport?.closeConsumer({ | ||
// producerId: consumer.producerId, | ||
// }); | ||
// }); | ||
peer.close(); | ||
@@ -949,23 +971,27 @@ this.__remotePeers.delete(peerId); | ||
return; | ||
const newProducerSet = new Set(latestPeerInfo.producers.map((p) => p.id)); | ||
peer.consumers.forEach((consumer) => { | ||
if (!newProducerSet.has(consumer.producerId)) { | ||
peer._removeConsumer({ producerId: consumer.producerId }); | ||
if (consumer.consuming) { | ||
this.__recvTransport?.closeConsumer({ | ||
producerId: consumer.producerId, | ||
}); | ||
} | ||
} | ||
}); | ||
const currentConsumerSet = new Set(peer.consumers.map((c) => c.producerId)); | ||
latestPeerInfo.producers.forEach((producer) => { | ||
if (!currentConsumerSet.has(producer.id)) { | ||
const consumer = peer._addConsumer({ | ||
producerId: producer.id, | ||
label: producer.label, | ||
}); | ||
this.emit("new-consumer", { consumer, label: consumer.label }); | ||
} | ||
}); | ||
// const newProducerSet = new Set( | ||
// latestPeerInfo.producers.map((p) => p.id), | ||
// ); | ||
// peer.consumers.forEach((consumer) => { | ||
// if (!newProducerSet.has(consumer.producerId)) { | ||
// peer._removeConsumer({ producerId: consumer.producerId }); | ||
// if (consumer.consuming) { | ||
// this.__recvTransport?.closeConsumer({ | ||
// producerId: consumer.producerId, | ||
// }); | ||
// } | ||
// } | ||
// }); | ||
// const currentConsumerSet = new Set( | ||
// peer.consumers.map((c) => c.producerId), | ||
// ); | ||
// latestPeerInfo.producers.forEach((producer) => { | ||
// if (!currentConsumerSet.has(producer.id)) { | ||
// const consumer = peer._addConsumer({ | ||
// producerId: producer.id, | ||
// label: producer.label, | ||
// }); | ||
// this.emit("new-consumer", { consumer, label: consumer.label }); | ||
// } | ||
// }); | ||
}); | ||
@@ -979,3 +1005,3 @@ // Handle new peers | ||
latestPeer.producers.forEach((p) => { | ||
remotePeer._addConsumer({ producerId: p.id, label: p.label }); | ||
remotePeer._addLabelData({ producerId: p.id, label: p.label }); | ||
}); | ||
@@ -1061,10 +1087,6 @@ this.__remotePeers.set(latestPeer.peerId, remotePeer); | ||
const remotePeer = this.room.getRemotePeerById(peerId); | ||
const consumer = remotePeer._addConsumer({ | ||
remotePeer._addLabelData({ | ||
producerId, | ||
label, | ||
}); | ||
this.emit("new-consumer", { | ||
consumer, | ||
label: consumer.label, | ||
}); | ||
} | ||
@@ -1080,30 +1102,25 @@ } | ||
logger.info({ | ||
id: data.consumerId, | ||
label: data.label, | ||
consumerId: data.consumerId, | ||
producerPeerId: data.producerPeerId, | ||
id: data.consumerId, | ||
appData: data.appData, | ||
}); | ||
try { | ||
const remotePeer = this.room.getRemotePeerById(data.producerPeerId); | ||
const consumer = remotePeer._getConsumerWithLabel(data.label); | ||
if (!consumer) { | ||
throw new Error(`❌ Consumer Not Found with label ${data.label} & id ${data.consumerId}`); | ||
if (!remotePeer.hasLabel(data.label)) { | ||
logger.error("❌ Remote Peer is not producing this label", { | ||
label: data.label, | ||
}); | ||
throw new Error(`❌ Remote Peer is not producing this label: ${data.label}`); | ||
} | ||
const mediaSoupConsumer = await this.recvTransport.consume({ | ||
const { consumer, mediaSoupConsumer } = await this.recvTransport.consume(data); | ||
remotePeer.emit("stream-playable", { | ||
consumer, | ||
consumerResponseData: data, | ||
}); | ||
consumer.setMediaSoupConsumer(mediaSoupConsumer); | ||
consumer.emit("playable", { | ||
consumer, | ||
label: consumer.label, | ||
producerPeerId: consumer.producerPeerId, | ||
}); | ||
this.socket.publish("resumeConsumer", { | ||
consumerId: mediaSoupConsumer.id, | ||
consumerId: data.consumerId, | ||
producerPeerId: data.producerPeerId, | ||
}); | ||
mediaSoupConsumer.resume(); | ||
this.__pendingConsumerTasks.delete(data.producerPeerId, data.label); | ||
} | ||
@@ -1113,25 +1130,20 @@ catch (error) { | ||
logger.error(error); | ||
this.__pendingConsumerTasks.delete(data.producerId); | ||
} | ||
}, | ||
resumeConsumerSuccess: async (data) => { | ||
try { | ||
const remotePeer = this.room.getRemotePeerById(data.producerPeerId); | ||
const consumer = remotePeer.getConsumerById(data.consumerId); | ||
consumer.resume(); | ||
} | ||
catch (err) { | ||
logger.error("❌ Error Resuming Consuming"); | ||
logger.error(err); | ||
} | ||
}, | ||
closeProducerSuccess: async (data) => { | ||
if (this.peerId === data.peerId) { | ||
if (this.peerId === data.peerId) | ||
return; | ||
} | ||
logger.info("✅ Producer Closed", data); | ||
const { peerId, producerId } = data; | ||
const { peerId, label } = data; | ||
try { | ||
const remotePeer = this.room.getRemotePeerById(peerId); | ||
remotePeer._removeConsumer({ producerId }); | ||
this.recvTransport.closeConsumer({ producerId }); | ||
const consumer = this.recvTransport.getConsumer({ | ||
label, | ||
peerId, | ||
}); | ||
if (consumer) { | ||
this.recvTransport.closeConsumer({ label, peerId }); | ||
remotePeer._removeLabelData(label); | ||
} | ||
} | ||
@@ -1146,2 +1158,19 @@ catch (err) { | ||
}, | ||
restartTransportIceResponse: async (data) => { | ||
logger.info("✅ Restart Transport Ice Response"); | ||
const { transportType, iceParameters } = data; | ||
const transport = transportType === "send" ? this.__sendTransport : this.__recvTransport; | ||
if (!transport) { | ||
logger.error(`❌ ${transportType} Transport Not Found`); | ||
return; | ||
} | ||
try { | ||
await transport.mediasoupTransport.restartIce({ iceParameters }); | ||
logger.info("✅ Restarted Ice for type: ", transportType); | ||
} | ||
catch (error) { | ||
logger.error("❌ Error Restarting Ice for type: ", transportType); | ||
logger.error(error); | ||
} | ||
}, | ||
newPeerJoined: (data) => { | ||
@@ -1231,3 +1260,3 @@ if (this.peerId === data.peerId) | ||
const remotePeer = this.room.getRemotePeerById(peerId); | ||
remotePeer._updateMetadata(metadata); | ||
remotePeer.metadata = metadata; | ||
} | ||
@@ -1254,11 +1283,21 @@ catch (error) { | ||
const { peerId } = data; | ||
const remotePeer = this.__remotePeers.get(peerId); | ||
if (!remotePeer) { | ||
logger.error("❌ No Remote Peer with peerId, ", peerId); | ||
return; | ||
} | ||
const activeConsumers = remotePeer.consumers.filter((consumer) => consumer.consuming); | ||
const remotePeer = this.room.getRemotePeerById(peerId); | ||
const labels = remotePeer.labels; | ||
const activeConsumers = []; | ||
labels.forEach((label) => { | ||
const consumer = this.recvTransport.getConsumer({ | ||
label, | ||
peerId: remotePeer.peerId, | ||
}); | ||
if (!consumer) { | ||
logger.error("❌ Consumer Not Found, label", label); | ||
return; | ||
} | ||
if (consumer.consuming) | ||
activeConsumers.push(consumer); | ||
}); | ||
activeConsumers?.map((consumer) => { | ||
this.__recvTransport?.closeConsumer({ | ||
producerId: consumer.producerId, | ||
this.recvTransport.closeConsumer({ | ||
label: consumer.label, | ||
peerId: consumer.producerPeerId, | ||
}); | ||
@@ -1300,3 +1339,3 @@ }); | ||
}); | ||
this.emit("new-producer", { | ||
this.emit("stream-playabale", { | ||
label: data.label, | ||
@@ -1352,3 +1391,3 @@ producer, | ||
producers.map((p) => { | ||
remotePeer._addConsumer({ | ||
remotePeer._addLabelData({ | ||
producerId: p.id, | ||
@@ -1355,0 +1394,0 @@ label: p.label, |
import Consumer from "./Consumer"; | ||
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
type TRemotePeerEvents = { | ||
"new-consumer": [ | ||
"stream-available": [ | ||
data: { | ||
label: string; | ||
remotePeer: RemotePeer; | ||
labelData: { | ||
producerId: string; | ||
}; | ||
} | ||
]; | ||
"stream-playable": [ | ||
data: { | ||
label: string; | ||
consumer: Consumer; | ||
} | ||
]; | ||
"consumer-closed": [ | ||
"stream-closed": [ | ||
data: { | ||
label: string; | ||
remotePeer: RemotePeer; | ||
} | ||
@@ -29,6 +35,2 @@ ]; | ||
/** | ||
* Map of producerId to consumer | ||
*/ | ||
private __consumers; | ||
/** | ||
* Stores the Metadata for the Remote Peer. | ||
@@ -38,10 +40,31 @@ */ | ||
/** | ||
* Map of label to consumer | ||
* Labels are the unique identifier for the media stream that the remote peer is producing | ||
*/ | ||
readonly labelToConsumer: Map<string, Consumer>; | ||
private readonly __labelsToProducerId; | ||
/** | ||
* Get all the consumers of the remote peer | ||
* Returns the list of labels that the remote peer is producing | ||
*/ | ||
get consumers(): Consumer[]; | ||
get labels(): string[]; | ||
/** | ||
* Checks if the remote peer is producing the label | ||
* @param label - Label to check if the remote peer is producing | ||
* @returns - Returns true if the remote peer is producing the label | ||
*/ | ||
hasLabel(label: string): boolean; | ||
/** | ||
* Returns the data associated to the label, this is the producerId | ||
* | ||
* @returns | ||
* producerId - Unique identifier for the producer | ||
*/ | ||
getLabelData(label: string): { | ||
producerId: string; | ||
} | undefined; | ||
/** | ||
* Get the associated consumer for the label | ||
* @param label - Unique identifier for the consumer e.g. `video` | `audio` | `screen-video` | string | ||
* @returns Consumer | null | ||
*/ | ||
getConsumer(label: string): Consumer | null; | ||
/** | ||
* Returns the metadata associated to the RemotePeer | ||
@@ -51,14 +74,8 @@ */ | ||
/** | ||
* Send Message to update the metadata of the Local Peer | ||
* Setter function to update the Remote Peer Metadata | ||
* | ||
* `NOTE: This will notify every user in the room about the metadata update` | ||
* `NOTE: This will NOT notify other Remote Peers of the update` | ||
*/ | ||
_updateMetadata: (data: string) => void; | ||
set metadata(data: string); | ||
/** | ||
* Get the Consumer with the given label, if nothing is found return null | ||
* @param label - Label of the consumer (e.g. video, audio, screen-share); | ||
* @returns - Consumer | null | ||
*/ | ||
_getConsumerWithLabel: (label: string) => Consumer | null; | ||
/** | ||
* Removes all the states of the remote peer and clears memory; | ||
@@ -73,42 +90,24 @@ * | ||
/** | ||
* @private | ||
* @protected | ||
* Add a New Label to the Remote Peer and associate it with the ProducerId | ||
* | ||
* Adds a new consumer to the remote peer, this is called when the local peer receives a new consumer event from the server | ||
* @param data - Data of the consumer | ||
* - producerId - ProducerId of the producer | ||
* - label - Label of the consumer | ||
* `NOTE: This is used internally by the Peer` | ||
* | ||
* @returns - Consumer | ||
* | ||
* @summary When a Peer in the meeting is producing any media to the room say camera then every peer in the room is notified about the new producer | ||
* in the room and its upto the other peers to decide to consume the producer or not | ||
* This function adds a new consumer to the remote peer and emits a new-consumer event | ||
* | ||
* But when the localPeer uses the consume function then we actually create a mediaConsumer and add it to the remotePeer | ||
* mediaConsumer is created by the transport only no other entity has that authority. | ||
* | ||
* @param data - Data to add the new label `label` and the `producerId` to associate it with | ||
*/ | ||
_addConsumer: (data: { | ||
_addLabelData: (data: { | ||
label: string; | ||
producerId: string; | ||
label: string; | ||
}) => Consumer; | ||
getConsumerById: (consumerId: string) => Consumer; | ||
}) => void; | ||
/** | ||
* @private | ||
* @protected | ||
* Remove a Label from the Remote Peer and emit a `stream-closed` event | ||
* | ||
* Removed the consumer entity from the remote peer, | ||
* `NOTE: This is used internally by the Peer` | ||
* | ||
* `NOTE`: This method is called when the local peer receives a consumer closed event from the server | ||
* to also close the mediaConsumer of the local peer we need to call the `recvTransport.closeConsumer` method | ||
* | ||
* @param data - Data of the consumer | ||
* - producerId - ProducerId of the producer | ||
* | ||
* @returns - void | ||
* @param data - Data to remove the label from the Remote Peer | ||
*/ | ||
_removeConsumer: (data: { | ||
producerId: string; | ||
}) => void; | ||
_removeLabelData: (label: string) => void; | ||
} | ||
export default RemotePeer; | ||
//# sourceMappingURL=RemotePeer.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import Consumer from "./Consumer"; | ||
import LocalPeer from "./LocalPeer"; | ||
import { mainLogger } from "./logger"; | ||
@@ -11,6 +11,2 @@ import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
/** | ||
* Map of producerId to consumer | ||
*/ | ||
__consumers = new Map(); | ||
/** | ||
* Stores the Metadata for the Remote Peer. | ||
@@ -20,12 +16,47 @@ */ | ||
/** | ||
* Map of label to consumer | ||
* Labels are the unique identifier for the media stream that the remote peer is producing | ||
*/ | ||
labelToConsumer = new Map(); | ||
__labelsToProducerId = new Map(); | ||
/** | ||
* Get all the consumers of the remote peer | ||
* Returns the list of labels that the remote peer is producing | ||
*/ | ||
get consumers() { | ||
return Array.from(this.__consumers.values()); | ||
get labels() { | ||
return Array.from(this.__labelsToProducerId.keys()); | ||
} | ||
/** | ||
* Checks if the remote peer is producing the label | ||
* @param label - Label to check if the remote peer is producing | ||
* @returns - Returns true if the remote peer is producing the label | ||
*/ | ||
hasLabel(label) { | ||
return this.__labelsToProducerId.has(label); | ||
} | ||
/** | ||
* Returns the data associated to the label, this is the producerId | ||
* | ||
* @returns | ||
* producerId - Unique identifier for the producer | ||
*/ | ||
getLabelData(label) { | ||
return this.__labelsToProducerId.get(label); | ||
} | ||
/** | ||
* Get the associated consumer for the label | ||
* @param label - Unique identifier for the consumer e.g. `video` | `audio` | `screen-video` | string | ||
* @returns Consumer | null | ||
*/ | ||
getConsumer(label) { | ||
try { | ||
const localPeer = LocalPeer.getInstance(); | ||
const consumer = localPeer.recvTransport.getConsumer({ | ||
label, | ||
peerId: this.peerId, | ||
}); | ||
return consumer; | ||
} | ||
catch (error) { | ||
return null; | ||
} | ||
} | ||
/** | ||
* Returns the metadata associated to the RemotePeer | ||
@@ -38,7 +69,7 @@ */ | ||
/** | ||
* Send Message to update the metadata of the Local Peer | ||
* Setter function to update the Remote Peer Metadata | ||
* | ||
* `NOTE: This will notify every user in the room about the metadata update` | ||
* `NOTE: This will NOT notify other Remote Peers of the update` | ||
*/ | ||
_updateMetadata = (data) => { | ||
set metadata(data) { | ||
const newMetadata = JSON.stringify(data); | ||
@@ -49,16 +80,4 @@ this.__metadata = newMetadata; | ||
}); | ||
}; | ||
} | ||
/** | ||
* Get the Consumer with the given label, if nothing is found return null | ||
* @param label - Label of the consumer (e.g. video, audio, screen-share); | ||
* @returns - Consumer | null | ||
*/ | ||
_getConsumerWithLabel = (label) => { | ||
const consumer = this.labelToConsumer.get(label); | ||
if (!consumer) { | ||
return null; | ||
} | ||
return consumer; | ||
}; | ||
/** | ||
* Removes all the states of the remote peer and clears memory; | ||
@@ -70,4 +89,2 @@ * | ||
logger.info("Closing Remote Peer"); | ||
this.__consumers.clear(); | ||
this.labelToConsumer.clear(); | ||
this.removeAllListeners(); | ||
@@ -80,70 +97,34 @@ }; | ||
/** | ||
* @private | ||
* @protected | ||
* Add a New Label to the Remote Peer and associate it with the ProducerId | ||
* | ||
* Adds a new consumer to the remote peer, this is called when the local peer receives a new consumer event from the server | ||
* @param data - Data of the consumer | ||
* - producerId - ProducerId of the producer | ||
* - label - Label of the consumer | ||
* `NOTE: This is used internally by the Peer` | ||
* | ||
* @returns - Consumer | ||
* | ||
* @summary When a Peer in the meeting is producing any media to the room say camera then every peer in the room is notified about the new producer | ||
* in the room and its upto the other peers to decide to consume the producer or not | ||
* This function adds a new consumer to the remote peer and emits a new-consumer event | ||
* | ||
* But when the localPeer uses the consume function then we actually create a mediaConsumer and add it to the remotePeer | ||
* mediaConsumer is created by the transport only no other entity has that authority. | ||
* | ||
* @param data - Data to add the new label `label` and the `producerId` to associate it with | ||
*/ | ||
_addConsumer = (data) => { | ||
const consumer = Consumer.create({ | ||
label: data.label, | ||
producerId: data.producerId, | ||
producerPeerId: this.peerId, | ||
_addLabelData = (data) => { | ||
const { label, producerId } = data; | ||
this.__labelsToProducerId.set(label, { producerId }); | ||
this.emit("stream-available", { | ||
label, | ||
labelData: { | ||
producerId, | ||
}, | ||
}); | ||
this.__consumers.set(consumer.producerId, consumer); | ||
this.labelToConsumer.set(consumer.label, consumer); | ||
this.emit("new-consumer", { | ||
consumer, | ||
label: consumer.label, | ||
remotePeer: this, | ||
}); | ||
return consumer; | ||
}; | ||
getConsumerById = (consumerId) => { | ||
const consumer = this.__consumers.get(consumerId); | ||
if (!consumer) { | ||
throw new Error("❌ Consumer Not Found"); | ||
} | ||
return consumer; | ||
}; | ||
/** | ||
* @private | ||
* @protected | ||
* Remove a Label from the Remote Peer and emit a `stream-closed` event | ||
* | ||
* Removed the consumer entity from the remote peer, | ||
* `NOTE: This is used internally by the Peer` | ||
* | ||
* `NOTE`: This method is called when the local peer receives a consumer closed event from the server | ||
* to also close the mediaConsumer of the local peer we need to call the `recvTransport.closeConsumer` method | ||
* | ||
* @param data - Data of the consumer | ||
* - producerId - ProducerId of the producer | ||
* | ||
* @returns - void | ||
* @param data - Data to remove the label from the Remote Peer | ||
*/ | ||
_removeConsumer = (data) => { | ||
const { producerId } = data; | ||
const consumer = this.__consumers.get(producerId); | ||
if (!consumer) { | ||
logger.error("❌ Consumer not found"); | ||
return; | ||
} | ||
this.__consumers.delete(consumer.producerId); | ||
this.labelToConsumer.delete(consumer.label); | ||
this.emit("consumer-closed", { | ||
label: consumer.label, | ||
remotePeer: this, | ||
_removeLabelData = (label) => { | ||
this.__labelsToProducerId.delete(label); | ||
this.emit("stream-closed", { | ||
label, | ||
}); | ||
return; | ||
}; | ||
} | ||
export default RemotePeer; |
@@ -10,3 +10,3 @@ import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
message: string; | ||
status: "DENIED" | "ROOM_CLOSED" | "ROOM_NOT_FOUND"; | ||
status: "DENIED" | "ROOM_CLOSED" | "ROOM_NOT_FOUND" | "ROOM_ERRORED"; | ||
} | ||
@@ -36,2 +36,6 @@ ]; | ||
/** | ||
* Returns the instance of the socket connection | ||
*/ | ||
private get socket(); | ||
/** | ||
* Room Id of the current room | ||
@@ -45,2 +49,37 @@ */ | ||
/** | ||
* Removed Lobby PeerId from the lobby | ||
* @param peerId - PeerId of the peer who joined the room | ||
*/ | ||
private removeLobbyPeer; | ||
/** | ||
* Room Config Object | ||
* - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
* - `allowConsume`: Allow non-admin Peers in the Room to consume Media Streams | ||
* - `allowSendData`: Allow non-admin Peers in the Room to send data message | ||
* - `roomLocked`: If the room is locked | ||
*/ | ||
private __config; | ||
/** | ||
* Auto consume flag, if set to true, Peers Joining the Room will automatically consume the media streams of the remote peers | ||
* | ||
* @default true | ||
* | ||
* @remarks - This flag is used by the `useRoom` hook to automatically consume the media streams of the remote peers, | ||
* - if set to false, the user will have to manually consume the media streams of the remote peers | ||
* using the `consume` method of the `LocalPeer` instance `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
readonly __autoConsume = true; | ||
/** | ||
* State of the Room | ||
*/ | ||
private __state; | ||
/** | ||
* Set the state of the room | ||
*/ | ||
private set state(value); | ||
/** | ||
* State of the room | ||
*/ | ||
get state(): RoomStates; | ||
/** | ||
* Get the Lobby Peers in the form of Set | ||
@@ -61,7 +100,2 @@ * @retruns - Set of Lobby PeerIds | ||
/** | ||
* Removed Lobby PeerId from the lobby | ||
* @param peerId - PeerId of the peer who joined the room | ||
*/ | ||
private removeLobbyPeer; | ||
/** | ||
* Room Config Object | ||
@@ -73,10 +107,2 @@ * - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
*/ | ||
private __config; | ||
/** | ||
* Room Config Object | ||
* - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
* - `allowConsume`: Allow non-admin Peers in the Room to consume Media Streams | ||
* - `allowSendData`: Allow non-admin Peers in the Room to send data message | ||
* - `roomLocked`: If the room is locked | ||
*/ | ||
get config(): TRoomInfo["config"]; | ||
@@ -89,14 +115,2 @@ set config(config: TRoomInfo["config"]); | ||
/** | ||
* State of the Room | ||
*/ | ||
private __state; | ||
/** | ||
* State of the room | ||
*/ | ||
get state(): RoomStates; | ||
/** | ||
* Set the state of the room | ||
*/ | ||
private set state(value); | ||
/** | ||
* Returns the Room Instance, if the instance is not initialized, it will initialize the instance and return it | ||
@@ -115,6 +129,2 @@ * | ||
/** | ||
* Returns the instance of the socket connection | ||
*/ | ||
private get socket(); | ||
/** | ||
* RoomId of the currently joined room. | ||
@@ -121,0 +131,0 @@ */ |
102
dist/Room.js
@@ -16,2 +16,8 @@ import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
/** | ||
* Returns the instance of the socket connection | ||
*/ | ||
get socket() { | ||
return this.__socket; | ||
} | ||
/** | ||
* Room Id of the current room | ||
@@ -25,2 +31,52 @@ */ | ||
/** | ||
* Removed Lobby PeerId from the lobby | ||
* @param peerId - PeerId of the peer who joined the room | ||
*/ | ||
removeLobbyPeer = (peerId) => { | ||
this.__lobbyPeerIds.delete(peerId); | ||
this.emit("lobby-peers-updated", this.lobbyPeerIds); | ||
}; | ||
/** | ||
* Room Config Object | ||
* - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
* - `allowConsume`: Allow non-admin Peers in the Room to consume Media Streams | ||
* - `allowSendData`: Allow non-admin Peers in the Room to send data message | ||
* - `roomLocked`: If the room is locked | ||
*/ | ||
__config = { | ||
roomLocked: false, | ||
allowProduce: true, | ||
allowProduceSources: ["cam", "mic", "screen"], | ||
allowConsume: true, | ||
allowSendData: true, | ||
}; | ||
/** | ||
* Auto consume flag, if set to true, Peers Joining the Room will automatically consume the media streams of the remote peers | ||
* | ||
* @default true | ||
* | ||
* @remarks - This flag is used by the `useRoom` hook to automatically consume the media streams of the remote peers, | ||
* - if set to false, the user will have to manually consume the media streams of the remote peers | ||
* using the `consume` method of the `LocalPeer` instance `localPeer.consume({ peerId, label: "video", appData: {} });` | ||
*/ | ||
__autoConsume = true; | ||
/** | ||
* State of the Room | ||
*/ | ||
__state = "idle"; | ||
/** | ||
* Set the state of the room | ||
*/ | ||
set state(newState) { | ||
if (this.state !== newState) { | ||
this.__state = newState; | ||
} | ||
} | ||
/** | ||
* State of the room | ||
*/ | ||
get state() { | ||
return this.__state; | ||
} | ||
/** | ||
* Get the Lobby Peers in the form of Set | ||
@@ -51,10 +107,2 @@ * @retruns - Set of Lobby PeerIds | ||
/** | ||
* Removed Lobby PeerId from the lobby | ||
* @param peerId - PeerId of the peer who joined the room | ||
*/ | ||
removeLobbyPeer = (peerId) => { | ||
this.__lobbyPeerIds.delete(peerId); | ||
this.emit("lobby-peers-updated", this.lobbyPeerIds); | ||
}; | ||
/** | ||
* Room Config Object | ||
@@ -66,16 +114,2 @@ * - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
*/ | ||
__config = { | ||
roomLocked: false, | ||
allowProduce: true, | ||
allowProduceSources: ["cam", "mic", "screen"], | ||
allowConsume: true, | ||
allowSendData: true, | ||
}; | ||
/** | ||
* Room Config Object | ||
* - `allowProduce`: Allow non-admin Peers in the Room to produce Media Streams | ||
* - `allowConsume`: Allow non-admin Peers in the Room to consume Media Streams | ||
* - `allowSendData`: Allow non-admin Peers in the Room to send data message | ||
* - `roomLocked`: If the room is locked | ||
*/ | ||
get config() { | ||
@@ -92,20 +126,2 @@ return this.__config; | ||
/** | ||
* State of the Room | ||
*/ | ||
__state = "idle"; | ||
/** | ||
* State of the room | ||
*/ | ||
get state() { | ||
return this.__state; | ||
} | ||
/** | ||
* Set the state of the room | ||
*/ | ||
set state(newState) { | ||
if (this.state !== newState) { | ||
this.__state = newState; | ||
} | ||
} | ||
/** | ||
* Returns the Room Instance, if the instance is not initialized, it will initialize the instance and return it | ||
@@ -135,8 +151,2 @@ * | ||
/** | ||
* Returns the instance of the socket connection | ||
*/ | ||
get socket() { | ||
return this.__socket; | ||
} | ||
/** | ||
* RoomId of the currently joined room. | ||
@@ -143,0 +153,0 @@ */ |
@@ -9,2 +9,3 @@ import * as mediasoup from "mediasoup-client"; | ||
import { TConsumeResponseData } from "types/dist/mq/response.types"; | ||
import { EnhancedMap } from "common-js/EnhancedMap"; | ||
export type TransportEvents = { | ||
@@ -33,3 +34,3 @@ connectTransportResponse: []; | ||
* | ||
* `Mapped with {producerId: Producer}` | ||
* `Mapped with {producerId => Producer}` | ||
*/ | ||
@@ -40,3 +41,3 @@ private readonly __producers; | ||
* | ||
* `Mapped with {producerId: Consumer}` | ||
* `Mapped with {label:RemotePeerId => Consumer}` | ||
*/ | ||
@@ -61,2 +62,7 @@ private readonly __consumers; | ||
/** | ||
* Debounce to handle concurrent request to restart Ice. Waits for some time before sending | ||
* more requests to restart ice. | ||
*/ | ||
private __iceRestartDebounce; | ||
/** | ||
* Connection State, which handles the connection state. | ||
@@ -73,7 +79,15 @@ * | ||
get mediasoupTransport(): mediasoup.types.Transport<mediasoup.types.AppData>; | ||
get producers(): Map<string, mediasoup.types.Producer<mediasoup.types.AppData>>; | ||
get consumers(): EnhancedMap<Consumer>; | ||
getProducerById(producerId: string): Producer; | ||
removeProducerById(producerId: string): boolean; | ||
getConsumerByProducerId(producerId: string): Consumer; | ||
private static createSendTransport; | ||
private static createRecvTransport; | ||
/** | ||
* Get the consumer by label and peerId | ||
* @param data | ||
* @returns Consumer | null; Returns null if consumer is not found | ||
*/ | ||
getConsumer: (data: { | ||
label: string; | ||
peerId: string; | ||
}) => Consumer | null; | ||
get transport(): mediasoup.types.Transport; | ||
@@ -111,8 +125,9 @@ addPendingProducerTask: (data: { | ||
}>>; | ||
consume: (data: { | ||
consume: (data: TConsumeResponseData) => Promise<{ | ||
consumer: Consumer; | ||
consumerResponseData: TConsumeResponseData; | ||
}) => Promise<mediasoup.types.Consumer<mediasoup.types.AppData>>; | ||
mediaSoupConsumer: mediasoup.types.Consumer<mediasoup.types.AppData>; | ||
}>; | ||
closeConsumer: (data: { | ||
producerId: string; | ||
label: string; | ||
peerId: string; | ||
}) => void; | ||
@@ -122,4 +137,5 @@ close: (data: { | ||
}) => Promise<void>; | ||
private __connectionStateChangeHandler; | ||
} | ||
export default Transport; | ||
//# sourceMappingURL=Transport.d.ts.map |
import { EnhancedEventEmitter } from "common-js/EnhancedEventEmitter"; | ||
import { mainLogger } from "./logger"; | ||
import Socket from "./Socket"; | ||
import Consumer from "./Consumer"; | ||
import { getMediaStreamKind, getMediaTrack } from "./helpers"; | ||
import { codecOptionsViaKind, encodingViaMediaType, } from "./constants/rtpConstants"; | ||
import { EnhancedMap } from "common-js/EnhancedMap"; | ||
const logger = mainLogger.createSubLogger("Transport"); | ||
@@ -27,3 +29,3 @@ class Transport extends EnhancedEventEmitter { | ||
* | ||
* `Mapped with {producerId: Producer}` | ||
* `Mapped with {producerId => Producer}` | ||
*/ | ||
@@ -34,5 +36,5 @@ __producers = new Map(); | ||
* | ||
* `Mapped with {producerId: Consumer}` | ||
* `Mapped with {label:RemotePeerId => Consumer}` | ||
*/ | ||
__consumers = new Map(); | ||
__consumers = new EnhancedMap({}); | ||
/** | ||
@@ -55,2 +57,7 @@ * Map of Identifiers to Producer Ids, which handles the mapping of identifiers to producer ids. | ||
/** | ||
* Debounce to handle concurrent request to restart Ice. Waits for some time before sending | ||
* more requests to restart ice. | ||
*/ | ||
__iceRestartDebounce = false; | ||
/** | ||
* Connection State, which handles the connection state. | ||
@@ -71,2 +78,8 @@ * | ||
} | ||
get producers() { | ||
return this.__producers; | ||
} | ||
get consumers() { | ||
return this.__consumers; | ||
} | ||
getProducerById(producerId) { | ||
@@ -82,16 +95,13 @@ const producer = this.__producers.get(producerId); | ||
} | ||
getConsumerByProducerId(producerId) { | ||
const consumer = this.__consumers.get(producerId); | ||
/** | ||
* Get the consumer by label and peerId | ||
* @param data | ||
* @returns Consumer | null; Returns null if consumer is not found | ||
*/ | ||
getConsumer = (data) => { | ||
const consumer = this.__consumers.get(data.label, data.peerId); | ||
if (!consumer) | ||
throw new Error("❌ Consumer Not Found"); | ||
return null; | ||
return consumer; | ||
} | ||
static createSendTransport = (data) => { | ||
const sendTransport = data.device.createSendTransport(data.transportOptions); | ||
return sendTransport; | ||
}; | ||
static createRecvTransport = (data) => { | ||
const recvTransport = data.device.createRecvTransport(data.transportOptions); | ||
return recvTransport; | ||
}; | ||
get transport() { | ||
@@ -126,2 +136,3 @@ const transport = this.__mediasoupTransport; | ||
logger.info(`🔔 Creating Client Side Transport, type: ${data.transportType}`); | ||
const { transportType, device } = data; | ||
const payload = { | ||
@@ -137,11 +148,5 @@ id: data.sdpInfo.id, | ||
}; | ||
const mediaSoupTransport = data.transportType === "send" | ||
? Transport.createSendTransport({ | ||
device: data.device, | ||
transportOptions: payload, | ||
}) | ||
: Transport.createRecvTransport({ | ||
device: data.device, | ||
transportOptions: payload, | ||
}); | ||
const mediasoupTransport = transportType === "send" | ||
? device.createSendTransport(payload) | ||
: device.createRecvTransport(payload); | ||
const transport = new Transport({ | ||
@@ -151,3 +156,3 @@ peerId: data.peerId, | ||
transportType: data.transportType, | ||
mediasoupTransport: mediaSoupTransport, | ||
mediasoupTransport, | ||
}); | ||
@@ -169,2 +174,5 @@ return transport; | ||
this.__mediasoupTransport = data.mediasoupTransport; | ||
this.__mediasoupTransport.on("connectionstatechange", (state) => { | ||
this.__connectionStateChangeHandler(state); | ||
}); | ||
this.peerId = data.peerId; | ||
@@ -274,5 +282,13 @@ this.__listenTransportConnect(); | ||
consume = async (data) => { | ||
const { consumer, consumerResponseData } = data; | ||
logger.info(`🔔 Consume Called for ${consumerResponseData.kind} from remote peer ${consumerResponseData.producerPeerId}`); | ||
const { label, producerPeerId, kind } = data; | ||
logger.info(`🔔 Consume Called for ${kind} from remote peer ${producerPeerId}`); | ||
try { | ||
if (this.transportType !== "recv") { | ||
throw new Error(`Cannot consume on ${this.transportType} transport`); | ||
} | ||
const consumer = Consumer.create({ | ||
producerId: data.producerId, | ||
producerPeerId, | ||
label, | ||
}); | ||
if (!this.__device.loaded) { | ||
@@ -285,16 +301,17 @@ throw new Error("Device Not Loaded"); | ||
const mediaSoupConsumer = await this.__mediasoupTransport.consume({ | ||
id: consumerResponseData.consumerId, | ||
rtpParameters: consumerResponseData.rtpParameters, | ||
appData: consumerResponseData.appData, | ||
kind: consumerResponseData.kind, | ||
producerId: consumerResponseData.producerId, | ||
id: data.consumerId, | ||
rtpParameters: data.rtpParameters, | ||
kind: data.kind, | ||
producerId: data.producerId, | ||
appData: data.appData, | ||
}); | ||
mediaSoupConsumer.on("transportclose", () => { | ||
this.closeConsumer({ producerId: consumerResponseData.producerId }); | ||
this.closeConsumer({ label, peerId: producerPeerId }); | ||
}); | ||
mediaSoupConsumer.on("trackended", () => { | ||
this.closeConsumer({ producerId: consumerResponseData.producerId }); | ||
this.closeConsumer({ label, peerId: producerPeerId }); | ||
}); | ||
this.__consumers.set(consumer.producerId, consumer); | ||
return mediaSoupConsumer; | ||
this.__consumers.set(consumer.label, consumer.producerPeerId, consumer); | ||
consumer.setMediaSoupConsumer(mediaSoupConsumer); | ||
return { consumer, mediaSoupConsumer }; | ||
} | ||
@@ -308,8 +325,8 @@ catch (error) { | ||
try { | ||
const consumer = this.__consumers.get(data.producerId); | ||
const consumer = this.getConsumer(data); | ||
if (!consumer) { | ||
logger.error("❌ Consumer not found"); | ||
return; | ||
throw new Error(`❌ Consumer not found label: ${data.label}, peerId: ${data.peerId}`); | ||
} | ||
this.__consumers.delete(data.producerId); | ||
consumer.close(); | ||
this.__consumers.delete(data.label, data.peerId); | ||
consumer.emit("closed"); | ||
@@ -340,3 +357,44 @@ consumer.removeListeners(); | ||
}; | ||
__connectionStateChangeHandler = (state) => { | ||
try { | ||
logger.debug(`🔔 ${this.transportType} Transport Connection State Changed, state: ${state}`); | ||
const transportType = this.transportType; | ||
const handler = { | ||
connected: () => { | ||
logger.debug(`🔔 ${this.transportType} Transport Connected`); | ||
}, | ||
disconnected: () => { | ||
if (this.__iceRestartDebounce) | ||
return; | ||
this.__iceRestartDebounce = true; | ||
this.__socket.publish("restartTransportIce", { | ||
transportId: this.__mediasoupTransport.id, | ||
transportType, | ||
}); | ||
setTimeout(() => { | ||
this.__iceRestartDebounce = false; | ||
}, 3000); | ||
logger.debug(`🔔 ${transportType} Transport Disconnected`); | ||
}, | ||
failed: () => { | ||
logger.debug(`🔔 ${transportType} Transport Failed`); | ||
}, | ||
connecting: () => { | ||
logger.debug(`🔔 ${transportType} Transport Connecting`); | ||
}, | ||
closed: () => { | ||
logger.debug(`🔔 ${transportType} Transport closed`); | ||
}, | ||
new: () => { | ||
logger.debug(`🔔 ${transportType} Transport new`); | ||
}, | ||
}; | ||
handler[state](); | ||
} | ||
catch (err) { | ||
logger.error("❌ Error in connectionStateChangeHandler"); | ||
logger.error(err); | ||
} | ||
}; | ||
} | ||
export default Transport; |
{ | ||
"name": "@huddle01/web-core", | ||
"version": "1.0.0-alpha.1", | ||
"version": "1.0.0-alpha.2", | ||
"description": "", | ||
@@ -12,3 +12,4 @@ "type": "module", | ||
"watch": "pnpm build --watch", | ||
"test": "pnpm vitest run --browser" | ||
"test": "pnpm vitest run --browser", | ||
"pub": "npm publish --access public --tag alpha" | ||
}, | ||
@@ -31,9 +32,9 @@ "exports": { | ||
"jsdom": "^22.1.0", | ||
"tsconfig": "*", | ||
"types": "*", | ||
"typescript": "^5.1.6", | ||
"tsconfig": "workspace:*", | ||
"types": "workspace:*", | ||
"typescript": "^5.2.2", | ||
"webdriverio": "^8.15.4" | ||
}, | ||
"dependencies": { | ||
"common-js": "*", | ||
"common-js": "workspace:*", | ||
"mediasoup-client": "^3.6.98", | ||
@@ -40,0 +41,0 @@ "tslog": "^4.9.1", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
2
220169
62
5372
3
- Removedcommon-js@0.3.8(transitive)
Updatedcommon-js@workspace:*