@fishjam-cloud/webrtc-client 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +142 -0
- package/README.md +30 -0
- package/dist/index.d.mts +761 -0
- package/dist/index.mjs +4952 -0
- package/dist/protobufs/fishjam/media_events/peer/peer.d.ts +125 -0
- package/dist/protobufs/fishjam/media_events/peer/peer.js +1214 -0
- package/dist/protobufs/fishjam/media_events/server/server.d.ts +197 -0
- package/dist/protobufs/fishjam/media_events/server/server.js +2121 -0
- package/dist/protobufs/fishjam/media_events/shared.d.ts +38 -0
- package/dist/protobufs/fishjam/media_events/shared.js +152 -0
- package/dist/webrtc-client/src/CommandsQueue.d.ts +23 -0
- package/dist/webrtc-client/src/CommandsQueue.js +87 -0
- package/dist/webrtc-client/src/ConnectionManager.d.ts +16 -0
- package/dist/webrtc-client/src/ConnectionManager.js +46 -0
- package/dist/webrtc-client/src/bitrate.d.ts +12 -0
- package/dist/webrtc-client/src/bitrate.js +15 -0
- package/dist/webrtc-client/src/deferred.d.ts +8 -0
- package/dist/webrtc-client/src/deferred.js +17 -0
- package/dist/webrtc-client/src/index.d.ts +5 -0
- package/dist/webrtc-client/src/index.js +3 -0
- package/dist/webrtc-client/src/internal.d.ts +27 -0
- package/dist/webrtc-client/src/internal.js +18 -0
- package/dist/webrtc-client/src/mediaEvent.d.ts +13 -0
- package/dist/webrtc-client/src/mediaEvent.js +20 -0
- package/dist/webrtc-client/src/tracks/Local.d.ts +49 -0
- package/dist/webrtc-client/src/tracks/Local.js +243 -0
- package/dist/webrtc-client/src/tracks/LocalTrack.d.ts +61 -0
- package/dist/webrtc-client/src/tracks/LocalTrack.js +212 -0
- package/dist/webrtc-client/src/tracks/LocalTrackManager.d.ts +39 -0
- package/dist/webrtc-client/src/tracks/LocalTrackManager.js +76 -0
- package/dist/webrtc-client/src/tracks/Remote.d.ts +32 -0
- package/dist/webrtc-client/src/tracks/Remote.js +165 -0
- package/dist/webrtc-client/src/tracks/RemoteTrack.d.ts +40 -0
- package/dist/webrtc-client/src/tracks/RemoteTrack.js +71 -0
- package/dist/webrtc-client/src/tracks/TrackCommon.d.ts +9 -0
- package/dist/webrtc-client/src/tracks/TrackCommon.js +1 -0
- package/dist/webrtc-client/src/tracks/bandwidth.d.ts +1 -0
- package/dist/webrtc-client/src/tracks/bandwidth.js +27 -0
- package/dist/webrtc-client/src/tracks/encodings.d.ts +3 -0
- package/dist/webrtc-client/src/tracks/encodings.js +11 -0
- package/dist/webrtc-client/src/tracks/muteTrackUtils.d.ts +3 -0
- package/dist/webrtc-client/src/tracks/muteTrackUtils.js +18 -0
- package/dist/webrtc-client/src/tracks/transceivers.d.ts +2 -0
- package/dist/webrtc-client/src/tracks/transceivers.js +93 -0
- package/dist/webrtc-client/src/types.d.ts +259 -0
- package/dist/webrtc-client/src/types.js +1 -0
- package/dist/webrtc-client/src/webRTCEndpoint.d.ts +298 -0
- package/dist/webrtc-client/src/webRTCEndpoint.js +659 -0
- package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.js +21 -0
- package/dist/webrtc-client/tests/events/connectedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/connectedEvent.test.js +70 -0
- package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.js +60 -0
- package/dist/webrtc-client/tests/events/endpointAddedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/endpointAddedEvent.test.js +57 -0
- package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.js +64 -0
- package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.js +78 -0
- package/dist/webrtc-client/tests/events/trackAddedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/trackAddedEvent.test.js +84 -0
- package/dist/webrtc-client/tests/events/trackRemovedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/trackRemovedEvent.test.js +26 -0
- package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.js +77 -0
- package/dist/webrtc-client/tests/events/vadNotificationEvent.test.d.ts +1 -0
- package/dist/webrtc-client/tests/events/vadNotificationEvent.test.js +42 -0
- package/dist/webrtc-client/tests/fixtures.d.ts +27 -0
- package/dist/webrtc-client/tests/fixtures.js +172 -0
- package/dist/webrtc-client/tests/methods/addTrackMethod.test.d.ts +1 -0
- package/dist/webrtc-client/tests/methods/addTrackMethod.test.js +46 -0
- package/dist/webrtc-client/tests/methods/cleanUpMethod.test.d.ts +1 -0
- package/dist/webrtc-client/tests/methods/cleanUpMethod.test.js +14 -0
- package/dist/webrtc-client/tests/methods/connectMethod.test.d.ts +1 -0
- package/dist/webrtc-client/tests/methods/connectMethod.test.js +40 -0
- package/dist/webrtc-client/tests/methods/disconnectMethod.test.d.ts +1 -0
- package/dist/webrtc-client/tests/methods/disconnectMethod.test.js +29 -0
- package/dist/webrtc-client/tests/mocks.d.ts +5 -0
- package/dist/webrtc-client/tests/mocks.js +80 -0
- package/dist/webrtc-client/tests/utils.d.ts +3 -0
- package/dist/webrtc-client/tests/utils.js +56 -0
- package/package.json +80 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
2
|
+
export declare const protobufPackage = "fishjam.media_events";
|
|
3
|
+
export declare enum Variant {
|
|
4
|
+
VARIANT_UNSPECIFIED = 0,
|
|
5
|
+
VARIANT_LOW = 1,
|
|
6
|
+
VARIANT_MEDIUM = 2,
|
|
7
|
+
VARIANT_HIGH = 3,
|
|
8
|
+
UNRECOGNIZED = -1
|
|
9
|
+
}
|
|
10
|
+
export declare function variantFromJSON(object: any): Variant;
|
|
11
|
+
export declare function variantToJSON(object: Variant): string;
|
|
12
|
+
/** Contains information about an ICE candidate which will be sent to the peer/server */
|
|
13
|
+
export interface Candidate {
|
|
14
|
+
candidate: string;
|
|
15
|
+
sdpMLineIndex: number;
|
|
16
|
+
sdpMid: string;
|
|
17
|
+
usernameFragment: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const Candidate: MessageFns<Candidate>;
|
|
20
|
+
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
21
|
+
export type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
|
|
22
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
23
|
+
} : Partial<T>;
|
|
24
|
+
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
25
|
+
export type Exact<P, I extends P> = P extends Builtin ? P : P & {
|
|
26
|
+
[K in keyof P]: Exact<P[K], I[K]>;
|
|
27
|
+
} & {
|
|
28
|
+
[K in Exclude<keyof I, KeysOfUnion<P>>]: never;
|
|
29
|
+
};
|
|
30
|
+
export interface MessageFns<T> {
|
|
31
|
+
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
|
32
|
+
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
|
33
|
+
fromJSON(object: any): T;
|
|
34
|
+
toJSON(message: T): unknown;
|
|
35
|
+
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
|
36
|
+
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
|
+
// versions:
|
|
3
|
+
// protoc-gen-ts_proto v2.6.1
|
|
4
|
+
// protoc v5.29.2
|
|
5
|
+
// source: fishjam/media_events/shared.proto
|
|
6
|
+
/* eslint-disable */
|
|
7
|
+
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
8
|
+
export const protobufPackage = "fishjam.media_events";
|
|
9
|
+
export var Variant;
|
|
10
|
+
(function (Variant) {
|
|
11
|
+
Variant[Variant["VARIANT_UNSPECIFIED"] = 0] = "VARIANT_UNSPECIFIED";
|
|
12
|
+
Variant[Variant["VARIANT_LOW"] = 1] = "VARIANT_LOW";
|
|
13
|
+
Variant[Variant["VARIANT_MEDIUM"] = 2] = "VARIANT_MEDIUM";
|
|
14
|
+
Variant[Variant["VARIANT_HIGH"] = 3] = "VARIANT_HIGH";
|
|
15
|
+
Variant[Variant["UNRECOGNIZED"] = -1] = "UNRECOGNIZED";
|
|
16
|
+
})(Variant || (Variant = {}));
|
|
17
|
+
export function variantFromJSON(object) {
|
|
18
|
+
switch (object) {
|
|
19
|
+
case 0:
|
|
20
|
+
case "VARIANT_UNSPECIFIED":
|
|
21
|
+
return Variant.VARIANT_UNSPECIFIED;
|
|
22
|
+
case 1:
|
|
23
|
+
case "VARIANT_LOW":
|
|
24
|
+
return Variant.VARIANT_LOW;
|
|
25
|
+
case 2:
|
|
26
|
+
case "VARIANT_MEDIUM":
|
|
27
|
+
return Variant.VARIANT_MEDIUM;
|
|
28
|
+
case 3:
|
|
29
|
+
case "VARIANT_HIGH":
|
|
30
|
+
return Variant.VARIANT_HIGH;
|
|
31
|
+
case -1:
|
|
32
|
+
case "UNRECOGNIZED":
|
|
33
|
+
default:
|
|
34
|
+
return Variant.UNRECOGNIZED;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function variantToJSON(object) {
|
|
38
|
+
switch (object) {
|
|
39
|
+
case Variant.VARIANT_UNSPECIFIED:
|
|
40
|
+
return "VARIANT_UNSPECIFIED";
|
|
41
|
+
case Variant.VARIANT_LOW:
|
|
42
|
+
return "VARIANT_LOW";
|
|
43
|
+
case Variant.VARIANT_MEDIUM:
|
|
44
|
+
return "VARIANT_MEDIUM";
|
|
45
|
+
case Variant.VARIANT_HIGH:
|
|
46
|
+
return "VARIANT_HIGH";
|
|
47
|
+
case Variant.UNRECOGNIZED:
|
|
48
|
+
default:
|
|
49
|
+
return "UNRECOGNIZED";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function createBaseCandidate() {
|
|
53
|
+
return { candidate: "", sdpMLineIndex: 0, sdpMid: "", usernameFragment: "" };
|
|
54
|
+
}
|
|
55
|
+
export const Candidate = {
|
|
56
|
+
encode(message, writer = new BinaryWriter()) {
|
|
57
|
+
if (message.candidate !== "") {
|
|
58
|
+
writer.uint32(10).string(message.candidate);
|
|
59
|
+
}
|
|
60
|
+
if (message.sdpMLineIndex !== 0) {
|
|
61
|
+
writer.uint32(16).int32(message.sdpMLineIndex);
|
|
62
|
+
}
|
|
63
|
+
if (message.sdpMid !== "") {
|
|
64
|
+
writer.uint32(26).string(message.sdpMid);
|
|
65
|
+
}
|
|
66
|
+
if (message.usernameFragment !== "") {
|
|
67
|
+
writer.uint32(34).string(message.usernameFragment);
|
|
68
|
+
}
|
|
69
|
+
return writer;
|
|
70
|
+
},
|
|
71
|
+
decode(input, length) {
|
|
72
|
+
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
73
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
74
|
+
const message = createBaseCandidate();
|
|
75
|
+
while (reader.pos < end) {
|
|
76
|
+
const tag = reader.uint32();
|
|
77
|
+
switch (tag >>> 3) {
|
|
78
|
+
case 1: {
|
|
79
|
+
if (tag !== 10) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
message.candidate = reader.string();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
case 2: {
|
|
86
|
+
if (tag !== 16) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
message.sdpMLineIndex = reader.int32();
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
case 3: {
|
|
93
|
+
if (tag !== 26) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
message.sdpMid = reader.string();
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
case 4: {
|
|
100
|
+
if (tag !== 34) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
message.usernameFragment = reader.string();
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
reader.skip(tag & 7);
|
|
111
|
+
}
|
|
112
|
+
return message;
|
|
113
|
+
},
|
|
114
|
+
fromJSON(object) {
|
|
115
|
+
return {
|
|
116
|
+
candidate: isSet(object.candidate) ? globalThis.String(object.candidate) : "",
|
|
117
|
+
sdpMLineIndex: isSet(object.sdpMLineIndex) ? globalThis.Number(object.sdpMLineIndex) : 0,
|
|
118
|
+
sdpMid: isSet(object.sdpMid) ? globalThis.String(object.sdpMid) : "",
|
|
119
|
+
usernameFragment: isSet(object.usernameFragment) ? globalThis.String(object.usernameFragment) : "",
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
toJSON(message) {
|
|
123
|
+
const obj = {};
|
|
124
|
+
if (message.candidate !== "") {
|
|
125
|
+
obj.candidate = message.candidate;
|
|
126
|
+
}
|
|
127
|
+
if (message.sdpMLineIndex !== 0) {
|
|
128
|
+
obj.sdpMLineIndex = Math.round(message.sdpMLineIndex);
|
|
129
|
+
}
|
|
130
|
+
if (message.sdpMid !== "") {
|
|
131
|
+
obj.sdpMid = message.sdpMid;
|
|
132
|
+
}
|
|
133
|
+
if (message.usernameFragment !== "") {
|
|
134
|
+
obj.usernameFragment = message.usernameFragment;
|
|
135
|
+
}
|
|
136
|
+
return obj;
|
|
137
|
+
},
|
|
138
|
+
create(base) {
|
|
139
|
+
return Candidate.fromPartial(base ?? {});
|
|
140
|
+
},
|
|
141
|
+
fromPartial(object) {
|
|
142
|
+
const message = createBaseCandidate();
|
|
143
|
+
message.candidate = object.candidate ?? "";
|
|
144
|
+
message.sdpMLineIndex = object.sdpMLineIndex ?? 0;
|
|
145
|
+
message.sdpMid = object.sdpMid ?? "";
|
|
146
|
+
message.usernameFragment = object.usernameFragment ?? "";
|
|
147
|
+
return message;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
function isSet(value) {
|
|
151
|
+
return value !== null && value !== undefined;
|
|
152
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConnectionManager } from './ConnectionManager';
|
|
2
|
+
import type { Deferred } from './deferred';
|
|
3
|
+
import type { LocalTrackManager } from './tracks/LocalTrackManager';
|
|
4
|
+
export type Command = {
|
|
5
|
+
handler: () => Promise<void>;
|
|
6
|
+
parse?: () => void;
|
|
7
|
+
resolutionNotifier: Deferred<void>;
|
|
8
|
+
resolve: 'after-renegotiation' | 'on-handler-resolve';
|
|
9
|
+
};
|
|
10
|
+
export declare class CommandsQueue {
|
|
11
|
+
private readonly localTrackManager;
|
|
12
|
+
private connection;
|
|
13
|
+
private clearConnectionCallbacks;
|
|
14
|
+
constructor(localTrackManager: LocalTrackManager);
|
|
15
|
+
initConnection: (connection: ConnectionManager) => void;
|
|
16
|
+
private commandsQueue;
|
|
17
|
+
private commandResolutionNotifier;
|
|
18
|
+
pushCommand: (command: Command) => void;
|
|
19
|
+
processNextCommand: () => void;
|
|
20
|
+
private handleCommand;
|
|
21
|
+
private resolvePreviousCommand;
|
|
22
|
+
cleanUp: () => void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export class CommandsQueue {
|
|
2
|
+
localTrackManager;
|
|
3
|
+
connection = null;
|
|
4
|
+
clearConnectionCallbacks = null;
|
|
5
|
+
constructor(localTrackManager) {
|
|
6
|
+
this.localTrackManager = localTrackManager;
|
|
7
|
+
}
|
|
8
|
+
initConnection = (connection) => {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
const onSignalingStateChange = () => {
|
|
11
|
+
if (connection.getConnection().signalingState === 'stable') {
|
|
12
|
+
this.processNextCommand();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const onIceGatheringStateChange = () => {
|
|
16
|
+
if (connection.getConnection().iceGatheringState === 'complete') {
|
|
17
|
+
this.processNextCommand();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const onConnectionStateChange = () => {
|
|
21
|
+
if (connection.getConnection().connectionState === 'connected') {
|
|
22
|
+
this.processNextCommand();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const onIceConnectionStateChange = () => {
|
|
26
|
+
if (connection.getConnection().iceConnectionState === 'connected') {
|
|
27
|
+
this.processNextCommand();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
this.clearConnectionCallbacks = () => {
|
|
31
|
+
connection.getConnection().removeEventListener('signalingstatechange', onSignalingStateChange);
|
|
32
|
+
connection.getConnection().removeEventListener('icegatheringstatechange', onIceGatheringStateChange);
|
|
33
|
+
connection.getConnection().removeEventListener('connectionstatechange', onConnectionStateChange);
|
|
34
|
+
connection.getConnection().removeEventListener('iceconnectionstatechange', onIceConnectionStateChange);
|
|
35
|
+
};
|
|
36
|
+
connection.getConnection().addEventListener('icegatheringstatechange', onIceConnectionStateChange);
|
|
37
|
+
connection.getConnection().addEventListener('connectionstatechange', onConnectionStateChange);
|
|
38
|
+
connection.getConnection().addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
|
|
39
|
+
connection.getConnection().addEventListener('signalingstatechange', onSignalingStateChange);
|
|
40
|
+
};
|
|
41
|
+
commandsQueue = [];
|
|
42
|
+
commandResolutionNotifier = null;
|
|
43
|
+
pushCommand = (command) => {
|
|
44
|
+
this.commandsQueue.push(command);
|
|
45
|
+
this.processNextCommand();
|
|
46
|
+
};
|
|
47
|
+
processNextCommand = () => {
|
|
48
|
+
if (this.localTrackManager.isNegotiationInProgress())
|
|
49
|
+
return;
|
|
50
|
+
if (this.connection?.isConnectionUnstable())
|
|
51
|
+
return;
|
|
52
|
+
this.resolvePreviousCommand();
|
|
53
|
+
const command = this.commandsQueue.shift();
|
|
54
|
+
if (!command)
|
|
55
|
+
return;
|
|
56
|
+
this.commandResolutionNotifier = command.resolutionNotifier;
|
|
57
|
+
this.handleCommand(command);
|
|
58
|
+
};
|
|
59
|
+
handleCommand = async (command) => {
|
|
60
|
+
try {
|
|
61
|
+
command.parse?.();
|
|
62
|
+
const promise = command.handler();
|
|
63
|
+
if (command.resolve === 'on-handler-resolve') {
|
|
64
|
+
await promise;
|
|
65
|
+
this.resolvePreviousCommand();
|
|
66
|
+
this.processNextCommand();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
this.commandResolutionNotifier?.reject(error);
|
|
71
|
+
this.commandResolutionNotifier = null;
|
|
72
|
+
this.processNextCommand();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
resolvePreviousCommand = () => {
|
|
76
|
+
if (!this.commandResolutionNotifier)
|
|
77
|
+
return;
|
|
78
|
+
this.commandResolutionNotifier.resolve();
|
|
79
|
+
this.commandResolutionNotifier = null;
|
|
80
|
+
};
|
|
81
|
+
cleanUp = () => {
|
|
82
|
+
this.commandResolutionNotifier?.reject('Disconnected');
|
|
83
|
+
this.commandResolutionNotifier = null;
|
|
84
|
+
this.commandsQueue = [];
|
|
85
|
+
this.clearConnectionCallbacks?.();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MediaEvent_OfferData_TrackTypes } from '@fishjam-cloud/protobufs/server';
|
|
2
|
+
import type { MediaStreamTrackId } from './types';
|
|
3
|
+
export declare class ConnectionManager {
|
|
4
|
+
private readonly connection;
|
|
5
|
+
constructor(iceServers: RTCIceServer[]);
|
|
6
|
+
isConnectionUnstable: () => boolean;
|
|
7
|
+
getConnection: () => RTCPeerConnection;
|
|
8
|
+
addTransceiversIfNeeded: (serverTracks: MediaEvent_OfferData_TrackTypes) => void;
|
|
9
|
+
addTransceiver: (track: MediaStreamTrack, transceiverConfig: RTCRtpTransceiverInit) => void;
|
|
10
|
+
setOnTrackReady: (onTrackReady: (event: RTCTrackEvent) => void) => void;
|
|
11
|
+
setRemoteDescription: (data: RTCSessionDescriptionInit) => Promise<void>;
|
|
12
|
+
isTrackInUse: (track: MediaStreamTrack) => boolean;
|
|
13
|
+
removeTrack: (sender: RTCRtpSender) => void;
|
|
14
|
+
findSender: (mediaStreamTrackId: MediaStreamTrackId) => RTCRtpSender;
|
|
15
|
+
addIceCandidate: (iceCandidate: RTCIceCandidate) => Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export class ConnectionManager {
|
|
2
|
+
connection;
|
|
3
|
+
constructor(iceServers) {
|
|
4
|
+
this.connection = new RTCPeerConnection({
|
|
5
|
+
bundlePolicy: 'max-bundle',
|
|
6
|
+
iceServers: iceServers,
|
|
7
|
+
iceTransportPolicy: 'all',
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
isConnectionUnstable = () => {
|
|
11
|
+
if (!this.connection)
|
|
12
|
+
return false;
|
|
13
|
+
const isSignalingUnstable = this.connection.signalingState !== 'stable';
|
|
14
|
+
const isConnectionNotConnected = this.connection.connectionState !== 'connected';
|
|
15
|
+
const isIceNotConnected = this.connection.iceConnectionState !== 'connected';
|
|
16
|
+
return isSignalingUnstable && isConnectionNotConnected && isIceNotConnected;
|
|
17
|
+
};
|
|
18
|
+
getConnection = () => {
|
|
19
|
+
return this.connection;
|
|
20
|
+
};
|
|
21
|
+
addTransceiversIfNeeded = (serverTracks) => {
|
|
22
|
+
const recvTransceivers = this.connection.getTransceivers().filter((elem) => elem.direction === 'recvonly');
|
|
23
|
+
const videoTransceiversAmount = recvTransceivers.filter((elem) => elem.receiver.track.kind === 'video').length;
|
|
24
|
+
const audioTransceiversAmount = recvTransceivers.filter((elem) => elem.receiver.track.kind === 'audio').length;
|
|
25
|
+
const videoNeededTypes = Array(serverTracks.video - videoTransceiversAmount).fill('video');
|
|
26
|
+
const audioNeededTypes = Array(serverTracks.audio - audioTransceiversAmount).fill('audio');
|
|
27
|
+
[...videoNeededTypes, ...audioNeededTypes].forEach((kind) => this.connection.addTransceiver(kind, { direction: 'recvonly' }));
|
|
28
|
+
};
|
|
29
|
+
addTransceiver = (track, transceiverConfig) => {
|
|
30
|
+
this.connection.addTransceiver(track, transceiverConfig);
|
|
31
|
+
};
|
|
32
|
+
setOnTrackReady = (onTrackReady) => {
|
|
33
|
+
this.connection.ontrack = onTrackReady;
|
|
34
|
+
};
|
|
35
|
+
setRemoteDescription = async (data) => {
|
|
36
|
+
await this.connection.setRemoteDescription(data);
|
|
37
|
+
};
|
|
38
|
+
isTrackInUse = (track) => this.connection.getSenders().some((val) => val.track === track);
|
|
39
|
+
removeTrack = (sender) => {
|
|
40
|
+
this.connection.removeTrack(sender);
|
|
41
|
+
};
|
|
42
|
+
findSender = (mediaStreamTrackId) => this.connection.getSenders().find((sender) => sender.track && sender.track.id === mediaStreamTrackId);
|
|
43
|
+
addIceCandidate = async (iceCandidate) => {
|
|
44
|
+
await this.connection.addIceCandidate(iceCandidate);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Variant } from '@fishjam-cloud/protobufs/shared';
|
|
2
|
+
import { type BandwidthLimit } from './types';
|
|
3
|
+
export type Bitrate = number;
|
|
4
|
+
export type Bitrates = Record<Variant, Bitrate> | Bitrate;
|
|
5
|
+
export declare const defaultBitrates: {
|
|
6
|
+
readonly audio: Bitrate;
|
|
7
|
+
readonly video: Bitrate;
|
|
8
|
+
};
|
|
9
|
+
export declare const UNLIMITED_BANDWIDTH: Bitrate;
|
|
10
|
+
export declare const defaultSimulcastBitrates: {
|
|
11
|
+
[key in Variant]: BandwidthLimit;
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Variant } from '@fishjam-cloud/protobufs/shared';
|
|
2
|
+
// The suggested bitrate values are based on our internal tests.
|
|
3
|
+
export const defaultBitrates = {
|
|
4
|
+
audio: 50_000,
|
|
5
|
+
video: 2_500_000,
|
|
6
|
+
};
|
|
7
|
+
export const UNLIMITED_BANDWIDTH = 0;
|
|
8
|
+
// The suggested bitrate values are based on our internal tests.
|
|
9
|
+
export const defaultSimulcastBitrates = {
|
|
10
|
+
[Variant.VARIANT_HIGH]: 2_500_000,
|
|
11
|
+
[Variant.VARIANT_MEDIUM]: 500_000,
|
|
12
|
+
[Variant.VARIANT_LOW]: 150_000,
|
|
13
|
+
[Variant.VARIANT_UNSPECIFIED]: 0,
|
|
14
|
+
[Variant.UNRECOGNIZED]: 0,
|
|
15
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class Deferred {
|
|
2
|
+
promise;
|
|
3
|
+
resolveFn = () => null;
|
|
4
|
+
rejectFn = () => null;
|
|
5
|
+
constructor() {
|
|
6
|
+
this.promise = new Promise((resolve, reject) => {
|
|
7
|
+
this.resolveFn = resolve;
|
|
8
|
+
this.rejectFn = reject;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
resolve(value) {
|
|
12
|
+
this.resolveFn(value);
|
|
13
|
+
}
|
|
14
|
+
reject(reason) {
|
|
15
|
+
this.rejectFn(reason);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { MediaEvent, SerializedMediaEvent } from './mediaEvent';
|
|
2
|
+
export type { BandwidthLimit, EncodingReason, Endpoint, SimulcastBandwidthLimit, TrackBandwidthLimit, TrackContext, TrackContextEvents, TrackKind, VadStatus, WebRTCEndpointEvents, } from './types';
|
|
3
|
+
export { WebRTCEndpoint } from './webRTCEndpoint';
|
|
4
|
+
export { MediaEvent_Track_SimulcastConfig as SimulcastConfig } from '@fishjam-cloud/protobufs/server';
|
|
5
|
+
export { Variant } from '@fishjam-cloud/protobufs/shared';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MediaEvent_Track_SimulcastConfig } from '@fishjam-cloud/protobufs/server';
|
|
2
|
+
import type { Variant } from '@fishjam-cloud/protobufs/shared';
|
|
3
|
+
import type TypedEmitter from 'typed-emitter';
|
|
4
|
+
import type { EncodingReason, Endpoint, TrackBandwidthLimit, TrackContext, TrackContextEvents, TrackKind, TrackNegotiationStatus, VadStatus } from './types';
|
|
5
|
+
export declare const isTrackKind: (kind: string) => kind is TrackKind;
|
|
6
|
+
declare const TrackContextImpl_base: new () => TypedEmitter<Required<TrackContextEvents>>;
|
|
7
|
+
export declare class TrackContextImpl extends TrackContextImpl_base implements TrackContext {
|
|
8
|
+
endpoint: Endpoint;
|
|
9
|
+
trackId: string;
|
|
10
|
+
track: MediaStreamTrack | null;
|
|
11
|
+
trackKind: TrackKind | null;
|
|
12
|
+
stream: MediaStream | null;
|
|
13
|
+
metadata?: unknown;
|
|
14
|
+
metadataParsingError?: any;
|
|
15
|
+
simulcastConfig?: MediaEvent_Track_SimulcastConfig;
|
|
16
|
+
maxBandwidth: TrackBandwidthLimit;
|
|
17
|
+
encoding?: Variant;
|
|
18
|
+
encodingReason?: EncodingReason;
|
|
19
|
+
vadStatus: VadStatus;
|
|
20
|
+
negotiationStatus: TrackNegotiationStatus;
|
|
21
|
+
pendingMetadataUpdate: boolean;
|
|
22
|
+
constructor(endpoint: Endpoint, trackId: string, metadata: any, simulcastConfig?: MediaEvent_Track_SimulcastConfig);
|
|
23
|
+
}
|
|
24
|
+
export type EndpointWithTrackContext = Omit<Endpoint, 'tracks'> & {
|
|
25
|
+
tracks: Map<string, TrackContextImpl>;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
export const isTrackKind = (kind) => kind === 'audio' || kind === 'video';
|
|
3
|
+
export class TrackContextImpl extends EventEmitter {
|
|
4
|
+
constructor(endpoint, trackId, metadata, simulcastConfig = { enabled: false, enabledVariants: [], disabledVariants: [] }) {
|
|
5
|
+
super();
|
|
6
|
+
this.endpoint = endpoint;
|
|
7
|
+
this.trackId = trackId;
|
|
8
|
+
this.track = null;
|
|
9
|
+
this.trackKind = null;
|
|
10
|
+
this.stream = null;
|
|
11
|
+
this.metadata = metadata;
|
|
12
|
+
this.simulcastConfig = simulcastConfig;
|
|
13
|
+
this.maxBandwidth = 0;
|
|
14
|
+
this.vadStatus = 'silence';
|
|
15
|
+
this.negotiationStatus = 'awaiting';
|
|
16
|
+
this.pendingMetadataUpdate = false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer';
|
|
2
|
+
import { MediaEvent as ServerMediaEvent } from '@fishjam-cloud/protobufs/server';
|
|
3
|
+
export type SerializedMediaEvent = Uint8Array;
|
|
4
|
+
export interface MediaEvent {
|
|
5
|
+
type: keyof PeerMediaEvent;
|
|
6
|
+
key?: string;
|
|
7
|
+
data?: any;
|
|
8
|
+
}
|
|
9
|
+
export declare function serializePeerMediaEvent(mediaEvent: PeerMediaEvent): Uint8Array;
|
|
10
|
+
export declare function serializeServerMediaEvent(mediaEvent: ServerMediaEvent): Uint8Array;
|
|
11
|
+
export declare function deserializeServerMediaEvent(serializedMediaEvent: SerializedMediaEvent): ServerMediaEvent;
|
|
12
|
+
export declare function deserializePeerMediaEvent(serializedMediaEvent: SerializedMediaEvent): PeerMediaEvent;
|
|
13
|
+
export declare function generateMediaEvent<T extends keyof PeerMediaEvent>(type: T, data?: PeerMediaEvent[T]): MediaEvent;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MediaEvent as PeerMediaEvent } from '@fishjam-cloud/protobufs/peer';
|
|
2
|
+
import { MediaEvent as ServerMediaEvent } from '@fishjam-cloud/protobufs/server';
|
|
3
|
+
export function serializePeerMediaEvent(mediaEvent) {
|
|
4
|
+
const encodedEvent = PeerMediaEvent.encode(mediaEvent).finish();
|
|
5
|
+
return encodedEvent;
|
|
6
|
+
}
|
|
7
|
+
export function serializeServerMediaEvent(mediaEvent) {
|
|
8
|
+
const encodedEvent = ServerMediaEvent.encode(mediaEvent).finish();
|
|
9
|
+
return encodedEvent;
|
|
10
|
+
}
|
|
11
|
+
export function deserializeServerMediaEvent(serializedMediaEvent) {
|
|
12
|
+
return ServerMediaEvent.decode(serializedMediaEvent);
|
|
13
|
+
}
|
|
14
|
+
export function deserializePeerMediaEvent(serializedMediaEvent) {
|
|
15
|
+
return PeerMediaEvent.decode(serializedMediaEvent);
|
|
16
|
+
}
|
|
17
|
+
export function generateMediaEvent(type, data) {
|
|
18
|
+
const mediaEvent = { type, data };
|
|
19
|
+
return mediaEvent;
|
|
20
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MediaEvent as PeerMediaEvent, MediaEvent_SdpOffer } from '@fishjam-cloud/protobufs/peer';
|
|
2
|
+
import { type MediaEvent_Track_SimulcastConfig } from '@fishjam-cloud/protobufs/server';
|
|
3
|
+
import { Variant } from '@fishjam-cloud/protobufs/shared';
|
|
4
|
+
import type { ConnectionManager } from '../ConnectionManager';
|
|
5
|
+
import type { EndpointWithTrackContext } from '../internal';
|
|
6
|
+
import { TrackContextImpl } from '../internal';
|
|
7
|
+
import type { BandwidthLimit, RemoteTrackId, TrackBandwidthLimit, WebRTCEndpointEvents } from '../types';
|
|
8
|
+
import type { WebRTCEndpoint } from '../webRTCEndpoint';
|
|
9
|
+
import { LocalTrack } from './LocalTrack';
|
|
10
|
+
import type { EndpointId, TrackId } from './TrackCommon';
|
|
11
|
+
/**
|
|
12
|
+
* This class encapsulates methods related to handling the list of local tracks and local endpoint.
|
|
13
|
+
* It stores and mutates part of the client state for this local peer.
|
|
14
|
+
* It emits local events for local tracks and endpoints,
|
|
15
|
+
* and delegates mutation logic to the appropriate `LocalTrack` objects.
|
|
16
|
+
* It's responsible for creating `MidToTrackId` record which is required in `sdpOffer`
|
|
17
|
+
*/
|
|
18
|
+
export declare class Local {
|
|
19
|
+
private readonly localTracks;
|
|
20
|
+
private readonly localEndpoint;
|
|
21
|
+
private readonly emit;
|
|
22
|
+
private readonly sendMediaEvent;
|
|
23
|
+
private connection;
|
|
24
|
+
constructor(emit: <E extends keyof Required<WebRTCEndpointEvents>>(event: E, ...args: Parameters<Required<WebRTCEndpointEvents>[E]>) => void, sendMediaEvent: (mediaEvent: PeerMediaEvent) => void);
|
|
25
|
+
updateSenders: () => void;
|
|
26
|
+
updateMLineIds: (midToTrackIds: Record<string, string>) => void;
|
|
27
|
+
updateConnection: (connection: ConnectionManager) => void;
|
|
28
|
+
createSdpOfferEvent: (sdpOffer: RTCSessionDescriptionInit) => MediaEvent_SdpOffer;
|
|
29
|
+
addTrack: (connection: ConnectionManager | undefined, trackId: string, track: MediaStreamTrack, stream: MediaStream, trackMetadata: unknown | undefined, simulcastConfig: MediaEvent_Track_SimulcastConfig, maxBandwidth: TrackBandwidthLimit) => LocalTrack;
|
|
30
|
+
getTrackByMidOrNull: (mid: string) => LocalTrack | null;
|
|
31
|
+
removeTrack: (trackId: TrackId) => void;
|
|
32
|
+
replaceTrack: (webrtc: WebRTCEndpoint, trackId: TrackId, newTrack: MediaStreamTrack | null) => Promise<void>;
|
|
33
|
+
setEndpointMetadata: (metadata: unknown) => void;
|
|
34
|
+
getEndpoint: () => EndpointWithTrackContext;
|
|
35
|
+
setTrackBandwidth: (trackId: string, bandwidth: BandwidthLimit) => Promise<void>;
|
|
36
|
+
getTrackIdToTrack: () => Map<RemoteTrackId, TrackContextImpl>;
|
|
37
|
+
setLocalEndpointId: (endpointId: EndpointId) => void;
|
|
38
|
+
setEncodingBandwidth: (trackId: TrackId, rid: Variant, bandwidth: BandwidthLimit) => Promise<void>;
|
|
39
|
+
updateEndpointMetadata: (metadata: unknown) => void;
|
|
40
|
+
updateLocalTrackMetadata: (trackId: TrackId, metadata: unknown) => void;
|
|
41
|
+
disableLocalTrackEncoding: (trackId: string, encoding: Variant) => Promise<void>;
|
|
42
|
+
enableLocalTrackEncoding: (trackId: TrackId, variant: Variant) => Promise<void>;
|
|
43
|
+
private getTrackIdToMetadataJson;
|
|
44
|
+
private getTrackIdToTrackBitrates;
|
|
45
|
+
private getMidToTrackId;
|
|
46
|
+
private getTransceiverMapping;
|
|
47
|
+
setLocalTrackStatusToOffered: () => void;
|
|
48
|
+
addAllTracksToConnection: () => void;
|
|
49
|
+
}
|