cduss Claude Opus 4.5 commited on
Commit
d1a2d70
·
1 Parent(s): 9366b67

Fix GStreamer signaling protocol handling

Browse files

- Fix producer list parsing (array not object)
- Consumer acts as answerer (GStreamer webrtcsink sends offer)
- Remove STUN server for LAN connections
- Better error handling in peer message handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +48 -49
index.html CHANGED
@@ -277,20 +277,21 @@
277
  const container = document.getElementById('producerList');
278
  container.innerHTML = '';
279
 
280
- if (!producers || Object.keys(producers).length === 0) {
 
281
  container.innerHTML = '<em style="color: #666;">No producers available</em>';
282
  return;
283
  }
284
 
285
- for (const [peerId, meta] of Object.entries(producers)) {
286
  const div = document.createElement('div');
287
  div.className = 'producer-item';
288
- div.innerHTML = `<strong>${meta.name || 'Unknown'}</strong><br><small>${peerId}</small>`;
289
- div.onclick = () => selectProducer(peerId, div);
290
  container.appendChild(div);
291
  }
292
 
293
- log(`Found ${Object.keys(producers).length} producer(s)`, 'success');
294
  }
295
 
296
  function selectProducer(peerId, element) {
@@ -311,8 +312,9 @@
311
  log('Creating peer connection...');
312
  updateWebrtcStatus('connecting');
313
 
 
314
  const config = {
315
- iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
316
  };
317
 
318
  peerConnection = new RTCPeerConnection(config);
@@ -341,7 +343,8 @@
341
 
342
  peerConnection.oniceconnectionstatechange = () => {
343
  log(`ICE connection state: ${peerConnection.iceConnectionState}`);
344
- if (peerConnection.iceConnectionState === 'connected') {
 
345
  updateWebrtcStatus('connected');
346
  document.getElementById('stopStreamBtn').disabled = false;
347
  document.getElementById('sendPoseBtn').disabled = false;
@@ -358,54 +361,50 @@
358
  setupDataChannel(dataChannel);
359
  };
360
 
361
- // Add transceivers for receiving
362
- peerConnection.addTransceiver('video', { direction: 'recvonly' });
363
- peerConnection.addTransceiver('audio', { direction: 'recvonly' });
364
-
365
- // Create and send offer
366
- try {
367
- const offer = await peerConnection.createOffer();
368
- await peerConnection.setLocalDescription(offer);
369
-
370
- log('Sending SDP offer to signaling server...');
371
- signalingWs.send(JSON.stringify({
372
- type: 'startSession',
373
- peerId: selectedProducerId
374
- }));
375
-
376
- // Send SDP
377
- signalingWs.send(JSON.stringify({
378
- type: 'peer',
379
- sessionId: selectedProducerId,
380
- sdp: {
381
- type: 'offer',
382
- sdp: offer.sdp
383
- }
384
- }));
385
- } catch (e) {
386
- log(`Failed to create offer: ${e.message}`, 'error');
387
- }
388
  }
389
 
390
  async function handlePeerMessage(msg) {
391
- if (msg.sdp) {
392
- log(`Received SDP ${msg.sdp.type}`);
393
- await peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp));
 
394
 
395
- if (msg.sdp.type === 'offer') {
396
- const answer = await peerConnection.createAnswer();
397
- await peerConnection.setLocalDescription(answer);
398
- signalingWs.send(JSON.stringify({
399
- type: 'peer',
400
- sessionId: selectedProducerId,
401
- sdp: { type: 'answer', sdp: answer.sdp }
402
- }));
 
 
 
 
 
 
 
 
403
  }
404
- }
405
 
406
- if (msg.ice) {
407
- log('Received ICE candidate');
408
- await peerConnection.addIceCandidate(new RTCIceCandidate(msg.ice));
 
 
 
 
 
 
 
409
  }
410
  }
411
 
 
277
  const container = document.getElementById('producerList');
278
  container.innerHTML = '';
279
 
280
+ // GStreamer returns array of {id, meta} objects
281
+ if (!producers || !Array.isArray(producers) || producers.length === 0) {
282
  container.innerHTML = '<em style="color: #666;">No producers available</em>';
283
  return;
284
  }
285
 
286
+ for (const producer of producers) {
287
  const div = document.createElement('div');
288
  div.className = 'producer-item';
289
+ div.innerHTML = `<strong>${producer.meta?.name || 'Unknown'}</strong><br><small>${producer.id}</small>`;
290
+ div.onclick = () => selectProducer(producer.id, div);
291
  container.appendChild(div);
292
  }
293
 
294
+ log(`Found ${producers.length} producer(s)`, 'success');
295
  }
296
 
297
  function selectProducer(peerId, element) {
 
312
  log('Creating peer connection...');
313
  updateWebrtcStatus('connecting');
314
 
315
+ // LAN connection - no STUN needed for local network
316
  const config = {
317
+ iceServers: []
318
  };
319
 
320
  peerConnection = new RTCPeerConnection(config);
 
343
 
344
  peerConnection.oniceconnectionstatechange = () => {
345
  log(`ICE connection state: ${peerConnection.iceConnectionState}`);
346
+ if (peerConnection.iceConnectionState === 'connected' ||
347
+ peerConnection.iceConnectionState === 'completed') {
348
  updateWebrtcStatus('connected');
349
  document.getElementById('stopStreamBtn').disabled = false;
350
  document.getElementById('sendPoseBtn').disabled = false;
 
361
  setupDataChannel(dataChannel);
362
  };
363
 
364
+ // GStreamer webrtcsink is the offerer, we are the answerer
365
+ // Just request to start a session, then wait for SDP offer
366
+ log('Requesting session with producer...');
367
+ signalingWs.send(JSON.stringify({
368
+ type: 'startSession',
369
+ peerId: selectedProducerId
370
+ }));
371
+ // SDP offer will arrive via handlePeerMessage
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  }
373
 
374
  async function handlePeerMessage(msg) {
375
+ if (!peerConnection) {
376
+ log('No peer connection, ignoring message', 'error');
377
+ return;
378
+ }
379
 
380
+ try {
381
+ if (msg.sdp) {
382
+ log(`Received SDP ${msg.sdp.type}`);
383
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(msg.sdp));
384
+
385
+ if (msg.sdp.type === 'offer') {
386
+ log('Creating answer...');
387
+ const answer = await peerConnection.createAnswer();
388
+ await peerConnection.setLocalDescription(answer);
389
+ log('Sending SDP answer');
390
+ signalingWs.send(JSON.stringify({
391
+ type: 'peer',
392
+ sessionId: selectedProducerId,
393
+ sdp: { type: 'answer', sdp: answer.sdp }
394
+ }));
395
+ }
396
  }
 
397
 
398
+ if (msg.ice) {
399
+ log('Received ICE candidate');
400
+ try {
401
+ await peerConnection.addIceCandidate(new RTCIceCandidate(msg.ice));
402
+ } catch (e) {
403
+ log(`Failed to add ICE candidate: ${e.message}`, 'error');
404
+ }
405
+ }
406
+ } catch (e) {
407
+ log(`Error handling peer message: ${e.message}`, 'error');
408
  }
409
  }
410