This commit is contained in:
2026-05-24 01:18:27 +08:00
parent 0d8a567c95
commit a30c74f8da

View File

@@ -159,11 +159,51 @@ class CallStateManager {
// 更新本地媒体状态 // 更新本地媒体状态
async updateLocalMedia(mediaType, value) { async updateLocalMedia(mediaType, value) {
await this._updateLocalMediaRefactored(mediaType, value);
return;
}
// 如果是开启视频,重新获取摄像头资源 async _updateLocalMediaRefactored(mediaType, value) {
if (mediaType === 'video' && value) { if (mediaType === 'video' && value) {
await this._enableLocalVideo();
this._notifyUserListUpdate();
return;
}
this.state.session.localUser.mediaState[mediaType] = value;
this._notifyLocalMediaChange(mediaType, value);
this.emitMediaStateChange();
if (mediaType === 'video' && !value) {
this._disableLocalVideoTracks();
}
if (mediaType === 'audio') {
this._setLocalAudioTrackEnabled(value);
}
this._notifyUserListUpdate();
}
async _enableLocalVideo() {
try { try {
// 只获取新的视频轨道,不干扰正在工作的音频 const newVideoTrack = await this._requestNewVideoTrack();
this._replaceLocalVideoTrack(newVideoTrack);
await this._updateOutgoingVideoTrack(newVideoTrack);
this.state.session.localUser.mediaState.video = true;
this.notify({ type: 'LOCAL_STREAM_OBTAINED', stream: this.state.localStream });
this._notifyLocalMediaChange('video', true);
this.emitMediaStateChange();
this.startActivityDetection(this.state.localStream, { isLocal: true });
} catch (error) {
console.error('Error reopening video:', error);
this.state.session.localUser.mediaState.video = false;
this._notifyLocalMediaChange('video', false);
}
}
async _requestNewVideoTrack() {
const newVideoStream = await navigator.mediaDevices.getUserMedia(VIDEO_ONLY_CONSTRAINT); const newVideoStream = await navigator.mediaDevices.getUserMedia(VIDEO_ONLY_CONSTRAINT);
const newVideoTrack = newVideoStream.getVideoTracks()[0]; const newVideoTrack = newVideoStream.getVideoTracks()[0];
@@ -171,7 +211,10 @@ class CallStateManager {
throw new Error('Failed to get video track'); throw new Error('Failed to get video track');
} }
// 更新本地流中的视频轨道(替换旧的已停止的轨道) return newVideoTrack;
}
_replaceLocalVideoTrack(newVideoTrack) {
if (this.state.localStream) { if (this.state.localStream) {
const oldVideoTracks = this.state.localStream.getVideoTracks(); const oldVideoTracks = this.state.localStream.getVideoTracks();
oldVideoTracks.forEach(track => { oldVideoTracks.forEach(track => {
@@ -179,130 +222,128 @@ class CallStateManager {
this.state.localStream.removeTrack(track); this.state.localStream.removeTrack(track);
}); });
this.state.localStream.addTrack(newVideoTrack); this.state.localStream.addTrack(newVideoTrack);
} else { return;
// 本地流不存在时(不应该发生),使用新流 }
this.state.localStream = newVideoStream;
this.state.localStream = new MediaStream([newVideoTrack]);
}
async _updateOutgoingVideoTrack(newVideoTrack) {
if (!this.renderstreaming) {
return;
} }
// 更新WebRTC连接中的视频轨道
if (this.renderstreaming) {
console.log('Updating video track in WebRTC connection'); console.log('Updating video track in WebRTC connection');
if (this.role === 'host') { if (this.role === 'host') {
// Host端需要遍历所有participant的peer来替换视频轨道
const participantIds = Object.keys(this.state.remoteStreams); const participantIds = Object.keys(this.state.remoteStreams);
for (const participantId of participantIds) { for (const participantId of participantIds) {
const transceivers = this.renderstreaming.getTransceivers(participantId); await this._updateVideoTrackForPeer(newVideoTrack, participantId);
if (!transceivers) continue; }
return;
}
const videoTransceivers = transceivers.filter(t => await this._updateVideoTrackForPeer(newVideoTrack);
t.sender && t.sender.track && t.sender.track.kind === 'video' }
async _updateVideoTrackForPeer(newVideoTrack, participantId = undefined) {
const transceivers = this.renderstreaming.getTransceivers(participantId);
if (!transceivers) {
return;
}
const videoTransceivers = transceivers.filter(transceiver =>
transceiver.sender && transceiver.sender.track && transceiver.sender.track.kind === 'video'
); );
if (videoTransceivers.length > 0) { if (videoTransceivers.length > 0) {
await this._replaceVideoTrackOnTransceivers(videoTransceivers, newVideoTrack, participantId);
} else {
this._addVideoTransceiver(newVideoTrack, participantId);
}
this._scheduleVideoSenderUpdate(participantId);
}
async _replaceVideoTrackOnTransceivers(videoTransceivers, newVideoTrack, participantId) {
for (const transceiver of videoTransceivers) { for (const transceiver of videoTransceivers) {
try { try {
await transceiver.sender.replaceTrack(newVideoTrack); await transceiver.sender.replaceTrack(newVideoTrack);
console.log(`Replaced video track for participant ${participantId}`); console.log(participantId
? `Replaced video track for participant ${participantId}`
: 'Successfully replaced video track');
} catch (error) { } catch (error) {
console.error(`Error replacing video track for ${participantId}:`, error); console.error(
participantId
? `Error replacing video track for ${participantId}:`
: 'Error replacing video track:',
error
);
} }
} }
} else { }
// 没有视频收发器,添加新的
_addVideoTransceiver(newVideoTrack, participantId) {
try { try {
if (participantId) {
this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' }, participantId); this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' }, participantId);
console.log(`Added new video transceiver for participant ${participantId}`); console.log(`Added new video transceiver for participant ${participantId}`);
} catch (error) { return;
console.error(`Error adding video transceiver for ${participantId}:`, error);
}
} }
// 设置编解码器偏好
setTimeout(() => { this.setCodecPreferences(participantId); }, 100);
setTimeout(() => { this.setVideoEncodingParameters(participantId); }, 200);
}
} else {
// Participant端使用单一peer
const transceivers = this.renderstreaming.getTransceivers();
if (transceivers) {
const videoTransceivers = transceivers.filter(t =>
t.sender && t.sender.track && t.sender.track.kind === 'video'
);
if (videoTransceivers.length > 0) {
for (const transceiver of videoTransceivers) {
try {
await transceiver.sender.replaceTrack(newVideoTrack);
console.log('Successfully replaced video track');
} catch (error) {
console.error('Error replacing video track:', error);
}
}
} else {
try {
this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' }); this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' });
console.log('Added new video transceiver'); console.log('Added new video transceiver');
} catch (error) { } catch (error) {
console.error('Error adding video transceiver:', error); console.error(
} participantId
} ? `Error adding video transceiver for ${participantId}:`
} : 'Error adding video transceiver:',
setTimeout(() => { this.setCodecPreferences(); }, 100); error
setTimeout(() => { this.setVideoEncodingParameters(); }, 200); );
} }
} }
// 更新状态和通知UI _scheduleVideoSenderUpdate(participantId) {
this.state.session.localUser.mediaState.video = true; setTimeout(() => { this.setCodecPreferences(participantId); }, 100);
this.notify({ type: 'LOCAL_STREAM_OBTAINED', stream: this.state.localStream }); setTimeout(() => { this.setVideoEncodingParameters(participantId); }, 200);
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: true });
this.emitMediaStateChange();
this.startActivityDetection(this.state.localStream, { isLocal: true });
} catch (error) {
console.error('Error reopening video:', error);
this.state.session.localUser.mediaState.video = false;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: false });
}
} else {
// 直接更新媒体状态
this.state.session.localUser.mediaState[mediaType] = value;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
// 发送媒体状态到服务器
this.emitMediaStateChange();
} }
_disableLocalVideoTracks() {
if (!this.state.localStream) {
return;
}
// 如果是关闭视频,释放摄像头资源
if (mediaType === 'video' && !value && this.state.localStream) {
this.state.session.localUser.mediaState.video = false; this.state.session.localUser.mediaState.video = false;
this.state.localStream.getTracks().forEach(track => { this.state.localStream.getTracks().forEach(track => {
if (track.kind === 'video') { if (track.kind === 'video') {
track.stop(); track.stop();
} }
}); });
// 发送媒体状态到服务器
this.emitMediaStateChange();
} }
// 如果是音频状态变化,控制本地音频轨道 _setLocalAudioTrackEnabled(value) {
if (mediaType === 'audio' && this.state.localStream) { if (!this.state.localStream) {
return;
}
this.state.session.localUser.mediaState.audio = value; this.state.session.localUser.mediaState.audio = value;
this.state.localStream.getTracks().forEach(track => { this.state.localStream.getTracks().forEach(track => {
if (track.kind === 'audio') { if (track.kind === 'audio') {
track.enabled = value; track.enabled = value;
} }
}); });
// 发送媒体状态到服务器
this.emitMediaStateChange();
} }
// 通知UI更新用户列表 _notifyLocalMediaChange(mediaType, value) {
this.notify({ type: 'USER_LIST_UPDATE', localUser: this.state.session.localUser, remoteUser: this.state.session.remoteUser }); this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
}
_notifyUserListUpdate() {
this.notify({
type: 'USER_LIST_UPDATE',
localUser: this.state.session.localUser,
remoteUser: this.state.session.remoteUser
});
} }
/** /**