feat(wsService, WebRTCManager): enhance call handling with media type support and negotiation improvements
Some checks failed
Frontend CI / ota-android (push) Successful in 11m25s
Frontend CI / build-and-push-web (push) Successful in 11m30s
Frontend CI / build-android-apk (push) Has been cancelled

- Added optional media_type to WSCallIncomingMessage for better call type differentiation.
- Updated call_type assignment in WebSocketService to prioritize media_type if available.
- Removed initialOfferCreated flag in WebRTCManager to simplify negotiation logic.
- Improved negotiation handling by releasing the negotiation lock after offer creation, ensuring smoother call setup.
This commit is contained in:
lafay
2026-03-28 01:06:51 +08:00
parent f6176c945b
commit 7c33409624
2 changed files with 14 additions and 11 deletions

View File

@@ -38,7 +38,6 @@ class WebRTCManager {
private isInitiator = false;
private callType: CallType = 'voice';
private isNegotiating = false;
private initialOfferCreated = false; // Flag to prevent duplicate offer creation
async initialize(iceServers: ICEServer[] = []): Promise<void> {
if (this.peerConnection) {
@@ -103,19 +102,17 @@ class WebRTCManager {
// @ts-ignore
pc.onnegotiationneeded = async () => {
console.log('[WebRTC] Negotiation needed, signalingState:', pc.signalingState, 'isNegotiating:', this.isNegotiating);
// Check if peer connection is still valid
if (!this.peerConnection || this.peerConnection !== pc) {
console.log('[WebRTC] PeerConnection changed or disposed, skipping negotiation');
return;
}
// Only start negotiation if:
// 1. We're in stable state
// 2. Not already negotiating
// 3. We are the initiator
// 4. Initial offer has NOT been created yet (prevent duplicate during startCall)
if (pc.signalingState === 'stable' && !this.isNegotiating && this.isInitiator && !this.initialOfferCreated) {
if (pc.signalingState === 'stable' && !this.isNegotiating) {
try {
this.isNegotiating = true;
const offer = await this.createOffer();
@@ -132,7 +129,7 @@ class WebRTCManager {
}, 500);
}
} else {
console.log('[WebRTC] Skipping negotiation: state=', pc.signalingState, 'isNegotiating=', this.isNegotiating, 'isInitiator=', this.isInitiator, 'initialOfferCreated=', this.initialOfferCreated);
console.log('[WebRTC] Skipping negotiation: state=', pc.signalingState, 'isNegotiating=', this.isNegotiating);
}
};
@@ -199,7 +196,7 @@ class WebRTCManager {
this.isInitiator = isInitiator;
this.callType = callType;
this.initialOfferCreated = true; // Set flag BEFORE creating connection to prevent onnegotiationneeded
this.isNegotiating = true; // Prevent onnegotiationneeded from firing during setup
this.peerConnection = this.createPeerConnection();
// Setup transceivers FIRST - this ensures consistent m-line order
@@ -207,7 +204,7 @@ class WebRTCManager {
// Now add local tracks to transceivers
const transceivers = this.peerConnection.getTransceivers();
// Add audio track to audio transceiver
const audioTrack = this.localStream.getAudioTracks()[0];
const audioTransceiver = transceivers.find(t => t.receiver.track?.kind === 'audio');
@@ -227,8 +224,14 @@ class WebRTCManager {
if (isInitiator) {
// For initiator, create offer directly here
const offer = await this.createOffer();
// Release negotiation lock after a delay to allow state to settle
setTimeout(() => {
this.isNegotiating = false;
}, 500);
return offer;
}
// For non-initiator, release lock immediately since no offer is created
this.isNegotiating = false;
return null;
}
@@ -547,7 +550,6 @@ class WebRTCManager {
this.eventHandlers.clear();
this.pendingCandidates = [];
this.isNegotiating = false;
this.initialOfferCreated = false;
if (this.localStream) {
this.localStream.getTracks().forEach((track) => track.stop());

View File

@@ -48,6 +48,7 @@ export interface WSCallIncomingMessage {
conversation_id: string;
caller_id: string;
call_type: string;
media_type?: string;
created_at: number;
lifetime?: number;
ice_servers?: ICEServerConfig[];
@@ -621,7 +622,7 @@ class WebSocketService {
call_id: payload.call_id,
conversation_id: payload.conversation_id,
caller_id: payload.caller_id,
call_type: payload.call_type,
call_type: payload.media_type || payload.call_type,
created_at: payload.created_at,
lifetime: payload.lifetime,
ice_servers: payload.ice_servers,