sip-connector 20.4.0 → 20.5.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.
- package/README.md +18 -1264
- package/dist/{@SipConnector-FYEV5h4G.js → @SipConnector-DoyWexFp.js} +758 -719
- package/dist/@SipConnector-TNDGO7MW.cjs +1 -0
- package/dist/ApiManager/events.d.ts +0 -1
- package/dist/ApiManager/index.d.ts +2 -2
- package/dist/AutoConnectorManager/index.d.ts +2 -1
- package/dist/CallManager/@CallManager.d.ts +60 -0
- package/dist/CallManager/CallStateMachine.d.ts +0 -7
- package/dist/CallManager/events.d.ts +3 -3
- package/dist/CallManager/index.d.ts +2 -2
- package/dist/ConferenceStateManager/events.d.ts +0 -1
- package/dist/ConferenceStateManager/index.d.ts +2 -3
- package/dist/ConnectionManager/@ConnectionManager.d.ts +59 -0
- package/dist/ConnectionManager/ConnectionStateMachine.d.ts +20 -17
- package/dist/ConnectionManager/events.d.ts +0 -1
- package/dist/ConnectionManager/index.d.ts +1 -1
- package/dist/IncomingCallManager/@IncomingCallManager.d.ts +32 -0
- package/dist/IncomingCallManager/events.d.ts +3 -4
- package/dist/IncomingCallManager/index.d.ts +2 -2
- package/dist/MainStreamHealthMonitor/index.d.ts +2 -0
- package/dist/PresentationManager/@PresentationManager.d.ts +53 -0
- package/dist/PresentationManager/events.d.ts +0 -1
- package/dist/PresentationManager/index.d.ts +2 -1
- package/dist/SessionManager/@SessionManager.d.ts +30 -0
- package/dist/SessionManager/events.d.ts +14 -0
- package/dist/{session → SessionManager}/index.d.ts +3 -2
- package/dist/SipConnector/@SipConnector.d.ts +5 -4
- package/dist/SipConnector/events.d.ts +15 -13
- package/dist/SipConnector/index.d.ts +1 -1
- package/dist/SipConnectorFacade/@SipConnectorFacade.d.ts +3 -3
- package/dist/StatsManager/@StatsManager.d.ts +12 -8
- package/dist/StatsManager/constants.d.ts +1 -0
- package/dist/StatsManager/events.d.ts +2 -2
- package/dist/StatsManager/index.d.ts +2 -1
- package/dist/StatsPeerConnection/index.d.ts +2 -2
- package/dist/VideoSendingBalancerManager/events.d.ts +0 -1
- package/dist/VideoSendingBalancerManager/index.d.ts +2 -2
- package/dist/__fixtures__/eventNames.d.ts +1 -1
- package/dist/doMock.cjs +1 -1
- package/dist/doMock.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/@SipConnector-DB4bLDI5.cjs +0 -1
- package/dist/session/createSession.d.ts +0 -26
- /package/dist/{session → SessionManager}/selectors.d.ts +0 -0
- /package/dist/{session → SessionManager}/types.d.ts +0 -0
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'
|
|
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
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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‑
|
|
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.
|