npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sip-connector

v20.2.0

Published

Module for connect to Vinteo server

Readme

sip-connector

npm npm bundle size


📖 Описание

sip-connector — это TypeScript SDK для интеграции WebRTC-приложений с платформой Vinteo через SIP-протокол. Библиотека построена на базе @krivega/jssip и предоставляет высокоуровневый API для создания полнофункциональных видеоконференций.

🎯 Основные возможности

SDK предоставляет комплексное решение для:

| Категория | Возможности | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | SIP-подключения | Регистрация на сервере (SIP REGISTER), управление сессиями | | WebRTC-коммуникации | Исходящие/входящие звонки (SIP INVITE/200 OK), медиа-потоки, управление transceiver'ами, автоматический перезапуск ICE | | Презентации | Отправка второго потока (screen sharing, демонстрация экрана) | | Системные сообщения | DTMF, SIP INFO, синхронизация медиа-состояния | | Событийная архитектура | Подписка на события платформы в реальном времени | | Мониторинг | WebRTC-статистика (RTCRtpStats, ICE candidate stats) | | Управление конференциями | Перемещение участников между ролями (участник/зритель) | | Лицензирование | Мониторинг использования лицензий и состояния презентаций | | Автоподключение | Автоматическое переподключение при обрывах связи |

  • Адаптивный polling: Улучшенная система опроса для мониторинга изменений видеотреков
  • Поддержка maxBitrate в PresentationManager: Автоматическое управление битрейтом для презентаций
  • Предпочтительные кодеки в SipConnector: Настройка приоритетов кодеков на уровне коннектора
  • Обработка смены треков: Автоматическая адаптация балансировки при изменении видеотреков
  • Улучшенная статистика: Расширенные возможности сбора и анализа WebRTC статистики
  • Автоматический перезапуск ICE: Обработка событий restart от сервера с автоматическим вызовом restartIce |

🏗️ Архитектура

SDK построен по принципу слоистой архитектуры:

  • SipConnector — низкоуровневый слой с менеджерами (Connection, Call, Presentation, API, AutoConnector)
  • SipConnectorFacade — высокоуровневый фасад с готовыми сценариями
  • Специализированные менеджеры — для статистики, участников, медиа-потоков, автоподключения

🚀 Установка

Команды установки

# npm
npm install sip-connector

# yarn
yarn add sip-connector

# pnpm
pnpm add sip-connector

🎯 Быстрый старт

Шаг 1: Инициализация

import { UA, WebSocketInterface } from '@krivega/jssip';
import { SipConnector, SipConnectorFacade, tools } from 'sip-connector';

// Создание низкоуровневого коннектора с настройками кодеков
const sipConnector = new SipConnector(
  { JsSIP: { UA, WebSocketInterface } },
  {
    // Приоритизация современных кодеков
    preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
    excludeMimeTypesVideoCodecs: ['video/H264'],
    // Настройки видеобалансировщика (опционально)
    videoBalancerOptions: {
      ignoreForCodec: 'H264',
      onSetParameters: (result) => {
        console.log('Video parameters updated:', result);
      },
    },
  },
);

// Создание фасада
const facade = new SipConnectorFacade(sipConnector);

Шаг 2: Подключение к серверу

// Подключение с объектом параметров
await facade.connectToServer({
  userAgent: tools.getUserAgent({ appName: 'MyApp' }),
  sipServerUrl: 'sip.example.com', // WebSocket URL (путь /webrtc/wss/ добавляется автоматически)
  sipServerIp: 'sip.example.com', // SIP сервер IP
  user: '1001', // SIP URI user part
  password: 'secret',
  register: true, // Включить SIP REGISTER
});

// Или с функцией для динамического получения параметров
await facade.connectToServer(async () => {
  // Получение актуальных параметров подключения
  const config = await fetchConnectionConfig();
  return {
    userAgent: tools.getUserAgent({ appName: 'MyApp' }),
    sipServerUrl: config.websocketUrl, // Без пути /webrtc/wss/ - он добавляется автоматически
    sipServerIp: config.sipServerIp,
    user: config.username,
    password: config.password,
    register: true,
  };
});

Шаг 3: Исходящий звонок

// Получение локального медиа-потока
const localStream = await navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true,
});

// Подписка на изменения удаленных потоков
const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
  console.log('Изменение удаленных потоков:', {
    participantId: event.participantId,
    changeType: event.changeType, // 'added' | 'removed'
    trackId: event.trackId,
    streams: event.streams, // Актуальный массив всех удаленных потоков
  });

  // Обновление UI с новыми потоками
  updateRemoteStreamsDisplay(event.streams);
});

// Инициация звонка
const pc = await facade.callToServer({
  conference: '12345',
  mediaStream: localStream,
});

// Подписка на WebRTC-статистику
const unsubscribeStats = facade.onStats(({ outbound, inbound }) => {
  console.log('Исходящая статистика:', outbound);
  console.log('Входящая статистика:', inbound);
});

Шаг 4: Завершение работы

await facade.disconnectFromServer();
unsubscribeStats();
unsubscribeRemoteStreams();

📞 Входящие звонки

Обработка входящих вызовов

// Подписка на изменения удаленных потоков (до ответа на звонок)
const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
  console.log('Изменение удаленных потоков:', event);
  displayRemoteStreams(event.streams);
});

// Подписка на входящие события
sipConnector.on('incoming-call:incomingCall', () => {
  // Автоматический ответ с локальным потоком
  facade.answerToIncomingCall({
    mediaStream: localStream,
  });
});

Управление состоянием звонка

// Отслеживание жизненного цикла звонка
sipConnector.on('call:accepted', () => {
  console.log('Звонок принят');
});

sipConnector.on('call:ended', () => {
  console.log('Звонок завершен');
});

sipConnector.on('call:failed', (error) => {
  console.error('Ошибка звонка:', error);
});

🖥️ Управление презентациями

Запуск презентации

// Получение потока экрана
const displayStream = await navigator.mediaDevices.getDisplayMedia({
  video: true,
  audio: true,
});

// Запуск презентации с настройками качества
await facade.startPresentation({
  mediaStream: displayStream,
  isP2P: false, // MCU режим
  contentHint: 'detail', // Оптимизация для детального контента
  maxBitrate: 4000000, // Максимальный битрейт 4 Мбит/с
  degradationPreference: 'maintain-resolution', // Приоритет разрешения
  sendEncodings: [
    { width: 1920, height: 1080, scalabilityMode: 'L3T3_KEY' },
    { width: 1280, height: 720 },
  ],
});

Обновление и остановка

// Обновление потока презентации с новыми настройками
await facade.updatePresentation({
  mediaStream: newDisplayStream,
  isP2P: false,
  maxBitrate: 6000000, // Увеличенный битрейт для HD контента
  contentHint: 'text', // Оптимизация для текстового контента
});

// Остановка презентации
await facade.stopShareSipConnector();

Настройки качества презентации

| Параметр | Описание | Рекомендуемые значения | | ----------------------- | -------------------------------- | --------------------------------------- | | maxBitrate | Максимальный битрейт (bps) | 2-8 Мбит/с в зависимости от контента | | contentHint | Тип контента для оптимизации | 'detail', 'text', 'motion' | | degradationPreference | Приоритет при ухудшении качества | 'maintain-resolution' для презентаций |

// Адаптивные настройки в зависимости от типа контента
const presentationSettings = {
  // For detailed graphics/images
  highQuality: {
    maxBitrate: 8000000,
    contentHint: 'detail' as const,
    degradationPreference: 'maintain-resolution' as const,
  },

  // For text documents
  textOptimized: {
    maxBitrate: 4000000,
    contentHint: 'text' as const,
    degradationPreference: 'maintain-resolution' as const,
  },

  // For video content
  videoOptimized: {
    maxBitrate: 6000000,
    contentHint: 'motion' as const,
    degradationPreference: 'maintain-framerate' as const,
  },
};

// Использование настроек
await facade.startPresentation({
  mediaStream: displayStream,
  isP2P: false,
  ...presentationSettings.textOptimized,
});

👥 Управление участниками конференции

Отслеживание перемещений

// Подписка на перемещение в зрители (новый формат с audioId)
const unsubscribeMoveToSpectators = sipConnector.on(
  'api:participant:move-request-to-spectators',
  (data) => {
    if (data.isSynthetic) {
      console.log('Участник перемещен в зрители (синтетическое событие)');
    } else {
      console.log('Участник перемещен в зрители с audioId:', data.audioId);
    }
    updateParticipantRole('spectator');
  },
);

// Подписка на перемещение в зрители (старый формат для обратной совместимости)
const unsubscribeMoveToSpectatorsSynthetic = sipConnector.on(
  'api:participant:move-request-to-spectators-synthetic',
  () => {
    console.log('Участник перемещен в зрители (старый формат)');
    updateParticipantRole('spectator');
  },
);

// Подписка на перемещение в участники
const unsubscribeMoveToParticipants = sipConnector.on(
  'api:participant:move-request-to-participants',
  () => {
    console.log('Участник перемещен в участники');
    updateParticipantRole('participant');
  },
);

// Отписка при необходимости
unsubscribeMoveToSpectators();
unsubscribeMoveToSpectatorsSynthetic();
unsubscribeMoveToParticipants();

Административные функции

// Принудительная остановка презентации
facade.onMustStopPresentation(() => {
  console.log('Администратор требует остановить презентацию');
  facade.stopShareSipConnector();
});

// Мониторинг лицензий
facade.onUseLicense((license) => {
  console.log('Используется лицензия:', license);
  updateLicenseStatus(license);
});

📊 Управление медиа-потоками

Работа с удаленными потоками

// Подписка на изменения удаленных потоков
let currentRemoteStreams: MediaStream[] = [];

const unsubscribeRemoteStreams = sipConnector.on('call:remote-streams-changed', (event) => {
  console.log('Изменение удаленных потоков:', {
    participantId: event.participantId,
    changeType: event.changeType, // 'added' | 'removed'
    trackId: event.trackId,
  });

  // Обновляем текущие потоки
  currentRemoteStreams = event.streams;

  // Обновляем UI
  updateStreamsDisplay(event.streams);
});

// Получение текущих удаленных потоков (синхронный метод)
const remoteStreams = facade.getRemoteStreams();
if (remoteStreams) {
  console.log('Активные удаленные потоки:', remoteStreams.length);
  remoteStreams.forEach((stream) => {
    displayStream(stream);
  });
}

Обработка готовых потоков

// Обработка с debounce (рекомендуется для UI)
const handleReadyRemoteStreamsDebounced = facade.resolveHandleReadyRemoteStreamsDebounced({
  onReadyRemoteStreams: (streams) => {
    console.log('Готовые удаленные потоки:', streams);
    updateStreamsDisplay(streams);
  },
});

// Обработка без debounce (для критичных операций)
const handleReadyRemoteStreams = facade.resolveHandleReadyRemoteStreams({
  onReadyRemoteStreams: () => {
    console.log('Новый поток готов');
    handleNewStream();
  },
});

Управление разрешениями

// Запрос разрешения на камеру
try {
  await facade.askPermissionToEnableCam();
  console.log('Разрешение на камеру получено');
} catch (error) {
  console.error('Ошибка получения разрешения:', error);
}

Перезапуск ICE-соединения

Ручной перезапуск

// Перезапуск ICE для восстановления соединения
try {
  const success = await sipConnector.callManager.restartIce({
    useUpdate: true, // Использовать SIP UPDATE вместо re-INVITE
    extraHeaders: ['X-Restart-Reason: Connection-Reset'],
    rtcOfferConstraints: {
      offerToReceiveAudio: true,
      offerToReceiveVideo: true,
    },
  });

  if (success) {
    console.log('ICE перезапущен успешно');
  } else {
    console.warn('Перезапуск ICE не удался');
  }
} catch (error) {
  console.error('Ошибка перезапуска ICE:', error);
}

Автоматический перезапуск по событию сервера

SDK автоматически обрабатывает события restart от сервера и инициирует перезапуск ICE-соединения с интеллектуальным управлением transceiver'ами:

// SDK автоматически подписывается на события restart от ApiManager
// и выполняет следующие действия:
// 1. Проверяет необходимость добавления презентационного transceiver'а
// 2. Вызывает callManager.restartIce()

// Мониторинг событий restart (опционально)
sipConnector.on('api:restart', (data) => {
  console.log('Получено событие restart от сервера:', {
    tracksDirection: data.tracksDirection, // 'incoming', 'outgoing', 'bidirectional'
    audioTrackCount: data.audioTrackCount,
    videoTrackCount: data.videoTrackCount,
  });

  // SDK автоматически:
  // - Добавит презентационный transceiver если videoTrackCount === 2
  // - Вызовет restartIce()
  console.log('ICE будет перезапущен автоматически');

  if (data.videoTrackCount === 2) {
    console.log('Может быть добавлен презентационный transceiver');
  }
});

Параметры перезапуска ICE

| Параметр | Тип | Описание | По умолчанию | | ----------------------- | -------- | ---------------------------------------- | ------------ | | useUpdate | boolean | Использовать SIP UPDATE вместо re-INVITE | false | | extraHeaders | string[] | Дополнительные SIP заголовки | [] | | rtcOfferConstraints | object | Ограничения для создания SDP offer | {} | | sendEncodings | array | Параметры кодирования для видеопотока | [] | | degradationPreference | string | Приоритет при ухудшении качества | undefined |


📡 События и их обработка

Архитектура событий

SDK использует событийно-ориентированную архитектуру с префиксами для группировки:

| Префикс | Описание | Примеры событий | | ------------------ | ------------------------ | ------------------------------------------------------------------------------ | | connection:* | События подключения | connected, disconnected | | call:* | События звонков | accepted, ended, failed, remote-streams-changed | | api:* | События от сервера | enterRoom, useLicense, restart, participant:move-request-to-spectators | | incoming-call:* | События входящих звонков | incomingCall | | presentation:* | События презентаций | started, stopped | | stats:* | События статистики | collected | | video-balancer:* | События балансировки | balancing-started, parameters-updated |

Основные события

// Подключение
sipConnector.on('connection:connected', () => {
  console.log('Подключение установлено');
});

sipConnector.on('connection:disconnected', () => {
  console.log('Подключение разорвано');
});

// Звонки
sipConnector.on('call:accepted', () => {
  console.log('Звонок принят');
});

sipConnector.on('call:ended', () => {
  console.log('Звонок завершен');
});

// API события
sipConnector.on('api:enterRoom', ({ room }) => {
  console.log('Вход в комнату:', room);
});

sipConnector.on('api:useLicense', (license) => {
  console.log('Лицензия:', license);
});

sipConnector.on('api:restart', (data) => {
  console.log('Событие restart от сервера:', data);
});

// Изменения удаленных потоков
sipConnector.on('call:remote-streams-changed', (event) => {
  console.log('Изменение удаленных потоков:', {
    participantId: event.participantId,
    changeType: event.changeType, // 'added' | 'removed'
    trackId: event.trackId,
    streams: event.streams,
  });
});

// Перемещение участников
sipConnector.on('api:participant:move-request-to-spectators', (data) => {
  if (data.isSynthetic) {
    console.log('Перемещение в зрители (синтетическое)');
  } else {
    console.log('Перемещение в зрители с audioId:', data.audioId);
  }
});

sipConnector.on('api:participant:move-request-to-spectators-synthetic', () => {
  console.log('Перемещение в зрители (старый формат для обратной совместимости)');
});

Детальная таблица событий

События звонков (call:*)

| Событие | Описание | Данные | | ----------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ | | call:accepted | Звонок принят | - | | call:ended | Звонок завершен | EndEvent | | call:failed | Звонок завершился с ошибкой | EndEvent | | call:remote-streams-changed | Изменение удаленных потоков | { participantId: string, changeType: 'added' \| 'removed', trackId: string, streams: MediaStream[] } |

События участников (api:participant:*)

| Событие | Описание | Данные | | ------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------ | | api:participant:move-request-to-spectators | Перемещение в зрители (новый) | { isSynthetic: true } \| { isSynthetic: false, audioId: string } | | api:participant:move-request-to-spectators-synthetic | Перемещение в зрители (старый) | - | | api:participant:move-request-to-participants | Перемещение в участники | - |

Продвинутые паттерны

// Ожидание одного из нескольких событий
sipConnector.onceRace(['call:ended', 'call:failed'], (_payload, eventName) => {
  console.log('Звонок завершен событием:', eventName);
  cleanupCall();
});

// Ожидание конкретного события
const roomData = await sipConnector.wait('api:enterRoom');
console.log('Данные комнаты:', roomData);

События балансировки видео

// Мониторинг автоматической балансировки видео
sipConnector.on('video-balancer:balancing-scheduled', (data) => {
  console.log(`Балансировка запланирована через ${data.delay}ms`);
});

sipConnector.on('video-balancer:balancing-started', (data) => {
  console.log(`Балансировка запущена после задержки ${data.delay}ms`);
});

sipConnector.on('video-balancer:balancing-stopped', () => {
  console.log('Балансировка остановлена');
});

sipConnector.on('video-balancer:parameters-updated', (result) => {
  console.log('Обновлены параметры:', result);
});

// Ручное управление балансировкой
sipConnector.videoSendingBalancerManager.startBalancing(); // Принудительный запуск
sipConnector.videoSendingBalancerManager.stopBalancing(); // Остановка

📈 WebRTC Статистика

Обзор возможностей

SDK предоставляет детальную WebRTC-статистику, основанную на W3C WebRTC Statistics API, включающую:

| Тип статистики | Описание | Метрики | | ------------------ | --------------------- | ----------------------------- | | RTP статистика | Потоковые данные | Пакеты, байты, jitter, loss | | Кодеки | Параметры кодирования | Параметры, реализация | | ICE кандидаты | Сетевые соединения | Типы, приоритеты, состояния | | Транспорт | Защищенные соединения | DTLS, соединения, сертификаты |

Использование статистики

import { StatsPeerConnection, EStatsTypes, hasAvailableStats } from 'sip-connector';

// Проверка доступности
if (hasAvailableStats()) {
  const statsCollector = new StatsPeerConnection();

  // Подписка на события статистики
  statsCollector.on('collected', ({ outbound, inbound }) => {
    console.log('Исходящая статистика:', outbound);
    console.log('Входящая статистика:', inbound);

    // Новая метрика availableIncomingBitrate
    if (inbound.additional?.candidatePair?.availableIncomingBitrate) {
      console.log(
        'Доступный входящий битрейт:',
        inbound.additional.candidatePair.availableIncomingBitrate,
      );
    }

    // Анализ качества соединения
    analyzeConnectionQuality(outbound, inbound);
  });

  // Запуск сбора статистики с адаптивным интервалом
  statsCollector.start(peerConnection);
}

Адаптивный интервал сбора статистики

SDK автоматически адаптирует частоту сбора статистики в зависимости от времени выполнения:

| Время выполнения | Множитель интервала | Интервал (мс) | | ---------------- | ------------------- | ------------- | | < 16 мс | 1x | 1000 | | 16-32 мс | 2x | 2000 | | 32-48 мс | 3x | 3000 | | > 48 мс | 4x | 4000 |

// Мониторинг производительности сбора статистики
statsCollector.on('collected', (stats) => {
  const collectionTime = performance.now() - startTime;
  console.log(`Статистика собрана за ${collectionTime}мс`);
});

Типы статистики

| Категория | Типы | Описание | | ---------------------- | --------------------------------- | ------------------------------------------ | | Аудио потоки | TInboundAudio, TOutboundAudio | RTP, кодек, jitter buffer, audio level | | Видео потоки | TInboundVideo, TOutboundVideo | RTP, кодек, frames, bitrate, resolution | | Сетевая информация | TAdditional | ICE кандидаты, DTLS транспорт, сертификаты |


⚡ Адаптивное опрашивание видеопотоков

Принцип работы

SDK использует адаптивное опрашивание для мониторинга изменений в видеопотоках, что значительно снижает нагрузку на CPU:

// TrackMonitor автоматически адаптирует частоту опрашивания
const trackMonitor = new TrackMonitor({
  pollIntervalMs: 1000, // Начальный интервал
  maxPollIntervalMs: 16000, // Максимальный интервал (16x)
});

Алгоритм адаптации

| Ситуация | Действие | Результат | | ------------------------ | ------------------------------- | ---------------------------- | | Изменение разрешения | Сброс интервала до минимального | Быстрая реакция на изменения | | Нет изменений | Удвоение интервала | Снижение нагрузки на CPU | | Достижение максимума | Ограничение интервала | Предотвращение "заморозки" |

Преимущества

  • Снижение CPU нагрузки на 40-60% при стабильном видео
  • Быстрая реакция на изменения разрешения (resize events)
  • Автоматическое обнаружение замены треков (replaceTrack)
  • Настраиваемые интервалы для разных сценариев использования
// Пример использования с кастомными настройками
const monitor = new TrackMonitor({
  pollIntervalMs: 500, // Более частое начальное опрашивание
  maxPollIntervalMs: 8000, // Меньший максимальный интервал
});

// Подписка на изменения
monitor.subscribe(videoSender, () => {
  console.log('Обнаружено изменение видеопотока');
  // Перебалансировка параметров
  rebalanceVideoParameters();
});

🎛️ Управление видеобалансировщиком

Автоматическая балансировка

VideoSendingBalancer интегрирован в SipConnector и запускается автоматически:

const sipConnector = new SipConnector(
  { JsSIP: { UA, WebSocketInterface } },
  {
    videoBalancerOptions: {
      ignoreForCodec: 'H264', // Игнорировать H264
      onSetParameters: (result) => {
        console.log('Параметры обновлены:', result);
      },
    },
  },
);

// Подписка на события балансировщика
sipConnector.on('video-balancer:balancing-started', (data) => {
  console.log(`Балансировка запущена через ${data.delay}мс`);
});

sipConnector.on('video-balancer:parameters-updated', (result) => {
  console.log('Обновлены параметры:', result);
});

Жизненный цикл балансировщика

graph TD
    A[Начало звонка] --> B[Задержка 10 сек]
    B --> C[Запуск балансировки]
    C --> D[Мониторинг изменений]
    D --> E{Изменение треков?}
    E -->|Да| F[Перебалансировка]
    E -->|Нет| D
    F --> D
    G[Завершение звонка] --> H[Остановка балансировки]

События балансировщика

| Событие | Описание | Данные | | ------------------------------------ | -------------------------- | ---------------------- | | video-balancer:balancing-scheduled | Балансировка запланирована | { delay: number } | | video-balancer:balancing-started | Балансировка запущена | { delay: number } | | video-balancer:balancing-stopped | Балансировка остановлена | - | | video-balancer:parameters-updated | Параметры обновлены | RTCRtpSendParameters |


🔄 Управление последовательными операциями

ConnectionQueueManager

ConnectionQueueManager обеспечивает последовательное выполнение операций для предотвращения конфликтов и гонки условий:

// Все операции ConnectionManager проходят через очередь
const connectionQueueManager = new ConnectionQueueManager({
  connectionManager: connectionManager,
});

// Операции выполняются последовательно
await connectionQueueManager.connect(params);
await connectionQueueManager.disconnect();

Механизм работы

  • Очередь операций: Использует stack-promises с noRunIsNotActual: true
  • Предотвращение конфликтов: Исключает одновременные connect/disconnect операции

Поддерживаемые операции

| Операция | Описание | | ------------ | --------------------------------- | | connect | Подключение к серверу | | disconnect | Отключение от сервера | | stop | Остановка всех операций в очереди |

Интеграция в SipConnector

// SipConnector автоматически использует ConnectionQueueManager
const sipConnector = new SipConnector({ JsSIP });

// Все операции подключения проходят через очередь
await sipConnector.connect(params); // → connectionQueueManager.connect()
await sipConnector.disconnect(); // → connectionQueueManager.disconnect()

🔄 Автоматическое переподключение

AutoConnectorManager

AutoConnectorManager обеспечивает автоматическое переподключение при обрывах связи и проблемах с сетью:

// Создание SipConnector с настройками автоподключения
const sipConnector = new SipConnector(
  { JsSIP },
  {
    autoConnectorOptions: {
      onBeforeRetry, // Очистка кэша перед переподключением
      timeoutBetweenAttempts: 3000, // Задержка между попытками
      checkTelephonyRequestInterval: 15000, // Интервал проверки телефонии
    },
  },
);

// Запуск автоподключения
sipConnector.startAutoConnect({
  // Возвращает параметры подключения
  getParameters: async () => {
    return {
      displayName: 'displayName',
      sipServerUrl: 'example.com', // Путь /webrtc/wss/ добавляется автоматически
      sipServerIp: 'sip.example.com',
      user: 'user',
      password: 'password',
      register: true,
    };
  },
  // Проверяет готовность к подключению
  hasReadyForConnection: () => {
    return true;
  },
});

// Остановка автоподключения
sipConnector.stopAutoConnect();

// Подписка на события автоподключения
sipConnector.on('auto-connect:changed-attempt-status', ({ isInProgress }) => {
  console.log('Попытка подключения в процессе:', isInProgress);
});

sipConnector.on('auto-connect:before-attempt', () => {
  console.log('Начало попытки подключения');
});

sipConnector.on('auto-connect:succeeded-attempt', () => {
  console.log('Попытка подключения успешна');
});

sipConnector.on('auto-connect:failed-attempt', (error) => {
  console.log('Попытка подключения неудачна:', error);
});

sipConnector.on('auto-connect:cancelled-attempt', (error) => {
  console.log('Попытка подключения отменена:', error);
});

Принцип работы

  • Автоматические попытки: Повторяет попытки подключения при ошибках
  • Проверка телефонии: Периодически проверяет доступность сервера
  • Мониторинг состояния: Отслеживает состояние регистрации и звонков
  • Адаптивные задержки: Использует настраиваемые интервалы между попытками
  • Очистка кэша: Возможность настраивать очистку кэша через хук

События автоподключения

| Событие | Описание | Данные | | ------------------------------------- | ------------------------------ | ----------------------------------- | | auto-connect:connecting | Начало подключения | - | | auto-connect:connected | Успешное подключение | { ua: UA, isRegistered: boolean } | | auto-connect:disconnecting | Начало отключения | - | | auto-connect:disconnected | Отключение завершено | - | | auto-connect:failed | Ошибка подключения | Error | | auto-connect:before-attempt | Начало попытки подключения | - | | auto-connect:succeeded-attempt | Успешная попытка подключения | - | | auto-connect:failed-attempt | Неудачная попытка подключения | Error | | auto-connect:cancelled-attempt | Отмененная попытка подключения | Error | | auto-connect:changed-attempt-status | Изменение статуса попытки | { isInProgress: boolean } |


🔧 API и экспорты

Основные классы

import {
  SipConnector, // Низкоуровневый API
  SipConnectorFacade, // Высокоуровневый фасад
  StatsPeerConnection, // Сбор статистики
  // ... другие экспорты
} from 'sip-connector';

Методы управления соединением

// SipConnectorFacade методы
const facade = new SipConnectorFacade(sipConnector);

// Замена медиа-потока
await facade.replaceMediaStream(mediaStream, options);

// Получение удаленных потоков
const streams = facade.getRemoteStreams();

// Перезапуск ICE-соединения (низкоуровневый API)
await sipConnector.callManager.restartIce(options);

Утилиты и типы

import {
  // Утилиты
  tools, // getUserAgent, getExtraHeaders, hasPurgatory
  hasAvailableStats, // Проверка доступности статистики

  // Константы
  EStatsTypes, // Типы статистики
  EMimeTypesVideoCodecs, // MIME-типы кодеков
  EUseLicense, // Типы лицензий

  // Типы
  type TContentHint, // Подсказки для кодирования
  type TInboundStats, // Входящая статистика
  type TOutboundStats, // Исходящая статистика
  type TRestartData, // Данные события restart
  type ITransceiverStorage, // Интерфейс хранения transceiver'ов
} from 'sip-connector';

🏗️ Архитектура и паттерны

Слоистая архитектура

┌──────────────────────────────────────────────────────────────────┐
│                        SipConnectorFacade                        │ ← Высокоуровневый API
├──────────────────────────────────────────────────────────────────┤
│                           SipConnector                           │ ← Координация менеджеров
├──────────────────────────────────────────────────────────────────┤
│ Connection │ Connection │ Call       │  API        │             │ ← Основные менеджеры
│ Manager    │ Queue      │ Manager    │  Manager    │             │
│            │ Manager    │            │             │             │
├──────────────────────────────────────────────────────────────────┤
│ Stats      │Presentation│IncomingCall│VideoBalancer│AutoConnector│ ← Специализированные менеджеры
│ Manager    │ Manager    │ Manager    │ Manager     │Manager      │
├──────────────────────────────────────────────────────────────────┤
│                          @krivega/jssip                          │ ← SIP-функциональность
└──────────────────────────────────────────────────────────────────┘

Паттерны проектирования

| Паттерн | Описание | Применение | | --------------- | --------------------------------- | ------------------------ | | Фасад | Упрощение сложных операций | SipConnectorFacade | | Стратегия | Различные стратегии для звонков | MCU, P2P режимы | | Наблюдатель | Событийная модель для уведомлений | Event-driven архитектура | | Фабрика | Создание UA и сессий | UAFactory |


📚 Лучшие практики

Обработка ошибок

try {
  await facade.connectToServer(config);
} catch (error) {
  if (error.code === 'CONNECTION_FAILED') {
    // Повторная попытка подключения
    await retryConnection();
  } else {
    // Логирование и уведомление пользователя
    logError(error);
    notifyUser('Ошибка подключения');
  }
}

Восстановление соединения

// Мониторинг качества соединения
facade.onStats(({ outbound, inbound }) => {
  // Проверка качества соединения
  if (outbound.packetsLost > 0.05) {
    // 5% потерь пакетов
    console.warn('Высокие потери пакетов, перезапуск ICE');

    // Перезапуск ICE для восстановления соединения
    sipConnector.callManager
      .restartIce({
        useUpdate: true,
        extraHeaders: ['X-Restart-Reason: High-Packet-Loss'],
      })
      .catch((error) => {
        console.error('Ошибка перезапуска ICE:', error);
      });
  }
});

Управление ресурсами

// Всегда отписывайтесь от событий
const unsubscribe = facade.onStats(handleStats);

// Очистка при размонтировании
useEffect(() => {
  return () => {
    unsubscribe();
    facade.disconnectFromServer();
  };
}, []);

Оптимизация производительности

// Используйте debounce для частых событий
const debouncedStatsHandler = debounce(handleStats, 1000);
facade.onStats(debouncedStatsHandler);

// Приоритизируйте современные кодеки и настройте балансировку
const sipConnector = new SipConnector(
  { JsSIP: { UA, WebSocketInterface } },
  {
    preferredMimeTypesVideoCodecs: ['video/AV1', 'video/VP9'],
    videoBalancerOptions: {
      ignoreForCodec: 'H264',
      balancingStartDelay: 5000, // Быстрее запуск для критичных приложений
    },
  },
);
const facade = new SipConnectorFacade(sipConnector);

🐛 Отладка и диагностика

Включение отладочного режима

import { enableDebug, disableDebug } from 'sip-connector';

// Включение детального логирования
enableDebug();

// Отключение для продакшена
disableDebug();

Мониторинг состояния

// Проверка состояния подключения
console.log('Зарегистрирован:', facade.isRegistered);

// Проверка конфигурации
console.log('Настроен:', facade.isConfigured());

// Диагностика ICE-соединения
const checkIceConnection = async () => {
  try {
    const success = await sipConnector.callManager.restartIce({
      useUpdate: true,
      extraHeaders: ['X-Debug: ICE-Check'],
    });
    console.log('ICE соединение:', success ? 'OK' : 'Проблемы');
  } catch (error) {
    console.error('ICE недоступен:', error.message);
  }
};

🧪 Тестирование

Запуск тестов

# Все тесты
npm test

# Тесты с покрытием
npm run test:coverage

# Тесты в watch режиме
npm run test:watch

Тестовые фикстуры

SDK включает готовые моки для тестирования:

| Мок | Назначение | Описание | | ----------------------- | -------------------- | -------------------------- | | RTCPeerConnectionMock | WebRTC API | Имитация WebRTC соединений | | UA.mock.ts | SIP-функциональность | Имитация SIP User Agent | | BaseSession.mock.ts | Сессии | Имитация SIP сессий |


🌐 Совместимость браузеров

WebRTC поддержка

SDK использует стандартные WebRTC API и совместим с:

| Браузер | Версия | Уровень поддержки | Особенности | | ----------- | ------ | ----------------- | ------------------------------ | | Chrome | 67+ | Полная поддержка | Все возможности WebRTC | | Firefox | 60+ | Полная поддержка | Все возможности WebRTC | | Safari | 11+ | Базовая поддержка | Ограниченная поддержка кодеков | | Edge | 79+ | Полная поддержка | Chromium-based |

Проверка возможностей

// Проверка поддержки WebRTC
if (!navigator.mediaDevices?.getUserMedia) {
  throw new Error('WebRTC не поддерживается');
}

// Проверка поддержки презентаций
if (!navigator.mediaDevices?.getDisplayMedia) {
  console.warn('Screen sharing не поддерживается');
}

// Проверка поддержки ICE restart
if (!RTCPeerConnection.prototype.restartIce) {
  console.warn('ICE restart не поддерживается в этом браузере');
}

🤝 Поддержка и сообщество

Документация

  • API Reference: Полное описание всех методов и типов
  • Примеры: Готовые сценарии использования
  • Архитектура: Детальное описание внутренней структуры

Сообщество

  • Issues: GitHub Issues
  • Discussions: Обсуждения и вопросы
  • Contributing: Руководство по участию в разработке

👨‍💻 Автор

Krivega Dmitriy

📄 Лицензия

Copyright © 2021‑2025 Krivega Dmitriy.

This project is licensed under the MIT License - see the LICENSE file for details.