277 lines
8.7 KiB
JavaScript
277 lines
8.7 KiB
JavaScript
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);
|
||
}
|
||
}
|