@vkontakte/calls-sdk 2.8.11-dev.5500645b.0 → 2.8.11-dev.6d8b858e.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.
@@ -20,7 +20,7 @@ import { IVideoDimentions } from '../types/MediaSettings';
20
20
  import { IAddMovieParams, IUpdateMovieData } from '../types/MovieShare';
21
21
  import MuteStates from '../types/MuteStates';
22
22
  import { CompositeUserId, IGetParticipantsParameters, ParticipantId, ParticipantStateData } from '../types/Participant';
23
- import { ParticipantLayout } from '../types/ParticipantLayout';
23
+ import { DisplayLayoutRequest, ParticipantLayout } from '../types/ParticipantLayout';
24
24
  import { ParticipantListChunkParameters } from '../types/ParticipantListChunk';
25
25
  import ParticipantPriority from '../types/ParticipantPriority';
26
26
  import { ParticipantStreamDescription } from '../types/ParticipantStreamDescription';
@@ -32,6 +32,7 @@ export default class Conversation extends EventEmitter {
32
32
  private readonly _api;
33
33
  private readonly _signaling;
34
34
  private readonly _signalingActor;
35
+ private readonly _displayLayoutRequester;
35
36
  private _mediaSource;
36
37
  private _conversation;
37
38
  private _myLastRequestedLayouts;
@@ -49,6 +50,7 @@ export default class Conversation extends EventEmitter {
49
50
  private _lastSignalledActiveSpeakerId;
50
51
  private _isRealTimeAsrRequested;
51
52
  private _serverSettings;
53
+ private _serverTimeOffset;
52
54
  private static _current;
53
55
  private static _activationMutex;
54
56
  private static _delayedHangup;
@@ -60,7 +62,6 @@ export default class Conversation extends EventEmitter {
60
62
  private _audioFix;
61
63
  private _streamByStreamId;
62
64
  private _streamIdByStreamDescription;
63
- private _participantIdByDebugStreamId;
64
65
  private _streamWaitTimerByStreamDescription;
65
66
  private _sequenceNumberByStreamDescription;
66
67
  private _cooldownTimestampByStreamDescription;
@@ -70,6 +71,7 @@ export default class Conversation extends EventEmitter {
70
71
  static current(): Conversation | null;
71
72
  static hangupAfterInit(): void;
72
73
  static id(): string | null;
74
+ static getSyncedTime(): number;
73
75
  onStart({ opponentIds, opponentType, mediaOptions, payload, joiningAllowed, requireAuthToJoin, onlyAdminCanShareMovie, externalIds, onFastStart, conversationId, }: ConversationOnStartParams): Promise<ConversationData>;
74
76
  onJoin(joinArgs: {
75
77
  conversationId?: string;
@@ -205,6 +207,7 @@ export default class Conversation extends EventEmitter {
205
207
  requestKeyFrame(participantStreamDescription: ParticipantStreamDescription): Promise<void | SignalingMessage>;
206
208
  requestTestMode(consumerCommand: string, producerCommand: string): Promise<void>;
207
209
  updateDisplayLayout(layouts: ParticipantLayout[]): Promise<void>;
210
+ requestDisplayLayout(requests: DisplayLayoutRequest[]): Promise<void>;
208
211
  feedback(key: string): Promise<SignalingMessage>;
209
212
  userFeedbackStats(userResponse: number, reason?: string, groupCallUsersCount?: number): void;
210
213
  sendClientEvent(eventType: string, eventData?: Record<string, string | number | boolean>, immediately?: boolean): void;
@@ -217,7 +220,6 @@ export default class Conversation extends EventEmitter {
217
220
  private _sendUpdateDisplayLayout;
218
221
  private _cleanupCooldownQueue;
219
222
  private _onParticipantSourcesUpdate;
220
- private _registerVideoStreamDebugParticipant;
221
223
  private _onParticipantPromoted;
222
224
  private _onChatRoomUpdated;
223
225
  private _onSharedMovieUpdate;
@@ -374,8 +376,6 @@ export default class Conversation extends EventEmitter {
374
376
  private _onAudioMixStall;
375
377
  private _onRemoteSignalledStall;
376
378
  private _onRemoteDataStats;
377
- private _onVideoStreamDebug;
378
- private _resolveVideoStreamDebugParticipantId;
379
379
  private _fixAudioDevice;
380
380
  private _fixVideoDevice;
381
381
  private _toggleJoinAvailability;
@@ -0,0 +1,32 @@
1
+ import BaseApi from '../abstract/BaseApi';
2
+ import { DisplayLayoutRequest, ParticipantLayout } from '../types/ParticipantLayout';
3
+ import { Participant, ParticipantId } from '../types/Participant';
4
+ type DisplayLayoutRequesterParams = {
5
+ api: BaseApi;
6
+ getParticipants: () => Promise<Record<ParticipantId, Participant>>;
7
+ isMe: (participantId: ParticipantId) => boolean;
8
+ updateDisplayLayout: (layouts: ParticipantLayout[]) => Promise<void>;
9
+ };
10
+ export default class DisplayLayoutRequester {
11
+ private readonly _api;
12
+ private readonly _getParticipants;
13
+ private readonly _isMe;
14
+ private readonly _updateDisplayLayout;
15
+ private _requestedLayouts;
16
+ private _pendingRequests;
17
+ private _pendingPromises;
18
+ private _timer;
19
+ private _inFlight;
20
+ private _lastFlushAt;
21
+ private _generation;
22
+ constructor({ api, getParticipants, isMe, updateDisplayLayout }: DisplayLayoutRequesterParams);
23
+ request(requests: DisplayLayoutRequest[]): Promise<void>;
24
+ cleanupParticipant(participantId: ParticipantId): void;
25
+ clear(): void;
26
+ private _schedule;
27
+ private _flush;
28
+ private _getRequestLayouts;
29
+ private _getDiff;
30
+ private _isChanged;
31
+ }
32
+ export {};
@@ -10,30 +10,25 @@ export default class Logger extends BaseLogger {
10
10
  static setConversationIdProvider(provider: () => string | null): void;
11
11
  static create(api: BaseApi, externalLogger: BaseLogger | null): void;
12
12
  static log(name: StatLog, value?: string, immediately?: boolean): void;
13
- static logCustom(name: StatLog, params: Record<string, string | number>, immediately?: boolean): void;
14
- static logClientStats(params: Record<string, string | number | undefined | null>, immediately?: boolean): void;
15
- static logClientEvent(params: Record<string, string | number | undefined | null>, immediately?: boolean): void;
13
+ static logClientStats(params: Record<string, string | number | boolean | undefined | null>, immediately?: boolean): void;
14
+ static logClientEvent(params: Record<string, string | number | boolean | undefined | null>, immediately?: boolean): void;
16
15
  static destroy(): void;
17
16
  private readonly _externalLogger;
18
17
  private readonly _api;
19
18
  private readonly _batchInterval;
20
- private _batchedLogItems;
21
19
  private _batchedClientStats;
22
20
  private _batchedClientEvents;
23
21
  private _batchTimeout;
24
22
  private _serverTimeDelta;
25
23
  constructor(api: BaseApi, externalLogger: BaseLogger | null);
26
24
  log(name: StatLog, value?: string, immediately?: boolean): void;
27
- logCustom(name: string, params: Record<string, string | number>, immediately?: boolean): void;
28
- logClientStats(data: Record<string, string | number | undefined | null>, immediately?: boolean): void;
29
- logClientEvent(data: Record<string, string | number | undefined | null>, immediately?: boolean): void;
25
+ logClientStats(data: Record<string, string | number | boolean | undefined | null>, immediately?: boolean): void;
26
+ logClientEvent(data: Record<string, string | number | boolean | undefined | null>, immediately?: boolean): void;
30
27
  destroy(): void;
31
- private _logInternal;
32
28
  private _getConversationId;
33
29
  private _sendBatch;
34
30
  private _startTimeout;
35
31
  private _stopTimeout;
36
- private _sendLogItems;
37
32
  private _sendClientStats;
38
33
  private _sendClientEvents;
39
34
  private _calculateServerTimeDelta;
@@ -7,9 +7,6 @@ import BaseTransport from './BaseTransport';
7
7
  export default class DirectTransport extends BaseTransport {
8
8
  private readonly _participantId;
9
9
  private readonly _isMaster;
10
- private _hasOutboundVideoDebug;
11
- private _hasInboundVideoDebug;
12
- private _videoDebugBytes;
13
10
  private _remoteSDP;
14
11
  private _remoteCandidates;
15
12
  private _lastRemoteSDP;
@@ -70,8 +67,6 @@ export default class DirectTransport extends BaseTransport {
70
67
  private static _patchRemoteDescription;
71
68
  private _onReplacedTrack;
72
69
  private _startStatInterval;
73
- private _reportVideoStreamDebug;
74
- private _getVideoDebugBitrate;
75
70
  /**
76
71
  * Check SVC support
77
72
  * @see https://webrtc.internaut.com/mc/
@@ -5,9 +5,6 @@ import ServerSettings from '../../types/ServerSettings';
5
5
  import { MediaSource } from '../MediaSource';
6
6
  import BaseTransport from './BaseTransport';
7
7
  export default class ServerTransport extends BaseTransport {
8
- private _hasOutboundVideoDebug;
9
- private _inboundVideoDebugParticipants;
10
- private _videoDebugBytes;
11
8
  private _producerNotification;
12
9
  private _producerCommand;
13
10
  private _producerScreen;
@@ -39,7 +36,6 @@ export default class ServerTransport extends BaseTransport {
39
36
  private _participantIdRegistry;
40
37
  private _disabledSenders;
41
38
  private _rtpReceiversByStreamId;
42
- private _participantIdByVideoTrackId;
43
39
  private _producerSessionId;
44
40
  private _newAudioShareTrack;
45
41
  private _simulcastInfo;
@@ -78,10 +74,6 @@ export default class ServerTransport extends BaseTransport {
78
74
  private _stopSettingsInterval;
79
75
  private _collectStat;
80
76
  private _reportStats;
81
- private _reportVideoStreamDebug;
82
- private _getVideoDebugStreamId;
83
- private _getVideoDebugTrackId;
84
- private _getVideoDebugBitrate;
85
77
  private _detectStaleTracks;
86
78
  private _allocateConsumer;
87
79
  private _processOffer;
@@ -1,6 +1,5 @@
1
1
  import type { AnimojiSvgData, RGBTuple } from '@vkontakte/calls-vmoji';
2
2
  import BaseSignaling from '../../abstract/BaseSignaling';
3
- import { VideoStreamDebugDirection } from '../../static/DebugVideoStats';
4
3
  import { ParticipantId } from '../../types/Participant';
5
4
  import ServerSettings from '../../types/ServerSettings';
6
5
  import EventEmitter from '../EventEmitter';
@@ -22,8 +21,7 @@ export declare const enum TransportEvent {
22
21
  ASR_TRANSCRIPTION = "ASR_TRANSCRIPTION",
23
22
  ANIMOJI_STREAM = "ANIMOJI_STREAM",
24
23
  ANIMOJI_ERROR = "ANIMOJI_ERROR",
25
- SCREEN_SHARING_STAT = "SCREEN_SHARING_STAT",
26
- VIDEO_STREAM_DEBUG = "VIDEO_STREAM_DEBUG"
24
+ SCREEN_SHARING_STAT = "SCREEN_SHARING_STAT"
27
25
  }
28
26
  export declare const enum TransportState {
29
27
  IDLE = "IDLE",
@@ -38,18 +36,6 @@ export declare const enum TransportTopology {
38
36
  DIRECT = "DIRECT",
39
37
  SERVER = "SERVER"
40
38
  }
41
- export type VideoStreamDebugEvent = {
42
- direction: VideoStreamDebugDirection;
43
- streamId?: ParticipantId;
44
- trackId?: string;
45
- mimeType?: string;
46
- width?: number;
47
- height?: number;
48
- frameRate?: number;
49
- bitrate?: number;
50
- packetsLost?: number;
51
- clear?: boolean;
52
- };
53
39
  export declare class Transport extends EventEmitter {
54
40
  private readonly _signaling;
55
41
  private readonly _mediaSource;
@@ -99,7 +85,6 @@ export declare class Transport extends EventEmitter {
99
85
  private _onPeerConnectionClosed;
100
86
  private _onServerAudioMixStall;
101
87
  private _onRemoteDataStats;
102
- private _onVideoStreamDebug;
103
88
  private _onRemoteTrackAdded;
104
89
  private _onRemoteTrackRemoved;
105
90
  private _onAsrTranscription;
package/default/Api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import BaseApi, { ClientEvent, ClientStats, LogItem } from '../abstract/BaseApi';
1
+ import BaseApi, { ClientEvent, ClientStats } from '../abstract/BaseApi';
2
2
  import CallType from '../enums/CallType';
3
3
  import HangupType from '../enums/HangupType';
4
4
  import ConversationParams from '../types/ConversationParams';
@@ -15,7 +15,6 @@ export default class Api extends BaseApi {
15
15
  protected _call(method: string, data?: any, noSession?: boolean): Promise<any>;
16
16
  userId(participantId: ParticipantId): Promise<ExternalParticipantId>;
17
17
  authorize(): Promise<void>;
18
- log(items: LogItem[]): void;
19
18
  logClientStats(items: ClientStats[]): void;
20
19
  logClientEvents(items: ClientEvent[]): void;
21
20
  uploadDebugLogs(conversationId: string, startTime: number, endTime: number, log: string): Promise<void>;
@@ -65,7 +64,6 @@ export default class Api extends BaseApi {
65
64
  getUserId(): OkUserId | null;
66
65
  setUserId(userId: OkUserId): void;
67
66
  hangupConversation(conversationId: string, reason?: HangupType): void;
68
- sendUserFeedbackStats(conversationId: string, userResponse: number, reason?: string, groupCallUsersCount?: number): void;
69
67
  removeHistoryRecords(recordIds: number[]): Promise<void>;
70
68
  cleanup(): void;
71
69
  private _getExternalIdsByOkIds;
@@ -39,7 +39,6 @@ export default class Signaling extends BaseSignaling {
39
39
  protected conversationReject: Function | null;
40
40
  private connected;
41
41
  private listenersReady;
42
- private postfix;
43
42
  protected peerId: number | null;
44
43
  protected conversationId: string | null;
45
44
  private reconnectTimer;
@@ -164,6 +163,7 @@ export default class Signaling extends BaseSignaling {
164
163
  startAsr(params: IAsrStartParams): Promise<SignalingMessage>;
165
164
  stopAsr(params?: IAsrStopParams): Promise<SignalingMessage>;
166
165
  requestAsr(request: boolean): Promise<SignalingMessage>;
166
+ private _buildUrl;
167
167
  protected _connect(connectionType: SignalingConnectionType, useWebTransport: boolean): void;
168
168
  protected _disconnect(code?: number): void;
169
169
  private _onOpen;
@@ -173,6 +173,7 @@ export default class Signaling extends BaseSignaling {
173
173
  protected _handleCachedMessages(): void;
174
174
  private _throwError;
175
175
  private _onError;
176
+ private _serializeErrorEvent;
176
177
  protected _onClose(event: CloseEvent): void;
177
178
  protected _closeSocket(error?: Error | null): void;
178
179
  protected _reconnect(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vkontakte/calls-sdk",
3
- "version": "2.8.11-dev.5500645b.0",
3
+ "version": "2.8.11-dev.6d8b858e.0",
4
4
  "author": "vk.com",
5
5
  "description": "Library for video calls based on the vk.com platform",
6
6
  "homepage": "https://vk.com",
package/static/Debug.d.ts CHANGED
@@ -8,10 +8,10 @@ export declare enum DebugMessageType {
8
8
  ERROR = "ERROR"
9
9
  }
10
10
  declare namespace Debug {
11
- let debug: Function;
12
- let log: Function;
13
- let warn: Function;
14
- let error: Function;
11
+ function debug(...args: any[]): void;
12
+ function log(...args: any[]): void;
13
+ function warn(...args: any[]): void;
14
+ function error(...args: any[]): void;
15
15
  function enabled(): boolean;
16
16
  function toggle(enable: boolean): void;
17
17
  function send(type: DebugMessageType, ...args: any[]): void;
@@ -563,9 +563,5 @@ declare namespace External {
563
563
  * Собеседник подключился к сигналлингу
564
564
  */
565
565
  function onPeerRegistered(): void;
566
- /**
567
- * Дебаг информация по видеопотоку, отформатированная SDK для отображения в клиенте.
568
- */
569
- function onVideoStreamDebug(externalParticipantId: ExternalParticipantId, debug: string): void;
570
566
  }
571
567
  export default External;
@@ -104,13 +104,6 @@ export type ParamsObject = {
104
104
  debugLog: boolean;
105
105
  /** @hidden */
106
106
  debug: boolean;
107
- /**
108
- * Вкчлючает отправку на клиент статы по входящим/исходящим видео-потокам
109
- * @hidden
110
- *
111
- * _По умолчанию: `false`_
112
- */
113
- debugVideoStreams: boolean;
114
107
  /**
115
108
  * Выставляет таймаут в ms для fetch запросов от sdk
116
109
  * По умолчанию 5000 ms
@@ -313,6 +306,12 @@ export type ParamsObject = {
313
306
  * _По умолчанию: `30`_
314
307
  */
315
308
  videoTracksCount: number;
309
+ /**
310
+ * Минимальный интервал отправки diff для requestDisplayLayout в миллисекундах.
311
+ *
312
+ * _По умолчанию: `250`_
313
+ */
314
+ requestDisplayLayoutThrottleMs: number;
316
315
  /** @hidden */
317
316
  movieShare: boolean;
318
317
  /** @hidden */
@@ -870,15 +869,6 @@ export type ParamsObject = {
870
869
  * Собеседник подключился к сигналлингу
871
870
  */
872
871
  onPeerRegistered?: () => void;
873
- /**
874
- * Дебаг информация по видеопотоку, отформатированная SDK для отображения в клиенте.
875
- *
876
- * Работает только если включен `debugVideoStreams`.
877
- *
878
- * @param externalParticipantId Id участника, к которому относится видеопоток
879
- * @param debug Отформатированный текст для отображения
880
- */
881
- onVideoStreamDebug?: (externalParticipantId: ExternalParticipantId, debug: string) => void;
882
872
  };
883
873
  export default abstract class Params {
884
874
  private static _params;
@@ -930,7 +920,6 @@ export default abstract class Params {
930
920
  static get waitMessageDelay(): number;
931
921
  static get waitAnotherTabDelay(): number;
932
922
  static get debugLog(): boolean;
933
- static get debugVideoStreams(): false | ((externalParticipantId: ExternalParticipantId, debug: string) => void) | undefined;
934
923
  static get forceRelayPolicy(): boolean;
935
924
  static set forceRelayPolicy(value: boolean);
936
925
  static get videoMinWidth(): number;
@@ -980,6 +969,7 @@ export default abstract class Params {
980
969
  static get audioNack(): boolean;
981
970
  static get movieShare(): boolean;
982
971
  static get videoTracksCount(): number;
972
+ static get requestDisplayLayoutThrottleMs(): number;
983
973
  static get breakVideoPayloadTypes(): boolean;
984
974
  static get useCallsToContacts(): boolean;
985
975
  static get useParticipantListChunk(): boolean;
@@ -1027,6 +1017,7 @@ export default abstract class Params {
1027
1017
  screenShareCongestionControl: boolean;
1028
1018
  screenShareCongestionControlThreshold: number;
1029
1019
  videoTracksCount: number;
1020
+ requestDisplayLayoutThrottleMs: number;
1030
1021
  asrDataChannel: boolean;
1031
1022
  videoMaxHeight: number;
1032
1023
  videoMaxWidth: number;
@@ -75,6 +75,7 @@ declare namespace WebRTCUtils {
75
75
  * Проверяет получены ли разрешения, необходимые для текущего звонка
76
76
  */
77
77
  function hasPermissions(needVideo?: boolean): boolean;
78
+ function releaseFirefoxMicrophonePermissionWarmup(): void;
78
79
  /**
79
80
  * Запрашивает камеру и микрофон пользователя
80
81
  *
@@ -54,4 +54,37 @@ export type ParticipantLayout = (Layout | StopStream | RequestKeyFrame) & {
54
54
  */
55
55
  streamName?: string;
56
56
  };
57
+ /**
58
+ * Запрос стрима, который клиент хочет получать для отображения.
59
+ */
60
+ export type DisplayLayoutRequest = {
61
+ /**
62
+ * Внешний ID пользователя
63
+ */
64
+ uid: ExternalParticipantId | string;
65
+ /**
66
+ * Тип медиа (видео с камеры, картинка с экрана, лайв или мувик)
67
+ */
68
+ mediaType: MediaType;
69
+ /**
70
+ * Ширина окошка в котором отображается видео, в пикселях
71
+ */
72
+ width: number;
73
+ /**
74
+ * Высота окошка в котором отображается видео, в пикселях
75
+ */
76
+ height: number;
77
+ /**
78
+ * Отображать видео как CSS object-fit: cover. По умолчанию используется contain.
79
+ */
80
+ cover?: boolean;
81
+ /**
82
+ * Приоритет
83
+ */
84
+ priority?: number;
85
+ /**
86
+ * ID лайва или мувика. null для камеры или скрин-шары.
87
+ */
88
+ streamName?: string;
89
+ };
57
90
  export default ParticipantLayout;
@@ -41,10 +41,11 @@ export interface CallStatReport {
41
41
  remote_connection_type?: RTCIceCandidateType;
42
42
  }
43
43
  export interface ICallStatLog extends Pick<CallStatReport, 'call_topology' | 'nack_sent' | 'nack_received' | 'pli_sent' | 'pli_received' | 'fir_sent' | 'fir_received' | 'frames_dropped' | 'stat_time_delta'>, Pick<Partial<CallStatReport>, 'rtt' | 'jitter_video' | 'jitter_audio' | 'interframe_delay_variance' | 'freeze_count' | 'total_freezes_duration' | 'ss_freeze_count' | 'ss_total_freezes_duration' | 'inserted_audio_samples_for_deceleration' | 'removed_audio_samples_for_acceleration' | 'concealed_audio_samples' | 'total_audio_energy' | 'local_address' | 'local_connection_type' | 'network_type' | 'transport' | 'remote_address' | 'remote_connection_type'> {
44
+ is_simulcast?: boolean;
44
45
  concealed_silent_audio_samples?: number;
45
46
  concealment_audio_avg_size?: number;
46
47
  video_loss?: number;
47
48
  audio_loss?: number;
48
49
  cpu_hardware_concurrency?: number;
49
- [k: string]: string | number | undefined;
50
+ [k: string]: string | number | boolean | undefined;
50
51
  }
@@ -366,6 +366,9 @@ declare namespace SignalingMessage {
366
366
  export interface Connection extends Notification {
367
367
  endpoint: string;
368
368
  conversation: Conversation;
369
+ conversationParams: {
370
+ serverTime: number;
371
+ };
369
372
  isConcurrent: boolean;
370
373
  peerId: PeerId;
371
374
  mediaModifiers: MediaModifiers;
@@ -113,10 +113,6 @@ export interface StatRtp extends StatRtpOutboundVideo {
113
113
  * Идентификатор пользователя, связанный с этим потоком
114
114
  */
115
115
  userId?: string;
116
- /**
117
- * Идентификатор media track, связанный с этим RTP потоком
118
- */
119
- trackId?: string;
120
116
  /**
121
117
  * Пропускная способность в битах в секунду
122
118
  */
@@ -20,6 +20,7 @@ declare class WebTransportEventual {
20
20
  private readChunks;
21
21
  private readLoop;
22
22
  send(data: string): Promise<void>;
23
+ private createErrorEvent;
23
24
  close(code?: number, reason?: string): void;
24
25
  static isBrowserSupported(): boolean;
25
26
  }
@@ -1,22 +0,0 @@
1
- import { ExternalParticipantId } from '../types/ExternalId';
2
- export declare const enum VideoStreamDebugDirection {
3
- INBOUND = "inbound",
4
- OUTBOUND = "outbound"
5
- }
6
- export type VideoStreamDebugData = {
7
- direction: VideoStreamDebugDirection;
8
- externalParticipantId: ExternalParticipantId;
9
- trackId?: string;
10
- mimeType?: string;
11
- width?: number;
12
- height?: number;
13
- frameRate?: number;
14
- bitrate?: number;
15
- packetsLost?: number;
16
- };
17
- declare namespace DebugVideoStats {
18
- function format(data: VideoStreamDebugData): string;
19
- function send(data: VideoStreamDebugData): void;
20
- function clear(externalParticipantId: ExternalParticipantId): void;
21
- }
22
- export default DebugVideoStats;