完成mp4
This commit is contained in:
parent
606715ced6
commit
1b7480cadc
4
main.js
4
main.js
|
|
@ -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(() => {
|
||||||
|
|
|
||||||
|
|
@ -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": "智汇享",
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue