@livedigital/client 1.14.1 → 2.0.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.
@@ -21,6 +21,7 @@ export declare type ProducerData = {
21
21
  peerId: string;
22
22
  label: TrackLabel;
23
23
  };
24
+ export declare type Role = 'audience' | 'host';
24
25
  export declare type PeerResponse = {
25
26
  id: string;
26
27
  channelIds: string[];
@@ -28,6 +29,7 @@ export declare type PeerResponse = {
28
29
  producers: ProducerData[];
29
30
  uid?: string;
30
31
  appData: Record<string, unknown>;
32
+ role: Role;
31
33
  };
32
34
  export declare type AvailableMediaDevices = {
33
35
  video: MediaDeviceInfo[];
@@ -39,6 +41,7 @@ export declare type JoinChannelParams = {
39
41
  sdkSecret: string;
40
42
  uid?: string;
41
43
  appData?: Record<string, unknown>;
44
+ role: Role;
42
45
  };
43
46
  export declare type ChannelEvent = {
44
47
  eventName: string;
@@ -125,9 +128,13 @@ export declare type CreateScreenVideoTrackOptions = CreateVideoTrackOptions & {
125
128
  export declare type CreateMicrophoneAudioTrackOptions = CreateTrackOptions & {
126
129
  encoderConfig?: AudioEncoderConfig;
127
130
  };
128
- export declare type CreateScreenAudioTrackOptions = CreateVideoTrackOptions & {
131
+ export declare type CreateScreenAudioTrackOptions = CreateMicrophoneAudioTrackOptions & {
129
132
  encoderConfig?: AudioEncoderConfig;
130
133
  };
134
+ export declare type CreateScreenMediaOptions = {
135
+ video: CreateScreenVideoTrackOptions;
136
+ audio: CreateScreenAudioTrackOptions;
137
+ };
131
138
  export declare type Track = VideoTrack | AudioTrack;
132
139
  export declare enum TrackLabel {
133
140
  Camera = "camera",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "1.14.1",
5
+ "version": "2.0.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -12,6 +12,7 @@ import {
12
12
  PayloadOfPublishedMedia,
13
13
  PayloadOfUnpublishedMedia,
14
14
  ChangePreferredLayersPayload,
15
+ Role,
15
16
  } from '../types/common';
16
17
  import Consumer from './media/Consumer';
17
18
  import VideoConsumer from './media/VideoConsumer';
@@ -33,6 +34,7 @@ interface PeerConstructor {
33
34
  loginDate: Date;
34
35
  uid?: string;
35
36
  appData?: Record<string, unknown>,
37
+ role: Role,
36
38
  }
37
39
 
38
40
  export enum ScoreThreshold {
@@ -54,6 +56,8 @@ class Peer {
54
56
 
55
57
  public appData: Record<string, unknown>;
56
58
 
59
+ public role: Role;
60
+
57
61
  private producers: Map<string, PeerProducer> = new Map();
58
62
 
59
63
  private consumers: Map<string, Consumer | VideoConsumer> = new Map();
@@ -77,6 +81,7 @@ class Peer {
77
81
  engine,
78
82
  appData,
79
83
  uid,
84
+ role,
80
85
  }: PeerConstructor) {
81
86
  this.id = id;
82
87
  this.channelIds = channelIds;
@@ -84,6 +89,7 @@ class Peer {
84
89
  this.loginDate = loginDate;
85
90
  this.appData = appData || {};
86
91
  this.uid = uid;
92
+ this.role = role;
87
93
  this.engine = engine;
88
94
  this.logger = new Logger('Peer');
89
95
  producers.forEach(this.handleNewProducer.bind(this));
@@ -2,11 +2,11 @@ import { RtpCapabilities } from 'mediasoup-client/lib/RtpParameters';
2
2
  import {
3
3
  CreateCameraVideoTrackOptions,
4
4
  CreateMicrophoneAudioTrackOptions,
5
- CreateScreenAudioTrackOptions,
6
- CreateScreenVideoTrackOptions,
5
+ CreateScreenMediaOptions,
7
6
  EndTrackPayload,
8
7
  JoinChannelParams,
9
8
  PeerResponse,
9
+ Role,
10
10
  SocketResponse,
11
11
  StartTrackPayload,
12
12
  Track,
@@ -126,6 +126,14 @@ class Engine {
126
126
  return Array.from(this.peersRepository.values());
127
127
  }
128
128
 
129
+ public get hostPeers(): Peer[] {
130
+ return this.peers.filter((peer) => peer.role === 'host');
131
+ }
132
+
133
+ public get audiencePeers(): Peer[] {
134
+ return this.peers.filter((peer) => peer.role === 'audience');
135
+ }
136
+
129
137
  public get mySocketId(): string | undefined {
130
138
  return this.network.socket.id;
131
139
  }
@@ -146,6 +154,15 @@ class Engine {
146
154
  }
147
155
  }
148
156
 
157
+ public async loadPeers(role?: Role): Promise<void> {
158
+ const { peers } = await this.network.socket
159
+ .request(CHANNEL_EVENTS.channelGetPeers, { role }) as { peers: PeerResponse[] };
160
+
161
+ peers
162
+ .filter((peer) => !this.peersRepository.has(peer.id))
163
+ .forEach((peer) => this.setPeer(peer));
164
+ }
165
+
149
166
  public setPeer(peerData: PeerResponse): Peer {
150
167
  const peer = new Peer({
151
168
  ...peerData,
@@ -197,7 +214,7 @@ class Engine {
197
214
  async createCameraVideoTrack(options?: CreateCameraVideoTrackOptions): Promise<Track> {
198
215
  const trackParams = Media.getCameraVideoTrackParams(options);
199
216
  try {
200
- const track = await this.media.createUserMediaTrack({
217
+ const [track] = await this.media.createUserMediaTracks({
201
218
  audio: false,
202
219
  video: trackParams.videoTrackOptions,
203
220
  });
@@ -215,7 +232,7 @@ class Engine {
215
232
 
216
233
  async createMicrophoneAudioTrack(options?: CreateMicrophoneAudioTrackOptions): Promise<Track> {
217
234
  try {
218
- const track = await this.media.createUserMediaTrack({
235
+ const [track] = await this.media.createUserMediaTracks({
219
236
  video: false,
220
237
  audio: {
221
238
  deviceId: options?.deviceId,
@@ -235,54 +252,47 @@ class Engine {
235
252
  }
236
253
  }
237
254
 
238
- async createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track> {
239
- const trackParams = Media.getScreenVideoTrackParams(options);
255
+ async createScreenMediaTracks(options?: CreateScreenMediaOptions): Promise<Track[]> {
256
+ const videoTrackParams = Media.getScreenVideoTrackParams(options?.video);
240
257
  try {
241
- const track = await this.media.createDisplayMediaTrack({
242
- audio: false,
243
- video: trackParams.videoTrackOptions,
244
- });
245
-
246
- track.mediaStreamTrack.addEventListener('ended', () => {
247
- this.unpublish(track);
258
+ const tracks = await this.media.createDisplayMediaTracks({
259
+ audio: options?.audio,
260
+ video: videoTrackParams.videoTrackOptions,
248
261
  });
249
262
 
250
- track.setLabel(TrackLabel.ScreenVideo);
251
- track.setEncoderConfig(trackParams.encoderConfig);
263
+ tracks.forEach((track) => {
264
+ track.mediaStreamTrack.addEventListener('ended', () => {
265
+ this.unpublish(track);
266
+ });
252
267
 
253
- this.logger.debug('createScreenVideoTrack()', { trackParams, track });
254
- return track;
255
- } catch (error) {
256
- this.logger.error('createScreenVideoTrack()', { error, trackParams });
257
- throw new Error(`Can not create screen video track: ${error.message}`);
258
- }
259
- }
268
+ if (track.kind === 'video') {
269
+ track.setLabel(TrackLabel.ScreenVideo);
270
+ track.setEncoderConfig(videoTrackParams.encoderConfig);
271
+ this.logger.debug('createScreenMediaTrack()', { trackParams: videoTrackParams, track });
272
+ return;
273
+ }
260
274
 
261
- async createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track> {
262
- try {
263
- const track = await this.media.createDisplayMediaTrack({
264
- video: false,
265
- audio: true,
266
- });
275
+ track.setLabel(TrackLabel.ScreenAudio);
276
+ if (options?.audio) {
277
+ track.setEncoderConfig(options.audio);
278
+ }
267
279
 
268
- track.mediaStreamTrack.addEventListener('ended', () => {
269
- this.unpublish(track);
280
+ this.logger.debug('createScreenMediaTrack()', { trackParams: options?.audio, track });
270
281
  });
271
282
 
272
- track.setLabel(TrackLabel.ScreenAudio);
273
- if (options?.encoderConfig) {
274
- track.setEncoderConfig(options.encoderConfig);
275
- }
276
-
277
- this.logger.debug('createScreenAudioTrack()', { options, track });
278
- return track;
283
+ return tracks;
279
284
  } catch (error) {
280
- this.logger.error('createScreenAudioTrack()', { error, options });
281
- throw new Error(`Can not create screen audio track: ${error.message}`);
285
+ this.logger.error('createScreenMediaTrack()', { error, trackParams: videoTrackParams });
286
+ throw new Error(`Can not create screen media tracks: ${error.message}`);
282
287
  }
283
288
  }
284
289
 
285
290
  public async publish(tracks: Track | Track[]): Promise<void> {
291
+ if (!this.cahPublish) {
292
+ this.logger.error('publish()', { message: 'Not enough access to publish' });
293
+ throw new Error('Not enough access to publish');
294
+ }
295
+
286
296
  const tracksToPublish = Array.isArray(tracks) ? tracks : [tracks];
287
297
  const actions = tracksToPublish.map(async (track) => {
288
298
  const encodings = 'getEncodings' in track
@@ -409,9 +419,6 @@ class Engine {
409
419
  try {
410
420
  await this.network.socket.request(CHANNEL_EVENTS.channelJoin, params);
411
421
  await this.initialize();
412
- const { peers } = await this.network.socket
413
- .request(CHANNEL_EVENTS.channelGetPeers) as { peers: PeerResponse[] };
414
- peers.forEach((peer: PeerResponse) => this.setPeer(peer));
415
422
  this.channelEventsHandler.subscribeToEvents();
416
423
  this.mediaSoupEventsHandler.subscribeToEvents();
417
424
  this.app = params.appId;
@@ -423,6 +430,10 @@ class Engine {
423
430
  throw error;
424
431
  }
425
432
  }
433
+
434
+ get cahPublish(): boolean {
435
+ return this.peers.find((item) => item.isMe)?.role === 'host';
436
+ }
426
437
  }
427
438
 
428
439
  export default Engine;
@@ -46,28 +46,33 @@ class Media {
46
46
  .find((c) => c.mimeType.toLowerCase() === `video/${track.getPreferredCodec()}`);
47
47
  }
48
48
 
49
- private createTrack(stream: MediaStream): Track {
50
- const mediaStreamTrack = stream.getTracks()[0];
51
-
52
- const track = mediaStreamTrack.kind === 'audio'
53
- ? new AudioTrack(mediaStreamTrack)
54
- : new VideoTrack(mediaStreamTrack);
55
-
56
- this.tracks.set(track.id, track);
57
- this.#logger.debug('createTrack() track created', { streamId: stream.id, trackId: track.id, kind: track.kind });
58
-
59
- return track;
49
+ private createTracks(stream: MediaStream): Track[] {
50
+ const mediaStreamTracks = stream.getTracks();
51
+ return mediaStreamTracks.map((mediaStreamTrack) => {
52
+ const track = mediaStreamTrack.kind === 'audio'
53
+ ? new AudioTrack(mediaStreamTrack)
54
+ : new VideoTrack(mediaStreamTrack);
55
+
56
+ this.tracks.set(track.id, track);
57
+ this.#logger.debug('createTrack() track created', {
58
+ streamId: stream.id,
59
+ trackId: track.id,
60
+ kind: track.kind,
61
+ });
62
+
63
+ return track;
64
+ });
60
65
  }
61
66
 
62
- async createUserMediaTrack(constraints: MediaStreamConstraints): Promise<Track> {
67
+ async createUserMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
63
68
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
64
69
  this.#logger.debug('createUserMediaTrack() stream created', { streamId: stream.id });
65
- return this.createTrack(stream);
70
+ return this.createTracks(stream);
66
71
  }
67
72
 
68
- async createDisplayMediaTrack(constraints: MediaStreamConstraints): Promise<Track> {
73
+ async createDisplayMediaTracks(constraints: MediaStreamConstraints): Promise<Track[]> {
69
74
  const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
70
- return this.createTrack(stream);
75
+ return this.createTracks(stream);
71
76
  }
72
77
 
73
78
  deleteTrack(track: Track) {
package/src/index.ts CHANGED
@@ -2,9 +2,9 @@ import {
2
2
  AvailableMediaDevices,
3
3
  CreateCameraVideoTrackOptions,
4
4
  CreateMicrophoneAudioTrackOptions,
5
- CreateScreenAudioTrackOptions,
6
- CreateScreenVideoTrackOptions,
5
+ CreateScreenMediaOptions,
7
6
  JoinChannelParams,
7
+ Role,
8
8
  Track,
9
9
  } from './types/common';
10
10
  import EnhancedEventEmitter from './EnhancedEventEmitter';
@@ -96,17 +96,17 @@ class Client {
96
96
  return this.engine.createMicrophoneAudioTrack(options);
97
97
  }
98
98
 
99
- createScreenVideoTrack(options?: CreateScreenVideoTrackOptions): Promise<Track> {
100
- return this.engine.createScreenVideoTrack(options);
101
- }
102
-
103
- createScreenAudioTrack(options?: CreateScreenAudioTrackOptions): Promise<Track> {
104
- return this.engine.createScreenAudioTrack(options);
99
+ createScreenMediaTracks(options?: CreateScreenMediaOptions): Promise<Track[]> {
100
+ return this.engine.createScreenMediaTracks(options);
105
101
  }
106
102
 
107
103
  deleteTrack(track: Track): void {
108
104
  return this.engine.deleteTrack(track);
109
105
  }
106
+
107
+ loadPeers(role?: Role): Promise<void> {
108
+ return this.engine.loadPeers(role);
109
+ }
110
110
  }
111
111
 
112
112
  export default Client;
@@ -30,6 +30,8 @@ export type ProducerData = {
30
30
  label: TrackLabel,
31
31
  };
32
32
 
33
+ export type Role = 'audience' | 'host';
34
+
33
35
  export type PeerResponse = {
34
36
  id: string,
35
37
  channelIds: string[],
@@ -37,6 +39,7 @@ export type PeerResponse = {
37
39
  producers: ProducerData[],
38
40
  uid?: string,
39
41
  appData: Record<string, unknown>,
42
+ role: Role,
40
43
  };
41
44
 
42
45
  export type AvailableMediaDevices = {
@@ -50,6 +53,7 @@ export type JoinChannelParams = {
50
53
  sdkSecret: string,
51
54
  uid?: string,
52
55
  appData?: Record<string, unknown>,
56
+ role: Role,
53
57
  };
54
58
 
55
59
  export type ChannelEvent = {
@@ -152,10 +156,15 @@ export type CreateMicrophoneAudioTrackOptions = CreateTrackOptions & {
152
156
  encoderConfig?: AudioEncoderConfig,
153
157
  };
154
158
 
155
- export type CreateScreenAudioTrackOptions = CreateVideoTrackOptions & {
159
+ export type CreateScreenAudioTrackOptions = CreateMicrophoneAudioTrackOptions & {
156
160
  encoderConfig?: AudioEncoderConfig,
157
161
  };
158
162
 
163
+ export type CreateScreenMediaOptions = {
164
+ video: CreateScreenVideoTrackOptions,
165
+ audio: CreateScreenAudioTrackOptions,
166
+ };
167
+
159
168
  export type Track = VideoTrack | AudioTrack;
160
169
 
161
170
  export enum TrackLabel {