log优化

This commit is contained in:
2026-05-24 14:16:28 +08:00
parent e48a6eae3c
commit 518f8a94b3
12 changed files with 213 additions and 1112 deletions

View File

@@ -8,6 +8,9 @@ import { AUDIO_CONFIG, VAD_CONFIG, VIDEO_ONLY_CONSTRAINT, buildVideoConstraints,
import { buildStatsLogPayload, createAudioAnalyser, getAudioLevel } from './media-monitoring.js';
import { bindInviteSocketEvents, buildSocketUserInfoPayload, createSignalingInstance, ensureSignalingStarted, getActiveSignalingInstance, sendInviteSignal, sendSocketUserInfo } from './signaling-session.js';
import { getNetworkQualityFromSummary, summarizeInboundStats } from './webrtc-stats.js';
import { createLogger } from './logger.js';
const logger = createLogger('store');
class CallStateManager {
constructor() {
this.state = {
@@ -55,11 +58,11 @@ class CallStateManager {
}
if (settings.resolution) {
this._savedResolution = settings.resolution;
console.log(`已恢复分辨率设置: ${settings.resolution.width}x${settings.resolution.height}`);
logger.debug(`已恢复分辨率设置: ${settings.resolution.width}x${settings.resolution.height}`);
}
}
catch (error) {
console.error('Error loading user settings:', error);
logger.error('Error loading user settings:', error);
}
}
}
@@ -69,9 +72,9 @@ class CallStateManager {
}
async getLocalStream() {
try {
console.log('Requesting camera permission...');
logger.debug('Requesting camera permission...');
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
console.error('getUserMedia is not supported');
logger.error('getUserMedia is not supported');
throw new Error('getUserMedia is not supported');
}
const videoConstraints = buildVideoConstraints(this._savedResolution);
@@ -79,13 +82,13 @@ class CallStateManager {
video: videoConstraints,
audio: AUDIO_CONFIG
});
console.log('Stream obtained successfully:', stream);
console.log('Video tracks:', stream.getVideoTracks());
console.log('Audio tracks:', stream.getAudioTracks());
logger.debug('Stream obtained successfully:', stream);
logger.debug('Video tracks:', stream.getVideoTracks());
logger.debug('Audio tracks:', stream.getAudioTracks());
this.state.localStream = stream;
this.state.session.localUser.mediaState.video = true;
this.state.session.localUser.mediaState.audio = true;
console.log('Local stream stored, notifying UI...');
logger.debug('Local stream stored, notifying UI...');
this.notify({ type: 'LOCAL_STREAM_OBTAINED', stream });
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: true });
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'audio', value: true });
@@ -93,7 +96,7 @@ class CallStateManager {
this.startActivityDetection(this.state.localStream, { isLocal: true });
}
catch (error) {
console.error('Error getting local stream:', error);
logger.error('Error getting local stream:', error);
this.state.session.localUser.mediaState.video = false;
this.state.session.localUser.mediaState.audio = false;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: false });
@@ -133,7 +136,7 @@ class CallStateManager {
this.startActivityDetection(this.state.localStream, { isLocal: true });
}
catch (error) {
console.error('Error reopening video:', error);
logger.error('Error reopening video:', error);
this.state.session.localUser.mediaState.video = false;
this._notifyLocalMediaChange('video', false);
}
@@ -162,7 +165,7 @@ class CallStateManager {
if (!this.renderstreaming) {
return;
}
console.log('Updating video track in WebRTC connection');
logger.debug('Updating video track in WebRTC connection');
if (this.role === 'host') {
const participantIds = Object.keys(this.state.remoteStreams);
for (const participantId of participantIds) {
@@ -190,12 +193,12 @@ class CallStateManager {
for (const transceiver of videoTransceivers) {
try {
await transceiver.sender.replaceTrack(newVideoTrack);
console.log(participantId
logger.debug(participantId
? `Replaced video track for participant ${participantId}`
: 'Successfully replaced video track');
}
catch (error) {
console.error(participantId
logger.error(participantId
? `Error replacing video track for ${participantId}:`
: 'Error replacing video track:', error);
}
@@ -205,14 +208,14 @@ class CallStateManager {
try {
if (participantId) {
this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' }, participantId);
console.log(`Added new video transceiver for participant ${participantId}`);
logger.debug(`Added new video transceiver for participant ${participantId}`);
return;
}
this.renderstreaming.addTransceiver(newVideoTrack, { direction: 'sendonly' });
console.log('Added new video transceiver');
logger.debug('Added new video transceiver');
}
catch (error) {
console.error(participantId
logger.error(participantId
? `Error adding video transceiver for ${participantId}:`
: 'Error adding video transceiver:', error);
}
@@ -262,10 +265,10 @@ class CallStateManager {
this._signaling = signaling;
this._inviteEventSignaling = bindInviteSocketEvents(this._signaling, this.socketEventHandlers, this._inviteEventSignaling);
if (reused) {
console.log('Signaling already connected, reusing existing instance');
logger.debug('Signaling already connected, reusing existing instance');
return this._signaling;
}
console.log('Signaling connected (WebSocket only, no room yet)');
logger.debug('Signaling connected (WebSocket only, no room yet)');
return this._signaling;
}
getActiveSignaling() {
@@ -295,7 +298,7 @@ class CallStateManager {
this.state.session.status = 'connecting';
this.notify({ type: 'CALL_STATUS_CHANGE', status: 'connecting' });
if (!this.state.localStream) {
console.log('Local stream not available, waiting for initialization...');
logger.debug('Local stream not available, waiting for initialization...');
await new Promise((resolve) => {
const checkStream = () => {
if (this.state.localStream) {
@@ -320,7 +323,7 @@ class CallStateManager {
}
_registerCallbacks() {
this.renderstreaming.onNewPeer = (participantId) => {
console.log(`New peer created for ${participantId}, adding local tracks`);
logger.debug(`New peer created for ${participantId}, adding local tracks`);
if (this.state.localStream) {
const tracks = this.state.localStream.getTracks();
for (const track of tracks) {
@@ -337,7 +340,7 @@ class CallStateManager {
if (data.participantId) {
this.selfParticipantId = data.participantId;
}
console.log(`Connected as ${this.role}, participantId: ${this.selfParticipantId}`);
logger.debug(`Connected as ${this.role}, participantId: ${this.selfParticipantId}`);
}
this.state.session.status = 'ongoing';
this.notify({ type: 'CALL_STATUS_CHANGE', status: 'ongoing' });
@@ -349,7 +352,7 @@ class CallStateManager {
}
this.state.session.localUser.mediaState.audio = false;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'audio', value: false });
console.log('Participant joined with audio muted by default');
logger.debug('Participant joined with audio muted by default');
}
this.sendMessage('user-info', {
id: this.state.session.localUser.id,
@@ -361,16 +364,16 @@ class CallStateManager {
this.showStatsMessage();
}
else {
console.error('Local stream is not available');
logger.error('Local stream is not available');
showNotification('本地视频流不可用', 'error');
}
};
this.renderstreaming.onDisconnect = () => {
console.log('Received disconnect from server, host left or room closed');
logger.debug('Received disconnect from server, host left or room closed');
this.hangUp();
};
this.renderstreaming.onGotAnswer = (connectionId) => {
console.log('SDP Answer received, resetting encoding parameters for connectionId:', connectionId);
logger.debug('SDP Answer received, resetting encoding parameters for connectionId:', connectionId);
if (this.role === 'host') {
const allParticipantIds = Object.keys(this.state.remoteStreams || {});
for (const pid of allParticipantIds) {
@@ -382,13 +385,13 @@ class CallStateManager {
}
};
this.renderstreaming.onParticipantJoined = (participantId) => {
console.log(`Participant joined: ${participantId}`);
logger.debug(`Participant joined: ${participantId}`);
this._upsertParticipant(participantId);
this._notifyParticipantsUpdate();
this.broadcastParticipantsList();
};
this.renderstreaming.onParticipantLeft = (participantId) => {
console.log(`Participant left: ${participantId}, room still active`);
logger.debug(`Participant left: ${participantId}, room still active`);
this.updateRemoteUserStatus('offline');
this.updateRemoteUserNetworkQuality('no_signal');
showNotification('对方已离开通话', 'warning');
@@ -428,14 +431,14 @@ class CallStateManager {
}
this.durationSynced = false;
const isHost = this.role === 'host';
console.log(`Disconnect peer on ${this.connectionId}. Role: ${this.role}`);
logger.debug(`Disconnect peer on ${this.connectionId}. Role: ${this.role}`);
if (this.renderstreaming) {
try {
await this.renderstreaming.deleteConnection();
await this.renderstreaming.stop();
}
catch (error) {
console.error('Error during hangUp:', error);
logger.error('Error during hangUp:', error);
}
this.renderstreaming = null;
}
@@ -463,7 +466,7 @@ class CallStateManager {
const isHost = this.role === 'host';
const targetStream = this._getOrCreateRemoteStream(trackParticipantId, isHost);
this._replaceTrackOfSameKind(targetStream, data.track);
console.log('Added new track:', data.track.kind, 'for participant:', trackParticipantId);
logger.debug('Added new track:', data.track.kind, 'for participant:', trackParticipantId);
if (isHost && !this.state.participants[trackParticipantId]) {
this._upsertParticipant(trackParticipantId);
this._notifyParticipantsUpdate();
@@ -500,7 +503,7 @@ class CallStateManager {
const existingTracks = targetStream.getTracks().filter(existingTrack => existingTrack.kind === track.kind);
existingTracks.forEach(existingTrack => {
targetStream.removeTrack(existingTrack);
console.log('Removed old track:', existingTrack.kind);
logger.debug('Removed old track:', existingTrack.kind);
});
targetStream.addTrack(track);
}
@@ -512,13 +515,13 @@ class CallStateManager {
connectionId: trackParticipantId,
isHost
});
console.log('Notified UI about remote stream update');
logger.debug('Notified UI about remote stream update');
};
if (trackKind === 'audio' && targetStream.getVideoTracks().length === 0) {
console.log('Audio track arrived first, delaying stream notification for video track...');
logger.debug('Audio track arrived first, delaying stream notification for video track...');
setTimeout(() => {
const nowHasVideo = targetStream.getVideoTracks().length > 0;
console.log(`After delay, stream has video: ${nowHasVideo}`);
logger.debug(`After delay, stream has video: ${nowHasVideo}`);
notifyStreamUpdate();
}, 200);
return;
@@ -526,7 +529,7 @@ class CallStateManager {
notifyStreamUpdate();
}
_handleRenderStreamingMessage(data) {
console.log('收到信令消息:', data);
logger.debug('收到信令消息:', data);
switch (data.type) {
case 'chat-message':
this._handleChatMessage(data);
@@ -578,7 +581,7 @@ class CallStateManager {
}
}
_handleMediaStateChangedMessage(data) {
console.log('收到媒体状态更新:', data.data, 'from participant:', data.participantId);
logger.debug('收到媒体状态更新:', data.data, 'from participant:', data.participantId);
if (this.role === 'host') {
if (data.participantId && this.state.participants[data.participantId]) {
this._upsertParticipant(data.participantId, {
@@ -600,12 +603,12 @@ class CallStateManager {
if (data.participantId === this.selfParticipantId) {
return;
}
console.log('Received media-state-changed from Host, updating remoteUser:', data.data);
logger.debug('Received media-state-changed from Host, updating remoteUser:', data.data);
this.updateRemoteMedia(data.data, data.participantId);
this._notifyParticipantsUpdate();
}
_handleUserInfoMessage(data) {
console.log('收到用户信息:', data.data, 'from participant:', data.participantId);
logger.debug('收到用户信息:', data.data, 'from participant:', data.participantId);
if (!data.data) {
return;
}
@@ -629,7 +632,7 @@ class CallStateManager {
if (this.role === 'host' || !data.data) {
return;
}
console.log('收到成员同步列表:', data.data);
logger.debug('收到成员同步列表:', data.data);
this.state.participants = omitParticipant(data.data, this.selfParticipantId);
this._notifyParticipantsUpdate();
this._syncCallDuration(data.callDuration);
@@ -646,7 +649,7 @@ class CallStateManager {
this.durationSynced = true;
this._startDurationTimer();
this.notify({ type: 'DURATION_UPDATE', duration: this.state.session.duration });
console.log(`Call duration synced: ${callDuration} seconds`);
logger.debug(`Call duration synced: ${callDuration} seconds`);
}
_startDurationTimer() {
if (this.durationInterval) {
@@ -706,7 +709,7 @@ class CallStateManager {
data: memberList,
callDuration: this.state.session.duration
});
console.log('Broadcast participants list:', Object.keys(memberList));
logger.debug('Broadcast participants list:', Object.keys(memberList));
}
setCodecPreferences(participantId) {
const capabilities = RTCRtpSender.getCapabilities('video');
@@ -744,10 +747,10 @@ class CallStateManager {
t.setCodecPreferences(selectedCodecs);
}
catch (e) {
console.error('Error setting codec preferences:', e);
logger.error('Error setting codec preferences:', e);
}
});
console.log(`Codec preferences set: ${selectedCodecs.map(c => c.mimeType).join(' > ')}`);
logger.debug(`Codec preferences set: ${selectedCodecs.map(c => c.mimeType).join(' > ')}`);
}
}
}
@@ -777,10 +780,10 @@ class CallStateManager {
params.degradationPreference = 'maintain-resolution';
}
sender.setParameters(params);
console.log(`Set video encoding: maxBitrate=${maxBitrate / 1000000}Mbps, scaleResolutionDownBy=1.0, xGoogleMinBitrate=${Math.floor(maxBitrate * 0.5)}${participantId ? ` for ${participantId}` : ''}`);
logger.debug(`Set video encoding: maxBitrate=${maxBitrate / 1000000}Mbps, scaleResolutionDownBy=1.0, xGoogleMinBitrate=${Math.floor(maxBitrate * 0.5)}${participantId ? ` for ${participantId}` : ''}`);
}
catch (error) {
console.error('Error setting video encoding parameters:', error);
logger.error('Error setting video encoding parameters:', error);
}
}
}
@@ -802,7 +805,7 @@ class CallStateManager {
height: { ideal: height, max: height },
frameRate: { ideal: 30, max: 30 }
});
console.log(`分辨率已切换为 ${width}x${height}`);
logger.debug(`分辨率已切换为 ${width}x${height}`);
const maxBitrate = getTargetResolutionBitrate(height);
this._applyMaxBitrate(maxBitrate);
const userSettings = JSON.parse(localStorage.getItem('userSettings') || '{}');
@@ -812,7 +815,7 @@ class CallStateManager {
showNotification('已切换为 ' + label, 'success');
}
catch (error) {
console.error('切换分辨率失败:', error);
logger.error('切换分辨率失败:', error);
showNotification('切换分辨率失败,摄像头可能不支持该分辨率', 'error');
}
}
@@ -835,10 +838,10 @@ class CallStateManager {
}
params.encodings[0].maxBitrate = maxBitrate;
sender.setParameters(params);
console.log(`Updated maxBitrate to ${maxBitrate} for ${pid || 'self'}`);
logger.debug(`Updated maxBitrate to ${maxBitrate} for ${pid || 'self'}`);
}
catch (error) {
console.error('Error updating maxBitrate:', error);
logger.error('Error updating maxBitrate:', error);
}
}
}
@@ -865,7 +868,7 @@ class CallStateManager {
this.updateRemoteMedia({ isSpeaking });
}
async endCall() {
console.log(`endCall called. Role: ${this.role}`);
logger.debug(`endCall called. Role: ${this.role}`);
await this.hangUp();
}
async joinCall(connectionId) {
@@ -898,7 +901,7 @@ class CallStateManager {
}
}
catch (error) {
console.error('Error detecting network quality:', error);
logger.error('Error detecting network quality:', error);
}
}
startActivityDetection(stream, { isLocal = false } = {}) {
@@ -936,10 +939,10 @@ class CallStateManager {
}
};
detectActivity();
console.log(`${isLocal ? 'Local' : 'Remote'} activity detection started`);
logger.debug(`${isLocal ? 'Local' : 'Remote'} activity detection started`);
}
catch (error) {
console.error(`Error starting ${isLocal ? 'local' : 'remote'} activity detection:`, error);
logger.error(`Error starting ${isLocal ? 'local' : 'remote'} activity detection:`, error);
}
}
startNetworkQualityDetection() {
@@ -958,7 +961,7 @@ class CallStateManager {
userId: this.state.session.localUser.id,
...this.state.session.localUser.mediaState
};
console.log('[WebSocket Emit] media-state-changed:', payload);
logger.debug('[WebSocket Emit] media-state-changed:', payload);
if (this.renderstreaming) {
this.renderstreaming.sendMessage({
type: 'media-state-changed',
@@ -967,7 +970,7 @@ class CallStateManager {
}
}
async showStatsMessage() {
console.log('Showing stats message');
logger.debug('Showing stats message');
await this.detectNetworkQuality();
this.statsInterval = setInterval(async () => {
if (!this.renderstreaming) {
@@ -980,19 +983,19 @@ class CallStateManager {
}
const statsSummary = summarizeInboundStats(stats);
const statsLog = buildStatsLogPayload(this.state.session.remoteUser.networkQuality, statsSummary);
console.log('=== WebRTC Statistics ===');
console.log(`Network Quality: ${statsLog.networkQuality}`);
console.log('Video Stats:', statsLog.video);
console.log('Audio Stats:', statsLog.audio);
console.log('========================');
logger.debug('=== WebRTC Statistics ===');
logger.debug(`Network Quality: ${statsLog.networkQuality}`);
logger.debug('Video Stats:', statsLog.video);
logger.debug('Audio Stats:', statsLog.audio);
logger.debug('========================');
}
catch (error) {
console.error('Error showing stats message:', error);
logger.error('Error showing stats message:', error);
}
}, 5000);
}
clearStatsMessage() {
console.log('Clearing stats message');
logger.debug('Clearing stats message');
if (this.statsInterval) {
clearInterval(this.statsInterval);
this.statsInterval = null;