@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.
Files changed (83) hide show
  1. package/LICENSE.txt +142 -0
  2. package/README.md +30 -0
  3. package/dist/index.d.mts +761 -0
  4. package/dist/index.mjs +4952 -0
  5. package/dist/protobufs/fishjam/media_events/peer/peer.d.ts +125 -0
  6. package/dist/protobufs/fishjam/media_events/peer/peer.js +1214 -0
  7. package/dist/protobufs/fishjam/media_events/server/server.d.ts +197 -0
  8. package/dist/protobufs/fishjam/media_events/server/server.js +2121 -0
  9. package/dist/protobufs/fishjam/media_events/shared.d.ts +38 -0
  10. package/dist/protobufs/fishjam/media_events/shared.js +152 -0
  11. package/dist/webrtc-client/src/CommandsQueue.d.ts +23 -0
  12. package/dist/webrtc-client/src/CommandsQueue.js +87 -0
  13. package/dist/webrtc-client/src/ConnectionManager.d.ts +16 -0
  14. package/dist/webrtc-client/src/ConnectionManager.js +46 -0
  15. package/dist/webrtc-client/src/bitrate.d.ts +12 -0
  16. package/dist/webrtc-client/src/bitrate.js +15 -0
  17. package/dist/webrtc-client/src/deferred.d.ts +8 -0
  18. package/dist/webrtc-client/src/deferred.js +17 -0
  19. package/dist/webrtc-client/src/index.d.ts +5 -0
  20. package/dist/webrtc-client/src/index.js +3 -0
  21. package/dist/webrtc-client/src/internal.d.ts +27 -0
  22. package/dist/webrtc-client/src/internal.js +18 -0
  23. package/dist/webrtc-client/src/mediaEvent.d.ts +13 -0
  24. package/dist/webrtc-client/src/mediaEvent.js +20 -0
  25. package/dist/webrtc-client/src/tracks/Local.d.ts +49 -0
  26. package/dist/webrtc-client/src/tracks/Local.js +243 -0
  27. package/dist/webrtc-client/src/tracks/LocalTrack.d.ts +61 -0
  28. package/dist/webrtc-client/src/tracks/LocalTrack.js +212 -0
  29. package/dist/webrtc-client/src/tracks/LocalTrackManager.d.ts +39 -0
  30. package/dist/webrtc-client/src/tracks/LocalTrackManager.js +76 -0
  31. package/dist/webrtc-client/src/tracks/Remote.d.ts +32 -0
  32. package/dist/webrtc-client/src/tracks/Remote.js +165 -0
  33. package/dist/webrtc-client/src/tracks/RemoteTrack.d.ts +40 -0
  34. package/dist/webrtc-client/src/tracks/RemoteTrack.js +71 -0
  35. package/dist/webrtc-client/src/tracks/TrackCommon.d.ts +9 -0
  36. package/dist/webrtc-client/src/tracks/TrackCommon.js +1 -0
  37. package/dist/webrtc-client/src/tracks/bandwidth.d.ts +1 -0
  38. package/dist/webrtc-client/src/tracks/bandwidth.js +27 -0
  39. package/dist/webrtc-client/src/tracks/encodings.d.ts +3 -0
  40. package/dist/webrtc-client/src/tracks/encodings.js +11 -0
  41. package/dist/webrtc-client/src/tracks/muteTrackUtils.d.ts +3 -0
  42. package/dist/webrtc-client/src/tracks/muteTrackUtils.js +18 -0
  43. package/dist/webrtc-client/src/tracks/transceivers.d.ts +2 -0
  44. package/dist/webrtc-client/src/tracks/transceivers.js +93 -0
  45. package/dist/webrtc-client/src/types.d.ts +259 -0
  46. package/dist/webrtc-client/src/types.js +1 -0
  47. package/dist/webrtc-client/src/webRTCEndpoint.d.ts +298 -0
  48. package/dist/webrtc-client/src/webRTCEndpoint.js +659 -0
  49. package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.d.ts +1 -0
  50. package/dist/webrtc-client/tests/events/bandwidthEstimationEvent.test.js +21 -0
  51. package/dist/webrtc-client/tests/events/connectedEvent.test.d.ts +1 -0
  52. package/dist/webrtc-client/tests/events/connectedEvent.test.js +70 -0
  53. package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.d.ts +1 -0
  54. package/dist/webrtc-client/tests/events/encodingSwitchedEvent.test.js +60 -0
  55. package/dist/webrtc-client/tests/events/endpointAddedEvent.test.d.ts +1 -0
  56. package/dist/webrtc-client/tests/events/endpointAddedEvent.test.js +57 -0
  57. package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.d.ts +1 -0
  58. package/dist/webrtc-client/tests/events/endpointRemovedEvent.test.js +64 -0
  59. package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.d.ts +1 -0
  60. package/dist/webrtc-client/tests/events/endpointUpdatedEvent.test.js +78 -0
  61. package/dist/webrtc-client/tests/events/trackAddedEvent.test.d.ts +1 -0
  62. package/dist/webrtc-client/tests/events/trackAddedEvent.test.js +84 -0
  63. package/dist/webrtc-client/tests/events/trackRemovedEvent.test.d.ts +1 -0
  64. package/dist/webrtc-client/tests/events/trackRemovedEvent.test.js +26 -0
  65. package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.d.ts +1 -0
  66. package/dist/webrtc-client/tests/events/trackUpdatedEvent.test.js +77 -0
  67. package/dist/webrtc-client/tests/events/vadNotificationEvent.test.d.ts +1 -0
  68. package/dist/webrtc-client/tests/events/vadNotificationEvent.test.js +42 -0
  69. package/dist/webrtc-client/tests/fixtures.d.ts +27 -0
  70. package/dist/webrtc-client/tests/fixtures.js +172 -0
  71. package/dist/webrtc-client/tests/methods/addTrackMethod.test.d.ts +1 -0
  72. package/dist/webrtc-client/tests/methods/addTrackMethod.test.js +46 -0
  73. package/dist/webrtc-client/tests/methods/cleanUpMethod.test.d.ts +1 -0
  74. package/dist/webrtc-client/tests/methods/cleanUpMethod.test.js +14 -0
  75. package/dist/webrtc-client/tests/methods/connectMethod.test.d.ts +1 -0
  76. package/dist/webrtc-client/tests/methods/connectMethod.test.js +40 -0
  77. package/dist/webrtc-client/tests/methods/disconnectMethod.test.d.ts +1 -0
  78. package/dist/webrtc-client/tests/methods/disconnectMethod.test.js +29 -0
  79. package/dist/webrtc-client/tests/mocks.d.ts +5 -0
  80. package/dist/webrtc-client/tests/mocks.js +80 -0
  81. package/dist/webrtc-client/tests/utils.d.ts +3 -0
  82. package/dist/webrtc-client/tests/utils.js +56 -0
  83. package/package.json +80 -0
@@ -0,0 +1,172 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { MediaEvent_Connected, MediaEvent_Endpoint, MediaEvent_EndpointAdded, MediaEvent_EndpointRemoved, MediaEvent_EndpointUpdated, MediaEvent_OfferData, MediaEvent_SdpAnswer, MediaEvent_TracksAdded, MediaEvent_TracksRemoved, MediaEvent_TrackUpdated, MediaEvent_TrackVariantSwitched, MediaEvent_VadNotification, } from '@fishjam-cloud/protobufs/server';
3
+ import { FakeMediaStreamTrack } from 'fake-mediastreamtrack';
4
+ import { vi } from 'vitest';
5
+ import { Variant } from '../src';
6
+ export const exampleEndpointId = 'exampleEndpointId';
7
+ export const notExistingEndpointId = 'notExistingEndpointId';
8
+ export const exampleTrackId = 'exampleTrackId';
9
+ export const notExistingTrackId = 'notExistingTrackId';
10
+ export const mockTrack = new FakeMediaStreamTrack({ kind: 'video' });
11
+ const MediaStreamMock = vi.fn().mockImplementation(() => { });
12
+ export const stream = new MediaStreamMock();
13
+ export const createTrackWithSimulcast = (metadata = undefined) => ({
14
+ metadataJson: JSON.stringify(metadata),
15
+ simulcastConfig: {
16
+ enabled: true,
17
+ enabledVariants: [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH],
18
+ disabledVariants: [],
19
+ },
20
+ });
21
+ export const createEmptyEndpoint = () => MediaEvent_Endpoint.create({ trackIdToTrack: {}, endpointType: 'webrtc' });
22
+ export const createConnectedEvent = (endpointId = faker.string.uuid()) => {
23
+ return MediaEvent_Connected.create({ endpointId, endpointIdToEndpoint: { [endpointId]: createEmptyEndpoint() } });
24
+ };
25
+ export const createEncodingSwitchedEvent = (endpointId, trackId, variant) => MediaEvent_TrackVariantSwitched.create({
26
+ variant,
27
+ endpointId,
28
+ trackId,
29
+ });
30
+ // export const createBandwidthEstimationEvent = (): CustomBandwidthEstimationEvent =>
31
+ // CustomBandwidthEstimationEventSchema.parse({
32
+ // data: {
33
+ // data: {
34
+ // estimation: 261506.7264961106,
35
+ // },
36
+ // type: 'bandwidthEstimation',
37
+ // },
38
+ // type: 'custom',
39
+ // });
40
+ export const createCustomVadNotificationEvent = (trackId, status) => MediaEvent_VadNotification.create({
41
+ trackId,
42
+ status,
43
+ });
44
+ export const createTrackUpdatedEvent = (trackId, endpointId, metadata) => MediaEvent_TrackUpdated.create({
45
+ endpointId: endpointId,
46
+ metadataJson: JSON.stringify(metadata),
47
+ trackId: trackId,
48
+ });
49
+ export const createEndpointAdded = (endpointId, metadata = undefined) => MediaEvent_EndpointAdded.create({
50
+ endpointId,
51
+ metadataJson: JSON.stringify(metadata),
52
+ });
53
+ export const createEndpointRemoved = (endpointId) => MediaEvent_EndpointRemoved.create({
54
+ endpointId,
55
+ });
56
+ export const createConnectedEventWithOneEndpoint = (endpointId = faker.string.uuid(), localEndpointId) => {
57
+ const connectedEvent = createConnectedEvent(localEndpointId);
58
+ const endpoint = createEmptyEndpoint();
59
+ connectedEvent.endpointIdToEndpoint = { ...connectedEvent.endpointIdToEndpoint, [endpointId]: endpoint };
60
+ return MediaEvent_Connected.create(connectedEvent);
61
+ };
62
+ export const createConnectedEventWithOneEndpointWithOneTrack = (remoteEndpointId, trackId, localEndpointId) => {
63
+ const connectedEvent = createConnectedEvent(localEndpointId);
64
+ const remoteEndpoint = createEmptyEndpoint();
65
+ remoteEndpoint.trackIdToTrack[trackId] = createTrackWithSimulcast();
66
+ connectedEvent.endpointIdToEndpoint = {
67
+ ...connectedEvent.endpointIdToEndpoint,
68
+ [remoteEndpointId]: remoteEndpoint,
69
+ };
70
+ return MediaEvent_Connected.create(connectedEvent);
71
+ };
72
+ export const createAddTrackMediaEvent = (endpointId, trackId, metadata = undefined) => MediaEvent_TracksAdded.create({
73
+ endpointId: endpointId,
74
+ trackIdToTrack: {
75
+ [trackId]: createTrackWithSimulcast(metadata),
76
+ },
77
+ });
78
+ export const createTracksRemovedEvent = (endpointId, trackIds) => MediaEvent_TracksRemoved.create({
79
+ endpointId: endpointId,
80
+ trackIds,
81
+ });
82
+ export const createCustomOfferDataEventWithOneVideoTrack = () => MediaEvent_OfferData.create({
83
+ tracksTypes: {
84
+ audio: 0,
85
+ video: 1,
86
+ },
87
+ });
88
+ export const createAddLocalTrackSDPOffer = () => MediaEvent_OfferData.create({
89
+ tracksTypes: {
90
+ audio: 0,
91
+ video: 0,
92
+ },
93
+ });
94
+ export const createAnswerData = (trackId) => MediaEvent_SdpAnswer.create({
95
+ sdp: `v=0\r
96
+ o=- 39483584182226872 0 IN IP4 127.0.0.1\r
97
+ s=-\r
98
+ t=0 0\r
99
+ a=group:BUNDLE 0\r
100
+ a=extmap-allow-mixed\r
101
+ a=ice-lite\r
102
+ m=video 9 UDP/TLS/RTP/SAVPF 102 103\r
103
+ c=IN IP4 0.0.0.0\r
104
+ a=sendonly\r
105
+ a=ice-ufrag:fXa4\r
106
+ a=ice-pwd:mC2wFgKGsN3cXnxadEhVaa\r
107
+ a=ice-options:trickle\r
108
+ a=fingerprint:sha-256 50:65:CB:9F:2B:B5:62:7F:20:59:79:C6:7B:49:D8:DF:C2:B5:59:1F:E2:7D:68:F8:C3:07:73:8B:16:70:FB:DD\r
109
+ a=setup:passive\r
110
+ a=mid:0\r
111
+ a=msid:60ff1fb2-6868-42be-8c92-311733034415 ea1339b9-54ce-445b-9cff-2568f9ac504b\r
112
+ a=rtcp-mux\r
113
+ a=rtpmap:102 H264/90000\r
114
+ a=fmtp:102 profile-level-id=42001f;level-asymmetry-allowed=1;packetization-mode=1\r
115
+ a=rtpmap:103 rtx/90000\r
116
+ a=fmtp:103 apt=102\r
117
+ a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r
118
+ a=rtcp-fb:102 transport-cc\r
119
+ a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r
120
+ a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r
121
+ a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r
122
+ a=rtcp-fb:102 ccm fir\r
123
+ a=rtcp-fb:102 nack\r
124
+ a=rtcp-fb:102 nack pli\r
125
+ a=rtcp-rsize\r
126
+ a=ssrc:663086196 cname:${trackId}-video-60ff1fb2-6868-42be-8c92-311733034415\r
127
+ `,
128
+ midToTrackId: {
129
+ '0': '9afe80ce-1964-4958-a386-d7a9e3097ca7:5c74b6b3-cb72-49f1-a76b-0df4895a3d32',
130
+ },
131
+ });
132
+ export const createAddLocalTrackAnswerData = (trackId) => MediaEvent_SdpAnswer.create({
133
+ midToTrackId: {
134
+ '0': trackId,
135
+ },
136
+ sdp: `v=0\r
137
+ o=- 63903156084304368 0 IN IP4 127.0.0.1\r
138
+ s=-\r
139
+ t=0 0\r
140
+ a=group:BUNDLE 0\r
141
+ a=extmap-allow-mixed\r
142
+ a=ice-lite\r
143
+ m=video 9 UDP/TLS/RTP/SAVPF 106 107\r
144
+ c=IN IP4 0.0.0.0\r
145
+ a=recvonly\r
146
+ a=ice-ufrag:dHiY\r
147
+ a=ice-pwd:IAPCE68QAQ8AxSF0OQIEZp\r
148
+ a=ice-options:trickle\r
149
+ a=fingerprint:sha-256 C1:50:4C:EC:98:1D:62:C8:DA:AE:F8:5B:44:4F:76:BB:4E:FF:5E:51:3E:A7:62:9B:58:38:A5:13:D0:B1:50:67\r
150
+ a=setup:passive\r
151
+ a=mid:0\r
152
+ a=msid:7bf8bef4-be67-456c-8635-ba58339c29e9 ad3deb09-60a6-4bfc-aa14-482ed4f60667\r
153
+ a=rtcp-mux\r
154
+ a=rtpmap:106 H264/90000\r
155
+ a=fmtp:106 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1\r
156
+ a=rtpmap:107 rtx/90000\r
157
+ a=fmtp:107 apt=106\r
158
+ a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r
159
+ a=rtcp-fb:106 transport-cc\r
160
+ a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r
161
+ a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r
162
+ a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r
163
+ a=rtcp-fb:106 ccm fir\r
164
+ a=rtcp-fb:106 nack\r
165
+ a=rtcp-fb:106 nack pli\r
166
+ a=rtcp-rsize\r
167
+ `,
168
+ });
169
+ export const createEndpointUpdatedPeerMetadata = (endpointId, metadata) => MediaEvent_EndpointUpdated.create({
170
+ endpointId,
171
+ metadataJson: JSON.stringify(metadata),
172
+ });
@@ -0,0 +1,46 @@
1
+ import { expect, it } from 'vitest';
2
+ import { WebRTCEndpoint } from '../../src';
3
+ import { deserializePeerMediaEvent, serializeServerMediaEvent } from '../../src/mediaEvent';
4
+ import { createConnectedEventWithOneEndpoint, mockTrack } from '../fixtures';
5
+ import { mockMediaStream, mockRTCPeerConnection } from '../mocks';
6
+ it('Adding track invokes renegotiation', () => new Promise((done) => {
7
+ // Given
8
+ const webRTCEndpoint = new WebRTCEndpoint();
9
+ mockMediaStream();
10
+ const serializedEvent = serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() });
11
+ webRTCEndpoint.receiveMediaEvent(serializedEvent);
12
+ webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => {
13
+ // Then
14
+ const event = deserializePeerMediaEvent(mediaEvent);
15
+ expect(event.renegotiateTracks).toBeTruthy();
16
+ done('');
17
+ // now it's time to create offer and answer
18
+ // webRTCEndpoint.receiveMediaEvent(JSON.stringify(createOfferData()))
19
+ // webRTCEndpoint.receiveMediaEvent(JSON.stringify(createAnswerData("9bf0cc85-c795-43b2-baf1-2c974cd770b9:1b6d99d1-3630-4e01-b386-15cbbfe5a41f")))
20
+ });
21
+ // When
22
+ webRTCEndpoint.addTrack(mockTrack);
23
+ }));
24
+ it('Adding track updates internal state', () => {
25
+ // Given
26
+ mockRTCPeerConnection();
27
+ mockMediaStream();
28
+ const webRTCEndpoint = new WebRTCEndpoint();
29
+ const serializedEvent = serializeServerMediaEvent({ connected: createConnectedEventWithOneEndpoint() });
30
+ webRTCEndpoint.receiveMediaEvent(serializedEvent);
31
+ // When
32
+ webRTCEndpoint.addTrack(mockTrack);
33
+ // Then
34
+ const localTrackIdToTrack = webRTCEndpoint['local'].getTrackIdToTrack();
35
+ expect(localTrackIdToTrack.size).toBe(1);
36
+ const localEndpoint = webRTCEndpoint['local'].getEndpoint();
37
+ expect(localEndpoint.tracks.size).toBe(1);
38
+ });
39
+ it('Adding track before being accepted by the server throws error', async () => {
40
+ // Given
41
+ mockRTCPeerConnection();
42
+ mockMediaStream();
43
+ const webRTCEndpoint = new WebRTCEndpoint();
44
+ // When
45
+ await expect(() => webRTCEndpoint.addTrack(mockTrack)).rejects.toThrow('Cannot add tracks before being accepted by the server');
46
+ });
@@ -0,0 +1,14 @@
1
+ import { expect, it } from 'vitest';
2
+ import { WebRTCEndpoint } from '../../src';
3
+ import { exampleEndpointId, exampleTrackId } from '../fixtures';
4
+ import { setupRoomWithMocks } from '../utils';
5
+ it('CleanUp sets connection to undefined', async () => {
6
+ // Given
7
+ const webRTCEndpoint = new WebRTCEndpoint();
8
+ await setupRoomWithMocks(webRTCEndpoint, exampleEndpointId, exampleTrackId);
9
+ // When
10
+ webRTCEndpoint.cleanUp();
11
+ // Then
12
+ const connection = webRTCEndpoint['connectionManager'];
13
+ expect(connection).toBe(undefined);
14
+ });
@@ -0,0 +1,40 @@
1
+ import { expect, it } from 'vitest';
2
+ import { WebRTCEndpoint } from '../../src';
3
+ import { deserializePeerMediaEvent } from '../../src/mediaEvent';
4
+ it('Method connect sends mediaEvent to backend', () => new Promise((done) => {
5
+ // Given
6
+ const webRTCEndpoint = new WebRTCEndpoint();
7
+ const peerMetadata = {};
8
+ webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => {
9
+ // Then
10
+ const event = deserializePeerMediaEvent(mediaEvent);
11
+ expect(event.connect).toBeTruthy();
12
+ done('');
13
+ });
14
+ // When
15
+ webRTCEndpoint.connect(peerMetadata);
16
+ }));
17
+ it("Method 'connect' sends metadata in event", () => new Promise((done) => {
18
+ // Given
19
+ const webRTCEndpoint = new WebRTCEndpoint();
20
+ const peerMetadata = { exampleField: 'exampleValue' };
21
+ webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => {
22
+ // Then
23
+ const event = deserializePeerMediaEvent(mediaEvent);
24
+ const metadataJson = event.connect?.metadataJson;
25
+ expect(metadataJson).toBeDefined();
26
+ expect(JSON.parse(metadataJson)).toMatchObject(peerMetadata);
27
+ done('');
28
+ });
29
+ // When
30
+ webRTCEndpoint.connect(peerMetadata);
31
+ }));
32
+ it("Method 'connect' sets metadata in local field", () => {
33
+ // Given
34
+ const webRTCEndpoint = new WebRTCEndpoint();
35
+ const peerMetadata = { exampleField: 'exampleValue' };
36
+ // When
37
+ webRTCEndpoint.connect(peerMetadata);
38
+ // Then
39
+ expect(webRTCEndpoint['local'].getEndpoint().metadata).toMatchObject(peerMetadata);
40
+ });
@@ -0,0 +1,29 @@
1
+ import { expect, it } from 'vitest';
2
+ import { WebRTCEndpoint } from '../../src';
3
+ import { deserializePeerMediaEvent } from '../../src/mediaEvent';
4
+ import { exampleEndpointId, exampleTrackId } from '../fixtures';
5
+ import { setupRoomWithMocks } from '../utils';
6
+ it('Disconnect sets connection to undefined', async () => {
7
+ // Given
8
+ const webRTCEndpoint = new WebRTCEndpoint();
9
+ await setupRoomWithMocks(webRTCEndpoint, exampleEndpointId, exampleTrackId);
10
+ // When
11
+ webRTCEndpoint.disconnect();
12
+ // Then
13
+ const connection = webRTCEndpoint['connectionManager'];
14
+ expect(connection).toBe(undefined);
15
+ });
16
+ it('Disconnect invokes disconnected event', () => new Promise((done) => {
17
+ (async () => {
18
+ // Given
19
+ const webRTCEndpoint = new WebRTCEndpoint();
20
+ await setupRoomWithMocks(webRTCEndpoint, exampleEndpointId, exampleTrackId);
21
+ webRTCEndpoint.on('sendMediaEvent', (mediaEvent) => {
22
+ const event = deserializePeerMediaEvent(mediaEvent);
23
+ expect(event.disconnect).toBeTruthy();
24
+ done('');
25
+ });
26
+ // When
27
+ webRTCEndpoint.disconnect();
28
+ })();
29
+ }));
@@ -0,0 +1,5 @@
1
+ export declare const mockMediaStream: () => void;
2
+ export declare const mockRTCPeerConnection: () => {
3
+ addTransceiverCallback: any;
4
+ runOnTrack: (ev: RTCTrackEvent) => void;
5
+ };
@@ -0,0 +1,80 @@
1
+ import { vi } from 'vitest';
2
+ import { undefined } from 'zod';
3
+ export const mockMediaStream = () => {
4
+ global.MediaStream = vi.fn().mockImplementation(() => {
5
+ return {
6
+ addTrack: () => { },
7
+ };
8
+ });
9
+ };
10
+ export const mockRTCPeerConnection = () => {
11
+ const addTransceiverCallback = vi.fn((_trackOrKind, _init) => null);
12
+ global.RTCPeerConnection = vi.fn().mockImplementation(() => {
13
+ const transceivers = [];
14
+ const senders = [];
15
+ const newVar = {
16
+ getTransceivers: () => {
17
+ return transceivers;
18
+ },
19
+ addTransceiver: (trackOrKind, init) => {
20
+ addTransceiverCallback(trackOrKind, init);
21
+ const sender = {};
22
+ sender.getParameters = () => {
23
+ // @ts-ignore
24
+ const encodings = [{}];
25
+ return { encodings: encodings };
26
+ };
27
+ if (init?.direction === 'sendonly') {
28
+ sender.track = typeof trackOrKind !== 'string' ? trackOrKind : { id: 'someTrackId' };
29
+ }
30
+ senders.push(sender);
31
+ // maybe move to callback declaration
32
+ const transceiver = {
33
+ currentDirection: null,
34
+ direction: init?.direction ?? 'inactive',
35
+ mid: 'someTransceiverMid',
36
+ receiver: {},
37
+ sender: sender,
38
+ setCodecPreferences: (_codecs) => { },
39
+ stop: () => { },
40
+ };
41
+ transceivers.push(transceiver);
42
+ return transceiver;
43
+ },
44
+ // @ts-ignore
45
+ createOffer: (_options) => {
46
+ // const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
47
+ return new Promise((resolve, _reject) => {
48
+ resolve({ sdp: '', type: 'offer' });
49
+ });
50
+ },
51
+ setLocalDescription: async (_description) => { },
52
+ setRemoteDescription: async (_description) => { },
53
+ getSenders: () => {
54
+ return senders;
55
+ },
56
+ close: () => { },
57
+ addEventListener: () => { },
58
+ removeEventListener: () => { },
59
+ };
60
+ return newVar;
61
+ });
62
+ global.RTCRtpTransceiver = vi.fn().mockImplementation(() => {
63
+ const newVar = {
64
+ currentDirection: null,
65
+ direction: 'stopped',
66
+ mid: '0',
67
+ // @ts-ignore
68
+ receiver: undefined,
69
+ // @ts-ignore
70
+ sender: undefined,
71
+ setCodecPreferences(_codecs) { },
72
+ stop() { },
73
+ };
74
+ return newVar;
75
+ });
76
+ const runOnTrack = (ev) => {
77
+ global.RTCPeerConnection.ontrack(ev);
78
+ };
79
+ return { addTransceiverCallback, runOnTrack };
80
+ };
@@ -0,0 +1,3 @@
1
+ import type { WebRTCEndpoint } from '../src';
2
+ export declare const setupRoom: (webRTCEndpoint: WebRTCEndpoint, endpointId: string, trackId: string) => void;
3
+ export declare const setupRoomWithMocks: (webRTCEndpoint: WebRTCEndpoint, endpointId: string, trackId: string) => Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { MediaEvent } from '@fishjam-cloud/protobufs/server';
2
+ import { createAddLocalTrackAnswerData, createAddLocalTrackSDPOffer, createAddTrackMediaEvent, createConnectedEventWithOneEndpointWithOneTrack, stream, } from './fixtures';
3
+ import { mockRTCPeerConnection } from './mocks';
4
+ export const setupRoom = (webRTCEndpoint, endpointId, trackId) => {
5
+ const connected = createConnectedEventWithOneEndpointWithOneTrack(endpointId, trackId);
6
+ const connectedEvent = MediaEvent.encode({ connected }).finish();
7
+ webRTCEndpoint.receiveMediaEvent(connectedEvent);
8
+ // Right now info about tracks from connectedEvent is ignored
9
+ // We need to fix it but not in this commit
10
+ // We need more tests (e2e) to introduce this change
11
+ const tracksAdded = createAddTrackMediaEvent(endpointId, trackId);
12
+ const tracksAddedEvent = MediaEvent.encode({ tracksAdded }).finish();
13
+ webRTCEndpoint.receiveMediaEvent(tracksAddedEvent);
14
+ };
15
+ // Fix proposal:
16
+ //
17
+ // "connected" handler
18
+ //
19
+ // const otherEndpoints: Endpoint[] = endpoints.map((endpoint) => {
20
+ // const endpointTracks: [string, any][] = Object.entries(endpoint.tracks || {});
21
+ // const tracks = this.mapMediaEventTracksToTrackContextImpl(endpointTracks, endpoint);
22
+ // return { ...endpoint, tracks };
23
+ // });
24
+ //
25
+ // otherEndpoints.forEach((endpoint) => {
26
+ // this.addEndpoint(endpoint);
27
+ // });
28
+ // otherEndpoints.forEach((endpoint) => {
29
+ // endpoint.tracks.forEach((ctx, trackId) => {
30
+ //
31
+ // "endpointAdded"
32
+ // this.addEndpoint({ ...endpoint, tracks: new Map() });
33
+ //
34
+ // remove todo if-else statement from addEndpoint function
35
+ export const setupRoomWithMocks = async (webRTCEndpoint, endpointId, trackId) => {
36
+ mockRTCPeerConnection();
37
+ setupRoom(webRTCEndpoint, endpointId, trackId);
38
+ const offerData = createAddLocalTrackSDPOffer();
39
+ webRTCEndpoint.receiveMediaEvent(MediaEvent.encode({ offerData }).finish());
40
+ const sdpAnswer = createAddLocalTrackAnswerData(trackId);
41
+ webRTCEndpoint.receiveMediaEvent(MediaEvent.encode({ sdpAnswer }).finish());
42
+ const connection = webRTCEndpoint['connectionManager'];
43
+ const transciever = new RTCRtpTransceiver();
44
+ // @ts-ignore
45
+ const rtcTrackEvent = {
46
+ streams: [stream],
47
+ transceiver: transciever,
48
+ // @ts-expect-error
49
+ track: {
50
+ kind: 'video',
51
+ },
52
+ };
53
+ // @ts-ignore
54
+ connection.getConnection().ontrack(rtcTrackEvent);
55
+ return new Promise((resolve) => resolve());
56
+ };
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@fishjam-cloud/webrtc-client",
3
+ "version": "0.14.0",
4
+ "description": "Typescript client library for ExWebRTC/WebRTC endpoint in Membrane RTC Engine",
5
+ "license": "Apache-2.0",
6
+ "author": "Fishjam Team",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/fishjam-cloud/web-client-sdk.git",
10
+ "directory": "packages/webrtc-client"
11
+ },
12
+ "homepage": "https://github.com/fishjam-cloud/web-client-sdk#readme",
13
+ "bugs": "https://github.com/fishjam-cloud/web-client-sdk/issues",
14
+ "keywords": [
15
+ "webrtc",
16
+ "fishjam"
17
+ ],
18
+ "main": "./dist/index.mjs",
19
+ "types": "./dist/index.d.mts",
20
+ "files": [
21
+ "dist/**"
22
+ ],
23
+ "tsup": {
24
+ "entry": [
25
+ "src/index.ts"
26
+ ],
27
+ "format": [
28
+ "esm"
29
+ ]
30
+ },
31
+ "scripts": {
32
+ "build": "yarn build:check && tsup --dts-resolve",
33
+ "build:check": "tsc --noEmit",
34
+ "format": "prettier --write . --ignore-path ./.eslintignore",
35
+ "format:check": "prettier --check . --ignore-path ./.eslintignore",
36
+ "lint": "eslint . --ext .ts,.tsx --fix",
37
+ "lint:check": "eslint . --ext .ts,.tsx",
38
+ "gen:proto": "sh scripts/protobuf.sh",
39
+ "test": "vitest run",
40
+ "test:e2e": "NODE_OPTIONS=--dns-result-order=ipv4first playwright test",
41
+ "test:coverage": "vitest run tests/** --coverage",
42
+ "prepare": "tsc",
43
+ "prepack": "yarn build"
44
+ },
45
+ "dependencies": {
46
+ "@bufbuild/protobuf": "^2.2.3",
47
+ "events": "^3.3.0",
48
+ "typed-emitter": "^2.1.0",
49
+ "uuid": "^11.1.0"
50
+ },
51
+ "devDependencies": {
52
+ "@faker-js/faker": "^9.5.0",
53
+ "@fishjam-cloud/protobufs": "workspace:*",
54
+ "@playwright/test": "^1.50.1",
55
+ "@types/events": "^3.0.3",
56
+ "@types/node": "^22.13.4",
57
+ "@types/uuid": "^10.0.0",
58
+ "@vitest/coverage-v8": "^3.0.5",
59
+ "fake-mediastreamtrack": "^1.2.0",
60
+ "husky": "^9.1.7",
61
+ "lint-staged": "^15.4.3",
62
+ "tsup": "^8.3.6",
63
+ "typed-emitter": "^2.1.0",
64
+ "typedoc": "^0.27.7",
65
+ "typedoc-plugin-external-resolver": "^1.0.3",
66
+ "typedoc-plugin-mdn-links": "^4.0.13",
67
+ "typescript": "^5.7.3",
68
+ "vitest": "^3.0.7",
69
+ "zod": "^3.24.1"
70
+ },
71
+ "lint-staged": {
72
+ "*": [
73
+ "yarn format:check"
74
+ ],
75
+ "*.(js|ts|tsx)": [
76
+ "yarn lint:check"
77
+ ]
78
+ },
79
+ "packageManager": "yarn@4.3.0"
80
+ }