-
Notifications
You must be signed in to change notification settings - Fork 1
Fixes ICE candidate issue #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,9 +41,12 @@ export const useWebRTC = (options?: UseWebRTCOptions) => { | |
|
|
||
| // Add local stream tracks | ||
| if (localStreamRef.current) { | ||
| console.log('Adding local tracks to peer connection for:', clientId); | ||
| localStreamRef.current.getTracks().forEach((track) => { | ||
| pc.addTrack(track, localStreamRef.current!); | ||
| }); | ||
| } else { | ||
| console.warn('No local stream available when creating peer connection for:', clientId); | ||
| } | ||
|
|
||
| // Handle remote stream | ||
|
|
@@ -96,15 +99,35 @@ export const useWebRTC = (options?: UseWebRTCOptions) => { | |
| }, [createPeerConnection]); | ||
|
|
||
| const handleOffer = useCallback(async (clientId: string, offer: RTCSessionDescriptionInit): Promise<RTCSessionDescriptionInit | null> => { | ||
| const pc = createPeerConnection(clientId); | ||
| // Check if we already have a peer connection for this client | ||
| let pc = peerConnectionsRef.current.get(clientId); | ||
| if (!pc) { | ||
| console.log('Creating new peer connection for offer from:', clientId); | ||
| pc = createPeerConnection(clientId); | ||
| } else { | ||
| console.log('Reusing existing peer connection for offer from:', clientId); | ||
| // If remote description is already set, we might be handling a duplicate offer | ||
| if (pc.remoteDescription) { | ||
| console.warn('Remote description already set for:', clientId, 'current state:', pc.signalingState); | ||
| // If we're in a state where we can set it again, continue, otherwise return | ||
| if (pc.signalingState === 'stable') { | ||
| // We can set it again | ||
| } else { | ||
| console.error('Cannot set remote description, signaling state:', pc.signalingState); | ||
| return null; | ||
| } | ||
| } | ||
|
Comment on lines
+113
to
+119
|
||
| } | ||
|
|
||
| try { | ||
| await pc.setRemoteDescription(new RTCSessionDescription(offer)); | ||
| console.log('Set remote description for:', clientId, 'signaling state:', pc.signalingState); | ||
| const answer = await pc.createAnswer(); | ||
| await pc.setLocalDescription(answer); | ||
| console.log('Created and set local answer for:', clientId); | ||
| return answer; | ||
| } catch (error) { | ||
| console.error('Error handling offer:', error); | ||
| console.error('Error handling offer:', error, 'signaling state:', pc?.signalingState); | ||
| return null; | ||
| } | ||
| }, [createPeerConnection]); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,11 +51,14 @@ export default function MeetingRoom() { | |
| } = useWebRTC({ | ||
| onIceCandidate: (clientId: string, candidate: RTCIceCandidate) => { | ||
| if (sendMessageRef.current) { | ||
| console.log('Sending ICE candidate to:', clientId); | ||
| sendMessageRef.current({ | ||
| type: WSMessageType.ICE_CANDIDATE, | ||
| target: clientId, | ||
| data: candidate.toJSON(), | ||
| }); | ||
| } else { | ||
| console.warn('sendMessageRef is null, cannot send ICE candidate to:', clientId); | ||
|
Comment on lines
+54
to
+61
|
||
| } | ||
| }, | ||
| }); | ||
|
|
@@ -78,22 +81,38 @@ export default function MeetingRoom() { | |
|
|
||
| // WebSocket message handler | ||
| const handleWebSocketMessage = useCallback((message: WSMessage) => { | ||
| console.log('Received WebSocket message:', message.type); | ||
| console.log('Received WebSocket message:', message.type, 'Full message:', message); | ||
|
|
||
| switch (message.type) { | ||
| case WSMessageType.USER_JOINED: | ||
| if (message.clientId && message.clientId !== clientId) { | ||
| setOtherParticipants((prev) => [...prev, message.clientId!]); | ||
| // Create offer to new participant | ||
| createOffer(message.clientId).then((offer) => { | ||
| if (offer && sendMessageRef.current) { | ||
| sendMessageRef.current({ | ||
| type: WSMessageType.OFFER, | ||
| target: message.clientId, | ||
| data: offer, | ||
| }); | ||
| console.log('User joined:', message.clientId); | ||
| setOtherParticipants((prev) => { | ||
| if (!prev.includes(message.clientId!)) { | ||
| return [...prev, message.clientId!]; | ||
| } | ||
| return prev; | ||
| }); | ||
| // Create offer to new participant only if we don't already have a connection | ||
| if (!hasPeerConnection(message.clientId) && sendMessageRef.current) { | ||
| console.log('Creating offer to:', message.clientId); | ||
| createOffer(message.clientId).then((offer) => { | ||
| if (offer && sendMessageRef.current) { | ||
| console.log('Sending offer to:', message.clientId); | ||
| sendMessageRef.current({ | ||
| type: WSMessageType.OFFER, | ||
| target: message.clientId, | ||
| data: offer, | ||
| }); | ||
| } else { | ||
| console.error('Failed to create offer or sendMessageRef is null'); | ||
| } | ||
| }).catch((error) => { | ||
| console.error('Error creating offer:', error); | ||
| }); | ||
| } else { | ||
| console.log('Already have peer connection with:', message.clientId); | ||
| } | ||
| } | ||
| break; | ||
|
|
||
|
|
@@ -114,52 +133,49 @@ export default function MeetingRoom() { | |
| const others = message.participants.filter((id: string) => id !== clientId); | ||
| setOtherParticipants(others); | ||
|
|
||
| // Create offers to existing participants when we first join | ||
| // Use hasPeerConnection to avoid creating duplicate offers | ||
| if (!isInitialized && others.length > 0) { | ||
| others.forEach((otherId: string) => { | ||
| // Only create offer if we don't already have a peer connection | ||
| // This prevents duplicate offers if we already received one from this peer | ||
| if (!hasPeerConnection(otherId) && sendMessageRef.current) { | ||
| createOffer(otherId).then((offer) => { | ||
| if (offer && sendMessageRef.current) { | ||
| sendMessageRef.current({ | ||
| type: WSMessageType.OFFER, | ||
| target: otherId, | ||
| data: offer, | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| // When we first join, we receive the list of existing participants | ||
| // We should wait for existing participants to send us offers (via USER_JOINED on their side) | ||
| // Don't create offers here - let existing participants initiate via USER_JOINED | ||
| setIsInitialized(true); | ||
| } | ||
| break; | ||
|
|
||
| case WSMessageType.OFFER: | ||
| if (message.from && message.data) { | ||
| console.log('Received offer from:', message.from, 'localStream ready:', !!localStream); | ||
| // Handle offer - the useWebRTC hook will check if local stream is ready | ||
| handleOffer(message.from, message.data).then((answer) => { | ||
| if (answer && sendMessageRef.current) { | ||
| console.log('Sending answer to:', message.from); | ||
| sendMessageRef.current({ | ||
| type: WSMessageType.ANSWER, | ||
| target: message.from, | ||
| data: answer, | ||
| }); | ||
| } else { | ||
| console.error('Failed to create answer or sendMessageRef is null'); | ||
| } | ||
| }).catch((error) => { | ||
| console.error('Error handling offer:', error); | ||
| }); | ||
| } | ||
| break; | ||
|
|
||
| case WSMessageType.ANSWER: | ||
| if (message.from && message.data) { | ||
| handleAnswer(message.from, message.data); | ||
| console.log('Received answer from:', message.from); | ||
| handleAnswer(message.from, message.data).catch((error) => { | ||
| console.error('Error handling answer:', error); | ||
| }); | ||
| } | ||
| break; | ||
|
|
||
| case WSMessageType.ICE_CANDIDATE: | ||
| if (message.from && message.data) { | ||
| handleRemoteIceCandidate(message.from, message.data); | ||
| console.log('Received ICE candidate from:', message.from); | ||
| handleRemoteIceCandidate(message.from, message.data).catch((error) => { | ||
| console.error('Error handling ICE candidate:', error); | ||
| }); | ||
| } | ||
| break; | ||
|
|
||
|
|
@@ -208,6 +224,10 @@ export default function MeetingRoom() { | |
| }); | ||
| } | ||
| break; | ||
|
|
||
| default: | ||
| console.warn('Unhandled WebSocket message type:', message.type, message); | ||
| break; | ||
| } | ||
| }, [clientId, createOffer, handleOffer, handleAnswer, handleRemoteIceCandidate, removePeer, hasPeerConnection, isInitialized]); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment suggests CORS_ORIGINS is an extra environment variable to be ignored, but it's actually being used by the
cors_origins_strproperty method viaos.getenv(). This comment is misleading since CORS_ORIGINS is a required configuration, not an extra variable to ignore.