diff --git a/main.js b/main.js index af61aba..bfb93c9 100644 --- a/main.js +++ b/main.js @@ -170,6 +170,10 @@ app.on('ready', () => { ipcMain.handle('getIsMaximized', () => { return mainWindow.isMaximized(); }); + // 获取app路径 + ipcMain.handle('getAppPath', () => { + return app.getAppPath(); + }); // 获取版本号 ipcMain.handle('getVersion', () => { return app.getVersion(); diff --git a/preload.js b/preload.js index ba34b71..3ebbaa3 100644 --- a/preload.js +++ b/preload.js @@ -17,6 +17,10 @@ window.electron = { getIsMaximized: () => { return ipcRenderer.invoke('getIsMaximized') }, + // 获取app路径 + getAppPath: () => { + return ipcRenderer.invoke('getAppPath') + }, // 获取版本号 getVersion: () => { return ipcRenderer.invoke('getVersion') diff --git a/src/App.tsx b/src/App.tsx index 82e43fc..de62109 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -143,6 +143,29 @@ const App: React.FC = () => { isAINoiseReduction: true, //是否开启ai降噪 aINoiseReduction: 1, // 降噪模式 isRecordingTips: true, //是否开启录制提示 + beautyEffect: { //美颜效果 + isBeautyEffect: false, //是否打开美颜 + lighteningContrastLevel: 1, //对比度 + lighteningLevel: 0, //美白程度 + smoothnessLevel: 0, //磨皮程度 + rednessLevel: 0, //红润度 + sharpnessLevel: 0, //锐化程度 + }, + colorEnhancement: { //色彩增强 + isColorEnhancement: false, //是否打开色彩增强 + strengthLevel: 0.5, //色彩增强程度 + skinProtectLevel: 1, //肤色保护程度 + }, + darkLightEnhancement: { //暗光增强 + isDarkLightEnhancement: false, //是否打开暗光增强 + level: 0, //暗光增强等级 + mode: 0, //暗光增强模式 + }, + virtualBackground: { //虚拟背景 + isVirtualBackground: false, //是否打开虚拟背景 + color: '0xFFFFFF', // 纯色 + sourceIndex: '', // 背景图下标 + }, })) } if (!storage.getItem('openChildWindow')) { diff --git a/src/assets/virtualBackground/1.png b/src/assets/virtualBackground/1.png new file mode 100644 index 0000000..a29260e Binary files /dev/null and b/src/assets/virtualBackground/1.png differ diff --git a/src/assets/virtualBackground/2.png b/src/assets/virtualBackground/2.png new file mode 100644 index 0000000..8a025ca Binary files /dev/null and b/src/assets/virtualBackground/2.png differ diff --git a/src/assets/virtualBackground/3.png b/src/assets/virtualBackground/3.png new file mode 100644 index 0000000..4211d42 Binary files /dev/null and b/src/assets/virtualBackground/3.png differ diff --git a/src/assets/virtualBackground/4.png b/src/assets/virtualBackground/4.png new file mode 100644 index 0000000..692d2fd Binary files /dev/null and b/src/assets/virtualBackground/4.png differ diff --git a/src/assets/virtualBackground/5.png b/src/assets/virtualBackground/5.png new file mode 100644 index 0000000..ec14085 Binary files /dev/null and b/src/assets/virtualBackground/5.png differ diff --git a/src/assets/virtualBackground/6.png b/src/assets/virtualBackground/6.png new file mode 100644 index 0000000..c9dffc7 Binary files /dev/null and b/src/assets/virtualBackground/6.png differ diff --git a/src/components/StupWizard/index.module.scss b/src/components/StupWizard/index.module.scss index 5a760a6..34f82ff 100644 --- a/src/components/StupWizard/index.module.scss +++ b/src/components/StupWizard/index.module.scss @@ -1,6 +1,6 @@ // 设置向导 .stupWizard { - height: 70vh; + height: 90vh; display: flex; .stupWizardLeft { @@ -68,6 +68,10 @@ } .videoComponents { + >div { + margin-bottom: 10px; + } + >div:nth-child(1) { width: 100%; height: 296px; @@ -88,7 +92,6 @@ >div:nth-child(2) { width: 100%; - margin-top: 10px; display: flex; align-items: center; @@ -96,6 +99,80 @@ color: white; } } + + >div:nth-child(3) { + >span { + color: white; + } + + >div { + display: flex; + flex-wrap: wrap; + + >div { + border: 1px transparent solid; + width: calc(100% /6); + padding: 5px; + box-sizing: border-box; + cursor: pointer; + + >div { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + + >label { + color: white; + background-color: rgba(0, 0, 0, 0.5); + } + + >input { + position: absolute; + visibility: hidden; + } + } + + >img { + width: 100%; + } + } + + .active { + border: 1px white solid; + } + } + } + + >div:nth-child(4) { + >div { + margin-bottom: 20px; + + >span { + color: white; + } + + >div { + margin-left: 40px; + flex-grow: 1; + + >div { + display: flex; + align-items: center; + + >span { + color: white; + white-space: nowrap; + } + + >div { + flex-grow: 1; + margin-left: 10px; + } + } + } + } + } } .audioComponents { diff --git a/src/components/StupWizard/index.tsx b/src/components/StupWizard/index.tsx index c6ff0ed..c476ef0 100644 --- a/src/components/StupWizard/index.tsx +++ b/src/components/StupWizard/index.tsx @@ -9,7 +9,7 @@ import path from 'path'; const fs = require('fs').promises; const { exec } = require('child_process'); -const StupWizard = forwardRef((props: any, ref: any) => { +const StupWizard = forwardRef((_props: any, ref: any) => { useImperativeHandle(ref, () => ({ changeModal: (index: number = 0) => { if (location.hash.indexOf('/meeting') === -1) { @@ -147,10 +147,107 @@ const VideoComponents = () => { list: [], item: null, }); + const [virtualBackgroundImg] = useState([ + ImageUrl.virtualBackground1, + ImageUrl.virtualBackground2, + ImageUrl.virtualBackground3, + ImageUrl.virtualBackground4, + ImageUrl.virtualBackground5, + ImageUrl.virtualBackground6, + ]); + const [beautyEffect, setBeautyEffect] = useState({ + isBeautyEffect: false, //是否打开美颜 + lighteningContrastLevel: 1, //对比度 + lighteningLevel: 0, //美白程度 + smoothnessLevel: 0, //磨皮程度 + rednessLevel: 0, //红润度 + sharpnessLevel: 0, //锐化程度 + }); + const [colorEnhancement, setColorEnhancement] = useState({ + isColorEnhancement: false, //是否打开色彩增强 + strengthLevel: 0.5, //色彩增强程度 + skinProtectLevel: 1, //肤色保护程度 + }); + const [darkLightEnhancement, setDarkLightEnhancement] = useState({ + isDarkLightEnhancement: false, //是否打开暗光增强 + level: 0, //暗光增强等级 + mode: 0, //暗光增强模式 + }); + const [virtualBackground, setVirtualBackground] = useState({ + isVirtualBackground: false, //是否打开虚拟背景 + color: '0xFFFFFF', // 纯色 + sourceIndex: '', // 背景图下标 + }); const setting = JSON.parse(storage.getItem('setting') as string) useEffect(() => { + if (setting.beautyEffect) { + setBeautyEffect(setting.beautyEffect) + } else { + setting.beautyEffect = { + isBeautyEffect: false, //是否打开美颜 + lighteningContrastLevel: 1, //对比度 + lighteningLevel: 0, //美白程度 + smoothnessLevel: 0, //磨皮程度 + rednessLevel: 0, //红润度 + sharpnessLevel: 0, //锐化程度 + } + } + if (setting.colorEnhancement) { + setColorEnhancement(setting.colorEnhancement) + } else { + setting.colorEnhancement = { + isColorEnhancement: false, //是否打开色彩增强 + strengthLevel: 0.5, //色彩增强程度 + skinProtectLevel: 1, //肤色保护程度 + } + } + if (setting.darkLightEnhancement) { + setDarkLightEnhancement(setting.darkLightEnhancement) + } else { + setting.darkLightEnhancement = { + isDarkLightEnhancement: false, //是否打开暗光增强 + level: 0, //暗光增强等级 + mode: 0, //暗光增强模式 + } + } + if (setting.virtualBackground) { + setVirtualBackground(setting.virtualBackground) + } else { + setting.darkLightEnhancement = { + isVirtualBackground: false, //是否打开虚拟背景 + color: '0xFFFFFF', // 纯色 + sourceIndex: '', // 背景图下标 + } + } + storage.setItem('setting', JSON.stringify(setting)) getVideoDeviceList() }, []); + useEffect(() => { + agora.setBeautyEffectOptions(beautyEffect.isBeautyEffect, beautyEffect) + }, [beautyEffect]); + useEffect(() => { + agora.setColorEnhanceOptions(colorEnhancement.isColorEnhancement, colorEnhancement) + }, [colorEnhancement]); + useEffect(() => { + agora.setLowlightEnhanceOptions(darkLightEnhancement.isDarkLightEnhancement, darkLightEnhancement) + }, [darkLightEnhancement]); + useEffect(() => { + if (typeof virtualBackground.sourceIndex === 'number') { + window.electron.getAppPath().then((res: string) => { + const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${virtualBackground.sourceIndex + 1}.png`); + agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { + source: imagePath, + background_source_type: 2, + color: Number(virtualBackground.color), + }) + }) + } else { + agora.enableVirtualBackground(virtualBackground.isVirtualBackground, { + background_source_type: 1, + color: Number(virtualBackground.color), + }) + } + }, [virtualBackground]); const getVideoDeviceList = async (): Promise => { const userInfo = JSON.parse(storage.getItem('user') as string) agora.getVideoDeviceManager().then(async (res) => { @@ -227,6 +324,242 @@ const VideoComponents = () => { }} /> +
+ + { + setting.virtualBackground.isVirtualBackground = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + isVirtualBackground: e.target.checked + }) + }}> + + 虚拟背景 + +
+
+
{ + setting.virtualBackground.sourceIndex = ''; + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + sourceIndex: '' + }) + }}> + { + let hexInt = parseInt('0x' + e.target.value.split('#')[1], 16) + setting.virtualBackground.color = hexInt + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + color: hexInt + }) + }} /> + +
+
+ { + virtualBackgroundImg.map((item, index) => { + return ( +
{ + setting.virtualBackground.sourceIndex = index; + storage.setItem('setting', JSON.stringify(setting)) + setVirtualBackground({ + ...virtualBackground, + sourceIndex: index + }) + }}> + +
+ ) + }) + } +
+
+
+
+ + { + setting.beautyEffect.isBeautyEffect = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + isBeautyEffect: e.target.checked + }) + }}> + + 美颜效果 + +
+
+ 对比度 +
+ { + setting.beautyEffect.lighteningContrastLevel = e.target.value; + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + lighteningContrastLevel: e.target.value + }) + }} style={{ flexShrink: 0, margin: '10px 0' }} value={beautyEffect.lighteningContrastLevel}> + 低对比度 + 正常对比度 + 高对比度 + +
+
+
+ 美白程度 +
+ { + setting.beautyEffect.lighteningLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + lighteningLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 磨皮程度 +
+ { + setting.beautyEffect.smoothnessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + smoothnessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 红润度 +
+ { + setting.beautyEffect.rednessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + rednessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+ 锐化程度 +
+ { + setting.beautyEffect.sharpnessLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setBeautyEffect({ + ...beautyEffect, + sharpnessLevel: e + }) + }} disabled={!beautyEffect.isBeautyEffect} /> +
+
+
+
+
+ + { + setting.colorEnhancement.isColorEnhancement = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + isColorEnhancement: e.target.checked + }) + }}> + + 色彩增强 + +
+
+ 色彩增强程度 +
+ { + setting.colorEnhancement.strengthLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + strengthLevel: e + }) + }} disabled={!colorEnhancement.isColorEnhancement} /> +
+
+
+ 肤色保护程度 +
+ { + setting.colorEnhancement.skinProtectLevel = e + storage.setItem('setting', JSON.stringify(setting)) + setColorEnhancement({ + ...colorEnhancement, + skinProtectLevel: e + }) + }} disabled={!colorEnhancement.isColorEnhancement} /> +
+
+
+
+
+ + { + setting.darkLightEnhancement.isDarkLightEnhancement = e.target.checked; + storage.setItem('setting', JSON.stringify(setting)) + setDarkLightEnhancement({ + ...darkLightEnhancement, + isDarkLightEnhancement: e.target.checked + }) + }}> + + 暗光增强 + +
+
+ 暗光增强等级 +
+ { + setting.darkLightEnhancement.level = e.target.value; + storage.setItem('setting', JSON.stringify(setting)) + setDarkLightEnhancement({ + ...darkLightEnhancement, + level: e.target.value + }) + }} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.level}> + (默认)优先画质的暗光增强,会处理视频图像的亮度、细节、噪声,消耗的性能适中,处理速度适中,综合画质最优。 + 优先性能的暗光增强,会处理视频图像的亮度、细节,消耗的性能较少,处理速度较快。 + +
+
+
+ 暗光增强模式 +
+ { + setting.darkLightEnhancement.mode = e.target.value; + storage.setItem('setting', JSON.stringify(setting)) + setDarkLightEnhancement({ + ...darkLightEnhancement, + mode: e.target.value + }) + }} style={{ flexShrink: 0, margin: '10px 0' }} value={darkLightEnhancement.mode}> + (默认)自动模式。SDK 会根据环境光亮度自动开启或关闭暗光增强功能,以适时补光和防止过曝。 + 手动模式。用户需手动开启或关闭暗光增强功能。 + +
+
+
+
+
diff --git a/src/render.d.ts b/src/render.d.ts index 3be8a2c..b4982bc 100644 --- a/src/render.d.ts +++ b/src/render.d.ts @@ -4,6 +4,7 @@ export interface IElectronAPI { getWindowSize: () => any; setViewStatus: (status: 'quit' | 'maximize' | 'minimize' | 'unmaximize' | 'hide' | 'show') => void; getIsMaximized: () => Promise; + getAppPath: () => Promise; setWriteText: (text: string) => void; onQuit: (callBack: Function) => void; onUpdate: (callBack: Function) => void; diff --git a/src/utils/package/agora.ts b/src/utils/package/agora.ts index 4c45306..7ff26dd 100644 --- a/src/utils/package/agora.ts +++ b/src/utils/package/agora.ts @@ -17,11 +17,16 @@ import { ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamReason, - LocalVideoStreamState + LocalVideoStreamState, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } from "agora-electron-sdk"; import { GetRoomRtcToken, GetAgoraConf } from "@/api/Home/Index"; import { storage } from '@/utils'; import { role } from "@/config/role"; +import path from "path"; const option: any = { appId: '', token: '', @@ -110,6 +115,25 @@ export const agora = { if (settingData.ecordingDeviceId) agora.setRecordingDevice(settingData.ecordingDeviceId) // 设置音频采集设备 if (settingData.ecordingVolume) agora.setRecordingDeviceVolume(settingData.ecordingVolume) // 设置音频设备音量 if (settingData.isAINoiseReduction) agora.setAINSMode(settingData.isAINoiseReduction, settingData.aINoiseReduction) // 设置ai降噪 + agora.setBeautyEffectOptions(settingData.beautyEffect.isBeautyEffect, settingData.beautyEffect) + agora.setColorEnhanceOptions(settingData.colorEnhancement.isColorEnhancement, settingData.colorEnhancement) + agora.setLowlightEnhanceOptions(settingData.darkLightEnhancement.isDarkLightEnhancement, settingData.darkLightEnhancement) + console.log(settingData); + if (typeof settingData.virtualBackground.sourceIndex === 'number') { + window.electron.getAppPath().then((res: string) => { + const imagePath = path.join(res, 'src', 'assets', 'virtualBackground', `${settingData.virtualBackground.sourceIndex + 1}.png`); + agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, { + source: imagePath, + background_source_type: 2, + color: Number(settingData.virtualBackground.color), + }) + }) + } else { + agora.enableVirtualBackground(settingData.virtualBackground.isVirtualBackground, { + background_source_type: 1, + color: Number(settingData.virtualBackground.color), + }) + } }, 1000); }, // 事件回调 @@ -512,5 +536,22 @@ export const agora = { stopRecordingDeviceTest: async () => { await rtcEngine.getAudioDeviceManager().stopRecordingDeviceTest() }, - + // 设置美颜效果 + setBeautyEffectOptions: async (enabled: boolean, options: BeautyOptions) => { + await rtcEngine.setBeautyEffectOptions(enabled, options) + }, + // 设置色彩增强 + setColorEnhanceOptions: async (enabled: boolean, options: ColorEnhanceOptions) => { + await rtcEngine.setColorEnhanceOptions(enabled, options) + }, + // 设置暗光增强 + setLowlightEnhanceOptions: async (enabled: boolean, options: LowlightEnhanceOptions) => { + await rtcEngine.setLowlightEnhanceOptions(enabled, options) + }, + // 开启/关闭虚拟背景。 + enableVirtualBackground: async (enabled: boolean, backgroundSource: VirtualBackgroundSource) => { + await rtcEngine.enableVirtualBackground(enabled, backgroundSource, { + greenCapacity: 1 + }) + }, } \ No newline at end of file diff --git a/src/utils/package/imageUrl.ts b/src/utils/package/imageUrl.ts index e0c31e2..2722e37 100644 --- a/src/utils/package/imageUrl.ts +++ b/src/utils/package/imageUrl.ts @@ -73,6 +73,12 @@ import icon48Select from '@/assets/icon48-select.png' import icon49 from '@/assets/icon49.png' import icon50 from '@/assets/icon50.png' import icon51 from '@/assets/icon51.png' +import virtualBackground1 from '@/assets/virtualBackground/1.png' +import virtualBackground2 from '@/assets/virtualBackground/2.png' +import virtualBackground3 from '@/assets/virtualBackground/3.png' +import virtualBackground4 from '@/assets/virtualBackground/4.png' +import virtualBackground5 from '@/assets/virtualBackground/5.png' +import virtualBackground6 from '@/assets/virtualBackground/6.png' export default { loading, icon, @@ -149,4 +155,10 @@ export default { icon49, icon50, icon51, + virtualBackground1, + virtualBackground2, + virtualBackground3, + virtualBackground4, + virtualBackground5, + virtualBackground6, } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index f52346c..f19a94c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -72,7 +72,11 @@ export default defineConfig({ ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamState, - LocalVideoStreamReason + LocalVideoStreamReason, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } = require("agora-electron-sdk") export { createAgoraRtcEngine, @@ -93,7 +97,11 @@ export default defineConfig({ ConnectionStateType, ConnectionChangedReasonType, LocalVideoStreamState, - LocalVideoStreamReason + LocalVideoStreamReason, + BeautyOptions, + ColorEnhanceOptions, + LowlightEnhanceOptions, + VirtualBackgroundSource } `, })