sip-connector 20.4.1 → 20.6.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 (47) hide show
  1. package/README.md +18 -1264
  2. package/dist/@SipConnector-BVCkiu8l.cjs +1 -0
  3. package/dist/{@SipConnector-FYEV5h4G.js → @SipConnector-nmoTV9l_.js} +758 -719
  4. package/dist/ApiManager/events.d.ts +0 -1
  5. package/dist/ApiManager/index.d.ts +2 -2
  6. package/dist/AutoConnectorManager/index.d.ts +2 -1
  7. package/dist/CallManager/CallStateMachine.d.ts +0 -7
  8. package/dist/CallManager/events.d.ts +3 -3
  9. package/dist/CallManager/index.d.ts +2 -2
  10. package/dist/ConferenceStateManager/events.d.ts +0 -1
  11. package/dist/ConferenceStateManager/index.d.ts +2 -3
  12. package/dist/ConnectionManager/ConnectionStateMachine.d.ts +20 -17
  13. package/dist/ConnectionManager/events.d.ts +0 -1
  14. package/dist/ConnectionManager/index.d.ts +1 -1
  15. package/dist/IncomingCallManager/@IncomingCallManager.d.ts +1 -1
  16. package/dist/IncomingCallManager/events.d.ts +3 -4
  17. package/dist/IncomingCallManager/index.d.ts +2 -2
  18. package/dist/MainStreamHealthMonitor/index.d.ts +2 -0
  19. package/dist/PresentationManager/events.d.ts +0 -1
  20. package/dist/PresentationManager/index.d.ts +2 -1
  21. package/dist/SessionManager/@SessionManager.d.ts +30 -0
  22. package/dist/SessionManager/events.d.ts +14 -0
  23. package/dist/SessionManager/index.d.ts +6 -0
  24. package/dist/{session → SessionManager}/selectors.d.ts +2 -1
  25. package/dist/SessionManager/types.d.ts +40 -0
  26. package/dist/SipConnector/@SipConnector.d.ts +5 -4
  27. package/dist/SipConnector/events.d.ts +15 -13
  28. package/dist/SipConnector/index.d.ts +1 -1
  29. package/dist/SipConnectorFacade/@SipConnectorFacade.d.ts +3 -3
  30. package/dist/StatsManager/@StatsManager.d.ts +12 -8
  31. package/dist/StatsManager/constants.d.ts +1 -0
  32. package/dist/StatsManager/events.d.ts +2 -2
  33. package/dist/StatsManager/index.d.ts +2 -1
  34. package/dist/StatsPeerConnection/index.d.ts +2 -2
  35. package/dist/VideoSendingBalancerManager/events.d.ts +0 -1
  36. package/dist/VideoSendingBalancerManager/index.d.ts +2 -2
  37. package/dist/__fixtures__/eventNames.d.ts +1 -1
  38. package/dist/doMock.cjs +1 -1
  39. package/dist/doMock.js +1 -1
  40. package/dist/index.cjs +1 -1
  41. package/dist/index.d.ts +8 -8
  42. package/dist/index.js +213 -186
  43. package/package.json +1 -1
  44. package/dist/@SipConnector-DB4bLDI5.cjs +0 -1
  45. package/dist/session/createSession.d.ts +0 -26
  46. package/dist/session/index.d.ts +0 -5
  47. package/dist/session/types.d.ts +0 -20
package/README.md CHANGED
@@ -13,77 +13,25 @@
13
13
 
14
14
  SDK предоставляет комплексное решение для:
15
15
 
16
- | Категория | Возможности |
17
- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
18
- | **SIP-подключения** | Регистрация на сервере (SIP REGISTER), управление сессиями |
19
- | **WebRTC-коммуникации** | Исходящие/входящие звонки (SIP INVITE/200 OK), медиа-потоки, управление transceiver'ами, автоматический перезапуск ICE |
20
- | **Презентации** | Отправка второго потока (screen sharing, демонстрация экрана) |
21
- | **Системные сообщения** | DTMF, SIP INFO, синхронизация медиа-состояния |
22
- | **Событийная архитектура** | Подписка на события платформы в реальном времени |
23
- | **Мониторинг** | WebRTC-статистика (RTCRtpStats, ICE candidate stats) |
24
- | **Управление конференциями** | Перемещение участников между ролями (участник/зритель) |
25
- | **Лицензирование** | Мониторинг использования лицензий и состояния презентаций |
26
- | **Автоподключение** | Автоматическое переподключение при обрывах связи |
16
+ | Категория | Возможности |
17
+ | ---------------------------- | --------------------------------------------------------------------------------------- |
18
+ | **SIP-подключения** | Регистрация на сервере (SIP REGISTER), управление сессиями |
19
+ | **WebRTC-коммуникации** | Исходящие/входящие звонки (SIP INVITE/200 OK), медиа-потоки, управление transceiver'ами |
20
+ | **Презентации** | Отправка второго потока (screen sharing, демонстрация экрана) |
21
+ | **Системные сообщения** | DTMF, SIP INFO, синхронизация медиа-состояния |
22
+ | **Событийная архитектура** | Подписка на события платформы в реальном времени |
23
+ | **Мониторинг** | WebRTC-статистика (RTCRtpStats, ICE candidate stats) |
24
+ | **Управление конференциями** | Перемещение участников между ролями (участник/зритель) |
25
+ | **Лицензирование** | Мониторинг использования лицензий и состояния презентаций |
26
+ | **Автоподключение** | Автоматическое переподключение при обрывах связи |
27
27
 
28
28
  - **Адаптивный polling**: Улучшенная система опроса для мониторинга изменений видеотреков
29
29
  - **Поддержка maxBitrate в PresentationManager**: Автоматическое управление битрейтом для презентаций
30
30
  - **Предпочтительные кодеки в SipConnector**: Настройка приоритетов кодеков на уровне коннектора
31
31
  - **Обработка смены треков**: Автоматическая адаптация балансировки при изменении видеотреков
32
32
  - **Улучшенная статистика**: Расширенные возможности сбора и анализа WebRTC статистики
33
- - **Автоматический перезапуск ICE**: Обработка событий `restart` от сервера с автоматическим вызовом `restartIce`
34
33
  |
35
34
 
36
- ### 🏗️ Архитектура
37
-
38
- SDK построен по принципу **слоистой архитектуры**:
39
-
40
- - **SipConnector** — низкоуровневый слой с менеджерами (Connection, Call, Presentation, API, AutoConnector)
41
- - **SipConnectorFacade** — высокоуровневый фасад с готовыми сценариями
42
- - **Специализированные менеджеры** — для статистики, участников, медиа-потоков, автоподключения
43
-
44
- ### 🧭 Состояния сеанса (XState)
45
-
46
- - Каждый доменный менеджер поднимает свой XState-актор: `connectionActor`, `callActor`, `incomingActor`, `presentationActor`.
47
- - Менеджеры сами кормят свои акторы событиями. Session — это тонкий агрегатор, который подписывается на `.subscribe` акторов менеджеров и отдает объединённый снапшот.
48
- - Клиент подписывается на статусы через `sipConnector.session.subscribe(selector, listener)` или читает снапшот через `sipConnector.session.getSnapshot()`.
49
- - Домены и статусы:
50
- - **connection**: `idle` → `connecting` → `initializing` → `connected` → `registered` → `disconnected` / `failed` (с возможностью `RESET` в `idle`).
51
- - **call**: `idle` → `connecting` → `ringing` → `accepted` → `inCall` → `ended` / `failed` (с возможностью `RESET` в `idle`).
52
- - **incoming**: `idle` → `ringing` → `consumed` / `declined` / `terminated` / `failed` → `idle`.
53
- - **presentation**: `idle` → `starting` → `active` → `stopping` → `idle` (`failed` на ошибках).
54
- - События источников:
55
- - `ConnectionManager.events` → `connectionActor`: `connect-started`, `connecting`, `connect-parameters-resolve-success`, `connected`, `registered`, `unregistered`, `disconnected`, `registrationFailed`, `connect-failed`.
56
- - `CallManager.events` → `callActor`: `connecting`, `progress`, `accepted`, `confirmed`, `ended`, `failed`, `presentation:start|started|end|ended|failed`.
57
- - `IncomingCallManager.events` → `incomingActor`: `incomingCall`, `declinedIncomingCall`, `terminatedIncomingCall`, `failedIncomingCall`, а также `INCOMING.CONSUMED` при ответе на звонок и `INCOMING.CLEAR` при завершении звонка/потере соединения.
58
- - `PresentationManager` прокидывает события презентации в `presentationActor` и реагирует на `CallManager`/`ConnectionManager` для корректного завершения статуса.
59
- - Машины состояний с валидацией:
60
- - **ConnectionStateMachine**: Управляет переходами состояний SIP-соединения с валидацией допустимых операций и типобезопасной обработкой ошибок.
61
- - **CallStateMachine**: Управляет переходами состояний звонков с валидацией, предотвращением недопустимых переходов и публичным API (геттеры `isIdle`, `isConnecting`, `isPending`, `isActive`, метод `reset()`).
62
- - Быстрый пример подписки:
63
-
64
- ```typescript
65
- import { selectConnectionStatus, selectCallStatus } from 'sip-connector';
66
-
67
- const unsubscribe = sipConnector.session.subscribe(
68
- (snapshot) => ({
69
- connection: selectConnectionStatus(snapshot),
70
- call: selectCallStatus(snapshot),
71
- }),
72
- ({ connection, call }) => {
73
- console.log('Connection:', connection, 'Call:', call);
74
- },
75
- );
76
-
77
- // ...
78
- unsubscribe(); // Когда больше не нужно слушать
79
- ```
80
-
81
- - Миграция клиента:
82
- 1. Включите фича-флаг и подключите `sipConnector.session` вместо локальной модели статусов.
83
- 2. Подпишитесь через селекторы и синхронизируйте store (MobX/MST/Redux) только по изменившимся срезам.
84
- 3. Принимая входящие звонки, используйте `selectIncomingStatus/RemoteCaller` и действуйте по `consumed/declined`.
85
- 4. Для UI статусов звонка используйте `selectCallStatus`, для блокировок по соединению — `selectConnectionStatus`.
86
-
87
35
  ---
88
36
 
89
37
  ## 🚀 Установка
@@ -103,1210 +51,16 @@ pnpm add sip-connector
103
51
 
104
52
  ---
105
53
 
106
- ## 🎯 Быстрый старт
107
-
108
- ### Шаг 1: Инициализация
109
-
110
- ```typescript
111
- import { UA, WebSocketInterface } from '@krivega/jssip';
112
- import { SipConnector, SipConnectorFacade, tools } from 'sip-connector';
113
-
114
- // Создание низкоуровневого коннектора с настройками кодеков
115
- const sipConnector = new SipConnector(
116
- { JsSIP: { UA, WebSocketInterface } },
117
- {
118
- // Приоритизация современных кодеков
119
- preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
120
- excludeMimeTypesVideoCodecs: ['video/H264'],
121
- // Настройки видеобалансировщика (опционально)
122
- videoBalancerOptions: {
123
- ignoreForCodec: 'H264',
124
- onSetParameters: (result) => {
125
- console.log('Video parameters updated:', result);
126
- },
127
- },
128
- },
129
- );
130
-
131
- // Создание фасада
132
- const facade = new SipConnectorFacade(sipConnector);
133
- ```
134
-
135
- ### Шаг 2: Подключение к серверу
136
-
137
- ```typescript
138
- // Подключение с объектом параметров
139
- await facade.connectToServer({
140
- userAgent: tools.getUserAgent({ appName: 'MyApp' }),
141
- sipServerUrl: 'sip.example.com', // WebSocket URL (путь /webrtc/wss/ добавляется автоматически)
142
- sipServerIp: 'sip.example.com', // SIP сервер IP
143
- user: '1001', // SIP URI user part
144
- password: 'secret',
145
- register: true, // Включить SIP REGISTER
146
- });
147
-
148
- // Или с функцией для динамического получения параметров
149
- await facade.connectToServer(async () => {
150
- // Получение актуальных параметров подключения
151
- const config = await fetchConnectionConfig();
152
- return {
153
- userAgent: tools.getUserAgent({ appName: 'MyApp' }),
154
- sipServerUrl: config.websocketUrl, // Без пути /webrtc/wss/ - он добавляется автоматически
155
- sipServerIp: config.sipServerIp,
156
- user: config.username,
157
- password: config.password,
158
- register: true,
159
- };
160
- });
161
-
162
- // Доступ к состоянию через ConnectionStateMachine (внутренний компонент)
163
- const connectionStateMachine = sipConnector.connectionManager.connectionStateMachine;
164
-
165
- // Проверка текущего состояния соединения
166
- console.log('Состояние соединения:', connectionStateMachine.state);
167
- console.log('Подключено:', connectionStateMachine.isActiveConnection); // true для connected/registered
168
- console.log('В процессе:', connectionStateMachine.isPending); // true для connecting/initializing
169
- console.log('Ошибка:', connectionStateMachine.error);
170
-
171
- // Получение списка допустимых событий
172
- const validEvents = connectionStateMachine.getValidEvents();
173
- console.log('Допустимые переходы:', validEvents);
174
-
175
- // Подписка на изменения состояния
176
- const unsubscribe = connectionStateMachine.onStateChange((state) => {
177
- console.log('Новое состояние соединения:', state);
178
- });
179
- ```
180
-
181
- ### Шаг 3: Исходящий звонок
182
-
183
- ```typescript
184
- // Получение локального медиа-потока
185
- const localStream = await navigator.mediaDevices.getUserMedia({
186
- audio: true,
187
- video: true,
188
- });
189
-
190
- // Подписка на изменения удаленных потоков
191
- const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
192
- console.log('Изменение удаленных потоков:', {
193
- participantId: event.participantId,
194
- changeType: event.changeType, // 'added' | 'removed'
195
- trackId: event.trackId,
196
- streams: event.streams, // Актуальный массив всех удаленных потоков
197
- });
198
-
199
- // Обновление UI с новыми потоками
200
- updateRemoteStreamsDisplay(event.streams);
201
- });
202
-
203
- // Инициация звонка
204
- const pc = await facade.callToServer({
205
- conference: '12345',
206
- mediaStream: localStream,
207
- });
208
-
209
- // Подписка на WebRTC-статистику
210
- const unsubscribeStats = facade.onStats(({ outbound, inbound }) => {
211
- console.log('Исходящая статистика:', outbound);
212
- console.log('Входящая статистика:', inbound);
213
- });
214
- ```
215
-
216
- ### Шаг 4: Завершение работы
217
-
218
- ```typescript
219
- await facade.disconnectFromServer();
220
- unsubscribeStats();
221
- unsubscribeRemoteStreams();
222
- ```
223
-
224
- ---
225
-
226
- ## 📞 Входящие звонки
227
-
228
- ### Обработка входящих вызовов
229
-
230
- ```typescript
231
- // Подписка на изменения удаленных потоков (до ответа на звонок)
232
- const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
233
- console.log('Изменение удаленных потоков:', event);
234
- displayRemoteStreams(event.streams);
235
- });
236
-
237
- // Подписка на входящие события
238
- sipConnector.on('incoming-call:incomingCall', () => {
239
- // Автоматический ответ с локальным потоком
240
- facade.answerToIncomingCall({
241
- mediaStream: localStream,
242
- });
243
- });
244
- ```
245
-
246
- ### Управление состоянием входящих звонков
247
-
248
- Доступ к состоянию через IncomingCallStateMachine:
249
-
250
- ```typescript
251
- const incomingStateMachine = sipConnector.incomingCallManager.incomingCallStateMachine;
252
-
253
- // Проверка текущего состояния
254
- console.log('Состояние входящего:', incomingStateMachine.state);
255
- console.log('Звонок поступает:', incomingStateMachine.isRinging);
256
- console.log('Обработан:', incomingStateMachine.isFinished);
257
- console.log('Данные вызывающего:', incomingStateMachine.remoteCallerData);
258
- console.log('Причина завершения:', incomingStateMachine.lastReason);
259
-
260
- // Сброс состояния
261
- if (incomingStateMachine.isFinished) {
262
- incomingStateMachine.reset();
263
- }
264
- ```
265
-
266
- ### Управление состоянием звонка
267
-
268
- ```typescript
269
- // Отслеживание жизненного цикла звонка через события
270
- sipConnector.on('call:accepted', () => {
271
- console.log('Звонок принят');
272
- });
273
-
274
- sipConnector.on('call:ended', () => {
275
- console.log('Звонок завершен');
276
- });
277
-
278
- sipConnector.on('call:failed', (error) => {
279
- console.error('Ошибка звонка:', error);
280
- });
281
-
282
- // Доступ к состоянию через CallStateMachine (внутренний компонент)
283
- const callStateMachine = sipConnector.callManager.callStateMachine;
284
-
285
- // Проверка текущего состояния
286
- console.log('Состояние звонка:', callStateMachine.state);
287
- console.log('Звонок активен:', callStateMachine.isActive); // true для accepted/inCall
288
- console.log('Ожидание:', callStateMachine.isPending); // true для connecting/ringing
289
- console.log('Последняя ошибка:', callStateMachine.lastError);
290
-
291
- // Сброс состояния после завершения
292
- if (callStateMachine.isEnded || callStateMachine.isFailed) {
293
- callStateMachine.reset(); // Переход в IDLE
294
- }
295
- ```
296
-
297
- ---
298
-
299
- ## 🖥️ Управление презентациями
300
-
301
- ### Запуск презентации
302
-
303
- ```typescript
304
- // Получение потока экрана
305
- const displayStream = await navigator.mediaDevices.getDisplayMedia({
306
- video: true,
307
- audio: true,
308
- });
309
-
310
- // Запуск презентации с настройками качества
311
- await facade.startPresentation({
312
- mediaStream: displayStream,
313
- isP2P: false, // MCU режим
314
- contentHint: 'detail', // Оптимизация для детального контента
315
- maxBitrate: 4000000, // Максимальный битрейт 4 Мбит/с
316
- degradationPreference: 'maintain-resolution', // Приоритет разрешения
317
- sendEncodings: [
318
- { width: 1920, height: 1080, scalabilityMode: 'L3T3_KEY' },
319
- { width: 1280, height: 720 },
320
- ],
321
- });
322
- ```
323
-
324
- ### Обновление и остановка
325
-
326
- ```typescript
327
- // Обновление потока презентации с новыми настройками
328
- await facade.updatePresentation({
329
- mediaStream: newDisplayStream,
330
- isP2P: false,
331
- maxBitrate: 6000000, // Увеличенный битрейт для HD контента
332
- contentHint: 'text', // Оптимизация для текстового контента
333
- });
334
-
335
- // Остановка презентации
336
- await facade.stopShareSipConnector();
337
- ```
338
-
339
- ### Настройки качества презентации
340
-
341
- | Параметр | Описание | Рекомендуемые значения |
342
- | ----------------------- | -------------------------------- | --------------------------------------- |
343
- | `maxBitrate` | Максимальный битрейт (bps) | 2-8 Мбит/с в зависимости от контента |
344
- | `contentHint` | Тип контента для оптимизации | `'detail'`, `'text'`, `'motion'` |
345
- | `degradationPreference` | Приоритет при ухудшении качества | `'maintain-resolution'` для презентаций |
346
-
347
- ```typescript
348
- // Адаптивные настройки в зависимости от типа контента
349
- const presentationSettings = {
350
- // For detailed graphics/images
351
- highQuality: {
352
- maxBitrate: 8000000,
353
- contentHint: 'detail' as const,
354
- degradationPreference: 'maintain-resolution' as const,
355
- },
356
-
357
- // For text documents
358
- textOptimized: {
359
- maxBitrate: 4000000,
360
- contentHint: 'text' as const,
361
- degradationPreference: 'maintain-resolution' as const,
362
- },
363
-
364
- // For video content
365
- videoOptimized: {
366
- maxBitrate: 6000000,
367
- contentHint: 'motion' as const,
368
- degradationPreference: 'maintain-framerate' as const,
369
- },
370
- };
371
-
372
- // Использование настроек
373
- await facade.startPresentation({
374
- mediaStream: displayStream,
375
- isP2P: false,
376
- ...presentationSettings.textOptimized,
377
- });
378
- ```
379
-
380
- ### Управление состоянием презентации
381
-
382
- Доступ к состоянию через PresentationStateMachine:
383
-
384
- ```typescript
385
- const presentationStateMachine = sipConnector.callManager.presentationStateMachine;
386
-
387
- // Проверка текущего состояния
388
- console.log('Состояние презентации:', presentationStateMachine.state);
389
- console.log('Активна:', presentationStateMachine.isActive);
390
- console.log('В процессе:', presentationStateMachine.isPending); // starting/stopping
391
- console.log('Активна или в процессе:', presentationStateMachine.isActiveOrPending);
392
- console.log('Ошибка:', presentationStateMachine.lastError);
393
-
394
- // Сброс состояния после ошибки
395
- if (presentationStateMachine.isFailed) {
396
- presentationStateMachine.reset();
397
- }
398
- ```
399
-
400
- ---
401
-
402
- ## 👥 Управление участниками конференции
403
-
404
- ### Отслеживание перемещений
405
-
406
- ```typescript
407
- // Подписка на перемещение в зрители (новый формат с audioId)
408
- const unsubscribeMoveToSpectators = sipConnector.on(
409
- 'api:participant:move-request-to-spectators',
410
- (data) => {
411
- if (data.isSynthetic) {
412
- console.log('Участник перемещен в зрители (синтетическое событие)');
413
- } else {
414
- console.log('Участник перемещен в зрители с audioId:', data.audioId);
415
- }
416
- updateParticipantRole('spectator');
417
- },
418
- );
419
-
420
- // Подписка на перемещение в зрители (старый формат для обратной совместимости)
421
- const unsubscribeMoveToSpectatorsSynthetic = sipConnector.on(
422
- 'api:participant:move-request-to-spectators-synthetic',
423
- () => {
424
- console.log('Участник перемещен в зрители (старый формат)');
425
- updateParticipantRole('spectator');
426
- },
427
- );
428
-
429
- // Подписка на перемещение в участники
430
- const unsubscribeMoveToParticipants = sipConnector.on(
431
- 'api:participant:move-request-to-participants',
432
- () => {
433
- console.log('Участник перемещен в участники');
434
- updateParticipantRole('participant');
435
- },
436
- );
437
-
438
- // Отписка при необходимости
439
- unsubscribeMoveToSpectators();
440
- unsubscribeMoveToSpectatorsSynthetic();
441
- unsubscribeMoveToParticipants();
442
- ```
443
-
444
- ### Административные функции
445
-
446
- ```typescript
447
- // Принудительная остановка презентации
448
- facade.onMustStopPresentation(() => {
449
- console.log('Администратор требует остановить презентацию');
450
- facade.stopShareSipConnector();
451
- });
452
-
453
- // Мониторинг лицензий
454
- facade.onUseLicense((license) => {
455
- console.log('Используется лицензия:', license);
456
- updateLicenseStatus(license);
457
- });
458
- ```
459
-
460
- ---
461
-
462
- ## 📊 Управление медиа-потоками
463
-
464
- ### Работа с удаленными потоками
465
-
466
- ```typescript
467
- // Подписка на изменения удаленных потоков
468
- let currentRemoteStreams: MediaStream[] = [];
469
-
470
- const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
471
- console.log('Изменение удаленных потоков:', {
472
- participantId: event.participantId,
473
- changeType: event.changeType, // 'added' | 'removed'
474
- trackId: event.trackId,
475
- });
476
-
477
- // Обновляем текущие потоки
478
- currentRemoteStreams = event.streams;
479
-
480
- // Обновляем UI
481
- updateStreamsDisplay(event.streams);
482
- });
483
-
484
- // Получение текущих удаленных потоков (синхронный метод)
485
- const remoteStreams = facade.getRemoteStreams();
486
- if (remoteStreams) {
487
- console.log('Активные удаленные потоки:', remoteStreams.length);
488
- remoteStreams.forEach((stream) => {
489
- displayStream(stream);
490
- });
491
- }
492
- ```
493
-
494
- ### Обработка готовых потоков
495
-
496
- ```typescript
497
- // Обработка с debounce (рекомендуется для UI)
498
- const handleReadyRemoteStreamsDebounced = facade.resolveHandleReadyRemoteStreamsDebounced({
499
- onReadyRemoteStreams: (streams) => {
500
- console.log('Готовые удаленные потоки:', streams);
501
- updateStreamsDisplay(streams);
502
- },
503
- });
504
-
505
- // Обработка без debounce (для критичных операций)
506
- const handleReadyRemoteStreams = facade.resolveHandleReadyRemoteStreams({
507
- onReadyRemoteStreams: () => {
508
- console.log('Новый поток готов');
509
- handleNewStream();
510
- },
511
- });
512
- ```
513
-
514
- ### Управление разрешениями
515
-
516
- ```typescript
517
- // Запрос разрешения на камеру
518
- try {
519
- await facade.askPermissionToEnableCam();
520
- console.log('Разрешение на камеру получено');
521
- } catch (error) {
522
- console.error('Ошибка получения разрешения:', error);
523
- }
524
- ```
525
-
526
- ### Перезапуск ICE-соединения
527
-
528
- #### Ручной перезапуск
529
-
530
- ```typescript
531
- // Перезапуск ICE для восстановления соединения
532
- try {
533
- const success = await sipConnector.callManager.restartIce({
534
- useUpdate: true, // Использовать SIP UPDATE вместо re-INVITE
535
- extraHeaders: ['X-Restart-Reason: Connection-Reset'],
536
- rtcOfferConstraints: {
537
- offerToReceiveAudio: true,
538
- offerToReceiveVideo: true,
539
- },
540
- });
541
-
542
- if (success) {
543
- console.log('ICE перезапущен успешно');
544
- } else {
545
- console.warn('Перезапуск ICE не удался');
546
- }
547
- } catch (error) {
548
- console.error('Ошибка перезапуска ICE:', error);
549
- }
550
- ```
551
-
552
- #### Автоматический перезапуск по событию сервера
553
-
554
- SDK автоматически обрабатывает события `restart` от сервера и инициирует перезапуск ICE-соединения с интеллектуальным управлением transceiver'ами:
555
-
556
- ```typescript
557
- // SDK автоматически подписывается на события restart от ApiManager
558
- // и выполняет следующие действия:
559
- // 1. Проверяет необходимость добавления презентационного transceiver'а
560
- // 2. Вызывает callManager.restartIce()
561
-
562
- // Мониторинг событий restart (опционально)
563
- sipConnector.on('api:restart', (data) => {
564
- console.log('Получено событие restart от сервера:', {
565
- tracksDirection: data.tracksDirection, // 'incoming', 'outgoing', 'bidirectional'
566
- audioTrackCount: data.audioTrackCount,
567
- videoTrackCount: data.videoTrackCount,
568
- });
569
-
570
- // SDK автоматически:
571
- // - Добавит презентационный transceiver если videoTrackCount === 2
572
- // - Вызовет restartIce()
573
- console.log('ICE будет перезапущен автоматически');
574
-
575
- if (data.videoTrackCount === 2) {
576
- console.log('Может быть добавлен презентационный transceiver');
577
- }
578
- });
579
- ```
580
-
581
- ### Параметры перезапуска ICE
582
-
583
- | Параметр | Тип | Описание | По умолчанию |
584
- | ----------------------- | -------- | ---------------------------------------- | ------------ |
585
- | `useUpdate` | boolean | Использовать SIP UPDATE вместо re-INVITE | `false` |
586
- | `extraHeaders` | string[] | Дополнительные SIP заголовки | `[]` |
587
- | `rtcOfferConstraints` | object | Ограничения для создания SDP offer | `{}` |
588
- | `sendEncodings` | array | Параметры кодирования для видеопотока | `[]` |
589
- | `degradationPreference` | string | Приоритет при ухудшении качества | `undefined` |
590
-
591
- ---
592
-
593
- ## 📡 События и их обработка
594
-
595
- ### Архитектура событий
596
-
597
- SDK использует **событийно-ориентированную архитектуру** с префиксами для группировки:
598
-
599
- | Префикс | Описание | Примеры событий |
600
- | ------------------ | ------------------------ | ------------------------------------------------------------------------------ |
601
- | `connection:*` | События подключения | `connected`, `disconnected` |
602
- | `call:*` | События звонков | `accepted`, `ended`, `failed`, `remote-streams-changed` |
603
- | `api:*` | События от сервера | `enterRoom`, `useLicense`, `restart`, `participant:move-request-to-spectators` |
604
- | `incoming-call:*` | События входящих звонков | `incomingCall` |
605
- | `presentation:*` | События презентаций | `started`, `stopped` |
606
- | `stats:*` | События статистики | `collected` |
607
- | `video-balancer:*` | События балансировки | `balancing-started`, `parameters-updated` |
608
-
609
- ### Основные события
610
-
611
- ```typescript
612
- // Подключение
613
- sipConnector.on('connection:connected', () => {
614
- console.log('Подключение установлено');
615
- });
616
-
617
- sipConnector.on('connection:disconnected', () => {
618
- console.log('Подключение разорвано');
619
- });
620
-
621
- // Звонки
622
- sipConnector.on('call:accepted', () => {
623
- console.log('Звонок принят');
624
- });
625
-
626
- sipConnector.on('call:ended', () => {
627
- console.log('Звонок завершен');
628
- });
629
-
630
- // API события
631
- sipConnector.on('api:enterRoom', ({ room }) => {
632
- console.log('Вход в комнату:', room);
633
- });
634
-
635
- sipConnector.on('api:useLicense', (license) => {
636
- console.log('Лицензия:', license);
637
- });
638
-
639
- sipConnector.on('api:restart', (data) => {
640
- console.log('Событие restart от сервера:', data);
641
- });
642
-
643
- // Изменения удаленных потоков
644
- sipConnector.on('call:remote-streams-changed', (event) => {
645
- console.log('Изменение удаленных потоков:', {
646
- participantId: event.participantId,
647
- changeType: event.changeType, // 'added' | 'removed'
648
- trackId: event.trackId,
649
- streams: event.streams,
650
- });
651
- });
652
-
653
- // Перемещение участников
654
- sipConnector.on('api:participant:move-request-to-spectators', (data) => {
655
- if (data.isSynthetic) {
656
- console.log('Перемещение в зрители (синтетическое)');
657
- } else {
658
- console.log('Перемещение в зрители с audioId:', data.audioId);
659
- }
660
- });
661
-
662
- sipConnector.on('api:participant:move-request-to-spectators-synthetic', () => {
663
- console.log('Перемещение в зрители (старый формат для обратной совместимости)');
664
- });
665
- ```
666
-
667
- ### Детальная таблица событий
668
-
669
- #### События звонков (`call:*`)
670
-
671
- | Событие | Описание | Данные |
672
- | ----------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ |
673
- | `call:accepted` | Звонок принят | - |
674
- | `call:ended` | Звонок завершен | `EndEvent` |
675
- | `call:failed` | Звонок завершился с ошибкой | `EndEvent` |
676
- | `call:remote-streams-changed` | Изменение удаленных потоков | `{ participantId: string, changeType: 'added' \| 'removed', trackId: string, streams: MediaStream[] }` |
677
-
678
- #### События участников (`api:participant:*`)
679
-
680
- | Событие | Описание | Данные |
681
- | ------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------ |
682
- | `api:participant:move-request-to-spectators` | Перемещение в зрители (новый) | `{ isSynthetic: true } \| { isSynthetic: false, audioId: string }` |
683
- | `api:participant:move-request-to-spectators-synthetic` | Перемещение в зрители (старый) | - |
684
- | `api:participant:move-request-to-participants` | Перемещение в участники | - |
685
-
686
- ### Продвинутые паттерны
687
-
688
- ```typescript
689
- // Ожидание одного из нескольких событий
690
- sipConnector.onceRace(['call:ended', 'call:failed'], (_payload, eventName) => {
691
- console.log('Звонок завершен событием:', eventName);
692
- cleanupCall();
693
- });
694
-
695
- // Ожидание конкретного события
696
- const roomData = await sipConnector.wait('api:enterRoom');
697
- console.log('Данные комнаты:', roomData);
698
- ```
699
-
700
- ### События балансировки видео
701
-
702
- ```typescript
703
- // Мониторинг автоматической балансировки видео
704
- sipConnector.on('video-balancer:balancing-scheduled', (data) => {
705
- console.log(`Балансировка запланирована через ${data.delay}ms`);
706
- });
707
-
708
- sipConnector.on('video-balancer:balancing-started', (data) => {
709
- console.log(`Балансировка запущена после задержки ${data.delay}ms`);
710
- });
711
-
712
- sipConnector.on('video-balancer:balancing-stopped', () => {
713
- console.log('Балансировка остановлена');
714
- });
715
-
716
- sipConnector.on('video-balancer:parameters-updated', (result) => {
717
- console.log('Обновлены параметры:', result);
718
- });
719
-
720
- // Ручное управление балансировкой
721
- sipConnector.videoSendingBalancerManager.startBalancing(); // Принудительный запуск
722
- sipConnector.videoSendingBalancerManager.stopBalancing(); // Остановка
723
- ```
724
-
725
- ---
726
-
727
- ## 📈 WebRTC Статистика
728
-
729
- ### Обзор возможностей
730
-
731
- SDK предоставляет детальную WebRTC-статистику, основанную на [W3C WebRTC Statistics API](https://www.w3.org/TR/webrtc-stats/), включающую:
732
-
733
- | Тип статистики | Описание | Метрики |
734
- | ------------------ | --------------------- | ----------------------------- |
735
- | **RTP статистика** | Потоковые данные | Пакеты, байты, jitter, loss |
736
- | **Кодеки** | Параметры кодирования | Параметры, реализация |
737
- | **ICE кандидаты** | Сетевые соединения | Типы, приоритеты, состояния |
738
- | **Транспорт** | Защищенные соединения | DTLS, соединения, сертификаты |
739
-
740
- ### Использование статистики
741
-
742
- ```typescript
743
- import { StatsPeerConnection, EStatsTypes, hasAvailableStats } from 'sip-connector';
744
-
745
- // Проверка доступности
746
- if (hasAvailableStats()) {
747
- const statsCollector = new StatsPeerConnection();
748
-
749
- // Подписка на события статистики
750
- statsCollector.on('collected', ({ outbound, inbound }) => {
751
- console.log('Исходящая статистика:', outbound);
752
- console.log('Входящая статистика:', inbound);
753
-
754
- // Новая метрика availableIncomingBitrate
755
- if (inbound.additional?.candidatePair?.availableIncomingBitrate) {
756
- console.log(
757
- 'Доступный входящий битрейт:',
758
- inbound.additional.candidatePair.availableIncomingBitrate,
759
- );
760
- }
761
-
762
- // Анализ качества соединения
763
- analyzeConnectionQuality(outbound, inbound);
764
- });
765
-
766
- // Запуск сбора статистики с адаптивным интервалом
767
- statsCollector.start(peerConnection);
768
- }
769
- ```
770
-
771
- ### Адаптивный интервал сбора статистики
772
-
773
- SDK автоматически адаптирует частоту сбора статистики в зависимости от времени выполнения:
774
-
775
- | Время выполнения | Множитель интервала | Интервал (мс) |
776
- | ---------------- | ------------------- | ------------- |
777
- | < 16 мс | 1x | 1000 |
778
- | 16-32 мс | 2x | 2000 |
779
- | 32-48 мс | 3x | 3000 |
780
- | > 48 мс | 4x | 4000 |
781
-
782
- ```typescript
783
- // Мониторинг производительности сбора статистики
784
- statsCollector.on('collected', (stats) => {
785
- const collectionTime = performance.now() - startTime;
786
- console.log(`Статистика собрана за ${collectionTime}мс`);
787
- });
788
- ```
789
-
790
- ### Типы статистики
791
-
792
- | Категория | Типы | Описание |
793
- | ---------------------- | --------------------------------- | ------------------------------------------ |
794
- | **Аудио потоки** | `TInboundAudio`, `TOutboundAudio` | RTP, кодек, jitter buffer, audio level |
795
- | **Видео потоки** | `TInboundVideo`, `TOutboundVideo` | RTP, кодек, frames, bitrate, resolution |
796
- | **Сетевая информация** | `TAdditional` | ICE кандидаты, DTLS транспорт, сертификаты |
797
-
798
- ---
799
-
800
- ## ⚡ Адаптивное опрашивание видеопотоков
54
+ ## Документация
801
55
 
802
- ### Принцип работы автоподключения
803
-
804
- SDK использует **адаптивное опрашивание** для мониторинга изменений в видеопотоках, что значительно снижает нагрузку на CPU:
805
-
806
- ```typescript
807
- // TrackMonitor автоматически адаптирует частоту опрашивания
808
- const trackMonitor = new TrackMonitor({
809
- pollIntervalMs: 1000, // Начальный интервал
810
- maxPollIntervalMs: 16000, // Максимальный интервал (16x)
811
- });
812
- ```
813
-
814
- ### Алгоритм адаптации
815
-
816
- | Ситуация | Действие | Результат |
817
- | ------------------------ | ------------------------------- | ---------------------------- |
818
- | **Изменение разрешения** | Сброс интервала до минимального | Быстрая реакция на изменения |
819
- | **Нет изменений** | Удвоение интервала | Снижение нагрузки на CPU |
820
- | **Достижение максимума** | Ограничение интервала | Предотвращение "заморозки" |
821
-
822
- ### Преимущества
823
-
824
- - **Снижение CPU нагрузки на 40-60%** при стабильном видео
825
- - **Быстрая реакция** на изменения разрешения (resize events)
826
- - **Автоматическое обнаружение** замены треков (replaceTrack)
827
- - **Настраиваемые интервалы** для разных сценариев использования
828
-
829
- ```typescript
830
- // Пример использования с кастомными настройками
831
- const monitor = new TrackMonitor({
832
- pollIntervalMs: 500, // Более частое начальное опрашивание
833
- maxPollIntervalMs: 8000, // Меньший максимальный интервал
834
- });
835
-
836
- // Подписка на изменения
837
- monitor.subscribe(videoSender, () => {
838
- console.log('Обнаружено изменение видеопотока');
839
- // Перебалансировка параметров
840
- rebalanceVideoParameters();
841
- });
842
- ```
56
+ - **API Reference**: [Справочная документация по API](./docs/api/README.md)
57
+ - **Рецепты**: [Практические примеры и рецепты использования](./docs/recipes/README.md)
58
+ - **Архитектура**: [Детальное описание внутренней структуры](./docs/arch/README.md)
59
+ - **Тестирование**: [Руководство по тестированию](./docs/testing.md)
60
+ - **Совместимость браузеров**: [Поддержка браузеров и WebRTC](./docs/browser-compatibility.md)
843
61
 
844
62
  ---
845
63
 
846
- ## 🎛️ Управление видеобалансировщиком
847
-
848
- ### Автоматическая балансировка
849
-
850
- `VideoSendingBalancer` интегрирован в `SipConnector` и запускается автоматически:
851
-
852
- ```typescript
853
- const sipConnector = new SipConnector(
854
- { JsSIP: { UA, WebSocketInterface } },
855
- {
856
- videoBalancerOptions: {
857
- ignoreForCodec: 'H264', // Игнорировать H264
858
- onSetParameters: (result) => {
859
- console.log('Параметры обновлены:', result);
860
- },
861
- },
862
- },
863
- );
864
-
865
- // Подписка на события балансировщика
866
- sipConnector.on('video-balancer:balancing-started', (data) => {
867
- console.log(`Балансировка запущена через ${data.delay}мс`);
868
- });
869
-
870
- sipConnector.on('video-balancer:parameters-updated', (result) => {
871
- console.log('Обновлены параметры:', result);
872
- });
873
- ```
874
-
875
- ### Жизненный цикл балансировщика
876
-
877
- ```mermaid
878
- graph TD
879
- A[Начало звонка] --> B[Задержка 10 сек]
880
- B --> C[Запуск балансировки]
881
- C --> D[Мониторинг изменений]
882
- D --> E{Изменение треков?}
883
- E -->|Да| F[Перебалансировка]
884
- E -->|Нет| D
885
- F --> D
886
- G[Завершение звонка] --> H[Остановка балансировки]
887
- ```
888
-
889
- ### События балансировщика
890
-
891
- | Событие | Описание | Данные |
892
- | ------------------------------------ | -------------------------- | ---------------------- |
893
- | `video-balancer:balancing-scheduled` | Балансировка запланирована | `{ delay: number }` |
894
- | `video-balancer:balancing-started` | Балансировка запущена | `{ delay: number }` |
895
- | `video-balancer:balancing-stopped` | Балансировка остановлена | - |
896
- | `video-balancer:parameters-updated` | Параметры обновлены | `RTCRtpSendParameters` |
897
-
898
- ---
899
-
900
- ## 🔄 Управление последовательными операциями
901
-
902
- ### ConnectionQueueManager
903
-
904
- `ConnectionQueueManager` обеспечивает **последовательное выполнение операций** для предотвращения конфликтов и гонки условий:
905
-
906
- ```typescript
907
- // Все операции ConnectionManager проходят через очередь
908
- const connectionQueueManager = new ConnectionQueueManager({
909
- connectionManager: connectionManager,
910
- });
911
-
912
- // Операции выполняются последовательно
913
- await connectionQueueManager.connect(params);
914
- await connectionQueueManager.disconnect();
915
- ```
916
-
917
- ### Механизм работы
918
-
919
- - **Очередь операций**: Использует `stack-promises` с `noRunIsNotActual: true`
920
- - **Предотвращение конфликтов**: Исключает одновременные connect/disconnect операции
921
-
922
- ### Поддерживаемые операции
923
-
924
- | Операция | Описание |
925
- | ------------ | --------------------------------- |
926
- | `connect` | Подключение к серверу |
927
- | `disconnect` | Отключение от сервера |
928
- | `stop` | Остановка всех операций в очереди |
929
-
930
- ### Интеграция в SipConnector
931
-
932
- ```typescript
933
- // SipConnector автоматически использует ConnectionQueueManager
934
- const sipConnector = new SipConnector({ JsSIP });
935
-
936
- // Все операции подключения проходят через очередь
937
- await sipConnector.connect(params); // → connectionQueueManager.connect()
938
- await sipConnector.disconnect(); // → connectionQueueManager.disconnect()
939
- ```
940
-
941
- ---
942
-
943
- ## 🔄 Автоматическое переподключение
944
-
945
- ### AutoConnectorManager
946
-
947
- `AutoConnectorManager` обеспечивает **автоматическое переподключение** при обрывах связи и проблемах с сетью:
948
-
949
- ```typescript
950
- // Создание SipConnector с настройками автоподключения
951
- const sipConnector = new SipConnector(
952
- { JsSIP },
953
- {
954
- autoConnectorOptions: {
955
- onBeforeRetry, // Очистка кэша перед переподключением
956
- timeoutBetweenAttempts: 3000, // Задержка между попытками
957
- checkTelephonyRequestInterval: 15000, // Интервал проверки телефонии
958
- },
959
- },
960
- );
961
-
962
- // Запуск автоподключения
963
- sipConnector.startAutoConnect({
964
- // Возвращает параметры подключения
965
- getParameters: async () => {
966
- return {
967
- displayName: 'displayName',
968
- sipServerUrl: 'example.com', // Путь /webrtc/wss/ добавляется автоматически
969
- sipServerIp: 'sip.example.com',
970
- user: 'user',
971
- password: 'password',
972
- register: true,
973
- };
974
- },
975
- // Проверяет готовность к подключению
976
- hasReadyForConnection: () => {
977
- return true;
978
- },
979
- });
980
-
981
- // Остановка автоподключения
982
- sipConnector.stopAutoConnect();
983
-
984
- // Подписка на события автоподключения
985
- sipConnector.on('auto-connect:changed-attempt-status', ({ isInProgress }) => {
986
- console.log('Попытка подключения в процессе:', isInProgress);
987
- });
988
-
989
- sipConnector.on('auto-connect:before-attempt', () => {
990
- console.log('Начало попытки подключения');
991
- });
992
-
993
- sipConnector.on('auto-connect:succeeded-attempt', () => {
994
- console.log('Попытка подключения успешна');
995
- });
996
-
997
- sipConnector.on('auto-connect:failed-attempt', (error) => {
998
- console.log('Попытка подключения неудачна:', error);
999
- });
1000
-
1001
- sipConnector.on('auto-connect:cancelled-attempt', (error) => {
1002
- console.log('Попытка подключения отменена:', error);
1003
- });
1004
- ```
1005
-
1006
- ### Принцип работы
1007
-
1008
- - **Автоматические попытки**: Повторяет попытки подключения при ошибках
1009
- - **Проверка телефонии**: Периодически проверяет доступность сервера
1010
- - **Мониторинг состояния**: Отслеживает состояние регистрации и звонков
1011
- - **Адаптивные задержки**: Использует настраиваемые интервалы между попытками
1012
- - **Очистка кэша**: Возможность настраивать очистку кэша через хук
1013
-
1014
- ### События автоподключения
1015
-
1016
- | Событие | Описание | Данные |
1017
- | ------------------------------------- | ------------------------------ | ----------------------------------- |
1018
- | `auto-connect:connecting` | Начало подключения | - |
1019
- | `auto-connect:connected` | Успешное подключение | `{ ua: UA, isRegistered: boolean }` |
1020
- | `auto-connect:disconnecting` | Начало отключения | - |
1021
- | `auto-connect:disconnected` | Отключение завершено | - |
1022
- | `auto-connect:failed` | Ошибка подключения | `Error` |
1023
- | `auto-connect:before-attempt` | Начало попытки подключения | - |
1024
- | `auto-connect:succeeded-attempt` | Успешная попытка подключения | - |
1025
- | `auto-connect:failed-attempt` | Неудачная попытка подключения | `Error` |
1026
- | `auto-connect:cancelled-attempt` | Отмененная попытка подключения | `Error` |
1027
- | `auto-connect:changed-attempt-status` | Изменение статуса попытки | `{ isInProgress: boolean }` |
1028
-
1029
- ---
1030
-
1031
- ## 🔧 API и экспорты
1032
-
1033
- ### Основные классы
1034
-
1035
- ```typescript
1036
- import {
1037
- SipConnector, // Низкоуровневый API
1038
- SipConnectorFacade, // Высокоуровневый фасад
1039
- StatsPeerConnection, // Сбор статистики
1040
- // ... другие экспорты
1041
- } from 'sip-connector';
1042
- ```
1043
-
1044
- ### Методы управления соединением
1045
-
1046
- ```typescript
1047
- // SipConnectorFacade методы
1048
- const facade = new SipConnectorFacade(sipConnector);
1049
-
1050
- // Замена медиа-потока
1051
- await facade.replaceMediaStream(mediaStream, options);
1052
-
1053
- // Получение удаленных потоков
1054
- const streams = facade.getRemoteStreams();
1055
-
1056
- // Перезапуск ICE-соединения (низкоуровневый API)
1057
- await sipConnector.callManager.restartIce(options);
1058
- ```
1059
-
1060
- ### Утилиты и типы
1061
-
1062
- ```typescript
1063
- import {
1064
- // Утилиты
1065
- tools, // getUserAgent, getExtraHeaders, hasPurgatory
1066
- hasAvailableStats, // Проверка доступности статистики
1067
-
1068
- // Константы
1069
- EStatsTypes, // Типы статистики
1070
- EMimeTypesVideoCodecs, // MIME-типы кодеков
1071
- EUseLicense, // Типы лицензий
1072
-
1073
- // Типы
1074
- type TContentHint, // Подсказки для кодирования
1075
- type TInboundStats, // Входящая статистика
1076
- type TOutboundStats, // Исходящая статистика
1077
- type TRestartData, // Данные события restart
1078
- type ITransceiverStorage, // Интерфейс хранения transceiver'ов
1079
- } from 'sip-connector';
1080
- ```
1081
-
1082
- ---
1083
-
1084
- ## 🏗️ Архитектура и паттерны
1085
-
1086
- ### Слоистая архитектура
1087
-
1088
- ```shell
1089
- ┌──────────────────────────────────────────────────────────────────┐
1090
- │ SipConnectorFacade │ ← Высокоуровневый API
1091
- ├──────────────────────────────────────────────────────────────────┤
1092
- │ SipConnector │ ← Координация менеджеров
1093
- ├──────────────────────────────────────────────────────────────────┤
1094
- │ Connection │ Connection │ Call │ API │ │ ← Основные менеджеры
1095
- │ Manager │ Queue │ Manager │ Manager │ │
1096
- │ │ Manager │ │ │ │
1097
- ├──────────────────────────────────────────────────────────────────┤
1098
- │ Stats │Presentation│IncomingCall│VideoBalancer│AutoConnector│ ← Специализированные менеджеры
1099
- │ Manager │ Manager │ Manager │ Manager │Manager │
1100
- ├──────────────────────────────────────────────────────────────────┤
1101
- │ @krivega/jssip │ ← SIP-функциональность
1102
- └──────────────────────────────────────────────────────────────────┘
1103
- ```
1104
-
1105
- ### Паттерны проектирования
1106
-
1107
- | Паттерн | Описание | Применение |
1108
- | --------------- | --------------------------------- | ------------------------ |
1109
- | **Фасад** | Упрощение сложных операций | `SipConnectorFacade` |
1110
- | **Стратегия** | Различные стратегии для звонков | MCU, P2P режимы |
1111
- | **Наблюдатель** | Событийная модель для уведомлений | Event-driven архитектура |
1112
- | **Фабрика** | Создание UA и сессий | `UAFactory` |
1113
-
1114
- ---
1115
-
1116
- ## 📚 Лучшие практики
1117
-
1118
- ### Обработка ошибок
1119
-
1120
- ```typescript
1121
- try {
1122
- await facade.connectToServer(config);
1123
- } catch (error) {
1124
- if (error.code === 'CONNECTION_FAILED') {
1125
- // Повторная попытка подключения
1126
- await retryConnection();
1127
- } else {
1128
- // Логирование и уведомление пользователя
1129
- logError(error);
1130
- notifyUser('Ошибка подключения');
1131
- }
1132
- }
1133
- ```
1134
-
1135
- ### Восстановление соединения
1136
-
1137
- ```typescript
1138
- // Мониторинг качества соединения
1139
- facade.onStats(({ outbound, inbound }) => {
1140
- // Проверка качества соединения
1141
- if (outbound.packetsLost > 0.05) {
1142
- // 5% потерь пакетов
1143
- console.warn('Высокие потери пакетов, перезапуск ICE');
1144
-
1145
- // Перезапуск ICE для восстановления соединения
1146
- sipConnector.callManager
1147
- .restartIce({
1148
- useUpdate: true,
1149
- extraHeaders: ['X-Restart-Reason: High-Packet-Loss'],
1150
- })
1151
- .catch((error) => {
1152
- console.error('Ошибка перезапуска ICE:', error);
1153
- });
1154
- }
1155
- });
1156
- ```
1157
-
1158
- ### Управление ресурсами
1159
-
1160
- ```typescript
1161
- // Всегда отписывайтесь от событий
1162
- const unsubscribe = facade.onStats(handleStats);
1163
-
1164
- // Очистка при размонтировании
1165
- useEffect(() => {
1166
- return () => {
1167
- unsubscribe();
1168
- facade.disconnectFromServer();
1169
- };
1170
- }, []);
1171
- ```
1172
-
1173
- ### Оптимизация производительности
1174
-
1175
- ```typescript
1176
- // Используйте debounce для частых событий
1177
- const debouncedStatsHandler = debounce(handleStats, 1000);
1178
- facade.onStats(debouncedStatsHandler);
1179
-
1180
- // Приоритизируйте современные кодеки и настройте балансировку
1181
- const sipConnector = new SipConnector(
1182
- { JsSIP: { UA, WebSocketInterface } },
1183
- {
1184
- preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
1185
- videoBalancerOptions: {
1186
- ignoreForCodec: 'H264',
1187
- balancingStartDelay: 5000, // Быстрее запуск для критичных приложений
1188
- },
1189
- },
1190
- );
1191
- const facade = new SipConnectorFacade(sipConnector);
1192
- ```
1193
-
1194
- ---
1195
-
1196
- ## 🐛 Отладка и диагностика
1197
-
1198
- ### Включение отладочного режима
1199
-
1200
- ```typescript
1201
- import { enableDebug, disableDebug } from 'sip-connector';
1202
-
1203
- // Включение детального логирования
1204
- enableDebug();
1205
-
1206
- // Отключение для продакшена
1207
- disableDebug();
1208
- ```
1209
-
1210
- ### Мониторинг состояния
1211
-
1212
- ```typescript
1213
- // Проверка состояния подключения
1214
- console.log('Зарегистрирован:', facade.isRegistered);
1215
-
1216
- // Проверка конфигурации
1217
- console.log('Настроен:', facade.isConfigured());
1218
-
1219
- // Диагностика ICE-соединения
1220
- const checkIceConnection = async () => {
1221
- try {
1222
- const success = await sipConnector.callManager.restartIce({
1223
- useUpdate: true,
1224
- extraHeaders: ['X-Debug: ICE-Check'],
1225
- });
1226
- console.log('ICE соединение:', success ? 'OK' : 'Проблемы');
1227
- } catch (error) {
1228
- console.error('ICE недоступен:', error.message);
1229
- }
1230
- };
1231
- ```
1232
-
1233
- ---
1234
-
1235
- ## 🧪 Тестирование
1236
-
1237
- ### Запуск тестов
1238
-
1239
- ```bash
1240
- # Все тесты
1241
- npm test
1242
-
1243
- # Тесты с покрытием
1244
- npm run test:coverage
1245
-
1246
- # Тесты в watch режиме
1247
- npm run test:watch
1248
- ```
1249
-
1250
- ### Тестовые фикстуры
1251
-
1252
- SDK включает готовые моки для тестирования:
1253
-
1254
- | Мок | Назначение | Описание |
1255
- | ----------------------- | -------------------- | -------------------------- |
1256
- | `RTCPeerConnectionMock` | WebRTC API | Имитация WebRTC соединений |
1257
- | `UA.mock.ts` | SIP-функциональность | Имитация SIP User Agent |
1258
- | `BaseSession.mock.ts` | Сессии | Имитация SIP сессий |
1259
-
1260
- ---
1261
-
1262
- ## 🌐 Совместимость браузеров
1263
-
1264
- ### WebRTC поддержка
1265
-
1266
- SDK использует стандартные WebRTC API и совместим с:
1267
-
1268
- | Браузер | Версия | Уровень поддержки | Особенности |
1269
- | ----------- | ------ | ----------------- | ------------------------------ |
1270
- | **Chrome** | 67+ | Полная поддержка | Все возможности WebRTC |
1271
- | **Firefox** | 60+ | Полная поддержка | Все возможности WebRTC |
1272
- | **Safari** | 11+ | Базовая поддержка | Ограниченная поддержка кодеков |
1273
- | **Edge** | 79+ | Полная поддержка | Chromium-based |
1274
-
1275
- ### Проверка возможностей
1276
-
1277
- ```typescript
1278
- // Проверка поддержки WebRTC
1279
- if (!navigator.mediaDevices?.getUserMedia) {
1280
- throw new Error('WebRTC не поддерживается');
1281
- }
1282
-
1283
- // Проверка поддержки презентаций
1284
- if (!navigator.mediaDevices?.getDisplayMedia) {
1285
- console.warn('Screen sharing не поддерживается');
1286
- }
1287
-
1288
- // Проверка поддержки ICE restart
1289
- if (!RTCPeerConnection.prototype.restartIce) {
1290
- console.warn('ICE restart не поддерживается в этом браузере');
1291
- }
1292
- ```
1293
-
1294
- ---
1295
-
1296
- ## 🤝 Поддержка и сообщество
1297
-
1298
- ### Документация
1299
-
1300
- - **API Reference**: Полное описание всех методов и типов
1301
- - **Примеры**: Готовые сценарии использования
1302
- - **Архитектура**: Детальное описание внутренней структуры
1303
-
1304
- ### Сообщество
1305
-
1306
- - **Issues**: [GitHub Issues](https://github.com/Krivega/sip-connector/issues)
1307
- - **Discussions**: Обсуждения и вопросы
1308
- - **Contributing**: Руководство по участию в разработке
1309
-
1310
64
  ## 👨‍💻 Автор
1311
65
 
1312
66
  ### Krivega Dmitriy
@@ -1317,6 +71,6 @@ if (!RTCPeerConnection.prototype.restartIce) {
1317
71
 
1318
72
  ## 📄 Лицензия
1319
73
 
1320
- Copyright © 2021‑2025 [Krivega Dmitriy](https://github.com/Krivega).
74
+ Copyright © 2021‑2026 [Krivega Dmitriy](https://github.com/Krivega).
1321
75
 
1322
76
  This project is licensed under the [MIT License](https://github.com/Krivega/sip-connector/blob/master/LICENSE) - see the LICENSE file for details.