Files
webRtc/WebApp/client/src/signaling.js
2026-04-25 13:29:35 +08:00

277 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as Logger from "./logger.js";
export class Signaling extends EventTarget {
constructor(interval = 1000) {
super();
this.running = false;
this.interval = interval;
this.sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
}
headers() {
if (this.sessionId !== undefined) {
return { 'Content-Type': 'application/json', 'Session-Id': this.sessionId };
}
else {
return { 'Content-Type': 'application/json' };
}
}
url(method, parameter = '') {
let ret = location.origin + '/signaling';
if (method)
ret += '/' + method;
if (parameter)
ret += '?' + parameter;
return ret;
}
async start() {
if (this.running) {
return;
}
this.running = true;
while (!this.sessionId) {
const createResponse = await fetch(this.url(''), { method: 'PUT', headers: this.headers() });
const session = await createResponse.json();
this.sessionId = session.sessionId;
if (!this.sessionId) {
await this.sleep(this.interval);
}
}
this.loopGetAll();
}
async loopGetAll() {
let lastTimeRequest = Date.now() - 30000;
while (this.running) {
const res = await this.getAll(lastTimeRequest);
const data = await res.json();
lastTimeRequest = data.datetime ? data.datetime : Date.now();
const messages = data.messages;
for (const msg of messages) {
switch (msg.type) {
case "connect":
break;
case "disconnect":
this.dispatchEvent(new CustomEvent('disconnect', { detail: msg }));
break;
case "offer":
this.dispatchEvent(new CustomEvent('offer', { detail: msg }));
break;
case "answer":
this.dispatchEvent(new CustomEvent('answer', { detail: msg }));
break;
case "candidate":
this.dispatchEvent(new CustomEvent('candidate', { detail: msg }));
break;
case "on-message":
this.dispatchEvent(new CustomEvent('on-message', { detail: msg.data }));
break;
default:
break;
}
}
await this.sleep(this.interval);
}
}
async stop() {
this.running = false;
await fetch(this.url(''), { method: 'DELETE', headers: this.headers() });
this.sessionId = null;
}
async createConnection(connectionId) {
const data = { 'connectionId': connectionId };
const res = await fetch(this.url('connection'), { method: 'PUT', headers: this.headers(), body: JSON.stringify(data) });
const json = await res.json();
Logger.log(`Signaling: HTTP create connection, connectionId: ${json.connectionId}, polite:${json.polite}`);
this.dispatchEvent(new CustomEvent('connect', { detail: json }));
return json;
}
async deleteConnection(connectionId) {
const data = { 'connectionId': connectionId };
const res = await fetch(this.url('connection'), { method: 'DELETE', headers: this.headers(), body: JSON.stringify(data) });
const json = await res.json();
this.dispatchEvent(new CustomEvent('disconnect', { detail: json }));
return json;
}
async sendOffer(connectionId, sdp) {
const data = { 'sdp': sdp, 'connectionId': connectionId };
Logger.log('sendOffer:' + data);
await fetch(this.url('offer'), { method: 'POST', headers: this.headers(), body: JSON.stringify(data) });
}
async sendAnswer(connectionId, sdp) {
const data = { 'sdp': sdp, 'connectionId': connectionId };
Logger.log('sendAnswer:' + data);
await fetch(this.url('answer'), { method: 'POST', headers: this.headers(), body: JSON.stringify(data) });
}
async sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex) {
const data = {
'candidate': candidate,
'sdpMLineIndex': sdpMLineIndex,
'sdpMid': sdpMid,
'connectionId': connectionId
};
Logger.log('sendCandidate:' + data);
await fetch(this.url('candidate'), { method: 'POST', headers: this.headers(), body: JSON.stringify(data) });
}
// 在 Signaling 类中添加
async sendMessage(connectionId, message) {
const data = {
'message': message,
'connectionId': connectionId
};
await fetch(this.url('on-message'), { method: 'POST', headers: this.headers(), body: JSON.stringify(data) });
}
async getAll(fromTime = 0) {
return await fetch(this.url(``, `fromtime=${fromTime}`), { method: 'GET', headers: this.headers() });
}
}
export class WebSocketSignaling extends EventTarget {
constructor(interval = 1000) {
super();
this.interval = interval;
this.sleep = msec => new Promise(resolve => setTimeout(resolve, msec));
let websocketUrl;
if (location.protocol === "https:") {
websocketUrl = "wss://" + location.host;
} else {
websocketUrl = "ws://" + location.host;
}
this.websocket = new WebSocket(websocketUrl);
this.connectionId = null;
this.websocket.onopen = () => {
this.isWsOpen = true;
};
this.websocket.onclose = () => {
this.isWsOpen = false;
};
this.websocket.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (!msg || !this) {
return;
}
Logger.log(msg);
switch (msg.type) {
case "connect":
this.dispatchEvent(new CustomEvent('connect', { detail: msg }));
break;
case "disconnect":
this.dispatchEvent(new CustomEvent('disconnect', { detail: msg }));
break;
case "offer":
this.dispatchEvent(new CustomEvent('offer', { detail: { connectionId: msg.from, sdp: msg.data.sdp, polite: msg.data.polite, participantId: msg.participantId } }));
break;
case "answer":
this.dispatchEvent(new CustomEvent('answer', { detail: { connectionId: msg.from, sdp: msg.data.sdp, participantId: msg.participantId } }));
break;
case "candidate":
this.dispatchEvent(new CustomEvent('candidate', { detail: { connectionId: msg.from, candidate: msg.data.candidate, sdpMLineIndex: msg.data.sdpMLineIndex, sdpMid: msg.data.sdpMid, participantId: msg.participantId } }));
break;
case "on-message":
// 将participantId附加到消息数据中以便Host识别消息发送者
if (msg.participantId) {
msg.data.participantId = msg.participantId;
}
this.dispatchEvent(new CustomEvent('on-message', { detail: msg.data }));
break;
case "participant-left":
this.dispatchEvent(new CustomEvent('participant-left', { detail: msg }));
break;
case "participant-joined":
this.dispatchEvent(new CustomEvent('participant-joined', { detail: msg }));
break;
case "broadcast":
this.dispatchEvent(new CustomEvent('on-message', { detail: msg.message }));
break;
default:
break;
}
};
}
async start() {
while (!this.isWsOpen) {
await this.sleep(100);
}
}
async stop() {
this.websocket.close();
while (this.isWsOpen) {
await this.sleep(100);
}
}
createConnection(connectionId) {
const sendJson = JSON.stringify({ type: "connect", connectionId: connectionId });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
deleteConnection(connectionId) {
const sendJson = JSON.stringify({ type: "disconnect", connectionId: connectionId });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
sendOffer(connectionId, sdp, participantId) {
const data = { 'sdp': sdp, 'connectionId': connectionId };
const sendJson = JSON.stringify({ type: "offer", from: connectionId, data: data, participantId: participantId || '' });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
sendAnswer(connectionId, sdp, participantId) {
const data = { 'sdp': sdp, 'connectionId': connectionId };
const sendJson = JSON.stringify({ type: "answer", from: connectionId, data: data, participantId: participantId || '' });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
sendCandidate(connectionId, candidate, sdpMLineIndex, sdpMid, participantId) {
const data = {
'candidate': candidate,
'sdpMLineIndex': sdpMLineIndex,
'sdpMid': sdpMid,
'connectionId': connectionId
};
const sendJson = JSON.stringify({ type: "candidate", from: connectionId, data: data, participantId: participantId || '' });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
// 在 WebSocketSignaling 类中添加
sendMessage(connectionId, message) {
const data = {
'message': message,
'senderId': message.senderId,
'connectionId': connectionId
};
const sendJson = JSON.stringify({ type: "on-message", data: data });
Logger.log(sendJson);
this.websocket.send(sendJson);
}
}