会议监控测试

This commit is contained in:
yj 2024-08-27 10:44:15 +08:00
parent 94bb8d5ff0
commit 92d8e9e443
16 changed files with 633 additions and 122 deletions

14
main.js
View File

@ -16,6 +16,7 @@ const { autoUpdater, CancellationToken } = require('electron-updater');
const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false;
let mainWindow = null;
let newWindow = null;
let isMaximized = false;
let env;
@ -264,7 +265,10 @@ app.on('ready', () => {
});
// 打开新窗口
ipcMain.handle('oepnWindow', (event, data) => {
const newWindow = new BrowserWindow({
if (newWindow) {
newWindow.focus();
} else {
newWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1200,
@ -290,6 +294,14 @@ app.on('ready', () => {
}
}
});
}
});
// 关闭会议监控窗口
ipcMain.handle('closeMonitorWindow', () => {
if (newWindow) {
newWindow.close();
newWindow = null
}
});
}
});

143
package-lock.json generated
View File

@ -1,15 +1,16 @@
{
"name": "multi.person.meeting",
"name": "WGShare.Metting",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "multi.person.meeting",
"name": "WGShare.Metting",
"version": "0.0.1",
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@microsoft/signalr": "^8.0.0",
"@reduxjs/toolkit": "^2.2.7",
"@types/node": "^20.14.9",
"agora-electron-sdk": "^4.3.2",
"animate.css": "^4.1.1",
@ -24,6 +25,7 @@
"postcss-px-to-viewport-8-plugin": "^1.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"sass": "^1.77.5",
"tldraw": "^2.2.0"
@ -2243,6 +2245,29 @@
"react-dom": ">=16.9.0"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "2.2.7",
"resolved": "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.2.7.tgz",
"integrity": "sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g==",
"dependencies": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": {
"version": "1.17.1",
"resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.17.1.tgz",
@ -2519,6 +2544,11 @@
"resolved": "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz",
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@types/verror": {
"version": "1.10.10",
"resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.10.tgz",
@ -7265,6 +7295,15 @@
}
]
},
"node_modules/immer": {
"version": "10.1.1",
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/immutable": {
"version": "4.3.6",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz",
@ -9620,6 +9659,28 @@
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
},
"node_modules/react-redux": {
"version": "9.1.2",
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz",
"integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
"dependencies": {
"@types/use-sync-external-store": "^0.0.3",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^18.2.25",
"react": "^18.0",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.13.0",
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.13.0.tgz",
@ -9863,6 +9924,19 @@
"node": ">=0.10.0"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@ -9999,6 +10073,11 @@
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@ -11675,6 +11754,14 @@
}
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
"integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/utf8-byte-length": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
@ -13473,6 +13560,17 @@
"rc-util": "^5.38.0"
}
},
"@reduxjs/toolkit": {
"version": "2.2.7",
"resolved": "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.2.7.tgz",
"integrity": "sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g==",
"requires": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
}
},
"@remix-run/router": {
"version": "1.17.1",
"resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.17.1.tgz",
@ -13721,6 +13819,11 @@
"resolved": "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz",
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
},
"@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"@types/verror": {
"version": "1.10.10",
"resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.10.tgz",
@ -17293,6 +17396,11 @@
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"immer": {
"version": "10.1.1",
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="
},
"immutable": {
"version": "4.3.6",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz",
@ -19025,6 +19133,15 @@
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
},
"react-redux": {
"version": "9.1.2",
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.1.2.tgz",
"integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
"requires": {
"@types/use-sync-external-store": "^0.0.3",
"use-sync-external-store": "^1.0.0"
}
},
"react-refresh": {
"version": "0.13.0",
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.13.0.tgz",
@ -19193,6 +19310,17 @@
}
}
},
"redux": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"requires": {}
},
"regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@ -19298,6 +19426,11 @@
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@ -20592,6 +20725,12 @@
"tslib": "^2.0.0"
}
},
"use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
"integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
"requires": {}
},
"utf8-byte-length": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",

View File

@ -25,6 +25,7 @@
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@microsoft/signalr": "^8.0.0",
"@reduxjs/toolkit": "^2.2.7",
"@types/node": "^20.14.9",
"agora-electron-sdk": "^4.3.2",
"animate.css": "^4.1.1",
@ -39,6 +40,7 @@
"postcss-px-to-viewport-8-plugin": "^1.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"sass": "^1.77.5",
"tldraw": "^2.2.0"

View File

@ -65,4 +65,8 @@ window.electron = {
oepnWindow: (data) => {
ipcRenderer.invoke('oepnWindow', data)
},
// 关闭会议监控窗口
closeMonitorWindow: () => {
ipcRenderer.invoke('closeMonitorWindow')
},
}

View File

@ -33,8 +33,7 @@ const App: React.FC = () => {
});
const [spinning, setSpinning] = useState(false);
const [isState, setIsState] = useState(true);
const urlWindow = ['#/userVideo']
if (urlWindow.indexOf(location.hash) === -1) {
if (location.href.indexOf('#/userVideo') === -1) {
useEffect(() => {
let userInfo = JSON.parse(storage.getItem('user') as string)
let loginInfo = JSON.parse(storage.getItem('login') as string)
@ -151,6 +150,9 @@ const App: React.FC = () => {
if (location.href.indexOf('/login') !== -1) {
onStop()
}
if (location.href.indexOf('#/meeting') === -1) {
window.electron.closeMonitorWindow()
}
}, [navigate])
}
useEffect(() => {

View File

@ -130,3 +130,8 @@ export const GetApplySpeak = (roomNum: string) =>
url: `/room/apply-speak?roomNum=${roomNum}`,
method: 'get'
})
export const GetPolling = (roomNum: string, count: string) =>
request({
url: `/room/polling?roomNum=${roomNum}&count=${count}`,
method: 'get'
})

View File

@ -71,9 +71,12 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
})
}
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
await GetRoomRtcToken(roomNum).then(res => {
if (res.code === 200) {
callBack(res.data)
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
if (res[0].code === 200 && res[1].code === 200) {
callBack({
token: res[0].data,
tokenA: res[1].data,
})
}
})
}
@ -169,8 +172,8 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
}
isGetCheckoutRoomNum(roomNumber, (bool: boolean) => {
if (bool) {
getRoomRtcToken(roomNumber, (token: string) => {
if (token) {
getRoomRtcToken(roomNumber, (options: any) => {
if (options) {
postRefresh(() => {
clearInterval(time)
setJoinRoomSettingModal(false)
@ -180,7 +183,8 @@ const JoinSetting = forwardRef((_props: any, ref: any) => {
navigate(`/meeting`, {
state: {
channelId: roomNumber,
token,
token: options.token,
tokenA: options.tokenA,
roomId: res.data.id,
roomName: res.data.roomName,
enableMicr: joinRoomSettingForm[0].active,

View File

@ -5,10 +5,14 @@ import { HashRouter } from 'react-router-dom';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import 'animate.css';
import { Provider } from 'react-redux'
import store from '@/utils/package/store';
ReactDOM.createRoot(document.getElementById('root')!).render(
<HashRouter>
<ConfigProvider locale={zhCN}>
<Provider store={store}>
<App />
</Provider>
</ConfigProvider>
</HashRouter>
)

View File

@ -85,13 +85,13 @@ const Meeting: React.FC = () => {
active: false,
select: false,
},
// {
// title: '会议监控',
// icon: ImageUrl.icon48,
// iconSelect: ImageUrl.icon48Select,
// active: false,
// select: false,
// },
{
title: '会议监控',
icon: ImageUrl.icon48,
iconSelect: ImageUrl.icon48Select,
active: false,
select: false,
},
{
title: '录制',
icon: ImageUrl.icon27,
@ -188,6 +188,7 @@ const Meeting: React.FC = () => {
agora.init(true)
agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
if (info.channelId === state.channelId) {
if (String(info.localUid).length !== 9) {
await getJoin(state.enableMicr, state.enableCamera)
setTimeout(async () => {
@ -200,8 +201,10 @@ const Meeting: React.FC = () => {
getShowUser();
}, 1000);
}
}
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
if (info.channelId === state.channelId) {
if (String(remoteUid).length === 9) {
setIsShare(remoteUid)
} else {
@ -213,8 +216,10 @@ const Meeting: React.FC = () => {
})
}, 1000);
}
}
},
onUserOffline: async (info: any, remoteUid: any, _reason: any) => {
if (info.channelId === state.channelId) {
if (String(remoteUid).length === 9) {
setIsShare(null)
renderVideo()
@ -230,6 +235,7 @@ const Meeting: React.FC = () => {
}
return res
})
}
},
onAudioVolumeIndication: async (speakers: any) => {
speakers.forEach((item: any) => {
@ -241,11 +247,14 @@ const Meeting: React.FC = () => {
});
}
})
if (state.enableCamera) {
agora.startCameraCapture()
}
agora.setJoinChannel({
channelId: state.channelId,
uid: userInfo.uid,
token: state.token,
tokenA: userInfo.uid === '1' ? '' : state.tokenA,
})
storage.setItem('noViewChatList', 0)
window.addEventListener('customStorageChange', handleCustomStorageChange);
@ -465,6 +474,10 @@ const Meeting: React.FC = () => {
pauseOnHover: false,
});
break;
// 管理员查看随机用户
case 'Watch':
console.log(item);
break;
}
})
return () => {
@ -915,7 +928,7 @@ const Meeting: React.FC = () => {
break;
case '会议监控':
window.electron.oepnWindow({
url: location.origin + '/#/userVideo'
url: location.origin + `/#/userVideo?channelId=${state.channelId + 'a'}&token=${state.tokenA}`
})
break;
}

View File

@ -1,10 +1,15 @@
import { GetPolling } from '@/api/Meeting';
import styles from '@/page/UserVideo/index.module.scss'
import { storage } from '@/utils';
import monitorAgora from '@/utils/package/monitorAgora';
import { CloseOutlined } from '@ant-design/icons';
import { Button, Select } from 'antd';
import { VideoSourceType } from 'agora-electron-sdk';
import { Button, Select, message } from 'antd';
import { useEffect, useState } from "react";
const UserVideo: React.FC = () => {
let userInfo = JSON.parse(storage.getItem('user') as string)
const [user, setUser] = useState<any>({});
const [from, setFrom] = useState<any>({
cycleIntervalList: [
{ value: 30, label: '30秒' },
@ -22,15 +27,86 @@ const UserVideo: React.FC = () => {
],
viewPeopleValue: 4,
})
const [timeNumber, setTimeNumber] = useState(30);
const [timeStatus, setTimeStatus] = useState(false);
useEffect(() => {
setUser(userInfo)
monitorAgora.init()
monitorAgora.setJoinChannel({
channelId: getQueryParameterRegex('channelId'),
uid: userInfo.uid,
token: getQueryParameterRegex('token'),
})
monitorAgora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
// await monitorAgora.setupLocalVideo({
// uid: info.localUid,
// view: document.getElementById(`video-${info.localUid}`),
// channelId: info.channelId,
// sourceType: VideoSourceType.VideoSourceCameraPrimary,
// })
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
},
onUserOffline: async (info: any, remoteUid: any, _reason: any) => {
},
})
window.addEventListener('customStorageChange', handleCustomStorageChange);
return () => {
window.removeEventListener('customStorageChange', handleCustomStorageChange);
}
}, []);
useEffect(() => {
let time = null as any;
if (timeStatus) {
time = setInterval(() => {
setTimeNumber((timeNumber: number) => {
if (timeNumber === 1) {
getPolling()
message.success('刷新成功!')
return from.cycleIntervalValue
} else {
return timeNumber - 1
}
})
}, 1000)
} else {
clearInterval(time)
}
return () => {
clearInterval(time)
}
}, [timeStatus])
useEffect(() => {
getPolling()
}, [from.viewPeopleValue])
// 监听缓存变化
const handleCustomStorageChange = async (e: any): Promise<void> => {
// console.log(e);
};
// 获取地址栏参数
const getQueryParameterRegex = (name: string): string | null => {
const reg = new RegExp(`[?&]${name}=([^&#]*)`);
const results = window.location.href.match(reg);
return results === null ? null : results[1];
}
// 获取轮训用户
const getPolling = async (): Promise<void> => {
GetPolling(getQueryParameterRegex('channelId') as string, from.viewPeopleValue).then((res: any) => {
console.log(res);
})
};
return (
<>
<div className={styles.userVideo}>
<div className={styles.userVideoTitle}>
<CloseOutlined className='drag' />
<CloseOutlined className='drag' onClick={() => {
window.electron.closeMonitorWindow()
}} />
</div>
<div className={`${styles.userVideoContent} drag`}>
<div className={styles.userVideoContentHeader}>
@ -42,6 +118,7 @@ const UserVideo: React.FC = () => {
options={from.cycleIntervalList}
value={from.cycleIntervalValue} onChange={(e) => {
setFrom({ ...from, cycleIntervalValue: e })
setTimeNumber(e)
}} />
</div>
<div>
@ -55,47 +132,26 @@ const UserVideo: React.FC = () => {
</div>
</div>
<div>
<span>30</span>
{/* <Button
<span>{timeNumber}</span>
{timeStatus ? <Button
type="primary"
onClick={() => { }}
style={{ backgroundColor: '#EC3C3C', marginLeft: '14px' }}
></Button> */}
onClick={() => {
setTimeStatus(!timeStatus)
}}
style={{ backgroundColor: '#EC3C3C' }}
></Button> :
<Button
type="primary"
className='m-ant-btn'
onClick={() => { }}
></Button>
onClick={() => {
setTimeStatus(!timeStatus)
}}
></Button>}
</div>
</div>
<div className={styles.userVideoContentList}>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
</div>
</div>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
</div>
</div>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
</div>
</div>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
</div>
</div>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
</div>
</div>
<div className={styles.userVideoContentListItem}>
<div className={styles.userVideoContentListItemVideo}>
<div className={styles.userVideoContentListItemVideo} id={`video-${user.uid}`}>
</div>
</div>

2
src/render.d.ts vendored
View File

@ -16,6 +16,8 @@ export interface IElectronAPI {
quitAndInstall: (callBack: Function) => void;
getVersion: () => Promise<string>;
oepnWindow: (data: any) => any;
closeMonitorWindow: () => void
}
declare global {

View File

@ -12,6 +12,7 @@ import { storage } from '@/utils';
const option: any = {
appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
token: '',
tokenA: '',
channelId: '',
uid: '',
}
@ -216,6 +217,21 @@ const agora = {
}
);
},
// 所有用户加入的第二个房间
allJoinChannelEx: async () => {
await rtcEngine.joinChannelEx(
option.tokenA,
{ channelId: option.channelId + 'a', localUid: Number(option.uid) },
{
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
autoSubscribeVideo: false,//设置是否自动订阅所有视频流
publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频
publishCameraTrack: true,//设置是否发布摄像头采集的视频
clientRoleType: ClientRoleType.ClientRoleAudience,//用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
publishScreenTrack: false,//设置是否发布屏幕采集的视频
}
);
},
// 离开共享屏幕频道
leaveChannelEx: async (uid: any) => {
await rtcEngine.leaveChannelEx({ channelId: option.channelId, localUid: Number(uid) })
@ -250,9 +266,13 @@ const agora = {
// 加入频道
setJoinChannel: async (data: any) => {
option.token = data.token;
option.tokenA = data.tokenA;
option.channelId = data.channelId;
option.uid = Number(data.uid);
await agora.joinChannel()
if (data.tokenA) {
await agora.allJoinChannelEx()
}
},
// 桌面捕获音频和视频的媒体源的信息
getDesktopCapturerVideo: async () => {

View File

@ -0,0 +1,15 @@
// 暂时无用
import { createSlice } from '@reduxjs/toolkit';
export const agoraReducer = createSlice({
name: 'agora',
initialState: {
value: 0,
} as any,
reducers: {
initAgora: async (state) => {
},
},
});
export const { initAgora } = agoraReducer.actions;
export default agoraReducer.reducer;

View File

@ -0,0 +1,218 @@
import {
createAgoraRtcEngine,
ClientRoleType,
VideoSourceType,
VideoViewSetupMode,
RenderModeType,
} from "agora-electron-sdk";
import { storage } from "..";
const option: any = {
appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
token: '',
channelId: '',
uid: '',
}
let rtcEngine: any = '';
const monitorAgora = {
// 初始化
init: async () => {
rtcEngine = createAgoraRtcEngine();
await rtcEngine.initialize({
appId: option.appId,
});
await monitorAgora.setDeviceManager()
},
// 获取当前设备是否存在不存在就获取默认设备
setDeviceManager: async (bool: boolean = false) => {
const setting = await JSON.parse(storage.getItem('setting') as string)
// 摄像头
if (setting.videoDeviceId) {
await monitorAgora.getVideoDeviceManager().then(async (res) => {
let item = res.list.find((item: any) => item.deviceId === setting.videoDeviceId);
if (item) {
await monitorAgora.setVideoDeviceManager(setting.videoDeviceId)
} else {
await monitorAgora.setVideoDeviceManager(await rtcEngine.getVideoDeviceManager().getDevice())
setting.videoDeviceId = await rtcEngine.getVideoDeviceManager().getDevice()
}
})
} else {
await monitorAgora.setVideoDeviceManager(await rtcEngine.getVideoDeviceManager().getDevice())
setting.videoDeviceId = await rtcEngine.getVideoDeviceManager().getDevice()
}
// 播放设备
if (setting.playBackDeviceId) {
await monitorAgora.getAudioMediaList().then(async (res) => {
let item = res.playBackList.find((item: any) => item.deviceId === setting.playBackDeviceId);
if (item) {
await monitorAgora.setPlaybackDevice(setting.playBackDeviceId)
} else {
await monitorAgora.setPlaybackDevice(res.playBackItem.deviceId)
setting.playBackDeviceId = res.playBackItem.deviceId
}
})
} else {
let deviceId = await rtcEngine.getAudioDeviceManager().getPlaybackDefaultDevice().deviceId;
await monitorAgora.setPlaybackDevice(deviceId)
setting.playBackDeviceId = deviceId
}
// 音频设备
if (setting.ecordingDeviceId) {
await monitorAgora.getAudioMediaList().then(async (res) => {
let item = res.ecordingList.find((item: any) => item.deviceId === setting.ecordingDeviceId);
if (item) {
await monitorAgora.setRecordingDevice(setting.ecordingDeviceId)
} else {
await monitorAgora.setRecordingDevice(res.ecordingItem.deviceId)
setting.ecordingDeviceId = res.ecordingItem.deviceId
}
})
} else {
let deviceId = await rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice().deviceId;
await monitorAgora.setRecordingDevice(deviceId)
setting.ecordingDeviceId = deviceId
}
setTimeout(async () => {
storage.setItem('setting', JSON.stringify(setting))
if (bool) {
const setting = await JSON.parse(storage.getItem('setting') as string)
if (setting.videoDeviceId) monitorAgora.setVideoDeviceManager(setting.videoDeviceId) //指定摄像头头采集设备
if (setting.playBackDeviceId) monitorAgora.setPlaybackDevice(setting.playBackDeviceId) //指定播放设备
if (setting.playBackVolume) monitorAgora.setPlaybackDeviceVolume(setting.playBackVolume) // 设置播放设备音量
if (setting.ecordingDeviceId) monitorAgora.setRecordingDevice(setting.ecordingDeviceId) // 设置音频采集设备
if (setting.ecordingVolume) monitorAgora.setRecordingDeviceVolume(setting.ecordingVolume) // 设置音频设备音量
}
}, 1000);
},
// 事件回调
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline }: any) => {
rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件
onJoinChannelSuccess: async (info: any, elapsed: any) => {
await onJoinChannelSuccess(info, elapsed)
},
// 监听远端用户加入频道事件
onUserJoined: async (info: any, remoteUid: any, elapsed: any) => {
await onUserJoined(info, remoteUid, elapsed)
},
// 监听用户离开频道事件
onUserOffline: async (info: any, remoteUid: any, reason: any) => {
await onUserOffline(info, remoteUid, reason)
},
});
},
// 加入频道
setJoinChannel: async (data: any) => {
option.token = data.token;
option.channelId = data.channelId;
option.uid = Number(data.uid);
await monitorAgora.JoinChannelEx()
},
// 所有用户加入的第二个房间
JoinChannelEx: async () => {
await rtcEngine.startCameraCapture(VideoSourceType.VideoSourceCamera, {
format: {
width: 640,
height: 360,
fps: 15,
}
})
await rtcEngine.joinChannelEx(
option.token,
{ channelId: option.channelId, localUid: Number(option.uid) },
{
autoSubscribeAudio: false,//设置是否自动订阅所有音频流
autoSubscribeVideo: false,//设置是否自动订阅所有视频流
publishMicrophoneTrack: false,//设置是否发布麦克风采集到的音频
publishCameraTrack: true,//设置是否发布摄像头采集的视频
clientRoleType: ClientRoleType.ClientRoleBroadcaster,//用户角色 ClientRoleBroadcaster 主播 ClientRoleAudience 观众
publishScreenTrack: false,//设置是否发布屏幕采集的视频
}
);
},
// 本地加入
setupLocalVideo: async (item: any) => {
if (item.view?.childNodes.length === 0) {
await rtcEngine.setupLocalVideo({
renderMode: RenderModeType.RenderModeHidden,
sourceType: item.sourceType,
uid: item.uid,
view: item.view,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
});
}
},
// 远端加入
setupRemoteVideoJoin: async (item: any) => {
if (item.view?.childNodes.length === 0) {
await rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeHidden,
sourceType: VideoSourceType.VideoSourceRemote,
uid: item.uid,
view: item.view,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId: item.channelId },
);
}
},
// 退出
setupRemoteVideo: async (item: any) => {
await rtcEngine.setupRemoteVideo(
{
renderMode: RenderModeType.RenderModeHidden,
sourceType: VideoSourceType.VideoSourceRemote,
uid: item.uid,
view: item.view,
setupMode: VideoViewSetupMode.VideoViewSetupRemove,
},
);
},
// 销毁
release: async () => {
await rtcEngine.release()
},
// 获取系统中所有的视频设备列表。
getVideoDeviceManager: async (): Promise<any> => {
return {
list: rtcEngine.getVideoDeviceManager().enumerateVideoDevices(),
item: rtcEngine.getVideoDeviceManager().getDevice()
}
},
// 通过设备 ID 指定视频采集设备。
setVideoDeviceManager: async (deviceIdUTF8: string) => {
await rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8)
},
// 获取输入输出设备列表
getAudioMediaList: async () => {
return {
playBackList: rtcEngine.getAudioDeviceManager().enumeratePlaybackDevices(),
ecordingList: rtcEngine.getAudioDeviceManager().enumerateRecordingDevices(),
playBackItem: rtcEngine.getAudioDeviceManager().getPlaybackDefaultDevice(),
ecordingItem: rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice(),
ecordingVolume: rtcEngine.getAudioDeviceManager().getRecordingDeviceVolume(),
}
},
// 指定播放设备
setPlaybackDevice: async (deviceId: string) => {
await rtcEngine.getAudioDeviceManager().setPlaybackDevice(deviceId)
},
// 设置音频采集设备
setRecordingDevice: async (deviceId: string) => {
await rtcEngine.getAudioDeviceManager().setRecordingDevice(deviceId)
},
// 设置播放设备音量
setPlaybackDeviceVolume: async (volume: number) => {
await rtcEngine.getAudioDeviceManager().setPlaybackDeviceVolume(volume)
},
// 设置音频设备音量
setRecordingDeviceVolume: async (volume: number) => {
await rtcEngine.getAudioDeviceManager().setRecordingDeviceVolume(volume)
},
}
export default monitorAgora;

View File

@ -153,6 +153,13 @@ export const onSignalr = (callBack: Function) => {
uname
})
});
// 管理员查看随机用户
connection.on("Watch", (watchUids: string[]) => {
callBack({
key: 'Watch',
watchUids
})
});
}
}
export const offSignalr = () => {
@ -170,6 +177,7 @@ export const offSignalr = () => {
connection.off('OperCamera');
connection.off('ManagerRefresh');
connection.off('ApplyToSpeak');
connection.off('Watch');
}
}
export const onInvoke = async (str: string, data: any) => {

View File

@ -0,0 +1,7 @@
import { configureStore } from '@reduxjs/toolkit';
import agoraReducer from '@/utils/package/features/agoraReducer'
export default configureStore({
reducer: {
agora: agoraReducer,
}
});