修复录屏麦克风问题
This commit is contained in:
parent
27d3a59438
commit
1ecec388ce
|
|
@ -174,6 +174,7 @@ const Meeting: React.FC = () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false)
|
const [isVideoFullScreen, setIsVideoFullScreen] = useState<boolean>(false)
|
||||||
|
const [observer, setObserver] = useState<IntersectionObserver>()
|
||||||
let userInfo = JSON.parse(storage.getItem('user') as string)
|
let userInfo = JSON.parse(storage.getItem('user') as string)
|
||||||
const msgTips = '您不是管理员或发言人,无法开启此功能!'
|
const msgTips = '您不是管理员或发言人,无法开启此功能!'
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -583,6 +584,33 @@ const Meeting: React.FC = () => {
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [isClicked]);
|
}, [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 () => {
|
const agoraInit = async () => {
|
||||||
await agora.init(true)
|
await agora.init(true)
|
||||||
|
|
@ -990,52 +1018,75 @@ const Meeting: React.FC = () => {
|
||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
case '录制':
|
case '录制':
|
||||||
const setting = await JSON.parse(storage.getItem('setting') as string)
|
const setting = await JSON.parse(storage.getItem('setting') as string);
|
||||||
try {
|
try {
|
||||||
await fs.access(setting.recordingFilesPath, fs.constants.F_OK)
|
await fs.access(setting.recordingFilesPath, fs.constants.F_OK);
|
||||||
footerListTemplate[itemIndex][rowIndex].title = '录制中'
|
footerListTemplate[itemIndex][rowIndex].title = '录制中';
|
||||||
footerListTemplate[itemIndex][rowIndex].active = true
|
footerListTemplate[itemIndex][rowIndex].active = true;
|
||||||
setFooterList(footerListTemplate)
|
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) {
|
|
||||||
|
|
||||||
|
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))
|
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);
|
||||||
})
|
})
|
||||||
} catch (error: any) {
|
|
||||||
if (error.code === 'ENOENT') {
|
// 合并音频流与视频流
|
||||||
message.error({
|
const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]);
|
||||||
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
|
||||||
stupWizardRef.current.changeModal(3)
|
// 开始录制
|
||||||
}}>前往设置</span></div>
|
const recorder = new MediaRecorder(combinedSource, {
|
||||||
})
|
mimeType: 'video/webm;codecs=vp9,opus',
|
||||||
return
|
videoBitsPerSecond: 1.5e6,
|
||||||
} else {
|
});
|
||||||
message.error(error)
|
|
||||||
}
|
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;
|
break;
|
||||||
case '录制中':
|
case '录制中':
|
||||||
footerListTemplate[itemIndex][rowIndex].title = '录制'
|
footerListTemplate[itemIndex][rowIndex].title = '录制'
|
||||||
|
|
@ -1510,7 +1561,7 @@ const Meeting: React.FC = () => {
|
||||||
{roomUserList.map((item: any, index: number) => {
|
{roomUserList.map((item: any, index: number) => {
|
||||||
return (index <= 19 && item.isRoom && item.isAdmin ? <div
|
return (index <= 19 && item.isRoom && item.isAdmin ? <div
|
||||||
id={item.uid}
|
id={item.uid}
|
||||||
className={`${styles.meetingContentSwiperCard}`}
|
className={`${styles.meetingContentSwiperCard} intersectionObserver-view`}
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (String(isShare) === item.screenShareId) {
|
if (String(isShare) === item.screenShareId) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue