This commit is contained in:
yj 2024-07-26 11:03:48 +08:00
parent 51582be3e0
commit a275885115
4 changed files with 375 additions and 139 deletions

View File

@ -76,7 +76,7 @@
border: 1px #292E33 solid; border: 1px #292E33 solid;
position: relative; position: relative;
>video { >div {
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 1; z-index: 1;
@ -96,4 +96,117 @@
color: white; color: white;
} }
} }
}
.audioComponents {
>div {
padding-bottom: 20px;
border-bottom: 1px solid #292E33;
margin-bottom: 20px;
&:last-child {
padding-bottom: 0px;
margin-bottom: 0px;
border: none;
}
}
.audioComponentsSelect {
display: flex;
align-items: center;
margin-bottom: 10px;
>span {
color: #878787;
flex-shrink: 0;
}
>div {
color: #5575F2;
flex-shrink: 0;
cursor: pointer;
}
}
.audioComponentsVolume {
display: flex;
align-items: center;
margin-bottom: 10px;
>img {
margin-right: 20px;
height: 26px;
}
>div {
height: 40px;
position: relative;
>img {
height: 100%;
}
>div {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0%;
overflow: hidden;
>img {
height: 100%;
}
}
}
}
.audioComponentsSlider {
display: flex;
align-items: center;
margin-bottom: 10px;
>span {
color: #878787;
}
}
}
.recordingComponents {
>span {
color: #bfbfbf;
font-size: 16px;
}
>div {
display: flex;
align-items: center;
margin-top: 10px;
>span {
color: #878787;
white-space: nowrap;
}
}
}
.fileComponents {
>span {
color: #bfbfbf;
font-size: 16px;
}
>div {
display: flex;
align-items: center;
margin-top: 10px;
>span {
color: #878787;
white-space: nowrap;
}
}
} }

View File

@ -1,17 +1,16 @@
import styles from '@/components/StupWizard/index.module.scss' import styles from '@/components/StupWizard/index.module.scss'
import ImageUrl from '@/utils/package/ImageUrl'; import ImageUrl from '@/utils/package/ImageUrl';
import { Button, Empty, message, Modal, Select, Slider } from 'antd'; import { Button, Checkbox, Empty, Input, message, Modal, Select, Slider } from 'antd';
import { useState, useImperativeHandle, forwardRef, useEffect } from "react"; import { useState, useImperativeHandle, forwardRef, useEffect } from "react";
import agora from '@/utils/package/agora' import agora from '@/utils/package/agora'
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons'; import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
import { storage } from '@/utils';
let userInfo = JSON.parse(storage.getItem('user') as string)
const StupWizard = forwardRef((props: any, ref: any) => { const StupWizard = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: () => { changeModal: () => {
agora.init() agora.init()
setIsStupWizard(true) setIsStupWizard(true)
// getAudioMediaList(true)
// agora.startPlaybackDeviceTest()
// agora.setPlaybackDeviceVolume(100)
} }
})) }))
const [list, setList] = useState([ const [list, setList] = useState([
@ -41,25 +40,6 @@ const StupWizard = forwardRef((props: any, ref: any) => {
} }
]) ])
const [isStupWizard, setIsStupWizard] = useState(false); const [isStupWizard, setIsStupWizard] = useState(false);
const [audioDeviceManager, setAudioDeviceManager] = useState<any>({
currentDevices: [],
currentDevice: {},
currentVolume: 0,
});
// agora.startRecordingDeviceTest(200)
const getAudioMediaList = (bool: boolean): void => {
const { currentDevices, currentVolume, currentDevice } = agora.getAudioMediaList(bool);
setAudioDeviceManager({
currentDevices: currentDevices.map((row: any) => {
return {
value: row.deviceId,
label: row.deviceName
}
}),
currentDevice: currentDevice.deviceId,
currentVolume,
})
}
return ( return (
<> <>
<Modal <Modal
@ -76,13 +56,12 @@ const StupWizard = forwardRef((props: any, ref: any) => {
{list.map((row: any, index: number) => { {list.map((row: any, index: number) => {
return ( return (
<div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => { <div key={index} className={`${row.active ? styles.active : ''}`} onClick={async () => {
const newList = [...list] const newList = [...list];
newList.forEach(item => item.active = false) newList.forEach(item => item.active = false);
newList[index].active = true; newList[index].active = true;
if (newList[index].title !== '视频') {
await agora.stopVideoDevice('videoPreview')
}
setList(newList) setList(newList)
agora.stopPlaybackDeviceTest()
agora.stopRecordingDeviceTest()
}}> }}>
<img src={row.active ? row.iconActive : row.icon} alt="" /> <img src={row.active ? row.iconActive : row.icon} alt="" />
<span>{row.title}</span> <span>{row.title}</span>
@ -99,7 +78,7 @@ const StupWizard = forwardRef((props: any, ref: any) => {
cursor: 'pointer' cursor: 'pointer'
}} }}
onClick={() => { onClick={() => {
agora.stopVideoDevice('videoPreview') agora.release()
setIsStupWizard(false) setIsStupWizard(false)
}} }}
/> />
@ -135,8 +114,7 @@ const VideoComponents = () => {
item: item ? item : null, item: item ? item : null,
}) })
if (item) { if (item) {
await agora.stopVideoDevice('videoPreview') agora.startPreview('videoPreview', Number(userInfo.account))
agora.startPreview('videoPreview', item)
} }
}) })
} }
@ -148,14 +126,15 @@ const VideoComponents = () => {
{ {
videoDeviceManager.item ? videoDeviceManager.item ?
<div> <div>
<video id='videoPreview'></video> <div id='videoPreview'>
<LoadingOutlined style={{ <LoadingOutlined style={{
position: 'absolute', position: 'absolute',
color: 'white', color: 'white',
right: '50%', right: '50%',
fontSize: '30px', fontSize: '30px',
top: '50%', top: '50%',
}} /> }} />
</div>
</div> : </div> :
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Empty description={'未检测到摄像头'} /></div> <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Empty description={'未检测到摄像头'} /></div>
} }
@ -170,7 +149,6 @@ const VideoComponents = () => {
item: e item: e
}) })
agora.setVideoDeviceManager(e) agora.setVideoDeviceManager(e)
agora.startPreview('videoPreview', e)
}} />; }} />;
</div> </div>
</div> </div>
@ -179,31 +157,214 @@ const VideoComponents = () => {
) )
} }
const AudioComponents = () => { const AudioComponents = () => {
const [audioDeviceManager, setAudioDeviceManager] = useState<any>({
playBackList: [],
ecordingList: [],
playBackItem: null,
ecordingItem: null,
playBackVolume: 127,
playBackActive: false,
ecordingActive: false,
ecordingVolume: 0,
});
useEffect(() => {
getAudioMediaList()
agora.registerEventHandler({
onAudioVolumeIndication: (percentage: number) => {
const dom = document.getElementById('deviceTest') as any;
if (dom) {
dom.style.width = `${percentage}%`
}
}
})
}, []);
const getAudioMediaList = (): void => {
const { playBackList, ecordingList, playBackItem, ecordingItem, ecordingVolume } = agora.getAudioMediaList();
setAudioDeviceManager({
...audioDeviceManager,
playBackList: playBackList.map((row: any) => {
return {
value: row.deviceId,
label: row.deviceName
}
}),
ecordingList: ecordingList.map((row: any) => {
return {
value: row.deviceId,
label: row.deviceName
}
}),
playBackItem: playBackItem.deviceId ? playBackItem.deviceId : null,
ecordingItem: ecordingItem.deviceId ? ecordingItem.deviceId : null,
ecordingVolume,
})
}
return ( return (
<> <>
<div> <div>
<span></span> <span></span>
<div></div> <div className={styles.audioComponents}>
<div>
<div className={styles.audioComponentsSelect}>
<span></span>
<Select
placeholder={audioDeviceManager.ecordingList.length ? '请选择设备' : '未检测到麦克风'}
options={audioDeviceManager.ecordingList} style={{ flexGrow: 1 }}
value={audioDeviceManager.ecordingItem} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
ecordingItem: e
})
agora.setRecordingDevice(e)
}} />;
{audioDeviceManager.ecordingActive ? <div onClick={() => {
agora.stopRecordingDeviceTest()
setAudioDeviceManager({
...audioDeviceManager,
playBackActive: false,
ecordingActive: false,
})
}}></div> : <div onClick={() => {
agora.stopPlaybackDeviceTest()
agora.startRecordingDeviceTest(100)
setAudioDeviceManager({
...audioDeviceManager,
playBackActive: false,
ecordingActive: true,
})
}}></div>}
</div>
{audioDeviceManager.ecordingActive ? <div className={styles.audioComponentsVolume}>
<img src={ImageUrl.icon36} alt="" />
<div>
<img src={ImageUrl.icon34} alt="" />
<div id='deviceTest'>
<img src={ImageUrl.icon35} alt="" />
</div>
</div>
</div> : null}
<div className={styles.audioComponentsSlider}>
<span></span>
<Slider value={audioDeviceManager.ecordingVolume} style={{ flexGrow: 1 }} max={255} onChange={(e) => {
agora.setRecordingDeviceVolume(e)
setAudioDeviceManager({
...audioDeviceManager,
ecordingVolume: e,
})
}} disabled={!audioDeviceManager.ecordingItem} />
</div>
<div>
<Checkbox onChange={() => {
}}></Checkbox>
</div>
</div>
<div>
<div className={styles.audioComponentsSelect}>
<span></span>
<Select
placeholder={audioDeviceManager.playBackList.length ? '请选择设备' : '未检测到麦克风'}
options={audioDeviceManager.playBackList} style={{ flexGrow: 1 }}
value={audioDeviceManager.playBackItem} onChange={(e) => {
setAudioDeviceManager({
...audioDeviceManager,
playBackItem: e
})
agora.setPlaybackDevice(e)
}} />;
{audioDeviceManager.playBackActive ? <div onClick={() => {
agora.stopPlaybackDeviceTest()
setAudioDeviceManager({
...audioDeviceManager,
playBackActive: false,
ecordingActive: false,
})
}}></div> : <div onClick={() => {
agora.stopRecordingDeviceTest()
agora.startPlaybackDeviceTest()
setAudioDeviceManager({
...audioDeviceManager,
playBackActive: true,
ecordingActive: false,
})
}}></div>}
</div>
{audioDeviceManager.playBackActive ? <div className={styles.audioComponentsVolume}>
<img src={ImageUrl.icon36} alt="" />
<div>
<img src={ImageUrl.icon34} alt="" />
<div id='deviceTest'>
<img src={ImageUrl.icon35} alt="" />
</div>
</div>
</div> : null}
<div className={styles.audioComponentsSlider}>
<span></span>
<Slider value={audioDeviceManager.playBackVolume} style={{ flexGrow: 1 }} max={255} onChange={(e) => {
agora.setPlaybackDeviceVolume(e)
setAudioDeviceManager({
...audioDeviceManager,
playBackVolume: e,
})
}} disabled={!audioDeviceManager.playBackItem} />
</div>
</div>
</div>
</div> </div>
</> </>
) )
} }
const RecordingComponents = () => { const RecordingComponents = () => {
const [filePath, setFilePath] = useState('')
return ( return (
<> <>
<div> <div>
<span></span> <span></span>
<div></div> <div className={styles.recordingComponents}>
<span></span>
<div>
<span></span>
<Input
placeholder="请填入文件路径"
style={{ margin: '0 14px', flexGrow: 1 }}
value={filePath}
onChange={(e) => {
setFilePath(e.target.value)
}}
/>
<Button type="primary" onClick={() => { }} style={{ backgroundColor: '#31353A' }}></Button>
</div>
</div>
</div> </div>
</> </>
) )
} }
const FileComponents = () => { const FileComponents = () => {
const [filePath, setFilePath] = useState('')
return ( return (
<> <>
<div> <div>
<span></span> <span></span>
<div></div> <div className={styles.fileComponents}>
<span></span>
<div>
<span></span>
<Input
placeholder="请填入保存目录"
style={{ margin: '0 14px', flexGrow: 1 }}
value={filePath}
onChange={(e) => {
setFilePath(e.target.value)
}}
/>
<Button type="primary" onClick={() => { }} style={{ backgroundColor: '#31353A' }}></Button>
</div>
<div>
<Checkbox onChange={() => {
}}></Checkbox>
</div>
</div>
</div> </div>
</> </>
) )

View File

@ -11,6 +11,7 @@ import {
MediaRecorderContainerFormat, MediaRecorderContainerFormat,
MediaRecorderStreamType MediaRecorderStreamType
} from "agora-electron-sdk"; } from "agora-electron-sdk";
import { GetRoomRtcToken } from "@/api/Home/Index";
const option: any = { const option: any = {
appId: 'dcfc466a6ecb4a1f972630065dfb1e75', appId: 'dcfc466a6ecb4a1f972630065dfb1e75',
token: '', token: '',
@ -28,9 +29,10 @@ const agora = {
rtcEngine.initialize({ rtcEngine.initialize({
appId: option.appId, appId: option.appId,
}); });
// console.log(rtcEngine.getAudioDeviceManager().enumeratePlaybackDevices());
}, },
// 事件回调 // 事件回调
registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline }: any) => { registerEventHandler: ({ onJoinChannelSuccess, onUserJoined, onUserOffline, onAudioVolumeIndication }: any) => {
rtcEngine.registerEventHandler({ rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件 // 监听本地用户加入频道事件
onJoinChannelSuccess: async (info: any, elapsed: any) => { onJoinChannelSuccess: async (info: any, elapsed: any) => {
@ -57,13 +59,10 @@ const agora = {
// } // }
// }, // },
// // 用户音量提示回调。 // // 用户音量提示回调。
// onAudioVolumeIndication: (connection: any, speakers: any, speakerNumber: any, totalVolume: any,) => { onAudioVolumeIndication: async (connection: any, speakers: any, speakerNumber: any, totalVolume: any,) => {
// const percentage = (totalVolume / 255) * 100 const percentage = (totalVolume / 255) * 100
// const dom = document.getElementById('recordingDeviceTest') as any; await onAudioVolumeIndication(percentage)
// if (dom) { }
// dom.style.width = `${percentage}%`
// }
// }
}); });
}, },
// 本地加入 // 本地加入
@ -109,6 +108,10 @@ const agora = {
}, },
); );
}, },
// 销毁
release: () => {
rtcEngine.release()
},
// 离开频道 // 离开频道
leaveChannel: async () => { leaveChannel: async () => {
await rtcEngine.leaveChannel({ await rtcEngine.leaveChannel({
@ -117,7 +120,7 @@ const agora = {
stopMicrophoneRecording: true, stopMicrophoneRecording: true,
}) })
agora.stopScreenCapture() agora.stopScreenCapture()
rtcEngine.release() agora.release()
}, },
// 加入频道 // 加入频道
joinChannel: () => { joinChannel: () => {
@ -275,97 +278,36 @@ const agora = {
rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8) rtcEngine.getVideoDeviceManager().setDevice(deviceIdUTF8)
}, },
// 开启本地视频预览 // 开启本地视频预览
startPreview: async (id: string, deviceId: string): Promise<boolean> => { startPreview: async (id: string, uid: number): Promise<void> => {
return new Promise((resolve, reject) => { rtcEngine.enableVideo();
navigator.mediaDevices.getUserMedia({ rtcEngine.startPreview();
video: { await GetRoomRtcToken(`${+new Date()}`).then(async (res) => {
deviceId: { exact: deviceId }, // 指定设备ID await rtcEngine.joinChannel(res.data, `${+new Date()}`, uid, {
}, channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
audio: true, clientRoleType: ClientRoleType.ClientRoleBroadcaster,
}).then((stream) => { publishMicrophoneTrack: true,
let dom = document.getElementById(id) as any; publishCameraTrack: true,
dom.srcObject = stream; autoSubscribeAudio: true,
dom.play() autoSubscribeVideo: true,
resolve(true) });
}).catch((error) => { rtcEngine.setupLocalVideo({
console.log(error); sourceType: VideoSourceType.VideoSourceCameraPrimary,
message.error('无法获取摄像头!'); uid,
resolve(true) view: document.getElementById(id),
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
}); });
}) })
}, },
stopVideoDevice: async (id: string) => { // 获取输入输出设备列表
let dom = document.getElementById(id) as any; getAudioMediaList: () => {
if (dom && dom.srcObject) { return {
const tracks = dom.srcObject.getTracks(); playBackList: rtcEngine.getAudioDeviceManager().enumeratePlaybackDevices(),
tracks.forEach((track: any) => { ecordingList: rtcEngine.getAudioDeviceManager().enumerateRecordingDevices(),
track.stop(); playBackItem: rtcEngine.getAudioDeviceManager().getPlaybackDefaultDevice(),
}); ecordingItem: rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice(),
dom.srcObject = null; ecordingVolume: rtcEngine.getAudioDeviceManager().getRecordingDeviceVolume(),
} }
}, },
// 停止音频设备回路测试
stopAudioDeviceLoopbackTest: () => {
rtcEngine.getAudioDeviceManager().stopAudioDeviceLoopbackTest()
rtcEngine.getAudioDeviceManager().stopRecordingDeviceTest()
let video = document.getElementById('startPreview') as any;
if (video.srcObject) {
const tracks = video.srcObject.getTracks();
tracks.forEach((track: any) => {
track.stop();
});
video.srcObject = null;
}
let audio = document.getElementById('startAudio') as any;
if (audio.srcObject) {
const tracks = audio.srcObject.getTracks();
tracks.forEach((track: any) => {
track.stop();
});
audio.srcObject = null;
}
},
// 启动音频采集设备测试
startRecordingDeviceTest: (indicationInterval: number) => {
rtcEngine.getAudioDeviceManager().startRecordingDeviceTest(indicationInterval)
navigator.mediaDevices.getUserMedia({ audio: true })
.then((stream) => {
let dom = document.getElementById('startAudio') as any;
dom.srcObject = stream;
dom.play()
})
.catch((error) => {
message.error('无法获取麦克风!');
});
},
// 获取音频设备列表
getAudioMediaList: (bool: boolean) => {
if (bool) {
return {
currentDevices: rtcEngine.getAudioDeviceManager().enumeratePlaybackDevices(),
currentDevice: rtcEngine.getAudioDeviceManager().getPlaybackDefaultDevice(),
currentVolume: 100,
}
} else {
return {
currentDevices: rtcEngine.getAudioDeviceManager().enumerateRecordingDevices(),
currentDevice: rtcEngine.getAudioDeviceManager().getRecordingDefaultDevice(),
currentVolume: rtcEngine.getAudioDeviceManager().getRecordingDeviceVolume()
}
}
},
// 设置音频设备音量
setRecordingDeviceVolume: (volume: number) => {
rtcEngine.getAudioDeviceManager().setRecordingDeviceVolume(volume)
},
// 设置音频采集设备
setRecordingDevice: (deviceId: string) => {
rtcEngine.getAudioDeviceManager().setRecordingDevice(deviceId)
},
// 启动音频播放设备测试。 // 启动音频播放设备测试。
startPlaybackDeviceTest: () => { startPlaybackDeviceTest: () => {
rtcEngine.getAudioDeviceManager().startPlaybackDeviceTest('https://wgshare.oss-cn-chengdu.aliyuncs.com/TestAudio.mp3') rtcEngine.getAudioDeviceManager().startPlaybackDeviceTest('https://wgshare.oss-cn-chengdu.aliyuncs.com/TestAudio.mp3')
@ -382,6 +324,22 @@ const agora = {
setPlaybackDevice: (deviceId: string) => { setPlaybackDevice: (deviceId: string) => {
rtcEngine.getAudioDeviceManager().setPlaybackDevice(deviceId) rtcEngine.getAudioDeviceManager().setPlaybackDevice(deviceId)
}, },
// 启动音频采集设备测试
startRecordingDeviceTest: (indicationInterval: number) => {
rtcEngine.getAudioDeviceManager().startRecordingDeviceTest(indicationInterval)
},
// 设置音频设备音量
setRecordingDeviceVolume: (volume: number) => {
rtcEngine.getAudioDeviceManager().setRecordingDeviceVolume(volume)
},
// 设置音频采集设备
setRecordingDevice: (deviceId: string) => {
rtcEngine.getAudioDeviceManager().setRecordingDevice(deviceId)
},
// 停止音频采集设备测试
stopRecordingDeviceTest: () => {
rtcEngine.getAudioDeviceManager().stopRecordingDeviceTest()
},
} }

View File

@ -318,6 +318,10 @@ $pagination-hover-background-color: #5575F2;
} }
} }
:where(.css-dev-only-do-not-override-98ntnt).ant-slider.ant-slider-disabled .ant-slider-rail {
background-color: darken(#D9D9D9, 50%) !important;
}
// empty // empty
.ant-empty { .ant-empty {
.ant-empty-description { .ant-empty-description {