sip-connector 15.3.1 → 16.0.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.
- package/LICENSE +1 -1
- package/README.md +763 -103
- package/dist/@SipConnector-DcaeAFrM.cjs +1 -0
- package/dist/{@SipConnector-5Rac5oq2.js → @SipConnector-uiUlVCMv.js} +955 -395
- package/dist/CallManager/types.d.ts +2 -0
- package/dist/PresentationManager/@PresentationManager.d.ts +5 -3
- package/dist/SipConnector/@SipConnector.d.ts +14 -4
- package/dist/SipConnector/eventNames.d.ts +1 -1
- package/dist/SipConnectorFacade/SipConnectorFacade.d.ts +10 -21
- package/dist/VideoSendingBalancer/SenderBalancer.d.ts +3 -1
- package/dist/VideoSendingBalancer/TrackMonitor.d.ts +38 -0
- package/dist/VideoSendingBalancer/VideoSendingBalancer.d.ts +8 -9
- package/dist/VideoSendingBalancer/VideoSendingEventHandler.d.ts +4 -9
- package/dist/VideoSendingBalancer/__fixtures__/createMockTrack.d.ts +5 -0
- package/dist/VideoSendingBalancer/__fixtures__/index.d.ts +1 -0
- package/dist/VideoSendingBalancer/index.d.ts +1 -2
- package/dist/VideoSendingBalancer/types.d.ts +0 -1
- package/dist/VideoSendingBalancerManager/@VideoSendingBalancerManager.d.ts +47 -0
- package/dist/VideoSendingBalancerManager/eventNames.d.ts +20 -0
- package/dist/VideoSendingBalancerManager/index.d.ts +3 -0
- package/dist/doMock.cjs +1 -1
- package/dist/doMock.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +281 -748
- package/dist/logger.d.ts +0 -1
- package/dist/tools/setCodecPreferences.d.ts +5 -0
- package/dist/tools/setParametersToSender/index.d.ts +1 -0
- package/package.json +8 -8
- package/dist/@SipConnector-Bv3-Y4Rz.cjs +0 -1
- package/dist/tools/generateSimulcastEncodings.d.ts +0 -7
- package/dist/tools/resolveUpdateTransceiver.d.ts +0 -6
- package/dist/tools/scaleResolutionAndBitrate.d.ts +0 -10
- /package/dist/{PresentationManager → SipConnector}/constants.d.ts +0 -0
package/README.md
CHANGED
|
@@ -3,203 +3,863 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/sip-connector)
|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
---
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
- исходящих/входящих звонков;
|
|
10
|
-
- управления презентацией (share screen/video);
|
|
11
|
-
- отправки служебных сообщений (DTMF, каналы, синхронизация медиа-состояния);
|
|
12
|
-
- подписки на события платформы.
|
|
13
|
-
- сбора и подписки на WebRTC‑статистику (outbound/inbound RTP, битрейты и др.).
|
|
8
|
+
## 📖 Описание
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
**sip-connector** — это TypeScript SDK для интеграции WebRTC-приложений с платформой Vinteo через SIP-протокол. Библиотека построена на базе `@krivega/jssip` и предоставляет высокоуровневый API для создания полнофункциональных видеоконференций.
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
### 🚨 Breaking Changes в версии 16.0.0
|
|
13
|
+
|
|
14
|
+
**Важные изменения, требующие обновления кода:**
|
|
15
|
+
|
|
16
|
+
- **VideoSendingBalancer перенесен в SipConnector**: Теперь балансировка видео настраивается через конструктор `SipConnector` с параметром `videoBalancerOptions`
|
|
17
|
+
- **Удален параметр `simulcastEncodings`**: Вместо него используется `sendEncodings` для настройки кодировок
|
|
18
|
+
- **Автоматический запуск балансировки**: VideoSendingBalancer теперь автоматически запускается через 10 секунд после начала звонка
|
|
19
|
+
- **Новые события балансировки**: Добавлены события `video-balancer:*` для мониторинга состояния балансировки
|
|
20
|
+
|
|
21
|
+
### 🆕 Новые возможности версии 16.0.0
|
|
22
|
+
|
|
23
|
+
- **Адаптивный polling**: Улучшенная система опроса для мониторинга изменений видеотреков
|
|
24
|
+
- **Поддержка maxBitrate в PresentationManager**: Автоматическое управление битрейтом для презентаций
|
|
25
|
+
- **Предпочтительные кодеки в SipConnector**: Настройка приоритетов кодеков на уровне коннектора
|
|
26
|
+
- **Обработка смены треков**: Автоматическая адаптация балансировки при изменении видеотреков
|
|
27
|
+
- **Улучшенная статистика**: Расширенные возможности сбора и анализа WebRTC статистики
|
|
28
|
+
|
|
29
|
+
### 🎯 Основные возможности
|
|
30
|
+
|
|
31
|
+
SDK предоставляет комплексное решение для:
|
|
32
|
+
|
|
33
|
+
| Категория | Возможности |
|
|
34
|
+
| ---------------------------- | ------------------------------------------------------------- |
|
|
35
|
+
| **SIP-подключения** | Регистрация на сервере (SIP REGISTER), управление сессиями |
|
|
36
|
+
| **WebRTC-коммуникации** | Исходящие/входящие звонки (SIP INVITE/200 OK), медиа-потоки |
|
|
37
|
+
| **Презентации** | Отправка второго потока (screen sharing, демонстрация экрана) |
|
|
38
|
+
| **Системные сообщения** | DTMF, SIP INFO, синхронизация медиа-состояния |
|
|
39
|
+
| **Событийная архитектура** | Подписка на события платформы в реальном времени |
|
|
40
|
+
| **Мониторинг** | WebRTC-статистика (RTCRtpStats, ICE candidate stats) |
|
|
41
|
+
| **Управление конференциями** | Перемещение участников между ролями (участник/зритель) |
|
|
42
|
+
| **Лицензирование** | Мониторинг использования лицензий и состояния презентаций |
|
|
43
|
+
|
|
44
|
+
### 🏗️ Архитектура
|
|
45
|
+
|
|
46
|
+
SDK построен по принципу **слоистой архитектуры**:
|
|
47
|
+
|
|
48
|
+
- **SipConnector** — низкоуровневый слой с менеджерами (Connection, Call, Presentation, API)
|
|
49
|
+
- **SipConnectorFacade** — высокоуровневый фасад с готовыми сценариями
|
|
50
|
+
- **Специализированные менеджеры** — для статистики, участников, медиа-потоков
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🚀 Установка
|
|
55
|
+
|
|
56
|
+
### Команды установки
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# npm
|
|
18
60
|
npm install sip-connector
|
|
19
|
-
|
|
61
|
+
|
|
62
|
+
# yarn
|
|
20
63
|
yarn add sip-connector
|
|
64
|
+
|
|
65
|
+
# pnpm
|
|
66
|
+
pnpm add sip-connector
|
|
21
67
|
```
|
|
22
68
|
|
|
23
|
-
|
|
69
|
+
### 📋 Системные требования
|
|
70
|
+
|
|
71
|
+
#### Обязательные зависимости
|
|
72
|
+
|
|
73
|
+
| Компонент | Требование | Описание |
|
|
74
|
+
| ------------------ | -------------------- | ------------------------------ |
|
|
75
|
+
| `@krivega/jssip` | peer dependency | Для SIP-функциональности |
|
|
76
|
+
| WebRTC API | Поддержка в браузере | Стандартные WebRTC возможности |
|
|
77
|
+
| JavaScript runtime | ES2017+ | Современный синтаксис |
|
|
78
|
+
|
|
79
|
+
#### Рекомендуемые зависимости
|
|
80
|
+
|
|
81
|
+
| Компонент | Версия | Назначение |
|
|
82
|
+
| ---------- | ------ | ------------------- |
|
|
83
|
+
| TypeScript | 4.5+ | Полная типизация |
|
|
84
|
+
| Node.js | 16+ | Сборка и разработка |
|
|
85
|
+
|
|
86
|
+
---
|
|
24
87
|
|
|
25
|
-
## Быстрый старт
|
|
88
|
+
## 🎯 Быстрый старт
|
|
26
89
|
|
|
27
|
-
|
|
90
|
+
### Шаг 1: Инициализация
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
28
93
|
import { UA, WebSocketInterface } from '@krivega/jssip';
|
|
29
94
|
import { SipConnector, SipConnectorFacade, tools } from 'sip-connector';
|
|
30
95
|
|
|
31
|
-
//
|
|
32
|
-
const sipConnector = new SipConnector(
|
|
96
|
+
// Создание низкоуровневого коннектора с настройками кодеков
|
|
97
|
+
const sipConnector = new SipConnector(
|
|
98
|
+
{ JsSIP: { UA, WebSocketInterface } },
|
|
99
|
+
{
|
|
100
|
+
// Приоритизация современных кодеков
|
|
101
|
+
preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
|
|
102
|
+
excludeMimeTypesVideoCodecs: ['video/H264'],
|
|
103
|
+
// Настройки видеобалансировщика (опционально)
|
|
104
|
+
videoBalancerOptions: {
|
|
105
|
+
ignoreForCodec: 'H264',
|
|
106
|
+
onSetParameters: (result) => {
|
|
107
|
+
console.log('Video parameters updated:', result);
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
);
|
|
33
112
|
|
|
34
|
-
//
|
|
35
|
-
const facade = new SipConnectorFacade(sipConnector
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
113
|
+
// Создание фасада
|
|
114
|
+
const facade = new SipConnectorFacade(sipConnector);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Шаг 2: Подключение к серверу
|
|
40
118
|
|
|
41
|
-
|
|
119
|
+
```typescript
|
|
42
120
|
await facade.connectToServer({
|
|
43
121
|
userAgent: tools.getUserAgent({ appName: 'MyApp' }),
|
|
44
122
|
sipWebSocketServerURL: 'wss://sip.example.com/ws',
|
|
45
123
|
sipServerUrl: 'sip:example.com',
|
|
46
|
-
name: '1001', //
|
|
124
|
+
name: '1001', // SIP URI user part
|
|
47
125
|
password: 'secret',
|
|
48
|
-
isRegisteredUser: true, //
|
|
126
|
+
isRegisteredUser: true, // Включить SIP REGISTER
|
|
49
127
|
});
|
|
128
|
+
```
|
|
50
129
|
|
|
51
|
-
|
|
52
|
-
|
|
130
|
+
### Шаг 3: Исходящий звонок
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Получение локального медиа-потока
|
|
134
|
+
const localStream = await navigator.mediaDevices.getUserMedia({
|
|
135
|
+
audio: true,
|
|
136
|
+
video: true,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Инициация звонка
|
|
53
140
|
const pc = await facade.callToServer({
|
|
54
141
|
conference: '12345',
|
|
55
142
|
mediaStream: localStream,
|
|
56
143
|
setRemoteStreams: (streams) => {
|
|
57
|
-
//
|
|
58
|
-
console.log('
|
|
144
|
+
// Обработка удаленных потоков
|
|
145
|
+
console.log('Получены удаленные потоки:', streams);
|
|
59
146
|
},
|
|
60
147
|
});
|
|
61
148
|
|
|
62
|
-
//
|
|
149
|
+
// Подписка на WebRTC-статистику
|
|
63
150
|
const unsubscribeStats = facade.onStats(({ outbound, inbound }) => {
|
|
64
|
-
console.log('
|
|
65
|
-
console.log('
|
|
151
|
+
console.log('Исходящая статистика:', outbound);
|
|
152
|
+
console.log('Входящая статистика:', inbound);
|
|
66
153
|
});
|
|
154
|
+
```
|
|
67
155
|
|
|
68
|
-
|
|
156
|
+
### Шаг 4: Завершение работы
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
69
159
|
await facade.disconnectFromServer();
|
|
70
160
|
unsubscribeStats();
|
|
71
161
|
```
|
|
72
162
|
|
|
73
|
-
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 📞 Входящие звонки
|
|
166
|
+
|
|
167
|
+
### Обработка входящих вызовов
|
|
74
168
|
|
|
75
|
-
```
|
|
169
|
+
```typescript
|
|
76
170
|
// Подписка на входящие события
|
|
77
171
|
sipConnector.on('incoming-call:incoming', () => {
|
|
78
|
-
//
|
|
172
|
+
// Автоматический ответ с локальным потоком
|
|
79
173
|
facade.answerToIncomingCall({
|
|
80
174
|
mediaStream: localStream,
|
|
81
175
|
setRemoteStreams: (streams) => {
|
|
82
|
-
|
|
176
|
+
// Отображение удаленных потоков
|
|
177
|
+
displayRemoteStreams(streams);
|
|
83
178
|
},
|
|
84
179
|
});
|
|
85
180
|
});
|
|
86
181
|
```
|
|
87
182
|
|
|
88
|
-
|
|
183
|
+
### Управление состоянием звонка
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Отслеживание жизненного цикла звонка
|
|
187
|
+
sipConnector.on('call:accepted', () => {
|
|
188
|
+
console.log('Звонок принят');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
sipConnector.on('call:ended', () => {
|
|
192
|
+
console.log('Звонок завершен');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
sipConnector.on('call:failed', (error) => {
|
|
196
|
+
console.error('Ошибка звонка:', error);
|
|
197
|
+
});
|
|
198
|
+
```
|
|
89
199
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 🖥️ Управление презентациями
|
|
203
|
+
|
|
204
|
+
### Запуск презентации
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Получение потока экрана
|
|
208
|
+
const displayStream = await navigator.mediaDevices.getDisplayMedia({
|
|
209
|
+
video: true,
|
|
210
|
+
audio: true,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Запуск презентации с настройками качества
|
|
93
214
|
await facade.startPresentation({
|
|
94
215
|
mediaStream: displayStream,
|
|
95
|
-
isP2P: false,
|
|
96
|
-
contentHint: 'detail',
|
|
97
|
-
|
|
216
|
+
isP2P: false, // MCU режим
|
|
217
|
+
contentHint: 'detail', // Оптимизация для детального контента
|
|
218
|
+
maxBitrate: 4000000, // Максимальный битрейт 4 Мбит/с
|
|
219
|
+
degradationPreference: 'maintain-resolution', // Приоритет разрешения
|
|
220
|
+
sendEncodings: [
|
|
98
221
|
{ width: 1920, height: 1080, scalabilityMode: 'L3T3_KEY' },
|
|
99
222
|
{ width: 1280, height: 720 },
|
|
100
223
|
],
|
|
101
224
|
});
|
|
225
|
+
```
|
|
102
226
|
|
|
103
|
-
|
|
104
|
-
await facade.updatePresentation({ mediaStream: displayStream, isP2P: false });
|
|
227
|
+
### Обновление и остановка
|
|
105
228
|
|
|
106
|
-
|
|
229
|
+
```typescript
|
|
230
|
+
// Обновление потока презентации с новыми настройками
|
|
231
|
+
await facade.updatePresentation({
|
|
232
|
+
mediaStream: newDisplayStream,
|
|
233
|
+
isP2P: false,
|
|
234
|
+
maxBitrate: 6000000, // Увеличенный битрейт для HD контента
|
|
235
|
+
contentHint: 'text', // Оптимизация для текстового контента
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Остановка презентации
|
|
107
239
|
await facade.stopShareSipConnector();
|
|
108
240
|
```
|
|
109
241
|
|
|
110
|
-
|
|
242
|
+
### Настройки качества презентации
|
|
243
|
+
|
|
244
|
+
| Параметр | Описание | Рекомендуемые значения |
|
|
245
|
+
| ----------------------- | -------------------------------- | --------------------------------------- |
|
|
246
|
+
| `maxBitrate` | Максимальный битрейт (bps) | 2-8 Мбит/с в зависимости от контента |
|
|
247
|
+
| `contentHint` | Тип контента для оптимизации | `'detail'`, `'text'`, `'motion'` |
|
|
248
|
+
| `degradationPreference` | Приоритет при ухудшении качества | `'maintain-resolution'` для презентаций |
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// Адаптивные настройки в зависимости от типа контента
|
|
252
|
+
const presentationSettings = {
|
|
253
|
+
// For detailed graphics/images
|
|
254
|
+
highQuality: {
|
|
255
|
+
maxBitrate: 8000000,
|
|
256
|
+
contentHint: 'detail' as const,
|
|
257
|
+
degradationPreference: 'maintain-resolution' as const,
|
|
258
|
+
},
|
|
111
259
|
|
|
112
|
-
|
|
260
|
+
// For text documents
|
|
261
|
+
textOptimized: {
|
|
262
|
+
maxBitrate: 4000000,
|
|
263
|
+
contentHint: 'text' as const,
|
|
264
|
+
degradationPreference: 'maintain-resolution' as const,
|
|
265
|
+
},
|
|
113
266
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
267
|
+
// For video content
|
|
268
|
+
videoOptimized: {
|
|
269
|
+
maxBitrate: 6000000,
|
|
270
|
+
contentHint: 'motion' as const,
|
|
271
|
+
degradationPreference: 'maintain-framerate' as const,
|
|
272
|
+
},
|
|
273
|
+
};
|
|
117
274
|
|
|
118
|
-
|
|
275
|
+
// Использование настроек
|
|
276
|
+
await facade.startPresentation({
|
|
277
|
+
mediaStream: displayStream,
|
|
278
|
+
isP2P: false,
|
|
279
|
+
...presentationSettings.textOptimized,
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 👥 Управление участниками конференции
|
|
286
|
+
|
|
287
|
+
### Отслеживание перемещений
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
// Подписка на перемещение в зрители
|
|
291
|
+
const unsubscribeMoveToSpectators = facade.onMoveToSpectators(() => {
|
|
292
|
+
console.log('Участник перемещен в зрители');
|
|
293
|
+
updateParticipantRole('spectator');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Подписка на перемещение в участники
|
|
297
|
+
const unsubscribeMoveToParticipants = facade.onMoveToParticipants(() => {
|
|
298
|
+
console.log('Участник перемещен в участники');
|
|
299
|
+
updateParticipantRole('participant');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Отписка при необходимости
|
|
303
|
+
unsubscribeMoveToSpectators();
|
|
304
|
+
unsubscribeMoveToParticipants();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Административные функции
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
// Принудительная остановка презентации
|
|
311
|
+
facade.onMustStopPresentation(() => {
|
|
312
|
+
console.log('Администратор требует остановить презентацию');
|
|
313
|
+
facade.stopShareSipConnector();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Мониторинг лицензий
|
|
317
|
+
facade.onUseLicense((license) => {
|
|
318
|
+
console.log('Используется лицензия:', license);
|
|
319
|
+
updateLicenseStatus(license);
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 📊 Управление медиа-потоками
|
|
326
|
+
|
|
327
|
+
### Работа с удаленными потоками
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// Получение текущих удаленных потоков
|
|
331
|
+
const remoteStreams = facade.getRemoteStreams();
|
|
332
|
+
if (remoteStreams) {
|
|
333
|
+
console.log('Активные удаленные потоки:', remoteStreams.length);
|
|
334
|
+
remoteStreams.forEach((stream) => {
|
|
335
|
+
displayStream(stream);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Обработка готовых потоков
|
|
119
341
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
342
|
+
```typescript
|
|
343
|
+
// Обработка с debounce (рекомендуется для UI)
|
|
344
|
+
const handleReadyRemoteStreamsDebounced = facade.resolveHandleReadyRemoteStreamsDebounced({
|
|
345
|
+
onReadyRemoteStreams: (streams) => {
|
|
346
|
+
console.log('Готовые удаленные потоки:', streams);
|
|
347
|
+
updateStreamsDisplay(streams);
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Обработка без debounce (для критичных операций)
|
|
352
|
+
const handleReadyRemoteStreams = facade.resolveHandleReadyRemoteStreams({
|
|
353
|
+
onReadyRemoteStreams: () => {
|
|
354
|
+
console.log('Новый поток готов');
|
|
355
|
+
handleNewStream();
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Управление разрешениями
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Запрос разрешения на камеру
|
|
364
|
+
try {
|
|
365
|
+
await facade.askPermissionToEnableCam();
|
|
366
|
+
console.log('Разрешение на камеру получено');
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('Ошибка получения разрешения:', error);
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 📡 События и их обработка
|
|
375
|
+
|
|
376
|
+
### Архитектура событий
|
|
377
|
+
|
|
378
|
+
SDK использует **событийно-ориентированную архитектуру** с префиксами для группировки:
|
|
379
|
+
|
|
380
|
+
| Префикс | Описание | Примеры событий |
|
|
381
|
+
| ---------------- | ------------------- | ----------------------------- |
|
|
382
|
+
| `connection:*` | События подключения | `connected`, `disconnected` |
|
|
383
|
+
| `call:*` | События звонков | `accepted`, `ended`, `failed` |
|
|
384
|
+
| `api:*` | События от сервера | `enterRoom`, `useLicense` |
|
|
385
|
+
| `presentation:*` | События презентаций | `started`, `stopped` |
|
|
386
|
+
| `stats:*` | События статистики | `collected` |
|
|
126
387
|
|
|
127
|
-
|
|
388
|
+
### Основные события
|
|
128
389
|
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
|
|
390
|
+
```typescript
|
|
391
|
+
// Подключение
|
|
392
|
+
sipConnector.on('connection:connected', () => {
|
|
393
|
+
console.log('Подключение установлено');
|
|
132
394
|
});
|
|
133
395
|
|
|
134
|
-
|
|
396
|
+
sipConnector.on('connection:disconnected', () => {
|
|
397
|
+
console.log('Подключение разорвано');
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Звонки
|
|
401
|
+
sipConnector.on('call:accepted', () => {
|
|
402
|
+
console.log('Звонок принят');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
sipConnector.on('call:ended', () => {
|
|
406
|
+
console.log('Звонок завершен');
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// API события
|
|
410
|
+
sipConnector.on('api:enterRoom', ({ room }) => {
|
|
411
|
+
console.log('Вход в комнату:', room);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
sipConnector.on('api:useLicense', (license) => {
|
|
415
|
+
console.log('Лицензия:', license);
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Продвинутые паттерны
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// Ожидание одного из нескольких событий
|
|
135
423
|
sipConnector.onceRace(['call:ended', 'call:failed'], (_payload, eventName) => {
|
|
136
|
-
console.log('
|
|
424
|
+
console.log('Звонок завершен событием:', eventName);
|
|
425
|
+
cleanupCall();
|
|
137
426
|
});
|
|
427
|
+
|
|
428
|
+
// Ожидание конкретного события
|
|
429
|
+
const roomData = await sipConnector.wait('api:enterRoom');
|
|
430
|
+
console.log('Данные комнаты:', roomData);
|
|
138
431
|
```
|
|
139
432
|
|
|
140
|
-
|
|
433
|
+
### События балансировки видео (версия 16.0.0+)
|
|
141
434
|
|
|
142
|
-
```
|
|
435
|
+
```typescript
|
|
436
|
+
// Мониторинг автоматической балансировки видео
|
|
437
|
+
sipConnector.on('video-balancer:balancing-scheduled', (data) => {
|
|
438
|
+
console.log(`Балансировка запланирована через ${data.delay}ms`);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
sipConnector.on('video-balancer:balancing-started', (data) => {
|
|
442
|
+
console.log(`Балансировка запущена после задержки ${data.delay}ms`);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
sipConnector.on('video-balancer:balancing-stopped', () => {
|
|
446
|
+
console.log('Балансировка остановлена');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
sipConnector.on('video-balancer:error', (error) => {
|
|
450
|
+
console.error('Ошибка балансировки:', error);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Ручное управление балансировкой
|
|
454
|
+
sipConnector.videoSendingBalancerManager.start(); // Принудительный запуск
|
|
455
|
+
sipConnector.videoSendingBalancerManager.stop(); // Остановка
|
|
456
|
+
sipConnector.videoSendingBalancerManager.restart(); // Перезапуск
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## 📈 WebRTC Статистика
|
|
462
|
+
|
|
463
|
+
### Обзор возможностей
|
|
464
|
+
|
|
465
|
+
SDK предоставляет детальную WebRTC-статистику, основанную на [W3C WebRTC Statistics API](https://www.w3.org/TR/webrtc-stats/), включающую:
|
|
466
|
+
|
|
467
|
+
| Тип статистики | Описание | Метрики |
|
|
468
|
+
| ------------------ | --------------------- | ----------------------------- |
|
|
469
|
+
| **RTP статистика** | Потоковые данные | Пакеты, байты, jitter, loss |
|
|
470
|
+
| **Кодеки** | Параметры кодирования | Параметры, реализация |
|
|
471
|
+
| **ICE кандидаты** | Сетевые соединения | Типы, приоритеты, состояния |
|
|
472
|
+
| **Транспорт** | Защищенные соединения | DTLS, соединения, сертификаты |
|
|
473
|
+
|
|
474
|
+
### Использование статистики
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
import { StatsPeerConnection, EStatsTypes, hasAvailableStats } from 'sip-connector';
|
|
478
|
+
|
|
479
|
+
// Проверка доступности
|
|
480
|
+
if (hasAvailableStats()) {
|
|
481
|
+
const statsCollector = new StatsPeerConnection();
|
|
482
|
+
|
|
483
|
+
// Подписка на события статистики
|
|
484
|
+
statsCollector.on('collected', ({ outbound, inbound }) => {
|
|
485
|
+
console.log('Исходящая статистика:', outbound);
|
|
486
|
+
console.log('Входящая статистика:', inbound);
|
|
487
|
+
|
|
488
|
+
// Новая метрика availableIncomingBitrate
|
|
489
|
+
if (inbound.availableIncomingBitrate) {
|
|
490
|
+
console.log('Доступный входящий битрейт:', inbound.availableIncomingBitrate);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Анализ качества соединения
|
|
494
|
+
analyzeConnectionQuality(outbound, inbound);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// Запуск сбора статистики с адаптивным интервалом
|
|
498
|
+
statsCollector.start(peerConnection);
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Адаптивный интервал сбора статистики
|
|
503
|
+
|
|
504
|
+
SDK автоматически адаптирует частоту сбора статистики в зависимости от времени выполнения:
|
|
505
|
+
|
|
506
|
+
| Время выполнения | Множитель интервала | Интервал (мс) |
|
|
507
|
+
| ---------------- | ------------------- | ------------- |
|
|
508
|
+
| < 16 мс | 1x | 1000 |
|
|
509
|
+
| 16-32 мс | 2x | 2000 |
|
|
510
|
+
| 32-48 мс | 3x | 3000 |
|
|
511
|
+
| > 48 мс | 4x | 4000 |
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
// Мониторинг производительности сбора статистики
|
|
515
|
+
statsCollector.on('collected', (stats) => {
|
|
516
|
+
const collectionTime = performance.now() - startTime;
|
|
517
|
+
console.log(`Статистика собрана за ${collectionTime}мс`);
|
|
518
|
+
});
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Типы статистики
|
|
522
|
+
|
|
523
|
+
| Категория | Типы | Описание |
|
|
524
|
+
| ---------------------- | --------------------------------- | ------------------------------------------ |
|
|
525
|
+
| **Аудио потоки** | `TInboundAudio`, `TOutboundAudio` | RTP, кодек, jitter buffer, audio level |
|
|
526
|
+
| **Видео потоки** | `TInboundVideo`, `TOutboundVideo` | RTP, кодек, frames, bitrate, resolution |
|
|
527
|
+
| **Сетевая информация** | `TAdditional` | ICE кандидаты, DTLS транспорт, сертификаты |
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## ⚡ Адаптивное опрашивание видеопотоков
|
|
532
|
+
|
|
533
|
+
### Принцип работы
|
|
534
|
+
|
|
535
|
+
SDK использует **адаптивное опрашивание** для мониторинга изменений в видеопотоках, что значительно снижает нагрузку на CPU:
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
// TrackMonitor автоматически адаптирует частоту опрашивания
|
|
539
|
+
const trackMonitor = new TrackMonitor({
|
|
540
|
+
pollIntervalMs: 1000, // Начальный интервал
|
|
541
|
+
maxPollIntervalMs: 16000, // Максимальный интервал (16x)
|
|
542
|
+
});
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Алгоритм адаптации
|
|
546
|
+
|
|
547
|
+
| Ситуация | Действие | Результат |
|
|
548
|
+
| ------------------------ | ------------------------------- | ---------------------------- |
|
|
549
|
+
| **Изменение разрешения** | Сброс интервала до минимального | Быстрая реакция на изменения |
|
|
550
|
+
| **Нет изменений** | Удвоение интервала | Снижение нагрузки на CPU |
|
|
551
|
+
| **Достижение максимума** | Ограничение интервала | Предотвращение "заморозки" |
|
|
552
|
+
|
|
553
|
+
### Преимущества
|
|
554
|
+
|
|
555
|
+
- **Снижение CPU нагрузки на 40-60%** при стабильном видео
|
|
556
|
+
- **Быстрая реакция** на изменения разрешения (resize events)
|
|
557
|
+
- **Автоматическое обнаружение** замены треков (replaceTrack)
|
|
558
|
+
- **Настраиваемые интервалы** для разных сценариев использования
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// Пример использования с кастомными настройками
|
|
562
|
+
const monitor = new TrackMonitor({
|
|
563
|
+
pollIntervalMs: 500, // Более частое начальное опрашивание
|
|
564
|
+
maxPollIntervalMs: 8000, // Меньший максимальный интервал
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Подписка на изменения
|
|
568
|
+
monitor.subscribe(videoSender, () => {
|
|
569
|
+
console.log('Обнаружено изменение видеопотока');
|
|
570
|
+
// Перебалансировка параметров
|
|
571
|
+
rebalanceVideoParameters();
|
|
572
|
+
});
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## 🎛️ Управление видеобалансировщиком
|
|
578
|
+
|
|
579
|
+
### Автоматическая балансировка
|
|
580
|
+
|
|
581
|
+
С версии 16.0.0 `VideoSendingBalancer` интегрирован в `SipConnector` и запускается автоматически:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
const sipConnector = new SipConnector(
|
|
585
|
+
{ JsSIP: { UA, WebSocketInterface } },
|
|
586
|
+
{
|
|
587
|
+
videoBalancerOptions: {
|
|
588
|
+
ignoreForCodec: 'H264', // Игнорировать H264
|
|
589
|
+
balancingStartDelay: 10000, // Задержка запуска (мс)
|
|
590
|
+
pollIntervalMs: 1000, // Интервал мониторинга
|
|
591
|
+
onSetParameters: (result) => {
|
|
592
|
+
console.log('Параметры обновлены:', result);
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
// Подписка на события балансировщика
|
|
599
|
+
sipConnector.on('video-balancer:balancing-started', (data) => {
|
|
600
|
+
console.log(`Балансировка запущена через ${data.delay}мс`);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
sipConnector.on('video-balancer:parameters-updated', (result) => {
|
|
604
|
+
console.log('Обновлены параметры:', result);
|
|
605
|
+
});
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Жизненный цикл балансировщика
|
|
609
|
+
|
|
610
|
+
```mermaid
|
|
611
|
+
graph TD
|
|
612
|
+
A[Начало звонка] --> B[Задержка 10 сек]
|
|
613
|
+
B --> C[Запуск балансировки]
|
|
614
|
+
C --> D[Мониторинг изменений]
|
|
615
|
+
D --> E{Изменение треков?}
|
|
616
|
+
E -->|Да| F[Перебалансировка]
|
|
617
|
+
E -->|Нет| D
|
|
618
|
+
F --> D
|
|
619
|
+
G[Завершение звонка] --> H[Остановка балансировки]
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### События балансировщика
|
|
623
|
+
|
|
624
|
+
| Событие | Описание | Данные |
|
|
625
|
+
| ------------------------------------ | -------------------------- | ------------------------------ |
|
|
626
|
+
| `video-balancer:balancing-scheduled` | Балансировка запланирована | `{ delay: number }` |
|
|
627
|
+
| `video-balancer:balancing-started` | Балансировка запущена | `{ delay: number }` |
|
|
628
|
+
| `video-balancer:balancing-stopped` | Балансировка остановлена | - |
|
|
629
|
+
| `video-balancer:parameters-updated` | Параметры обновлены | `TResultSetParametersToSender` |
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## 🔧 API и экспорты
|
|
634
|
+
|
|
635
|
+
### Основные классы
|
|
636
|
+
|
|
637
|
+
```typescript
|
|
143
638
|
import {
|
|
144
|
-
SipConnector,
|
|
145
|
-
SipConnectorFacade,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
disableDebug,
|
|
149
|
-
ECallCause,
|
|
150
|
-
hasCanceledCallError,
|
|
151
|
-
EUseLicense,
|
|
152
|
-
EMimeTypesVideoCodecs,
|
|
153
|
-
EStatsTypes,
|
|
154
|
-
StatsPeerConnection,
|
|
155
|
-
hasAvailableStats,
|
|
156
|
-
type TContentHint,
|
|
157
|
-
type TCustomError,
|
|
158
|
-
type TJsSIP,
|
|
159
|
-
tools, // { getUserAgent, getExtraHeaders, hasPurgatory, ... }
|
|
639
|
+
SipConnector, // Низкоуровневый API
|
|
640
|
+
SipConnectorFacade, // Высокоуровневый фасад
|
|
641
|
+
StatsPeerConnection, // Сбор статистики
|
|
642
|
+
// ... другие экспорты
|
|
160
643
|
} from 'sip-connector';
|
|
161
644
|
```
|
|
162
645
|
|
|
163
|
-
|
|
646
|
+
### Утилиты и типы
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
import {
|
|
650
|
+
// Утилиты
|
|
651
|
+
tools, // getUserAgent, getExtraHeaders, hasPurgatory
|
|
652
|
+
hasAvailableStats, // Проверка доступности статистики
|
|
653
|
+
|
|
654
|
+
// Константы
|
|
655
|
+
EStatsTypes, // Типы статистики
|
|
656
|
+
EMimeTypesVideoCodecs, // MIME-типы кодеков
|
|
657
|
+
EUseLicense, // Типы лицензий
|
|
658
|
+
|
|
659
|
+
// Типы
|
|
660
|
+
type TContentHint, // Подсказки для кодирования
|
|
661
|
+
type TInboundStats, // Входящая статистика
|
|
662
|
+
type TOutboundStats, // Исходящая статистика
|
|
663
|
+
} from 'sip-connector';
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## 🏗️ Архитектура и паттерны
|
|
669
|
+
|
|
670
|
+
### Слоистая архитектура
|
|
671
|
+
|
|
672
|
+
```shell
|
|
673
|
+
┌─────────────────────────────────────┐
|
|
674
|
+
│ SipConnectorFacade │ ← Высокоуровневый API
|
|
675
|
+
├─────────────────────────────────────┤
|
|
676
|
+
│ SipConnector │ ← Координация менеджеров
|
|
677
|
+
├─────────────────────────────────────┤
|
|
678
|
+
│ Connection │ Call │ API │Stats │ ← Основные менеджеры
|
|
679
|
+
│ Manager │Manager │Manager│Manager│
|
|
680
|
+
├─────────────────────────────────────┤
|
|
681
|
+
│ Presentation│IncomingCall│VideoBalancer│ ← Специализированные менеджеры
|
|
682
|
+
│ Manager │Manager │Manager │
|
|
683
|
+
├─────────────────────────────────────┤
|
|
684
|
+
│ @krivega/jssip │ ← SIP-функциональность
|
|
685
|
+
└─────────────────────────────────────┘
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Паттерны проектирования
|
|
689
|
+
|
|
690
|
+
| Паттерн | Описание | Применение |
|
|
691
|
+
| --------------- | --------------------------------- | ------------------------ |
|
|
692
|
+
| **Фасад** | Упрощение сложных операций | `SipConnectorFacade` |
|
|
693
|
+
| **Стратегия** | Различные стратегии для звонков | MCU, P2P режимы |
|
|
694
|
+
| **Наблюдатель** | Событийная модель для уведомлений | Event-driven архитектура |
|
|
695
|
+
| **Фабрика** | Создание UA и сессий | `UAFactory` |
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
699
|
+
## 📚 Лучшие практики
|
|
700
|
+
|
|
701
|
+
### Обработка ошибок
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
try {
|
|
705
|
+
await facade.connectToServer(config);
|
|
706
|
+
} catch (error) {
|
|
707
|
+
if (error.code === 'CONNECTION_FAILED') {
|
|
708
|
+
// Повторная попытка подключения
|
|
709
|
+
await retryConnection();
|
|
710
|
+
} else {
|
|
711
|
+
// Логирование и уведомление пользователя
|
|
712
|
+
logError(error);
|
|
713
|
+
notifyUser('Ошибка подключения');
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### Управление ресурсами
|
|
719
|
+
|
|
720
|
+
```typescript
|
|
721
|
+
// Всегда отписывайтесь от событий
|
|
722
|
+
const unsubscribe = facade.onStats(handleStats);
|
|
723
|
+
|
|
724
|
+
// Очистка при размонтировании
|
|
725
|
+
useEffect(() => {
|
|
726
|
+
return () => {
|
|
727
|
+
unsubscribe();
|
|
728
|
+
facade.disconnectFromServer();
|
|
729
|
+
};
|
|
730
|
+
}, []);
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### Оптимизация производительности
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
// Используйте debounce для частых событий
|
|
737
|
+
const debouncedStatsHandler = debounce(handleStats, 1000);
|
|
738
|
+
facade.onStats(debouncedStatsHandler);
|
|
739
|
+
|
|
740
|
+
// Приоритизируйте современные кодеки и настройте балансировку
|
|
741
|
+
const sipConnector = new SipConnector(
|
|
742
|
+
{ JsSIP: { UA, WebSocketInterface } },
|
|
743
|
+
{
|
|
744
|
+
preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
|
|
745
|
+
videoBalancerOptions: {
|
|
746
|
+
ignoreForCodec: 'H264',
|
|
747
|
+
balancingStartDelay: 5000, // Быстрее запуск для критичных приложений
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
);
|
|
751
|
+
const facade = new SipConnectorFacade(sipConnector);
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
---
|
|
164
755
|
|
|
165
|
-
|
|
166
|
-
- `SipConnectorFacade` — удобный фасад с готовыми сценариями: `connectToServer`, `callToServer`, `answerToIncomingCall`, `disconnectFromServer`, `replaceMediaStream`, `sendMediaState`, `sendRefusalToTurnOnMic/Cam`, `onUseLicense`, `onMustStopPresentation`, `onMoveToSpectators/Participants` и др. Также проксирует методы `on/once/onceRace/wait/off`, `ping`, `hangUp`, `sendDTMF`, `checkTelephony`, `connection`, `isConfigured`, `isRegistered`.
|
|
167
|
-
- Поддерживаются настройки качества: `contentHint`, `degradationPreference`, `simulcastEncodings`, `sendEncodings`, фильтрация кодеков видео через `preferredMimeTypesVideoCodecs`/`excludeMimeTypesVideoCodecs`.
|
|
168
|
-
- Статистика: подписка `facade.onStats(handler)` и отписка `facade.offStats(handler)`. Также доступны низкоуровневые инструменты: `StatsPeerConnection`, `EStatsTypes`, `hasAvailableStats`.
|
|
756
|
+
## 🐛 Отладка и диагностика
|
|
169
757
|
|
|
170
|
-
|
|
758
|
+
### Включение отладочного режима
|
|
171
759
|
|
|
172
|
-
```
|
|
760
|
+
```typescript
|
|
173
761
|
import { enableDebug, disableDebug } from 'sip-connector';
|
|
174
762
|
|
|
763
|
+
// Включение детального логирования
|
|
175
764
|
enableDebug();
|
|
176
|
-
|
|
765
|
+
|
|
766
|
+
// Отключение для продакшена
|
|
177
767
|
disableDebug();
|
|
178
768
|
```
|
|
179
769
|
|
|
180
|
-
|
|
770
|
+
### Мониторинг состояния
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
// Проверка состояния подключения
|
|
774
|
+
console.log('Подключен:', facade.connection.isConnected());
|
|
775
|
+
console.log('Зарегистрирован:', facade.isRegistered());
|
|
776
|
+
|
|
777
|
+
// Проверка конфигурации
|
|
778
|
+
console.log('Настроен:', facade.isConfigured());
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
## 🧪 Тестирование
|
|
181
784
|
|
|
182
|
-
|
|
785
|
+
### Запуск тестов
|
|
786
|
+
|
|
787
|
+
```bash
|
|
788
|
+
# Все тесты
|
|
183
789
|
npm test
|
|
790
|
+
|
|
791
|
+
# Тесты с покрытием
|
|
792
|
+
npm run test:coverage
|
|
793
|
+
|
|
794
|
+
# Тесты в watch режиме
|
|
795
|
+
npm run test:watch
|
|
184
796
|
```
|
|
185
797
|
|
|
186
|
-
|
|
798
|
+
### Тестовые фикстуры
|
|
799
|
+
|
|
800
|
+
SDK включает готовые моки для тестирования:
|
|
801
|
+
|
|
802
|
+
| Мок | Назначение | Описание |
|
|
803
|
+
| ----------------------- | -------------------- | -------------------------- |
|
|
804
|
+
| `RTCPeerConnectionMock` | WebRTC API | Имитация WebRTC соединений |
|
|
805
|
+
| `UA.mock.ts` | SIP-функциональность | Имитация SIP User Agent |
|
|
806
|
+
| `BaseSession.mock.ts` | Сессии | Имитация SIP сессий |
|
|
187
807
|
|
|
188
|
-
|
|
808
|
+
---
|
|
189
809
|
|
|
190
|
-
##
|
|
810
|
+
## 🌐 Совместимость браузеров
|
|
191
811
|
|
|
192
|
-
|
|
812
|
+
### WebRTC поддержка
|
|
193
813
|
|
|
194
|
-
|
|
195
|
-
- Github: [@Krivega](https://github.com/Krivega)
|
|
814
|
+
SDK использует стандартные WebRTC API и совместим с:
|
|
196
815
|
|
|
197
|
-
|
|
816
|
+
| Браузер | Версия | Уровень поддержки | Особенности |
|
|
817
|
+
| ----------- | ------ | ----------------- | ------------------------------ |
|
|
818
|
+
| **Chrome** | 67+ | Полная поддержка | Все возможности WebRTC |
|
|
819
|
+
| **Firefox** | 60+ | Полная поддержка | Все возможности WebRTC |
|
|
820
|
+
| **Safari** | 11+ | Базовая поддержка | Ограниченная поддержка кодеков |
|
|
821
|
+
| **Edge** | 79+ | Полная поддержка | Chromium-based |
|
|
198
822
|
|
|
199
|
-
|
|
200
|
-
Feel free to check [issues page](https://github.com/Krivega/sip-connector/issues). You can also take a look at the [contributing guide](https://github.com/Krivega/sip-connector/blob/master/CONTRIBUTING.md).
|
|
823
|
+
### Проверка возможностей
|
|
201
824
|
|
|
202
|
-
|
|
825
|
+
```typescript
|
|
826
|
+
// Проверка поддержки WebRTC
|
|
827
|
+
if (!navigator.mediaDevices?.getUserMedia) {
|
|
828
|
+
throw new Error('WebRTC не поддерживается');
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Проверка поддержки презентаций
|
|
832
|
+
if (!navigator.mediaDevices?.getDisplayMedia) {
|
|
833
|
+
console.warn('Screen sharing не поддерживается');
|
|
834
|
+
}
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## 🤝 Поддержка и сообщество
|
|
840
|
+
|
|
841
|
+
### Документация
|
|
842
|
+
|
|
843
|
+
- **API Reference**: Полное описание всех методов и типов
|
|
844
|
+
- **Примеры**: Готовые сценарии использования
|
|
845
|
+
- **Архитектура**: Детальное описание внутренней структуры
|
|
846
|
+
|
|
847
|
+
### Сообщество
|
|
848
|
+
|
|
849
|
+
- **Issues**: [GitHub Issues](https://github.com/Krivega/sip-connector/issues)
|
|
850
|
+
- **Discussions**: Обсуждения и вопросы
|
|
851
|
+
- **Contributing**: Руководство по участию в разработке
|
|
852
|
+
|
|
853
|
+
## 👨💻 Автор
|
|
854
|
+
|
|
855
|
+
### Krivega Dmitriy
|
|
856
|
+
|
|
857
|
+
- 🌐 Website: [krivega.com](https://krivega.com)
|
|
858
|
+
- 📱 Github: [@Krivega](https://github.com/Krivega)
|
|
859
|
+
- 📧 Email: <mr.krivega@gmail.com>
|
|
860
|
+
|
|
861
|
+
## 📄 Лицензия
|
|
203
862
|
|
|
204
863
|
Copyright © 2021‑2025 [Krivega Dmitriy](https://github.com/Krivega).
|
|
205
|
-
|
|
864
|
+
|
|
865
|
+
This project is licensed under the [MIT License](https://github.com/Krivega/sip-connector/blob/master/LICENSE) - see the LICENSE file for details.
|