translated comments
This commit is contained in:
parent
9aa8b6c5be
commit
dec8e2d337
4 changed files with 77 additions and 97 deletions
|
|
@ -33,7 +33,6 @@ export default class Call {
|
|||
private peerID: string,
|
||||
private getAssistVersion: () => number
|
||||
) {
|
||||
// Обработка событий сокета
|
||||
socket.on('call_end', () => {
|
||||
this.onRemoteCallEnd()
|
||||
});
|
||||
|
|
@ -55,7 +54,7 @@ export default class Call {
|
|||
});
|
||||
socket.on('messages_gz', () => {
|
||||
if (reconnecting) {
|
||||
// При восстановлении соединения инициируем повторное создание соединения
|
||||
// When the connection is restored, we initiate a re-creation of the connection
|
||||
this._callSessionPeer();
|
||||
reconnecting = false;
|
||||
}
|
||||
|
|
@ -80,21 +79,21 @@ export default class Call {
|
|||
this.assistVersion = this.getAssistVersion();
|
||||
}
|
||||
|
||||
// СОЗДАНИЕ ЛОКАЛЬНОГО ПИРА
|
||||
// CREATE A LOCAL PEER
|
||||
private async createPeerConnection(remotePeerId: string): Promise<RTCPeerConnection> {
|
||||
// создаем pc с конфигом ice
|
||||
// create pc with ice config
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
||||
});
|
||||
|
||||
// Если есть локальный поток добавляем его треки в соединение
|
||||
// If there is a local stream, add its tracks to the connection
|
||||
if (this.callArgs && this.callArgs.localStream && this.callArgs.localStream.stream) {
|
||||
this.callArgs.localStream.stream.getTracks().forEach((track) => {
|
||||
pc.addTrack(track, this.callArgs!.localStream.stream);
|
||||
});
|
||||
}
|
||||
|
||||
// когда готов ice отсылваем его
|
||||
// when ice is ready we send it
|
||||
pc.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
this.socket.emit('webrtc_call_ice_candidate', { from: remotePeerId, candidate: event.candidate });
|
||||
|
|
@ -103,7 +102,7 @@ export default class Call {
|
|||
}
|
||||
};
|
||||
|
||||
// когда получаем удаленный трек записываем его в videoStreams[peerId]
|
||||
// when we receive a remote track, we write it to videoStreams[peerId]
|
||||
pc.ontrack = (event) => {
|
||||
const stream = event.streams[0];
|
||||
if (stream) {
|
||||
|
|
@ -114,30 +113,17 @@ export default class Call {
|
|||
if (this.callArgs) {
|
||||
this.callArgs.onStream(stream);
|
||||
}
|
||||
try {
|
||||
|
||||
// Создаем элемент <video>
|
||||
const video = document.createElement('video');
|
||||
video.autoplay = true;
|
||||
video.playsInline = true;
|
||||
video.srcObject = stream;
|
||||
|
||||
// Добавляем <video> в <body>
|
||||
document.body.appendChild(video);
|
||||
} catch (error) {
|
||||
console.error('Error accessing media devices:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Если связь отвалилась заканчиваем звонок
|
||||
// If the connection is lost, we end the call
|
||||
pc.onconnectionstatechange = () => {
|
||||
if (pc.connectionState === "disconnected" || pc.connectionState === "failed") {
|
||||
this.onRemoteCallEnd();
|
||||
}
|
||||
};
|
||||
|
||||
// Обработка замены трека при изменении локального видео
|
||||
// Handle track replacement when local video changes
|
||||
if (this.callArgs && this.callArgs.localStream) {
|
||||
this.callArgs.localStream.onVideoTrack((vTrack: MediaStreamTrack) => {
|
||||
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
|
||||
|
|
@ -152,23 +138,23 @@ export default class Call {
|
|||
return pc;
|
||||
}
|
||||
|
||||
// УСТАНОВКА СОЕДИНЕНИЯ
|
||||
// ESTABLISHING A CONNECTION
|
||||
private async _peerConnection(remotePeerId: string) {
|
||||
try {
|
||||
// Создаём RTCPeerConnection
|
||||
// Create RTCPeerConnection
|
||||
const pc = await this.createPeerConnection(remotePeerId);
|
||||
this.connections[remotePeerId] = pc;
|
||||
|
||||
// Создаём SDP offer
|
||||
// Create an SDP offer
|
||||
const offer = await pc.createOffer();
|
||||
await pc.setLocalDescription(offer);
|
||||
|
||||
// Отправляем offer
|
||||
// Sending offer
|
||||
this.socket.emit('webrtc_call_offer', { from: remotePeerId, offer });
|
||||
this.connectAttempts = 0;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
// Пробуем переподключиться
|
||||
// Trying to reconnect
|
||||
const tryReconnect = async (error: any) => {
|
||||
if (error.type === 'peer-unavailable' && this.connectAttempts < 5) {
|
||||
this.connectAttempts++;
|
||||
|
|
@ -184,18 +170,17 @@ export default class Call {
|
|||
}
|
||||
}
|
||||
|
||||
// Обрабатываем полученный answer на offer
|
||||
// Process the received answer to offer
|
||||
private async handleAnswer(data: { from: string, answer: RTCSessionDescriptionInit }) {
|
||||
// устанавливаем в remotePeerId data.from
|
||||
// set to remotePeerId data.from
|
||||
const remotePeerId = data.from;
|
||||
// получаем peer
|
||||
const pc = this.connections[remotePeerId];
|
||||
if (!pc) {
|
||||
console.error("No connection found for remote peer", remotePeerId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// если связь еще не установлена то устанвливаем remoteDescription в peer
|
||||
// if the connection is not established yet, then set remoteDescription to peer
|
||||
if (pc.signalingState !== "stable") {
|
||||
await pc.setRemoteDescription(new RTCSessionDescription(data.answer));
|
||||
} else {
|
||||
|
|
@ -207,16 +192,14 @@ export default class Call {
|
|||
}
|
||||
}
|
||||
|
||||
// обрабатываем полученный iceCandidate
|
||||
// process the received iceCandidate
|
||||
private async handleIceCandidate(data: { from: string, candidate: RTCIceCandidateInit }) {
|
||||
const remotePeerId = data.from;
|
||||
// получаем peer
|
||||
const pc = this.connections[remotePeerId];
|
||||
if (!pc) return;
|
||||
// если есть ice кандидаты
|
||||
// if there are ice candidates then add candidate to peer
|
||||
if (data.candidate && (data.candidate.sdpMid || data.candidate.sdpMLineIndex !== null)) {
|
||||
try {
|
||||
// добавляем кандидат в peer
|
||||
await pc.addIceCandidate(new RTCIceCandidate(data.candidate));
|
||||
} catch (e) {
|
||||
console.error("Error adding ICE candidate", e);
|
||||
|
|
@ -226,40 +209,40 @@ export default class Call {
|
|||
}
|
||||
}
|
||||
|
||||
// обрабатываем окончания звонка
|
||||
// handle call ends
|
||||
private handleCallEnd() {
|
||||
// Если звонок не завершен, то вызываем onCallEnd
|
||||
// If the call is not completed, then call onCallEnd
|
||||
if (this.store.get().calling !== CallingState.NoCall) {
|
||||
this.callArgs && this.callArgs.onCallEnd();
|
||||
}
|
||||
// меняем state на NoCall
|
||||
// change state to NoCall
|
||||
this.store.update({ calling: CallingState.NoCall });
|
||||
// Закрываем все созданные RTCPeerConnection
|
||||
// Close all created RTCPeerConnection
|
||||
Object.values(this.connections).forEach((pc) => pc.close());
|
||||
this.callArgs?.onCallEnd();
|
||||
// Очищаем connections
|
||||
// Clear connections
|
||||
this.connections = {};
|
||||
this.callArgs = null;
|
||||
}
|
||||
|
||||
// Обработчик события завершения вызова по сигналу
|
||||
// Call completion event handler by signal
|
||||
private onRemoteCallEnd = () => {
|
||||
if ([CallingState.Requesting, CallingState.Connecting].includes(this.store.get().calling)) {
|
||||
// Если вызов еще не начался, то вызываем onReject
|
||||
// If the call has not started yet, then call onReject
|
||||
this.callArgs && this.callArgs.onReject();
|
||||
// Закрываем все соединения и обнуляем callArgs
|
||||
// Close all connections and reset callArgs
|
||||
Object.values(this.connections).forEach((pc) => pc.close());
|
||||
this.connections = {};
|
||||
this.callArgs?.onCallEnd();
|
||||
this.store.update({ calling: CallingState.NoCall });
|
||||
this.callArgs = null;
|
||||
} else {
|
||||
// Вызываем полный обработчик завершения вызова
|
||||
// Call the full call completion handler
|
||||
this.handleCallEnd();
|
||||
}
|
||||
};
|
||||
|
||||
// Завершает вызов и отправляет сигнал call_end
|
||||
// Ends the call and sends the call_end signal
|
||||
initiateCallEnd = async () => {
|
||||
const userName = userStore.account.name;
|
||||
this.emitData('call_end', userName);
|
||||
|
|
@ -300,9 +283,7 @@ export default class Call {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициирует вызов
|
||||
*/
|
||||
// Initiates a call
|
||||
call(thirdPartyPeers?: string[]): { end: () => void } {
|
||||
if (thirdPartyPeers && thirdPartyPeers.length > 0) {
|
||||
this.addPeerCall(thirdPartyPeers);
|
||||
|
|
@ -314,18 +295,17 @@ export default class Call {
|
|||
};
|
||||
}
|
||||
|
||||
// Уведомление пиров об изменении состояния локального видео
|
||||
// Notify peers of local video state change
|
||||
toggleVideoLocalStream(enabled: boolean) {
|
||||
// Передаём сигнал через socket
|
||||
this.emitData('videofeed', { streamId: this.peerID, enabled });
|
||||
}
|
||||
|
||||
// Соединяемся с другими агентами
|
||||
// Connect with other agents
|
||||
addPeerCall(thirdPartyPeers: string[]) {
|
||||
thirdPartyPeers.forEach((peerId) => this._peerConnection(peerId));
|
||||
}
|
||||
|
||||
// Вызывает метод создания соединения с пиром
|
||||
// Calls the method to create a connection with a peer
|
||||
private _callSessionPeer() {
|
||||
if (![CallingState.NoCall, CallingState.Reconnecting].includes(this.store.get().calling)) {
|
||||
return;
|
||||
|
|
@ -336,7 +316,7 @@ export default class Call {
|
|||
console.warn('No tab data to connect to peer');
|
||||
}
|
||||
|
||||
// Формируем идентификатор пира в зависимости от версии ассиста
|
||||
// Generate a peer identifier depending on the assist version
|
||||
const peerId =
|
||||
this.getAssistVersion() === 1
|
||||
? this.peerID
|
||||
|
|
@ -347,7 +327,7 @@ export default class Call {
|
|||
void this._peerConnection(peerId);
|
||||
}
|
||||
|
||||
// Метод для очистки ресурсов
|
||||
// Method for clearing resources
|
||||
clean() {
|
||||
void this.initiateCallEnd();
|
||||
Object.values(this.connections).forEach((pc) => pc.close());
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ function draw(
|
|||
|
||||
export default class CanvasReceiver {
|
||||
private streams: Map<string, MediaStream> = new Map();
|
||||
// Храним RTCPeerConnection для каждого удалённого пира
|
||||
// Store RTCPeerConnection for each remote peer
|
||||
private connections: Map<string, RTCPeerConnection> = new Map();
|
||||
private cId: string;
|
||||
|
||||
//sendSignal – для отправки сигналов (offer/answer/ICE)
|
||||
// sendSignal – for sending signals (offer/answer/ICE)
|
||||
constructor(
|
||||
private readonly peerIdPrefix: string,
|
||||
private readonly config: RTCIceServer[] | null,
|
||||
|
|
@ -30,7 +30,7 @@ export default class CanvasReceiver {
|
|||
private readonly agentInfo: Record<string, any>,
|
||||
private readonly socket: Socket,
|
||||
) {
|
||||
// Формируем идентификатор как в PeerJS
|
||||
// Form an id like in PeerJS
|
||||
this.cId = `${this.peerIdPrefix}-${this.agentInfo.id}-canvas`;
|
||||
|
||||
this.socket.on('webrtc_canvas_offer', (data: { data: { offer: RTCSessionDescriptionInit, id: string }}) => {
|
||||
|
|
@ -57,7 +57,7 @@ export default class CanvasReceiver {
|
|||
iceServers: this.config ? this.config : [{ urls: "stun:stun.l.google.com:19302" }],
|
||||
});
|
||||
|
||||
// Сохраняем соединение
|
||||
// Save the connection
|
||||
this.connections.set(id, pc);
|
||||
|
||||
pc.onicecandidate = (event) => {
|
||||
|
|
@ -70,7 +70,7 @@ export default class CanvasReceiver {
|
|||
|
||||
const stream = event.streams[0];
|
||||
if (stream) {
|
||||
// Определяем canvasId из удалённого peer id
|
||||
// Detect canvasId from remote peer id
|
||||
const canvasId = id.split('-')[4];
|
||||
this.streams.set(canvasId, stream);
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
</div>
|
||||
<div id="video-container" class="card-body bg-dark p-0 d-flex align-items-center position-relative">
|
||||
<div id="local-stream" class="ratio ratio-4x3 rounded m-0 p-0 shadow scale-x-[-1]">
|
||||
<!-- пофиксить отображение по горизонтали -->
|
||||
<!-- fix horizontal mirroring -->
|
||||
<video id="video-local" autoplay muted class="scale-x-[-1]"></video>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ export default class Assist {
|
|||
}
|
||||
})
|
||||
|
||||
// Если приходит videofeed то в ui показываем видео
|
||||
// If a videofeed arrives, then we show the video in the ui
|
||||
socket.on('videofeed', (_, info) => {
|
||||
if (app.getTabId() !== info.meta.tabId) return
|
||||
const feedState = info.data
|
||||
|
|
@ -505,8 +505,12 @@ export default class Assist {
|
|||
}
|
||||
}
|
||||
|
||||
// обработка окончания вызова
|
||||
// call end handling
|
||||
const handleCallEnd = () => {
|
||||
Object.values(this.calls).forEach(pc => pc.close())
|
||||
this.calls = {}
|
||||
Object.values(lStreams).forEach((stream) => { stream.stop() })
|
||||
Object.keys(lStreams).forEach((peerId: string) => { delete lStreams[peerId] })
|
||||
// UI
|
||||
closeCallConfirmWindow()
|
||||
if (this.remoteControl?.status === RCStatus.Disabled) {
|
||||
|
|
@ -532,23 +536,22 @@ export default class Assist {
|
|||
}
|
||||
}
|
||||
|
||||
// обрабатываем входящий вызов
|
||||
const handleIncomingCallOffer = async (from: string, offer: RTCSessionDescriptionInit) => {
|
||||
app.debug.log('handleIncomingCallOffer', from)
|
||||
let confirmAnswer: Promise<boolean>
|
||||
const callingPeerIds = JSON.parse(sessionStorage.getItem(this.options.session_calling_peer_key) || '[]')
|
||||
// если звонящий уже в списке, то сразу принимаем вызов без ui
|
||||
// if the caller is already in the list, then we immediately accept the call without ui
|
||||
if (callingPeerIds.includes(from) || this.callingState === CallingState.True) {
|
||||
confirmAnswer = Promise.resolve(true)
|
||||
} else {
|
||||
// ставим стейт в ожидание подтверждения
|
||||
// set the state to wait for confirmation
|
||||
this.setCallingState(CallingState.Requesting)
|
||||
// вызываем окно подтверждения вызова
|
||||
// call the call confirmation window
|
||||
confirmAnswer = requestCallConfirm()
|
||||
// звуковое уведомление звонка
|
||||
// sound notification of a call
|
||||
this.playNotificationSound()
|
||||
|
||||
// через 30 сек сбрасываем вызов
|
||||
// after 30 seconds we drop the call
|
||||
setTimeout(() => {
|
||||
if (this.callingState !== CallingState.Requesting) { return }
|
||||
initiateCallEnd()
|
||||
|
|
@ -556,7 +559,7 @@ export default class Assist {
|
|||
}
|
||||
|
||||
try {
|
||||
// ждем рещения по принятию вызова
|
||||
// waiting for a decision on accepting the challenge
|
||||
const agreed = await confirmAnswer
|
||||
// если отказали, то завершаем вызов
|
||||
if (!agreed) {
|
||||
|
|
@ -564,14 +567,14 @@ export default class Assist {
|
|||
this.options.onCallDeny?.()
|
||||
return
|
||||
}
|
||||
// если приняли то чекаем ui, если окна вызова нет то создаем, привязываем toggle локального видео в тоглу через сокет
|
||||
// if rejected, then terminate the call
|
||||
if (!callUI) {
|
||||
callUI = new CallWindow(app.debug.error, this.options.callUITemplate)
|
||||
callUI.setVideoToggleCallback((args: { enabled: boolean }) =>
|
||||
this.emit('videofeed', { streamId: from, enabled: args.enabled })
|
||||
);
|
||||
}
|
||||
// показыаем кнопочки в окне вызова
|
||||
// show buttons in the call window
|
||||
callUI.showControls(initiateCallEnd)
|
||||
if (!annot) {
|
||||
annot = new AnnotationCanvas()
|
||||
|
|
@ -580,38 +583,38 @@ export default class Assist {
|
|||
|
||||
// callUI.setLocalStreams(Object.values(lStreams))
|
||||
try {
|
||||
// если нет локальных стримов в lStrems то устанавливаем
|
||||
// if there are no local streams in lStrems then we set
|
||||
if (!lStreams[from]) {
|
||||
app.debug.log('starting new stream for', from)
|
||||
// запрашиваем локальный стрим, и устанавливаем в lStreams
|
||||
// request a local stream, and set it to lStreams
|
||||
lStreams[from] = await RequestLocalStream()
|
||||
}
|
||||
// полученные дорожки передаем в Call ui
|
||||
// we pass the received tracks to Call ui
|
||||
callUI.setLocalStreams(Object.values(lStreams))
|
||||
} catch (e) {
|
||||
app.debug.error('Error requesting local stream', e);
|
||||
// если что-то не получилось то обрываем вызов
|
||||
// if something didn't work out, we terminate the call
|
||||
initiateCallEnd();
|
||||
return;
|
||||
}
|
||||
// создаем новый RTCPeerConnection с конфигом ice серверов
|
||||
// create a new RTCPeerConnection with ice server config
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
||||
});
|
||||
|
||||
// получаем все локальные треки и добавляем их в RTCPeerConnection
|
||||
// get all local tracks and add them to RTCPeerConnection
|
||||
lStreams[from].stream.getTracks().forEach(track => {
|
||||
pc.addTrack(track, lStreams[from].stream);
|
||||
});
|
||||
|
||||
// Когда получаем локальные ice кандидаты эмитим их через сокет
|
||||
// When we receive local ice candidates, we emit them via socket
|
||||
pc.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
socket.emit('webrtc_call_ice_candidate', { from, candidate: event.candidate });
|
||||
}
|
||||
};
|
||||
|
||||
// когда получаем удаленный поток, добавляем его в call ui
|
||||
// when we get a remote stream, add it to call ui
|
||||
pc.ontrack = (event) => {
|
||||
const rStream = event.streams[0];
|
||||
if (rStream && callUI) {
|
||||
|
|
@ -625,26 +628,26 @@ export default class Assist {
|
|||
}
|
||||
};
|
||||
|
||||
// Сохраняем соединение с звонящим
|
||||
// Keep connection with the caller
|
||||
this.calls[from] = pc;
|
||||
|
||||
// устанавливаем remote description на входящий запрос
|
||||
// set remote description on incoming request
|
||||
await pc.setRemoteDescription(new RTCSessionDescription(offer));
|
||||
// создаем ответ на входящий запрос
|
||||
// create a response to the incoming request
|
||||
const answer = await pc.createAnswer();
|
||||
// устанавливаем ответ как локальный
|
||||
await pc.setLocalDescription(answer);
|
||||
// передаем ответ
|
||||
// set the response as local
|
||||
socket.emit('webrtc_call_answer', { from, answer });
|
||||
|
||||
// Если меняется стейт на ощибку обрываем звонок
|
||||
// If the state changes to an error, we terminate the call
|
||||
pc.onconnectionstatechange = () => {
|
||||
if (pc.connectionState === 'disconnected' || pc.connectionState === 'failed') {
|
||||
initiateCallEnd();
|
||||
}
|
||||
};
|
||||
|
||||
// Обновление трека при изменении локального видео
|
||||
// Update track when local video changes
|
||||
lStreams[from].onVideoTrack(vTrack => {
|
||||
const sender = pc.getSenders().find(s => s.track?.kind === 'video');
|
||||
if (!sender) {
|
||||
|
|
@ -654,16 +657,16 @@ export default class Assist {
|
|||
sender.replaceTrack(vTrack)
|
||||
})
|
||||
|
||||
// если пользователеь закрыл вкладку или переключился, то завершаем вызов
|
||||
// if the user closed the tab or switched, then we end the call
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
initiateCallEnd()
|
||||
})
|
||||
|
||||
// когда все установилось то стейт переводим в true
|
||||
// when everything is set, we change the state to true
|
||||
this.setCallingState(CallingState.True)
|
||||
if (!callEndCallback) { callEndCallback = this.options.onCallStart?.() }
|
||||
const callingPeerIdsNow = Object.keys(this.calls)
|
||||
// в session storage записываем всех с кем установлен вызов
|
||||
// in session storage we write down everyone with whom the call is established
|
||||
sessionStorage.setItem(this.options.session_calling_peer_key, JSON.stringify(callingPeerIdsNow))
|
||||
this.emit('UPDATE_SESSION', { agentIds: callingPeerIdsNow, isCallActive: true })
|
||||
} catch (reason) {
|
||||
|
|
@ -671,9 +674,9 @@ export default class Assist {
|
|||
}
|
||||
};
|
||||
|
||||
// Функции запроса подтверждения, завершения вызова, уведомления и т.д.
|
||||
// Functions for requesting confirmation, ending a call, notifying, etc.
|
||||
const requestCallConfirm = () => {
|
||||
if (callConfirmAnswer) { // Если уже запрошено подтверждение
|
||||
if (callConfirmAnswer) { // If confirmation has already been requested
|
||||
return callConfirmAnswer;
|
||||
}
|
||||
callConfirmWindow = new ConfirmWindow(callConfirmDefault(this.options.callConfirm || {
|
||||
|
|
@ -686,15 +689,12 @@ export default class Assist {
|
|||
});
|
||||
};
|
||||
|
||||
// функция завершения вызова
|
||||
const initiateCallEnd = () => {
|
||||
this.emit('call_end');
|
||||
handleCallEnd();
|
||||
};
|
||||
|
||||
const startCanvasStream = async (stream: MediaStream, id: number) => {
|
||||
// const canvasPID = `${app.getProjectKey()}-${sessionId}-${id}`;
|
||||
// const target = `${agent.agentInfo.peerId}-${agent.agentInfo.id}-canvas`;
|
||||
for (const agent of Object.values(this.agents)) {
|
||||
if (!agent.agentInfo) return;
|
||||
|
||||
|
|
@ -710,11 +710,11 @@ export default class Assist {
|
|||
this.canvasPeers[uniqueId]?.addTrack(track, stream);
|
||||
});
|
||||
|
||||
// Создаем SDP offer
|
||||
// Create SDP offer
|
||||
const offer = await this.canvasPeers[uniqueId].createOffer();
|
||||
await this.canvasPeers[uniqueId].setLocalDescription(offer);
|
||||
|
||||
// Отправляем offer через сервер сигналинга
|
||||
// Send offer via signaling server
|
||||
socket.emit('webrtc_canvas_offer', { offer, id: uniqueId });
|
||||
|
||||
}
|
||||
|
|
@ -758,7 +758,7 @@ export default class Assist {
|
|||
private setupPeerListeners(id: string) {
|
||||
const peer = this.canvasPeers[id];
|
||||
if (!peer) return;
|
||||
// ICE-кандидаты
|
||||
// ICE candidates
|
||||
peer.onicecandidate = (event) => {
|
||||
if (event.candidate && this.socket) {
|
||||
this.socket.emit('webrtc_canvas_ice_candidate', {
|
||||
|
|
@ -779,7 +779,7 @@ export default class Assist {
|
|||
}
|
||||
}
|
||||
|
||||
// очищаем все данные
|
||||
// clear all data
|
||||
private clean() {
|
||||
// sometimes means new agent connected, so we keep id for control
|
||||
this.remoteControl?.releaseControl(false, true);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue