@livedigital/client 2.22.1 → 2.23.0-get-stats.1

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.
@@ -236,6 +236,60 @@ export declare enum DeviceErrors {
236
236
  DeviceIsBusy = "DeviceIsBusy",
237
237
  NotAllowedError = "NotAllowedError"
238
238
  }
239
+ export declare type TrackInboundStats = {
240
+ consumerId: string;
241
+ codec?: RTCRtpCodecParameters;
242
+ currentSpatialLayerParams?: SpatialLayerParams;
243
+ availableSpatialLayers?: SpatialLayerParams[];
244
+ requestedSpatialLayer?: number;
245
+ score: number;
246
+ dtlsState?: RTCDtlsTransportState;
247
+ rtcStats?: ExtendedRTCInboundRtpStreamStats;
248
+ };
249
+ export declare type TrackOutboundStats = {
250
+ producerId?: string;
251
+ codec?: RTCRtpCodecParameters;
252
+ dtlsState?: RTCDtlsTransportState;
253
+ rtcStats?: RTCOutboundRtpStreamStats;
254
+ };
255
+ export declare type PeerTrackInfo = {
256
+ trackId: string;
257
+ readyState: MediaStreamTrackState;
258
+ isRemote: boolean;
259
+ kind: MediaKind;
260
+ label: TrackLabel;
261
+ paused: boolean;
262
+ width?: number;
263
+ height?: number;
264
+ frameRate?: number;
265
+ aspectRatio?: number;
266
+ trackInboundStats?: TrackInboundStats;
267
+ trackOutboundStats?: TrackOutboundStats;
268
+ };
269
+ export declare type BaseTrackInfo = {
270
+ trackId: string;
271
+ readyState: MediaStreamTrackState;
272
+ kind: MediaKind;
273
+ label: TrackLabel;
274
+ paused: boolean;
275
+ width?: number;
276
+ height?: number;
277
+ frameRate?: number;
278
+ aspectRatio?: number;
279
+ trackOutboundStats?: TrackOutboundStats;
280
+ };
281
+ export declare type PeerInfo = {
282
+ id: string;
283
+ appData: Record<string, unknown>;
284
+ role: Role;
285
+ isBroadcaster: boolean;
286
+ channelIds: string[];
287
+ appId: string;
288
+ uid?: string;
289
+ loginDate: Date;
290
+ connectionQuality: number;
291
+ tracks: PeerTrackInfo[];
292
+ };
239
293
  export declare type UpdatePeerAppDataPayload = {
240
294
  peerId: string;
241
295
  appData: Record<string, unknown>;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.22.1",
5
+ "version": "2.23.0-get-stats.1",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -8,6 +8,7 @@ import {
8
8
  ChangePreferredLayersPayload,
9
9
  Role,
10
10
  ProducerSetMaxSpatialLayer,
11
+ PeerInfo,
11
12
  } from '../types/common';
12
13
  import EnhancedEventEmitter from '../EnhancedEventEmitter';
13
14
  import Engine from './index';
@@ -109,6 +110,10 @@ class Peer {
109
110
  return this.id === this.engine.mySocketId;
110
111
  }
111
112
 
113
+ get isBroadcaster(): boolean {
114
+ return this.channelIds.length > 1;
115
+ }
116
+
112
117
  public get publishedMedia(): PayloadOfPublishedMedia[] {
113
118
  return Array.from(this.producers.values()).map((producer) => ({
114
119
  producerId: producer.id,
@@ -157,6 +162,26 @@ class Peer {
157
162
  }
158
163
  }
159
164
 
165
+ async getInfo(): Promise<PeerInfo> {
166
+ try {
167
+ return {
168
+ id: this.id,
169
+ appData: this.appData,
170
+ role: this.role,
171
+ isBroadcaster: this.isBroadcaster,
172
+ channelIds: this.channelIds,
173
+ appId: this.appId,
174
+ uid: this.uid,
175
+ loginDate: this.loginDate,
176
+ connectionQuality: this.overallConnectionQuality,
177
+ tracks: await Promise.all(Array.from(this.tracks.values()).map((track) => track.getInfo())),
178
+ };
179
+ } catch (error) {
180
+ this.logger.error('getInfo()', { peer: this, error });
181
+ throw new Error('Error get info');
182
+ }
183
+ }
184
+
160
185
  private handleNewProducer(producerData: ProducerData): void {
161
186
  if (this.producers.get(producerData.id)) {
162
187
  return;
@@ -6,6 +6,7 @@ import {
6
6
  ExtendedRTCInboundRtpStreamStats,
7
7
  LogLevel,
8
8
  SpatialLayerParams,
9
+ TrackInboundStats,
9
10
  } from '../types/common';
10
11
  import Logger from './Logger';
11
12
 
@@ -167,6 +168,24 @@ class PeerConsumer {
167
168
  this.currentMaxSpatialLayer = spatialLayer;
168
169
  }
169
170
 
171
+ async getStats(): Promise<TrackInboundStats | undefined> {
172
+ try {
173
+ return {
174
+ consumerId: this.consumer.id,
175
+ codec: this.consumer.rtpParameters.codecs.find((codec) => codec.mimeType !== 'rtx'),
176
+ score: this.score,
177
+ dtlsState: this.consumer.rtpReceiver?.transport?.state,
178
+ availableSpatialLayers: this.availableSpatialLayers,
179
+ currentSpatialLayerParams: this.currentSpatialLayerParams,
180
+ requestedSpatialLayer: this.requestedSpatialLayer,
181
+ rtcStats: await this.getInboundRTPStreamStats(),
182
+ };
183
+ } catch (error) {
184
+ this.logger.debug('getStats()', { error });
185
+ return undefined;
186
+ }
187
+ }
188
+
170
189
  async getInboundRTPStreamStats(): Promise<ExtendedRTCInboundRtpStreamStats> {
171
190
  return new Promise((resolve, reject) => {
172
191
  const getRTCStatsReport = async (attempt = 0) => {
@@ -1,10 +1,11 @@
1
1
  import { Producer } from 'mediasoup-client/lib/Producer';
2
2
  import { MediaKind } from 'mediasoup-client/lib/RtpParameters';
3
3
  import {
4
+ BaseTrackInfo,
4
5
  EncoderConfig,
5
6
  LogLevel,
6
7
  SocketResponse,
7
- TrackLabel,
8
+ TrackLabel, TrackOutboundStats,
8
9
  TrackProduceParams,
9
10
  } from '../../../types/common';
10
11
  import Logger from '../../Logger';
@@ -41,7 +42,7 @@ class BaseTrack {
41
42
 
42
43
  #producerRestarted = false;
43
44
 
44
- #clientEventEmitter: EnhancedEventEmitter;
45
+ readonly #clientEventEmitter: EnhancedEventEmitter;
45
46
 
46
47
  #closed = false;
47
48
 
@@ -379,6 +380,69 @@ class BaseTrack {
379
380
  throw new Error('Can`t resume track');
380
381
  }
381
382
  }
383
+
384
+ async getInfo(): Promise<BaseTrackInfo> {
385
+ const {
386
+ width, height, frameRate, aspectRatio,
387
+ } = this.#mediaStreamTrack.getSettings();
388
+ return {
389
+ trackId: this.#mediaStreamTrack.id,
390
+ readyState: this.#mediaStreamTrack.readyState,
391
+ kind: this.kind,
392
+ label: this.label,
393
+ width,
394
+ height,
395
+ frameRate,
396
+ aspectRatio,
397
+ paused: this.isPaused,
398
+ trackOutboundStats: await this.getStats(),
399
+ };
400
+ }
401
+
402
+ async getStats(): Promise<TrackOutboundStats | undefined> {
403
+ try {
404
+ return {
405
+ producerId: this.producer?.id,
406
+ codec: this.producer?.rtpParameters.codecs.find((codec) => codec.mimeType !== 'rtx'),
407
+ dtlsState: this.producer?.rtpSender?.transport?.state,
408
+ rtcStats: await this.getOutboundRTPStreamStats(),
409
+ };
410
+ } catch (error) {
411
+ this.logger.debug('getStats()', { error });
412
+ return undefined;
413
+ }
414
+ }
415
+
416
+ private getOutboundRTPStreamStats(): Promise<RTCOutboundRtpStreamStats> {
417
+ return new Promise((resolve, reject) => {
418
+ const getRTCStatsReport = async (attempt = 0) => {
419
+ if (!this.producer) {
420
+ throw new Error('Producer is missed');
421
+ }
422
+
423
+ try {
424
+ const rtcStatsReport = await this.producer.getStats();
425
+ const outboundRTPStreamStats: RTCOutboundRtpStreamStats = Array.from(rtcStatsReport.values())
426
+ .find((stat) => stat.type === 'outbound-rtp');
427
+ if (!outboundRTPStreamStats && attempt < 5) {
428
+ setTimeout(() => getRTCStatsReport(attempt + 1), 150);
429
+ return;
430
+ }
431
+
432
+ if (attempt >= 5 && !outboundRTPStreamStats) {
433
+ reject(new Error('OutboundRTPStreamStat not exist'));
434
+ }
435
+
436
+ resolve(outboundRTPStreamStats);
437
+ } catch (error) {
438
+ reject(new Error('Can not get RTCStatsReport'));
439
+ this.logger.debug('getOutboundRTPStreamStats()', { error });
440
+ }
441
+ };
442
+
443
+ getRTCStatsReport();
444
+ });
445
+ }
382
446
  }
383
447
 
384
448
  export default BaseTrack;
@@ -3,7 +3,8 @@ import {
3
3
  PreferredLayersParams,
4
4
  SetConsumerPriorityParams,
5
5
  SpatialLayerParams,
6
- TrackLabel,
6
+ PeerTrackInfo,
7
+ TrackLabel, TrackOutboundStats,
7
8
  } from '../../../types/common';
8
9
  import Logger from '../../Logger';
9
10
  import { MEDIASOUP_EVENTS, PEER_EVENTS } from '../../../constants/events';
@@ -57,6 +58,10 @@ class PeerTrack {
57
58
  this.#peerEventEmitter.safeEmit(PEER_EVENTS.trackStart, this);
58
59
  }
59
60
 
61
+ get isRemote(): boolean {
62
+ return !!this.consumer;
63
+ }
64
+
60
65
  get mediaStreamTrack(): MediaStreamTrack {
61
66
  return this.#mediaStreamTrack;
62
67
  }
@@ -238,6 +243,39 @@ class PeerTrack {
238
243
  return this.consumer.availableSpatialLayers;
239
244
  }
240
245
 
246
+ async getInfo(): Promise<PeerTrackInfo> {
247
+ const {
248
+ width, height, frameRate, aspectRatio,
249
+ } = this.#mediaStreamTrack.getSettings();
250
+ return {
251
+ trackId: this.#mediaStreamTrack.id,
252
+ readyState: this.#mediaStreamTrack.readyState,
253
+ isRemote: this.isRemote,
254
+ kind: this.kind,
255
+ label: this.label,
256
+ width,
257
+ height,
258
+ frameRate,
259
+ aspectRatio,
260
+ paused: this.#paused,
261
+ trackInboundStats: await this.consumer?.getStats(),
262
+ trackOutboundStats: await this.getOutboundStats(),
263
+ };
264
+ }
265
+
266
+ private async getOutboundStats(): Promise<TrackOutboundStats | undefined> {
267
+ if (this.isRemote) {
268
+ return undefined;
269
+ }
270
+
271
+ const outboundTrack = this.#engine.media.getAllTracks().find((track) => track.getLabel() === this.label);
272
+ if (!outboundTrack) {
273
+ return undefined;
274
+ }
275
+
276
+ return outboundTrack.getStats();
277
+ }
278
+
241
279
  private async requestMaxSpatialLayer(consumerId: string, spatialLayer: number): Promise<void> {
242
280
  try {
243
281
  if (!this.consumer) {
@@ -274,6 +274,65 @@ export enum DeviceErrors {
274
274
  NotAllowedError = 'NotAllowedError',
275
275
  }
276
276
 
277
+ export type TrackInboundStats = {
278
+ consumerId: string,
279
+ codec?: RTCRtpCodecParameters,
280
+ currentSpatialLayerParams?: SpatialLayerParams,
281
+ availableSpatialLayers?: SpatialLayerParams[],
282
+ requestedSpatialLayer?: number,
283
+ score: number,
284
+ dtlsState?: RTCDtlsTransportState,
285
+ rtcStats?: ExtendedRTCInboundRtpStreamStats,
286
+ };
287
+
288
+ export type TrackOutboundStats = {
289
+ producerId?: string,
290
+ codec?: RTCRtpCodecParameters,
291
+ dtlsState?: RTCDtlsTransportState,
292
+ rtcStats?: RTCOutboundRtpStreamStats,
293
+ };
294
+
295
+ export type PeerTrackInfo = {
296
+ trackId: string,
297
+ readyState: MediaStreamTrackState,
298
+ isRemote: boolean,
299
+ kind: MediaKind,
300
+ label: TrackLabel,
301
+ paused: boolean,
302
+ width?: number,
303
+ height?: number,
304
+ frameRate?: number,
305
+ aspectRatio?: number,
306
+ trackInboundStats?: TrackInboundStats,
307
+ trackOutboundStats?: TrackOutboundStats,
308
+ };
309
+
310
+ export type BaseTrackInfo = {
311
+ trackId: string,
312
+ readyState: MediaStreamTrackState,
313
+ kind: MediaKind,
314
+ label: TrackLabel,
315
+ paused: boolean,
316
+ width?: number,
317
+ height?: number,
318
+ frameRate?: number,
319
+ aspectRatio?: number,
320
+ trackOutboundStats?: TrackOutboundStats,
321
+ };
322
+
323
+ export type PeerInfo = {
324
+ id: string,
325
+ appData: Record<string, unknown>,
326
+ role: Role,
327
+ isBroadcaster: boolean;
328
+ channelIds: string[],
329
+ appId: string,
330
+ uid?: string,
331
+ loginDate: Date,
332
+ connectionQuality: number,
333
+ tracks: PeerTrackInfo[],
334
+ };
335
+
277
336
  export type UpdatePeerAppDataPayload = {
278
337
  peerId: string,
279
338
  appData: Record<string, unknown>,