视频录制开发
This commit is contained in:
@@ -9,6 +9,7 @@ import { buildStatsLogPayload, createAudioAnalyser, getAudioLevel } from './medi
|
||||
import { bindInviteSocketEvents, buildSocketUserInfoPayload, createSignalingInstance, ensureSignalingStarted, getActiveSignalingInstance, sendInviteSignal, sendSocketUserInfo } from './signaling-session.js';
|
||||
import { getNetworkQualityFromSummary, summarizeInboundStats } from './webrtc-stats.js';
|
||||
import { createLogger } from './logger.js';
|
||||
import { MeetingRecorder } from './meeting-recorder.js';
|
||||
|
||||
const logger = createLogger('store');
|
||||
class CallStateManager {
|
||||
@@ -27,6 +28,7 @@ class CallStateManager {
|
||||
this.listeners = [];
|
||||
this.socketEventHandlers = {};
|
||||
this._inviteEventSignaling = null;
|
||||
this.meetingRecorder = new MeetingRecorder();
|
||||
}
|
||||
subscribe(callback) {
|
||||
this.listeners.push(callback);
|
||||
@@ -107,6 +109,78 @@ class CallStateManager {
|
||||
await this._updateLocalMediaRefactored(mediaType, value);
|
||||
return;
|
||||
}
|
||||
async toggleRecording() {
|
||||
const isRecording = this.state.session.localUser.mediaState.recording || false;
|
||||
|
||||
if (isRecording) {
|
||||
return this.stopRecording();
|
||||
}
|
||||
|
||||
return this.startRecording();
|
||||
}
|
||||
async startRecording() {
|
||||
if (this.state.session.status !== 'ongoing') {
|
||||
throw new Error('会议连接成功后才能开始录制');
|
||||
}
|
||||
|
||||
await this.meetingRecorder.start({
|
||||
localStream: this.state.localStream,
|
||||
remoteStream: this.state.remoteStream,
|
||||
remoteStreams: this.state.remoteStreams,
|
||||
connectionId: this.connectionId
|
||||
});
|
||||
await this._updateLocalMediaRefactored('recording', true);
|
||||
|
||||
return {
|
||||
recording: true,
|
||||
message: '开始录制会议'
|
||||
};
|
||||
}
|
||||
async stopRecording() {
|
||||
const result = await this.meetingRecorder.stop();
|
||||
await this._updateLocalMediaRefactored('recording', false);
|
||||
if (!result) {
|
||||
return {
|
||||
recording: false,
|
||||
message: '停止录制会议'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const uploadResult = await this.uploadRecording(result);
|
||||
return {
|
||||
recording: false,
|
||||
message: uploadResult?.url ? `录制已上传到服务器:${uploadResult.url}` : `录制已上传:${result.filename}`
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Recording upload failed:', error);
|
||||
this.meetingRecorder.download(result.blob, result.filename);
|
||||
return {
|
||||
recording: false,
|
||||
message: `上传失败,已下载到本地:${result.filename}`
|
||||
};
|
||||
}
|
||||
}
|
||||
async uploadRecording({ blob, filename }) {
|
||||
const formData = new FormData();
|
||||
formData.append('meetingId', this.connectionId || this.state.session.id || 'unknown');
|
||||
formData.append('userId', this.state.session.localUser.id || '');
|
||||
formData.append('filename', filename);
|
||||
formData.append('recording', blob, filename);
|
||||
|
||||
const response = await fetch('/api/recordings', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const responseBody = await response.json().catch(() => ({}));
|
||||
if (!response.ok || responseBody.success === false) {
|
||||
throw new Error(responseBody.message || 'Recording upload failed');
|
||||
}
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
async _updateLocalMediaRefactored(mediaType, value) {
|
||||
if (mediaType === 'video' && value) {
|
||||
await this._enableLocalVideo();
|
||||
@@ -423,6 +497,14 @@ class CallStateManager {
|
||||
this.startActivityDetection(this.state.remoteStream, { isLocal: false });
|
||||
}
|
||||
async hangUp() {
|
||||
if (this.meetingRecorder?.isRecording()) {
|
||||
try {
|
||||
await this.stopRecording();
|
||||
}
|
||||
catch (error) {
|
||||
logger.error('Error stopping recording before hangUp:', error);
|
||||
}
|
||||
}
|
||||
this.clearStatsMessage();
|
||||
this.stopNetworkQualityDetection();
|
||||
if (this.durationInterval) {
|
||||
|
||||
Reference in New Issue
Block a user