import { BrowserWindow, screen } from 'electron'
/**
* @name: createMore
* @msg: 获取多个窗口实例创建渲染窗口
*/
async function createMore() {
const displays = screen.getAllDisplays()
for (let i = 0; i < displays.length; i++) {
const display = displays[i]
createWindow(display)
}
}
/**
* @name: createWindow
* @msg: 根据不同的显示屏创建窗口
* @param {any} display
*/
async function createWindow(display) {
const win = new BrowserWindow({
frame: platform === 'darwin',
fullscreen: true,
x: display.bounds.x,
y: display.bounds.y,
webPreferences: {
nodeIntegration: (process.env
.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
enableRemoteModule: true,
webSecurity: false,
},
})
}
主页面采集视频
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>parent</title>
</head>
<body>
<video id="video" style="width: 400px; height: 400px"></video>
<button id="start" onclick="start()">请先开启摄像头</button>
<button id="start" onclick="call()">新增远程页面</button>
<h5>下面是iframe区域可以播放父容器传过来的视频</h5>
<div id="content"></div>
<script>
/**
* @name:
* @msg: 监听子窗口的消息
*/
var pcMap = new Map(); // 本地链接对象集合
window.addEventListener(
"message",
async (message) => {
const mes = JSON.parse(message.data);
const content = mes.content;
switch (mes.type) {
// 监听到远程加入
case "addRemote":
var pc = await newPC(mes.content.displayId);
pcMap.set(mes.content.displayId, pc);
break;
case "RTCOnicecandidate":
const rtcIceCandidate = new RTCIceCandidate({
candidate: content.sdp,
sdpMid: content.sdpMid,
sdpMLineIndex: content.sdpMLineIndex,
});
//添加对端Candidate
var pc = pcMap.get(content.displayId);
if (pc) {
pc.addIceCandidate(rtcIceCandidate)
.then(() => {
console.log("连上了");
})
.catch((e) => {
console.log("Error: Failure during addIceCandidate()", e);
});
}
break;
//接收远端Answer
case "RTCAnswer":
const rtcDescription = { type: "answer", sdp: content.sdp };
//设置远端setRemoteDescription
var pc = pcMap.get(content.displayId);
pc.setRemoteDescription(
new RTCSessionDescription(rtcDescription)
);
break;
default:
break;
}
},
false
);
/**
* @name: start
* @msg: 开始摄像头
*/
function start() {
// var constraints = { audio: true, video: { width: 1280, height: 720 } };
var constraints = { audio: true, video: { width: 1280, height: 720 } };
navigator.mediaDevices
.getUserMedia(constraints)
.then(function (mediaStream) {
var video = document.querySelector("video");
video.srcObject = mediaStream;
video.onloadedmetadata = function (e) {
video.play();
};
})
.catch(function (err) {
console.log(err.name + ": " + err.message);
});
}
/**
* @name:call
* @msg: call呼叫子页面,简历rtc链接
*/
var id = 0;
function call() {
let iframe = document.createElement("iframe");
// 这边是模拟场景所以使用url传参的方式 真实场景id是每个设备id
iframe.src = `2.html?id=${id}`;
iframe.style = "width:400px;height:400px";
iframe.id = id;
document.getElementById("content").appendChild(iframe);
id++;
}
/**
* @name:newPC
* @msg:新建rtc链接
* @return {*}
*/
async function newPC(displayId) {
const config = {
// iceServers: [{ url: 'stun:stun.xten.com' }],
configuration: {
offerToReceiveAudio: true,
offerToReceiveVideo: true,
},
};
const pc = new RTCPeerConnection(config);
pc.onicecandidate = (event) => {
// 点对点链接
if (event.candidate) {
const mes = JSON.stringify({
type: "RTCOnicecandidate",
content: {
displayId: displayId,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex,
sdp: event.candidate.candidate,
},
});
document
.getElementById(displayId)
.contentWindow.postMessage(mes, "*");
}
};
pc.onnegotiationneeded = (e) => {
console.log("onnegotiationneeded", e);
};
pc.onicegatheringstatechange = (e) => {
console.log("onicegatheringstatechange", e);
};
pc.oniceconnectionstatechange = (e) => {
console.log("oniceconnectionstatechange", e);
};
pc.onsignalingstatechange = (e) => {
console.log("onsignalingstatechange", e);
};
pc.ontrack = (e) => {
console.log(e);
};
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
};
// 先添加流 其他远程页面才能收到
const stream = document.querySelector("video").captureStream();
pc.addStream(stream);
// 创建offer
const offer = await pc.createOffer(offerOptions);
// 设置本地
await pc.setLocalDescription(offer);
// 发送offer
const mes = JSON.stringify({
type: "RTCOffer",
content: {
displayId: displayId,
sdp: offer.sdp,
},
});
//向指定窗口发送消息
document.getElementById(displayId).contentWindow.postMessage(mes, "*");
return pc;
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>child</title>
</head>
<body>
<video
id="video"
style="width: 100%; height: 100%; object-fit: cover"
></video>
<script>
// 真实场景每个id独立唯一,子业务加载完成后告诉父页面子页面的id
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return false;
}
var displayId = getQueryVariable("id");
window.pc = newPC();
window.onload = function () {
const message = JSON.stringify({
type: "addRemote",
content: {
displayId: displayId,
},
});
window.parent.postMessage(message, "*");
};
window.addEventListener(
"message",
async (message) => {
const mes = JSON.parse(message.data);
const content = mes.content;
switch (mes.type) {
//监听到远程的offer//只处理自己设备的offer
case "RTCOffer":
if (content.displayId == displayId) {
// 创建的时间可以自定义不一定在offer中创建
window.pc = await newPC();
const rtcDescription = { type: "offer", sdp: content.sdp };
//设置远端setRemoteDescription
this.pc.setRemoteDescription(
new RTCSessionDescription(rtcDescription)
);
//createAnswer
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
};
pc.createAnswer(offerOptions).then(
(offer) => {
pc.setLocalDescription(offer);
//发送answer消息
const mes = JSON.stringify({
type: "RTCAnswer",
content: {
displayId: displayId,
sdp: offer.sdp,
},
});
window.parent.postMessage(mes, "*");
},
(error) => {
console.log(error);
}
);
}
break;
case "RTCOnicecandidate":
if (content.displayId == displayId) {
const rtcIceCandidate = new RTCIceCandidate({
candidate: content.sdp,
sdpMid: content.sdpMid,
sdpMLineIndex: content.sdpMLineIndex,
});
//添加对端Candidate
if (window.pc) {
pc.addIceCandidate(rtcIceCandidate)
.then(() => {
console.log("连上了");
})
.catch((e) => {
console.log("Error: Failure during addIceCandidate()", e);
});
}
}
break;
default:
break;
}
},
false
);
/**
* @name:newPC
* @msg:新建rtc链接
* @return {*}
*/
async function newPC() {
const config = {
configuration: {
offerToReceiveAudio: true,
offerToReceiveVideo: true,
},
};
const pc = new RTCPeerConnection(config);
pc.onicecandidate = (event) => {
// 点对点链接
if (event.candidate) {
const mes = JSON.stringify({
type: "RTCOnicecandidate",
content: {
displayId: displayId,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex,
sdp: event.candidate.candidate,
},
});
window.parent.postMessage(message, "*");
}
};
pc.onnegotiationneeded = (e) => {
console.log("onnegotiationneeded", e);
};
pc.onicegatheringstatechange = (e) => {
console.log("onicegatheringstatechange", e);
};
pc.oniceconnectionstatechange = (e) => {
console.log("oniceconnectionstatechange", e);
};
pc.onsignalingstatechange = (e) => {
console.log("onsignalingstatechange", e);
};
pc.ontrack = (e) => {
console.log(e);
};
// 监听到流了播放
pc.onaddstream = (e) => {
var video = document.querySelector("video");
video.srcObject = e.stream;
video.onloadedmetadata = function (e) {
video.play();
};
};
return pc;
}
</script>
</body>
</html>