Compare commits

..

16 Commits

Author SHA1 Message Date
yangqiang e820c3e53c Merge pull request 'yangjie' (#46) from yangjie into master
Reviewed-on: #46
2025-01-03 10:43:26 +08:00
yj 92cfef8f91 添加退出房间 2025-01-03 09:16:11 +08:00
yj 1c6f551a3b 修改会议监控关闭时摄像头卡住 2025-01-03 09:12:51 +08:00
yj d0ca3c9321 优化缩略图大小 2025-01-02 16:29:02 +08:00
yj 5c5780d150 优化 2025-01-02 15:46:56 +08:00
yj d51d1f9844 优化 2025-01-02 13:45:29 +08:00
yj 488724aa62 优化 2025-01-02 11:42:29 +08:00
yj d2d20d3efd 去除多余代码 2024-12-30 13:34:19 +08:00
yj 3723873d71 优化频繁切换用户身份系统卡顿 2024-12-27 16:49:44 +08:00
yj c07de8d2a1 停止采集摄像头参数修改 2024-12-27 15:25:59 +08:00
yj 3bc5994506 优化进入房间快速退出提示无效房间号 2024-12-26 15:15:36 +08:00
yj 1ad41eaf26 修改录制文件比特率 2024-12-20 10:03:12 +08:00
yj 2284b01d89 优化 2024-12-19 16:13:53 +08:00
yj c9ca37d323 优化 2024-12-19 15:43:08 +08:00
yj e26d8f0c3f 优化提升 2024-12-19 15:33:24 +08:00
yj 5d6c43e27a 优化 2024-12-18 15:21:48 +08:00
5 changed files with 144 additions and 76 deletions

18
main.js
View File

@ -402,6 +402,9 @@ app.on('ready', () => {
case 'show': case 'show':
mainWindow.show() mainWindow.show()
mainWindow.focus(); mainWindow.focus();
mainWindow.setSkipTaskbar(false)
mainWindow.setResizable(true)
mainWindow.setAlwaysOnTop(false)
break; break;
} }
}); });
@ -485,6 +488,11 @@ app.on('ready', () => {
}); });
// 设置桌面应用基础属性 // 设置桌面应用基础属性
ipcMain.handle('setMainWindowSize', (event, config) => { ipcMain.handle('setMainWindowSize', (event, config) => {
if (config.width === 250) {
mainWindow.setSkipTaskbar(true)
mainWindow.setResizable(false)
mainWindow.setAlwaysOnTop(true, 'screen-saver')
}
// 设置最小窗口尺寸 // 设置最小窗口尺寸
mainWindow.setMinimumSize(config.width, config.height); mainWindow.setMinimumSize(config.width, config.height);
// 设置最大尺寸 // 设置最大尺寸
@ -572,9 +580,6 @@ app.on('ready', () => {
childWindow[k] = "" childWindow[k] = ""
} }
} }
mainWindow.setSkipTaskbar(false)
mainWindow.setResizable(true)
mainWindow.setAlwaysOnTop(false)
} else { } else {
childWindow[key].close() childWindow[key].close()
childWindow[key] = "" childWindow[key] = ""
@ -609,6 +614,9 @@ app.on('ready', () => {
childWindow[config.key].hide() childWindow[config.key].hide()
} }
} }
} else {
if (config.bool) {
childWindow[config.key].hide()
} else { } else {
if (childWindow[config.key].isVisible()) { if (childWindow[config.key].isVisible()) {
childWindow[config.key].hide() childWindow[config.key].hide()
@ -616,6 +624,7 @@ app.on('ready', () => {
childWindow[config.key].show() childWindow[config.key].show()
} }
} }
}
}); });
// 定位主窗口 // 定位主窗口
ipcMain.handle('setPosition', (event, data) => { ipcMain.handle('setPosition', (event, data) => {
@ -715,9 +724,6 @@ function windowOperation(config) {
case 'shareScreenWindow': case 'shareScreenWindow':
x = Math.round((display.workArea.width - child.getSize()[0]) / 2); x = Math.round((display.workArea.width - child.getSize()[0]) / 2);
child.setPosition(x, 0); child.setPosition(x, 0);
mainWindow.setSkipTaskbar(true)
mainWindow.setResizable(false)
mainWindow.setAlwaysOnTop(true, 'screen-saver')
break; break;
case 'chatSmallWindow': case 'chatSmallWindow':
y = height - child.getSize()[1]; y = height - child.getSize()[1];

View File

@ -116,8 +116,59 @@ window.electron = {
ipcRenderer.invoke('setRegistry', uuid) ipcRenderer.invoke('setRegistry', uuid)
}, },
// 创建子窗口 // 创建子窗口
createChildWindow: (config) => { createChildWindow: (str) => {
ipcRenderer.invoke('createChildWindow', config) switch (str) {
case 'show':
ipcRenderer.invoke('setChildWindowShow', {
key: 'shareScreenWindow',
})
ipcRenderer.invoke('setChildWindowShow', {
key: 'chatSmallWindow',
})
break;
case 'hide':
ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/noticeWindow`,
width: 388,
height: 150,
key: 'noticeWindow',
})
ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/shareScreenWindow`,
width: 400,
height: 80,
key: 'shareScreenWindow',
})
ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/chatSmallWindow`,
width: 200,
height: 150,
key: 'chatSmallWindow',
})
ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/chatBigWindow`,
width: 540,
height: 640,
key: 'chatBigWindow',
})
ipcRenderer.invoke('createChildWindow', {
url: location.origin + `/#/userListWindow`,
width: 440,
height: 540,
key: 'userListWindow',
})
break;
case 'stop':
ipcRenderer.invoke('setChildWindowShow', {
key: 'shareScreenWindow',
bool: true
})
ipcRenderer.invoke('setChildWindowShow', {
key: 'chatSmallWindow',
bool: true
})
break;
}
}, },
// 关闭子窗口 // 关闭子窗口
closeChildWindow: (key) => { closeChildWindow: (key) => {

View File

@ -171,6 +171,7 @@ const Meeting: React.FC = () => {
}) })
const [networkOther, setNetworkOther] = useState<RtcStats>({}) const [networkOther, setNetworkOther] = useState<RtcStats>({})
const [isComputerAudio, setIsComputerAudio] = useState(false) const [isComputerAudio, setIsComputerAudio] = useState(false)
const [_isLeave, setIsLeave] = useState(false)
const [isScreenCapture, setIsScreenCapture] = useState(false) const [isScreenCapture, setIsScreenCapture] = useState(false)
const [isFluencyPriority, setIsFluencyPriority] = useState(false) const [isFluencyPriority, setIsFluencyPriority] = useState(false)
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
@ -208,6 +209,7 @@ const Meeting: React.FC = () => {
const channel = new BroadcastChannel('meeting_channel'); const channel = new BroadcastChannel('meeting_channel');
let storeDevice: any; let storeDevice: any;
useEffect(() => { useEffect(() => {
window.electron.createChildWindow('hide')
let time: NodeJS.Timeout; let time: NodeJS.Timeout;
setUser(userInfo) setUser(userInfo)
window.electron.getIsMaximized().then((res: boolean) => { window.electron.getIsMaximized().then((res: boolean) => {
@ -484,6 +486,7 @@ const Meeting: React.FC = () => {
window.removeEventListener('wheel', handleWheelChange); window.removeEventListener('wheel', handleWheelChange);
clearInterval(time) clearInterval(time)
channel.close(); channel.close();
window.electron.closeChildWindow('shareScreenWindow')
}; };
}, []); }, []);
@ -871,7 +874,22 @@ const Meeting: React.FC = () => {
useEffect(() => { useEffect(() => {
if (recorder) { if (recorder) {
recorder.start(); recorder.start()
recorder.onstart = async () => {
message.success('开始录制')
}
recorder.onerror = async () => {
setRecorder('')
setMediaStream([])
setIsClickedMediaSteam(false)
changeStatusList({
title: '录制中'
}, 1, 3)
message.error('录制失败,请重新录制!')
}
recorder.onstop = async () => {
}
recorder.ondataavailable = async (event: any) => { recorder.ondataavailable = async (event: any) => {
const blob = await fixWebmDuration(event.data); const blob = await fixWebmDuration(event.data);
const reader = new FileReader() as any; const reader = new FileReader() as any;
@ -900,6 +918,16 @@ const Meeting: React.FC = () => {
onCancel() { onCancel() {
} }
}) })
setIsLeave(bool => {
if (bool) {
if (userInfo.isAnonymous) {
storage.setItem('userLogin', false)
} else {
navigate('/home/index')
}
}
return false
})
}; };
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
} }
@ -1001,8 +1029,8 @@ const Meeting: React.FC = () => {
channelId: connection.channelId, channelId: connection.channelId,
sourceType: VideoSourceType.VideoSourceCameraPrimary, sourceType: VideoSourceType.VideoSourceCameraPrimary,
}) })
}, 1000);
getShowUser(); getShowUser();
}, 1500);
} }
} }
}, },
@ -1017,7 +1045,7 @@ const Meeting: React.FC = () => {
view: document.getElementById(`video-${remoteUid}`), view: document.getElementById(`video-${remoteUid}`),
channelId: connection.channelId, channelId: connection.channelId,
}) })
}, 1500); }, 1000);
} }
} }
}, },
@ -1200,22 +1228,18 @@ const Meeting: React.FC = () => {
// 刷新 // 刷新
const refreshVideoView = async (userItem: any): Promise<void> => { const refreshVideoView = async (userItem: any): Promise<void> => {
if (userItem.uid === userInfo.uid) { if (userItem.uid === userInfo.uid) {
setTimeout(async () => {
await agora.setupLocalVideo({ await agora.setupLocalVideo({
uid: Number(userItem.uid), uid: Number(userItem.uid),
view: document.getElementById(`video-${userItem.uid}`), view: document.getElementById(`video-${userItem.uid}`),
channelId: state.channelId, channelId: state.channelId,
sourceType: VideoSourceType.VideoSourceCameraPrimary, sourceType: VideoSourceType.VideoSourceCameraPrimary,
}) })
}, 1500);
} else { } else {
setTimeout(async () => {
await agora.setupRemoteVideoJoin({ await agora.setupRemoteVideoJoin({
uid: Number(userItem.uid), uid: Number(userItem.uid),
view: document.getElementById(`video-${userItem.uid}`), view: document.getElementById(`video-${userItem.uid}`),
channelId: state.channelId, channelId: state.channelId,
}) })
}, 1500);
} }
} }
// 替换数据 // 替换数据
@ -1578,7 +1602,7 @@ const Meeting: React.FC = () => {
// 开始录制 // 开始录制
const mediaRecorder = new MediaRecorder(combinedSource, { const mediaRecorder = new MediaRecorder(combinedSource, {
mimeType: 'video/webm;codecs=vp9,opus', mimeType: 'video/webm;codecs=vp9,opus',
videoBitsPerSecond: 1.5e6, videoBitsPerSecond: 1500,
}); });
setRecorder(mediaRecorder); setRecorder(mediaRecorder);
}); });
@ -1642,7 +1666,10 @@ const Meeting: React.FC = () => {
const stopRecorderMedia = async (): Promise<void> => { const stopRecorderMedia = async (): Promise<void> => {
setRecorder((res: any) => { setRecorder((res: any) => {
if (res) { if (res) {
res.stop(); try {
res.stop()
} catch (error) {
}
} }
return res return res
}) })
@ -1650,7 +1677,10 @@ const Meeting: React.FC = () => {
if (res.length) { if (res.length) {
res.forEach((item: any) => { res.forEach((item: any) => {
item.getTracks().forEach((track: any) => { item.getTracks().forEach((track: any) => {
try {
track.stop() track.stop()
} catch (error) {
}
}); });
}); });
} }
@ -1659,19 +1689,25 @@ const Meeting: React.FC = () => {
} }
// 退出房间 // 退出房间
const leaveChannel = async (bool: boolean = true): Promise<void> => { const leaveChannel = async (bool: boolean = true): Promise<void> => {
setIsLeave(true)
await stopScreenCapture() await stopScreenCapture()
await stopRecorderMedia() await stopRecorderMedia()
if (bool) { if (bool) {
await getLeave() await getLeave()
} }
await agora.leaveChannel() await agora.leaveChannel()
setTimeout(() => { setRecorder((res: any) => {
if (res) {
} else {
if (userInfo.isAnonymous) { if (userInfo.isAnonymous) {
storage.setItem('userLogin', false) storage.setItem('userLogin', false)
} else { } else {
navigate('/home/index') navigate('/home/index')
} }
}, 1000) }
return res
})
} }
// 分享屏幕 // 分享屏幕
const clickSharedScreen = async (): Promise<void> => { const clickSharedScreen = async (): Promise<void> => {
@ -1694,39 +1730,7 @@ const Meeting: React.FC = () => {
const isOpen = await getKeyOpenChildWindow('shareScreenWindow') const isOpen = await getKeyOpenChildWindow('shareScreenWindow')
setIsScreenCapture(true) setIsScreenCapture(true)
if (!isOpen) { if (!isOpen) {
window.electron.createChildWindow({ window.electron.createChildWindow('show')
url: location.origin + `/#/noticeWindow`,
width: 388,
height: 150,
key: 'noticeWindow',
show: true,
})
window.electron.createChildWindow({
url: location.origin + `/#/shareScreenWindow`,
width: 400,
height: 80,
key: 'shareScreenWindow',
show: true,
})
window.electron.createChildWindow({
url: location.origin + `/#/chatSmallWindow`,
width: 200,
height: 150,
key: 'chatSmallWindow',
show: true,
})
window.electron.createChildWindow({
url: location.origin + `/#/chatBigWindow`,
width: 540,
height: 640,
key: 'chatBigWindow',
})
window.electron.createChildWindow({
url: location.origin + `/#/userListWindow`,
width: 440,
height: 540,
key: 'userListWindow',
})
setKeyOpenChildWindow('shareScreenWindow', true) setKeyOpenChildWindow('shareScreenWindow', true)
window.electron.setMainWindowSize({ window.electron.setMainWindowSize({
width: 250, width: 250,
@ -1749,7 +1753,7 @@ const Meeting: React.FC = () => {
} }
// 获取桌面可共享屏幕的引用 // 获取桌面可共享屏幕的引用
const getDesktopCapturerVideo = (): void => { const getDesktopCapturerVideo = (): void => {
agora.getDesktopCapturerVideo({ width: 300, height: 300 }, { width: 300, height: 300 }, true).then((res: any) => { agora.getDesktopCapturerVideo({ width: 50, height: 50 }, { width: 30, height: 30 }, true).then((res: any) => {
res.forEach((item: any, index: number) => { res.forEach((item: any, index: number) => {
if (item.type === 1) { if (item.type === 1) {
item.sourceTitle = '桌面' + (index + 1) item.sourceTitle = '桌面' + (index + 1)
@ -1785,9 +1789,10 @@ const Meeting: React.FC = () => {
const footerListTemplate = [...footerList] const footerListTemplate = [...footerList]
await agora.leaveChannelEx(userInfo.screenShareId) await agora.leaveChannelEx(userInfo.screenShareId)
agora.stopScreenCapture() agora.stopScreenCapture()
await agora.destroyRendererByView()
footerListTemplate[1][0].title = '共享屏幕' footerListTemplate[1][0].title = '共享屏幕'
setFooterList(footerListTemplate) setFooterList(footerListTemplate)
window.electron.closeChildWindow('shareScreenWindow') window.electron.createChildWindow('stop')
setKeyOpenChildWindow('shareScreenWindow', false) setKeyOpenChildWindow('shareScreenWindow', false)
setIsScreenCapture(bool => { setIsScreenCapture(bool => {
if (bool) { if (bool) {
@ -2750,7 +2755,8 @@ const Meeting: React.FC = () => {
<div className={styles.meetingUserVideoList} style={{ width: statusList.userVideo ? '500px' : '500px' }}> <div className={styles.meetingUserVideoList} style={{ width: statusList.userVideo ? '500px' : '500px' }}>
<div className={styles.meetingUserVideoListTitle}> <div className={styles.meetingUserVideoListTitle}>
<span></span> <span></span>
<img src={ImageUrl.icon18} alt="" onClick={() => { <img src={ImageUrl.icon18} alt="" onClick={async () => {
await agora.allLeaveChannelEx()
setStatusList({ setStatusList({
userList: false, userList: false,
userChatList: false, userChatList: false,

2
src/render.d.ts vendored
View File

@ -29,7 +29,7 @@ export interface IElectronAPI {
isVisible: () => Promise<string>; isVisible: () => Promise<string>;
setRegistry: (uuid: string) => any; setRegistry: (uuid: string) => any;
getRegistry: () => any; getRegistry: () => any;
createChildWindow: (config: any) => void; createChildWindow: (str: string) => void;
setChildWindow: (config: any) => void; setChildWindow: (config: any) => void;
setChildWindowShow: (config: any) => void; setChildWindowShow: (config: any) => void;
closeChildWindow: (key: string) => void; closeChildWindow: (key: string) => void;

View File

@ -366,7 +366,6 @@ export const agora = {
}, },
// 退出第二个房间 // 退出第二个房间
allLeaveChannelEx: async () => { allLeaveChannelEx: async () => {
await agora.stopCameraCapture();
await rtcEngine.leaveChannelEx({ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) }) await rtcEngine.leaveChannelEx({ channelId: option.channelId + 'a', localUid: Number('1' + option.screenShareId) })
}, },
// 停止/恢复接收指定的视频流。 // 停止/恢复接收指定的视频流。
@ -385,6 +384,12 @@ export const agora = {
destroyRendererByConfig: async (uid: number, channelId?: string) => { destroyRendererByConfig: async (uid: number, channelId?: string) => {
await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid); await rtcEngine.destroyRendererByConfig(VideoSourceType.VideoSourceRemote, channelId, uid);
}, },
destroyRendererByView: async () => {
let dom = document.getElementById(`meetingAbsoluteVideo`);
if (dom) {
await rtcEngine.destroyRendererByView(dom);
}
},
// ai降噪 // ai降噪
setAINSMode: async (enabled: boolean, mode: AudioAinsMode) => { setAINSMode: async (enabled: boolean, mode: AudioAinsMode) => {
rtcEngine.setAINSMode(enabled, mode) rtcEngine.setAINSMode(enabled, mode)
@ -436,7 +441,7 @@ export const agora = {
}, },
// 停止采集摄像头 // 停止采集摄像头
stopCameraCapture: async () => { stopCameraCapture: async () => {
await rtcEngine.stopCameraCapture() await rtcEngine.stopCameraCapture(VideoSourceType.VideoSourceCamera)
}, },
// 加入频道 // 加入频道
setJoinChannel: async (data: any) => { setJoinChannel: async (data: any) => {