修复录屏麦克风问题
This commit is contained in:
parent
27d3a59438
commit
1ecec388ce
|
|
@ -174,6 +174,7 @@ const Meeting: React.FC = () => {
|
|||
}
|
||||
});
|
||||
const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false)
|
||||
const [observer, setObserver] = useState<IntersectionObserver>()
|
||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||
const msgTips = '您不是管理员或发言人,无法开启此功能!'
|
||||
useEffect(() => {
|
||||
|
|
@ -583,6 +584,33 @@ const Meeting: React.FC = () => {
|
|||
return () => clearTimeout(timer);
|
||||
}, [isClicked]);
|
||||
|
||||
useEffect(() => {
|
||||
const elements = document.querySelectorAll('.intersectionObserver-view');
|
||||
if (elements.length && currentVideoId) {
|
||||
elements.forEach(element => {
|
||||
observer?.unobserve(element);
|
||||
});
|
||||
const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => {
|
||||
entries.forEach(async (entry) => {
|
||||
if (entry.target.id !== user.uid) {
|
||||
await agora.muteRemoteVideoStreamEx(Number(entry.target.id), !entry.isIntersecting)
|
||||
}
|
||||
});
|
||||
await agora.muteRemoteVideoStreamEx(Number(currentVideoId), false)
|
||||
}, { threshold: 0, root: document.getElementById('videoView') });
|
||||
setObserver(observerObject)
|
||||
elements.forEach(element => {
|
||||
observerObject.observe(element);
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
elements.forEach(element => {
|
||||
observer?.unobserve(element);
|
||||
});
|
||||
observer?.disconnect();
|
||||
}
|
||||
}, [roomUserList, currentVideoId]);
|
||||
|
||||
// 声网初始化
|
||||
const agoraInit = async () => {
|
||||
await agora.init(true)
|
||||
|
|
@ -989,53 +1017,76 @@ const Meeting: React.FC = () => {
|
|||
}
|
||||
})
|
||||
break;
|
||||
case '录制':
|
||||
const setting = await JSON.parse(storage.getItem('setting') as string)
|
||||
try {
|
||||
await fs.access(setting.recordingFilesPath, fs.constants.F_OK)
|
||||
footerListTemplate[itemIndex][rowIndex].title = '录制中'
|
||||
footerListTemplate[itemIndex][rowIndex].active = true
|
||||
setFooterList(footerListTemplate)
|
||||
window.electron.getSources().then((sources: any) => {
|
||||
const screenId = sources[0].id;
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: screenId,
|
||||
}
|
||||
} as any,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: screenId,
|
||||
}
|
||||
} as any
|
||||
}).then(async (steam) => {
|
||||
try {
|
||||
const audioTracks = await navigator.mediaDevices
|
||||
.getUserMedia({ audio: true, video: false })
|
||||
.then(audioStream => audioStream.getAudioTracks()[0]);
|
||||
steam.addTrack(audioTracks);
|
||||
} catch (error) {
|
||||
|
||||
case '录制':
|
||||
const setting = await JSON.parse(storage.getItem('setting') as string);
|
||||
try {
|
||||
await fs.access(setting.recordingFilesPath, fs.constants.F_OK);
|
||||
footerListTemplate[itemIndex][rowIndex].title = '录制中';
|
||||
footerListTemplate[itemIndex][rowIndex].active = true;
|
||||
setFooterList(footerListTemplate);
|
||||
|
||||
window.electron.getSources().then(async (sources: any) => {
|
||||
const screenId = sources[0].id;
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: screenId,
|
||||
}
|
||||
setMediaStream(steam)
|
||||
setRecorder(new MediaRecorder(steam))
|
||||
})
|
||||
})
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') {
|
||||
message.error({
|
||||
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||
stupWizardRef.current.changeModal(3)
|
||||
}}>前往设置</span></div>
|
||||
})
|
||||
return
|
||||
} else {
|
||||
message.error(error)
|
||||
}
|
||||
},
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: screenId,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有音频输入设备
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const audioInputDevices = devices.filter(device => device.kind === 'audioinput' &&
|
||||
device.deviceId !== 'default' &&
|
||||
device.deviceId !== 'communications' );
|
||||
|
||||
// 使用Web Audio API来捕获系统声音和麦克风声音,将它们合并到同一个MediaStream中。
|
||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const systemSoundSource = audioCtx.createMediaStreamSource(stream);
|
||||
const systemSoundDestination = audioCtx.createMediaStreamDestination();
|
||||
systemSoundSource.connect(systemSoundDestination);
|
||||
|
||||
// 录制所有音频输入设备
|
||||
audioInputDevices.forEach( async device=>{
|
||||
const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } }});
|
||||
const micSoundSource = audioCtx.createMediaStreamSource(micStream);
|
||||
micSoundSource.connect(systemSoundDestination);
|
||||
})
|
||||
|
||||
// 合并音频流与视频流
|
||||
const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]);
|
||||
|
||||
// 开始录制
|
||||
const recorder = new MediaRecorder(combinedSource, {
|
||||
mimeType: 'video/webm;codecs=vp9,opus',
|
||||
videoBitsPerSecond: 1.5e6,
|
||||
});
|
||||
|
||||
setMediaStream(combinedSource);
|
||||
setRecorder(recorder);
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') {
|
||||
message.error({
|
||||
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
||||
stupWizardRef.current.changeModal(3);
|
||||
}}>前往设置</span></div>
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
message.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case '录制中':
|
||||
footerListTemplate[itemIndex][rowIndex].title = '录制'
|
||||
|
|
@ -1510,7 +1561,7 @@ const Meeting: React.FC = () => {
|
|||
{roomUserList.map((item: any, index: number) => {
|
||||
return (index <= 19 && item.isRoom && item.isAdmin ? <div
|
||||
id={item.uid}
|
||||
className={`${styles.meetingContentSwiperCard}`}
|
||||
className={`${styles.meetingContentSwiperCard} intersectionObserver-view`}
|
||||
key={index}
|
||||
onClick={() => {
|
||||
if (String(isShare) === item.screenShareId) {
|
||||
|
|
@ -2289,4 +2340,4 @@ const networkIcon = (network: number) => {
|
|||
}
|
||||
}
|
||||
|
||||
export default Meeting
|
||||
export default Meeting
|
||||
|
|
|
|||
Loading…
Reference in New Issue