import { TEffectiveQuality, TRecvQuality } from './quality'; type TConferenceNumber = string; type TSendOfferParams = { quality: TEffectiveQuality; audioChannel: string; }; type TConfigInput = { audioChannel: string; quality?: TRecvQuality; pcConfig?: RTCConfiguration; }; type TConfig = TConfigInput & { quality: TRecvQuality; effectiveQuality: TEffectiveQuality; }; type TCallParams = { conferenceNumber: TConferenceNumber; token: string; }; export type TTools = { sendOffer: (params: TSendOfferParams & { conferenceNumber: TConferenceNumber; token: string; }, offer: RTCSessionDescriptionInit) => Promise; }; /** * Управляет входящей сессией WebRTC для приёма медиа-потоков. * Обеспечивает безопасное пересогласование (renegotiation) с защитой от race conditions. */ declare class RecvSession { /** * Текущая операция отправки offer с повторными попытками при ошибках. * Может быть отменена при новом renegotiate для предотвращения конфликтов. */ private cancelableSendOfferWithRepeatedCalls; /** * Промис текущего выполняющегося renegotiate. * Используется для сериализации: новый renegotiate ждёт завершения предыдущего, * чтобы избежать конфликтов состояний RTCPeerConnection. */ private currentRenegotiation; private readonly config; private readonly tools; private readonly connection; private lastCallParams?; constructor(config: TConfigInput, tools: TTools); get settings(): TConfig; get peerConnection(): RTCPeerConnection; getAudioChannel(): string; getQuality(): TRecvQuality; getEffectiveQuality(): TEffectiveQuality; /** * Устанавливает качество приёма медиа-потоков. * Пересогласовывает соединение только если изменилось effectiveQuality * (например, 'auto' -> 'high' не требует renegotiate, если effectiveQuality уже 'high'). * * @param quality - запрошенное качество ('low' | 'medium' | 'high' | 'auto') * @returns true если качество изменилось, false если осталось прежним */ setQuality(quality: TRecvQuality): Promise; applyQuality(quality: TRecvQuality): Promise<{ applied: boolean; effectiveQuality: TEffectiveQuality; }>; close(): void; /** * Инициирует входящий вызов: пересогласовывает соединение и ждёт получения треков. * Выполняет renegotiate и waitForTracks параллельно для оптимизации времени ожидания. * * @param conferenceNumber - номер конференции * @param token - токен авторизации */ call({ conferenceNumber, token }: TCallParams): Promise; /** * Пересогласовывает соединение с новыми параметрами качества. * * Алгоритм защиты от race conditions: * 1. Отменяет текущий sendOffer (если есть) - предотвращает конфликт offer'ов * 2. Ждёт завершения предыдущего renegotiate (если есть) - сериализация операций * 3. Выполняет новый renegotiate через performRenegotiate * 4. Очищает currentRenegotiation только если это всё ещё текущая операция * (защита от случая, когда новый renegotiate начался до завершения текущего) * * @param conferenceNumber - номер конференции * @param token - токен авторизации * @returns true если пересогласование успешно */ renegotiate({ conferenceNumber, token }: TCallParams): Promise; /** * Выполняет фактическое пересогласование соединения. * * Алгоритм: * 1. Rollback: если состояние 'have-local-offer' (после отмены предыдущего renegotiate), * откатываем его в 'stable' для корректного старта нового negotiation * 2. Ждём stable state: гарантируем, что предыдущий setRemoteDescription завершён * 3. Создаём и устанавливаем новый offer * 4. Отправляем offer на сервер с повторными попытками при ошибках * 5. Устанавливаем полученный answer * 6. Ждём stable state: гарантируем, что setRemoteDescription полностью применён * перед возвратом (важно для предотвращения зависания потоков) * * @param conferenceNumber - номер конференции * @param token - токен авторизации * @returns true при успешном пересогласовании */ private performRenegotiate; /** * Создаёт и устанавливает локальный offer для пересогласования. * После вызова signalingState переходит в 'have-local-offer'. * * @returns созданный offer для отправки на сервер */ private createOffer; private setRemoteDescription; private hasStableSignalingState; /** * connectionState 'connected' — ICE соединение установлено (после setRemoteDescription). * connectionState 'new' — допустимо до первого negotiation (при начале performRenegotiate). */ private hasReadyConnectionState; /** connectionState 'failed' или 'closed' — финальные ошибки, восстановление невозможно */ private hasTerminalConnectionState; private isStableAndReady; private hasHaveLocalOfferSignalingState; /** * Ожидает перехода RTCPeerConnection в состояние 'stable' (signalingState) и готовности connectionState. * * connectionState 'connected' — ICE соединение установлено (после setRemoteDescription). * connectionState 'new' — допустимо до первого negotiation (при начале performRenegotiate). * connectionState 'failed' | 'closed' — немедленный reject, восстановление невозможно. * connectionState 'connecting' | 'disconnected' — ожидаем перехода в connected или таймаут. * * Используется для синхронизации операций пересогласования: * - В начале performRenegotiate: гарантирует завершение предыдущего setRemoteDescription * - В конце performRenegotiate: гарантирует применение setRemoteDescription и установку ICE-соединения * * Алгоритм: * 1. Если уже stable и ready — возвращаемся немедленно * 2. Если connectionState 'failed' или 'closed' — reject немедленно * 3. Подписываемся на signalingstatechange и connectionstatechange * 4. При достижении обоих условий — resolve * 5. При transition в failed/closed — reject * 6. При таймауте — reject * * @throws Error если состояния не достигнуты или connection перешёл в failed/closed */ private waitForPeerConnectionReady; private waitForTracks; /** * Добавляет трансиверы для приёма медиа-потоков. * Структура: 1 аудио (основной) + 4 видео (1 основной + 3 для контента/презентации). */ private addTransceivers; private addRecvOnlyTransceiver; /** * Отменяет текущую операцию отправки offer с повторными попытками. * Используется при новом renegotiate для предотвращения конфликтов. */ private cancelSendOfferWithRepeatedCalls; } export default RecvSession;