完成mp4

This commit is contained in:
youngq 2024-09-23 13:03:05 +08:00
parent 606715ced6
commit 1b7480cadc
3 changed files with 152 additions and 138 deletions

View File

@ -278,8 +278,8 @@ app.on('ready', () => {
return app.getPath('userData'); return app.getPath('userData');
}); });
// 用户数据目录路径 // 用户数据目录路径
global.userDataPath = app.getPath('userData'); // 全局变量 const userDataPath = app.getPath('userData'); // 全局变量
console.log('User Data Path:', global.userDataPath); console.log('User Data Path:', userDataPath);
// 检查并下载 ffmpeg // 检查并下载 ffmpeg
checkAndDownloadFFmpeg(userDataPath) checkAndDownloadFFmpeg(userDataPath)
.then(() => { .then(() => {

View File

@ -1,7 +1,7 @@
{ {
"name": "WGShare.Metting", "name": "WGShare.Metting",
"private": true, "private": true,
"version": "0.1.14", "version": "0.3.0",
"main": "main.js", "main": "main.js",
"authors": "yj", "authors": "yj",
"description": "智汇享", "description": "智汇享",

View File

@ -21,9 +21,10 @@ import StupWizard from '@/components/StupWizard';
import EquipmentManagement from '@/components/EquipmentManagement'; import EquipmentManagement from '@/components/EquipmentManagement';
import UserVideo from '@/components/UserVideo'; import UserVideo from '@/components/UserVideo';
import { role } from '@/config/role'; import { role } from '@/config/role';
import path from 'path';
const { confirm } = Modal;
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
import * as path from 'path';
const { confirm } = Modal;
const { exec } = require('child_process'); const { exec } = require('child_process');
const fs = require('fs').promises; const fs = require('fs').promises;
dayjs.extend(durationPlugin); dayjs.extend(durationPlugin);
@ -547,9 +548,8 @@ const Meeting: React.FC = () => {
}); });
const reader = new FileReader() as any; const reader = new FileReader() as any;
reader.onload = async () => { reader.onload = async () => {
// const setting = await JSON.parse(storage.getItem('setting') as string) try {
// const buffer = Buffer.from(reader.result); const userDataPath = await ipcRenderer.invoke('get-user-data-path');
// await fs.writeFile(`${setting.recordingFilesPath}会议录制_${state.roomName}_${state.channelId}_${+new Date()}.mp4`, buffer, {});
// 获取当前日期并格式化 // 获取当前日期并格式化
const date = new Date(); const date = new Date();
@ -563,15 +563,14 @@ const Meeting: React.FC = () => {
const setting = await JSON.parse(storage.getItem('setting') as string) const setting = await JSON.parse(storage.getItem('setting') as string)
const buffer = Buffer.from(reader.result); const buffer = Buffer.from(reader.result);
const mp4Path = `${setting.recordingFilesPath}会议录制_${state.roomName}_${state.channelId}_${formattedDate}_beforehanlder.mp4`; const mp4Path=`${setting.recordingFilesPath}会议录制_${state.roomName}_${state.channelId}_${formattedDate}_beforehanlder.mp4`;
await fs.writeFile(mp4Path, buffer); await fs.writeFile(mp4Path, buffer);
const userDataPath = await ipcRenderer.invoke('get-user-data-path');
// 获取应用程序安装路径 // 获取应用程序安装路径
const ffmpegPath = path.join(userDataPath, "ffmpeg.exe"); const ffmpegPath = path.join(userDataPath, "ffmpeg.exe");
const inputFilePath = mp4Path; // 输入文件路径 const inputFilePath = mp4Path; // 输入文件路径
const outputFilePath = mp4Path.replace('_beforehanlder', ''); // 输出文件路径 const outputFilePath = mp4Path.replace('_beforehanlder',''); // 输出文件路径
const command = `${ffmpegPath} -i "${inputFilePath}" -vcodec copy -acodec copy "${outputFilePath}"`; const command = `${ffmpegPath} -i "${inputFilePath}" -vcodec copy -acodec copy "${outputFilePath}"`;
exec(command, (error, stdout, stderr) => { exec(command, (error, stdout, stderr) => {
@ -579,6 +578,7 @@ const Meeting: React.FC = () => {
console.error('Error executing ffmpeg command:', error); console.error('Error executing ffmpeg command:', error);
return; return;
} }
// 删除输入文件 // 删除输入文件
fs.unlink(inputFilePath, (err) => { fs.unlink(inputFilePath, (err) => {
if (err) { if (err) {
@ -589,7 +589,6 @@ const Meeting: React.FC = () => {
}); });
confirm({ confirm({
title: '提示', title: '提示',
icon: <ExclamationCircleFilled />, icon: <ExclamationCircleFilled />,
@ -608,8 +607,14 @@ const Meeting: React.FC = () => {
onCancel() { onCancel() {
} }
}) })
})
}; });
} catch (err) {
console.error('处理录制时出错:', err);
}
}
reader.readAsArrayBuffer(blob); reader.readAsArrayBuffer(blob);
} }
}; };
@ -627,32 +632,32 @@ const Meeting: React.FC = () => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [isClicked]); }, [isClicked]);
// useEffect(() => { useEffect(() => {
// const elements = document.querySelectorAll('.intersectionObserver-view'); const elements = document.querySelectorAll('.intersectionObserver-view');
// if (elements.length && currentVideoId) { if (elements.length && currentVideoId) {
// elements.forEach(element => { elements.forEach(element => {
// observer?.unobserve(element); observer?.unobserve(element);
// }); });
// const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => { const observerObject = new IntersectionObserver(async (entries: IntersectionObserverEntry[], _observer: IntersectionObserver) => {
// entries.forEach(async (entry) => { entries.forEach(async (entry) => {
// if (entry.target.id !== user.uid) { if (entry.target.id !== user.uid) {
// await agora.muteRemoteVideoStreamEx(Number(entry.target.id), !entry.isIntersecting) await agora.muteRemoteVideoStreamEx(Number(entry.target.id), !entry.isIntersecting)
// } }
// }); });
// await agora.muteRemoteVideoStreamEx(Number(currentVideoId), false) await agora.muteRemoteVideoStreamEx(Number(currentVideoId), false)
// }, { threshold: 0, root: document.getElementById('videoView') }); }, { threshold: 0, root: document.getElementById('videoView') });
// setObserver(observerObject) setObserver(observerObject)
// elements.forEach(element => { elements.forEach(element => {
// observerObject.observe(element); observerObject.observe(element);
// }); });
// } }
// return () => { return () => {
// elements.forEach(element => { elements.forEach(element => {
// observer?.unobserve(element); observer?.unobserve(element);
// }); });
// observer?.disconnect(); observer?.disconnect();
// } }
// }, [roomUserList, currentVideoId]); }, [roomUserList, currentVideoId]);
// 声网初始化 // 声网初始化
const agoraInit = async () => { const agoraInit = async () => {
@ -1070,45 +1075,52 @@ const Meeting: React.FC = () => {
window.electron.getSources().then(async (sources: any) => { window.electron.getSources().then(async (sources: any) => {
const screenId = sources[0].id; const screenId = sources[0].id;
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
audio: { audio: {
mandatory: { mandatory: {
chromeMediaSource: 'desktop', chromeMediaSource: 'desktop',
chromeMediaSourceId: screenId, chromeMediaSourceId: screenId,
} }
} as any, },
video: { video: {
mandatory: { mandatory: {
chromeMediaSource: 'desktop', chromeMediaSource: 'desktop',
chromeMediaSourceId: screenId, chromeMediaSourceId: screenId,
} }
} as any }
}); });
// 获取所有音频输入设备 // 获取所有音频输入设备
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputDevices = devices.filter(device => device.kind === 'audioinput' && const audioInputDevices = devices.filter(device => device.kind === 'audioinput' &&
device.deviceId !== 'default' && device.deviceId !== 'default' &&
device.deviceId !== 'communications'); device.deviceId !== 'communications' );
// 使用Web Audio API来捕获系统声音和麦克风声音将它们合并到同一个MediaStream中。 // 使用Web Audio API来捕获系统声音和麦克风声音将它们合并到同一个MediaStream中。
const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)(); const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const systemSoundSource = audioCtx.createMediaStreamSource(stream); const systemSoundSource = audioCtx.createMediaStreamSource(stream);
const systemSoundDestination = audioCtx.createMediaStreamDestination(); const systemSoundDestination = audioCtx.createMediaStreamDestination();
systemSoundSource.connect(systemSoundDestination); systemSoundSource.connect(systemSoundDestination);
// 录制所有音频输入设备 // 录制所有音频输入设备
audioInputDevices.forEach(async device => { audioInputDevices.forEach( async device=>{
const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } } }); const micStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: device.deviceId } }});
setMediaStream(micStream);
const micSoundSource = audioCtx.createMediaStreamSource(micStream); const micSoundSource = audioCtx.createMediaStreamSource(micStream);
micSoundSource.connect(systemSoundDestination); micSoundSource.connect(systemSoundDestination);
}) })
// 合并音频流与视频流 // 合并音频流与视频流
const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]); const combinedSource = new MediaStream([...stream.getVideoTracks(), ...systemSoundDestination.stream.getAudioTracks()]);
// 开始录制 // 开始录制
const mediaRecorder = new MediaRecorder(combinedSource, { const recorder = new MediaRecorder(combinedSource, {
mimeType: 'video/webm;codecs=vp9,opus', mimeType: 'video/webm;codecs=vp9,opus',
videoBitsPerSecond: 1.5e6, videoBitsPerSecond: 1.5e6,
}); });
setRecorder(mediaRecorder);
setMediaStream(combinedSource);
setRecorder(recorder);
}); });
} catch (error: any) { } catch (error: any) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
@ -1329,6 +1341,8 @@ const Meeting: React.FC = () => {
} }
}) })
} }
// 开关麦克风 // 开关麦克风
const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise<void> => { const postOpenMicrApi = async (enableMicr: boolean, uid: string, isAll: boolean, isMessage: boolean = false): Promise<void> => {
if (isAll) { if (isAll) {