Skip to content

khusainovrm/webrtc

Repository files navigation

budu-webrtc

Репозиторий budu-webrtc - внутренняя библиотека для реализации консультаций на базе WebRTC. Библиотека представляет собой внутренний npm-пакет, который содержит:

  • ✅ класс RTCDoctor для работы консультаций на стороне ЛК Врача
  • ✅ класс RTCClient для работы консультаций на стороне клиента
🚨 Обновление версии библиотеки происходит через интерфейс gitlab, поэтому поднимать версию пакета вручную в package.json НЕ требуется.

Документация по использованию


ЛК Врача

// Импортируем класс webrtc для ЛК Врача, события и инстанс логерра
import {
    RTCDoctor,
    CallError,
    EVENT_LIST,
    SOCKET_MESSAGES_EVENT_LIST,
    CONNECTION_EVENT_LIST,
    logger,
} from '@frontend/budu-webrtc';

// Создаем инстанс доктора
const wrtc_doctor = new RTCDoctor(params);

// Устанавливает состояние девайсов (Опционально)
wrtc_doctor.changeDeviceState({
  isHidden: true,
  isMute: false,
});

// Подписываемся на необходимые события
wrtc_doctor.emitter.on(EVENT_LIST.HANG_UP, event => {...});
wrtc_doctor.emitter.on(CONNECTION_EVENT_LIST.CONNECTION_ICE_STATE, event => {...});
wrtc_doctor.emitter.on(SOCKET_MESSAGES_EVENT_LIST.ENTER, event => {...});
// ...Или можно подписаться на все события и фильтровать уже в обработчике
wrtc_doctor.emitter.on(EVENT_LIST.ALL, event => {
    switch (event.eventType) {
        case EVENT_LIST.HANG_UP: return HangUpHandler(event)
        case CONNECTION_EVENT_LIST.CONNECTION_ICE_STATE: return ConnectionIceStateHandler(event)
        case SOCKET_MESSAGES_EVENT_LIST.ENTER: return SocketEnterHandler(event)
    }
});

// Чтобы сделать вызов необходимо вызвать метод `call`
try {
    const localStream = await wrtc_doctor.call();
} catch (error: CallError) {
    switch (err.type) {
      case 'enter': ...
      case 'answer': ...
      case 'streamError': ...
    }
}

// Чтобы получить логи используйте инстанс логера `logger`
console.log(logger.getConnectionEvents())
console.log(logger.filter( l => l.event === 'enter'))

ЛК Клиента

// Импортируем класс webrtc для ЛК Врача, события и инстанс логерра
import {
    RTCClient,
    CallError,
    EVENT_LIST,
    SOCKET_MESSAGES_EVENT_LIST,
    CONNECTION_EVENT_LIST,
    logger,
} from '@frontend/budu-webrtc';

// Создаем инстанс доктора
const wrtc_client = new RTCClient(params);

// Устанавливаем состояние девайсов (Опционально)
wrtc_client.changeDeviceState({
  isHidden: true,
  isMute: false,
});

// Подписываемся на необходимые события
wrtc_client.emitter.on(EVENT_LIST.HANG_UP, event => {...});
wrtc_client.emitter.on(CONNECTION_EVENT_LIST.CONNECTION_ICE_STATE, event => {...});
wrtc_client.emitter.on(SOCKET_MESSAGES_EVENT_LIST.ENTER, event => {...});
// ...Или можно подписаться на все события и фильтровать уже в обработчике
wrtc_client.emitter.on(EVENT_LIST.ALL, event => {
    switch (event.eventType) {
        case EVENT_LIST.HANG_UP: return HangUpHandler(event)
        case CONNECTION_EVENT_LIST.CONNECTION_ICE_STATE: return ConnectionIceStateHandler(event)
        case SOCKET_MESSAGES_EVENT_LIST.ENTER: return SocketEnterHandler(event)
    }
});

// Чтобы ответить вызов необходимо вызвать метод `call`
try {
    const localStream = await wrtc_client.call();
} catch (error: CallError) {
    switch (err.type) {
      case 'enter': ...
      case 'streamError': ...
    }
}

// Чтобы получить логи используйте инстанс логера `logger`
console.log(logger.getConnectionEvents())
console.log(logger.filter( l => l.event === 'enter'))

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


Базовые классы

class RTCCore

Обеспечивает пул соединений на стороне.

export interface RTCCoreInterface {
  connectionConfig: RTCConfiguration;
  emitter: IEventsEmitter;
  params: RTCParams;
  mediaStream: MediaStream | null;
  allMediaStreams: MediaStream[];
  connection: RTCConnection | null;
  socketMessenger: ISocketMessenger;
  hasEntered: boolean;
  hasAnswered: boolean;

  call(): Promise<MediaStream>;
  socketMessageHandler(message: SocketMessage): Promise<void>;
  changeDeviceState(props: DeviceState): void;
  hangUp(): void;
  destroy(): void;
}

param RTCCore.connectionConfig - RTCConfiguration Конфиг для создания инстанса RTCPeerConnection

new RTCPeerConnection(this.connectionConfig);

param RTCCore.emitter - EventsEmitter Инстанс класса EventsEmitter для работы с событиями param RTCCore.params - RTCParams Вспомогательная информация

смотреть описания типа RTCParams

param RTCCore.mediaStream - MediaStream | null param RTCCore.allMediaStreams - MediaStream[]

param RTCCore.connection - RTCConnection | null Инстанс RTC соединения

param RTCCore.socketMessenger - ISocketMessenger Инстанс класса SocketMessenger для генерации сокетных сообщений

param RTCCore.hasEntered - boolean Флаг говорящий о успешном вхождение в комнату

param RTCCore.hasAnswered - boolean Флаг говорящий о успешном ответе кандидата

method RTCCore.call(): Promise<MediaStream> Инициация звонка кандидату:

  • Вхождение в комнату
  • Ожидание офера и ответ на него Возвращает Promise с локальным MediaStream

Возможны ошибки типа CallError (смотреть описание типа)

method RTCCore.socketMessageHandler(message: SocketMessage): Promise<void> Обработка серверных сообщений. Принимает сокетное сообщение SocketMessage

method RTCCore.changeDeviceState(props: DeviceState): void Функция для изменения состояния audio и video треков по флагам DeviceState

method RTCCore.hangUp(): void Остановка треков стрима и отправка сокетного события об отключении

method RTCCore.destroy(): void Закрытие всех соединений

class CallError

Класс ошибки вызова кандидата

export class CallError extends Error implements CallErrorType {
  name: string;
  message: string;
  type: CallErrorMessage;
  stack?: string | undefined;
  description?: string | null;

  constructor(message: string, type: CallErrorMessage) {
    super(message);
    this.type = type;
    this.message = message;
    this.name = 'CallError';
    this.description = null;
  }
}

Класс для работы с событиями библиотеки. Возможность подписываться, отписываться и вызывать сообщения

export interface IEventsEmitter {
  events: EventsDict;

  on<K extends keyof EventCallbackMap>(
    event: K,
    callback: EventCallbackMap[K]
  ): void;

  off<K extends keyof EventCallbackMap>(
    event: K,
    callback: EventCallbackMap[K]
  ): void;

  emit<K extends keyof EventMap>(
    eventType: K,
    payload?: EventDataMap[K],
    emitType?: EMIT_TYPE_LIST
  ): Promise<void>;
}

param EventsEmitter.events - EventsDict Объект событий, где ключ - EventList, а значение - массив EventCallback для этого события

  createPublishMessage<K extends SOCKET_MESSAGES_EVENT_LIST>(
    type: K
  ): IPublishMessage<K>;
  createTransientMessage(
    connectionId: string | null,
    data: any
  ): TransientMessage;

Types


export type IPublishMessage<T> = {
  data: {
    type: T;
    sender: {
      chatId: uuid;
      clientId: uuid;
      connectionId: uuid;
    };
    consultationId: uuid;
  };
  route: 'vcm';
};
export type TransientMessage = {
  type: string;
  chatId: string;
  data: {
    sender: {
      chatId: string;
      clientId: string;
      connectionId: string;
    };
    consultationId: string;
  };
  connectionIds?: string[];
};

type RTCParams

export type RTCParams = {
  name: string;
  clientId: string;
  connectionId: string | null;
  connectionsId?: string[];
  consultationId: string;
  doctor: DoctorInfo;
  isAudio: boolean;

  enterTimeoutSecond?: number;
  answerTimeoutSecond?: number;
};

type DoctorInfo

export type DoctorInfo = {
  avatarId: string | null;
  fullName: string;
  specialityName: string;
};

Тип расширяющий тип RTC соединения RTCPeerConnection

export type RTCConnection = RTCPeerConnection & {
  clientId: string;
  connectionId: string;
  name: string;
  iceCandidatesQueue: RTCIceCandidate[];
  isInviter: boolean;
  isClosing: boolean;
  negotiating: boolean;
};
export type CallErrorType = Error & {
  type: CallErrorMessage;
  description?: string | null;
};
export type CallErrorMessage = 'streamError' | 'enter' | 'answer';
export type SocketMessage = { type: string; data: any };
export type DeviceState = {
  isHidden: boolean;
  isMute: boolean;
};

type EventList

export type EventList =
  | EVENT_LIST
  | SOCKET_MESSAGES_EVENT_LIST
  | CONNECTION_EVENT_LIST;
export type EventCallback<T> = (event: T) => void;
export type EventsDict = {
  [K in keyof EventCallbackMap]: Array<EventCallbackMap[K]>;
};

Пример

type EventsDict = {
    allEvent: AnyCallback[];
    hangUpReceiveEvent: EventCallback<IEvent<EVENT_LIST.HANG_UP, void>>[];
    rtcRespondentPropsEvent: EventCallback<IEvent<EVENT_LIST.RTC_RESPONDENT_PROPS, IRtcSendPropsData>>[];
    ... 12 more ...;
    hangup: EventCallback<...>[];
}
export type EventCallbackMap = {
  [K in keyof EventMap]: K extends EVENT_LIST.ALL
    ? AnyCallback
    : EventCallback<EventMap[K]>;
};

Пример

type EventCallbackMap = {
    allEvent: AnyCallback;
    hangUpReceiveEvent: EventCallback<IEvent<EVENT_LIST.HANG_UP, void>>;
    rtcRespondentPropsEvent: EventCallback<IEvent<EVENT_LIST.RTC_RESPONDENT_PROPS, IRtcSendPropsData>>;
    ... 12 more ...;
    hangup: EventCallback<...>;
}

type EventMap

export type EventMap = {
  [K in keyof EventDataMap]: IEvent<K, EventDataMap[K]>;
};

Пример

type EventMap = {
    allEvent: IEvent<EVENT_LIST.ALL, any>;
    hangUpReceiveEvent: IEvent<EVENT_LIST.HANG_UP, void>;
    rtcRespondentPropsEvent: IEvent<EVENT_LIST.RTC_RESPONDENT_PROPS, IRtcSendPropsData>;
    ... 12 more ...;
    hangup: IEvent<...>;
}

type IEvent

export type IEvent<T, P> = {
  emitType: EMIT_TYPE_LIST;
  eventType: T;
  payload: P;
};
export type ISocketMessenger = {
  createPublishMessage<K extends SOCKET_MESSAGES_EVENT_LIST>(
    type: K
  ): IPublishMessage<K>;
  createTransientMessage(
    connectionId: string | null,
    data: any
  ): TransientMessage;
};

Enums


enum EMIT_TYPE_LIST <a name="enum-emit-type-list)>

export enum EMIT_TYPE_LIST {
  EVENT = 'event',
  ERROR = 'error',
}

Установка npm пакета локально


Подробное описание по особенностям работы с внутренними npm-пакетами можно прочесть вот здесь: https://confluence.renhealth.com/pages/viewpage.action?pageId=46654678

Обычная возникают проблемы с установкой npm пакетов локально на компьютерах разработчиков. Локально пакеты из нашего npm-хранилища можно устанавливать только при наличии авторизации, иначе при запросе npm i @frontend/budu-webrtc можно получить ошибку 404.

Для правильной авторизации создаем (если еще нету) файл .npmrc в домашней директории (для Windows это папка C:\Users\ваше.имя, а для unix-систем /home/{username}/.npmrc) и добавляем в него:

@frontend:registry=https://gitlab.dev.renhealth.com/api/v4/packages/npm/ //gitlab.dev.renhealth.com/api/v4/packages/npm/:_authToken= "ACCESS_TOKEN" //gitlab.dev.renhealth.com/api/v4/projects/248/packages/npm/:_authToken= "ACCESS_TOKEN"

Заменяем ACCESS_TOKEN на ваш персональный. Персональный токен формируем в gitlab на странице https://gitlab.dev.renhealth.com/-/profile/keys Для токена достаточно чтобы были права только на чтение

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published