client.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. const iceConnectionLog = document.getElementById('ice-connection-state'),
  2. iceGatheringLog = document.getElementById('ice-gathering-state'),
  3. signalingLog = document.getElementById('signaling-state'),
  4. dataChannelLog = document.getElementById('data-channel');
  5. const clientId = randomId(10);
  6. const websocket = new WebSocket('ws://127.0.0.1:8000/' + clientId);
  7. websocket.onopen = () => {
  8. document.getElementById('start').disabled = false;
  9. }
  10. websocket.onmessage = async (evt) => {
  11. if (typeof evt.data !== 'string') {
  12. return;
  13. }
  14. const message = JSON.parse(evt.data);
  15. if (message.type == "offer") {
  16. document.getElementById('offer-sdp').textContent = message.sdp;
  17. await handleOffer(message)
  18. }
  19. }
  20. let pc = null;
  21. let dc = null;
  22. function createPeerConnection() {
  23. const config = {
  24. bundlePolicy: "max-bundle",
  25. };
  26. if (document.getElementById('use-stun').checked) {
  27. config.iceServers = [{urls: ['stun:stun.l.google.com:19302']}];
  28. }
  29. let pc = new RTCPeerConnection(config);
  30. // Register some listeners to help debugging
  31. pc.addEventListener('iceconnectionstatechange', () =>
  32. iceConnectionLog.textContent += ' -> ' + pc.iceConnectionState);
  33. iceConnectionLog.textContent = pc.iceConnectionState;
  34. pc.addEventListener('icegatheringstatechange', () =>
  35. iceGatheringLog.textContent += ' -> ' + pc.iceGatheringState);
  36. iceGatheringLog.textContent = pc.iceGatheringState;
  37. pc.addEventListener('signalingstatechange', () =>
  38. signalingLog.textContent += ' -> ' + pc.signalingState);
  39. signalingLog.textContent = pc.signalingState;
  40. // Receive audio/video track
  41. pc.ontrack = (evt) => {
  42. document.getElementById('media').style.display = 'block';
  43. const video = document.getElementById('video');
  44. if (!video.srcObject) {
  45. video.srcObject = evt.streams[0]; // The stream groups audio and video tracks
  46. video.play();
  47. }
  48. };
  49. // Receive data channel
  50. pc.ondatachannel = (evt) => {
  51. dc = evt.channel;
  52. dc.onopen = () => {
  53. dataChannelLog.textContent += '- open\n';
  54. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  55. };
  56. let dcTimeout = null;
  57. dc.onmessage = (evt) => {
  58. if (typeof evt.data !== 'string') {
  59. return;
  60. }
  61. dataChannelLog.textContent += '< ' + evt.data + '\n';
  62. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  63. dcTimeout = setTimeout(() => {
  64. if (!dc) {
  65. return;
  66. }
  67. const message = `Pong ${currentTimestamp()}`;
  68. dataChannelLog.textContent += '> ' + message + '\n';
  69. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  70. dc.send(message);
  71. }, 1000);
  72. }
  73. dc.onclose = () => {
  74. clearTimeout(dcTimeout);
  75. dcTimeout = null;
  76. dataChannelLog.textContent += '- close\n';
  77. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  78. };
  79. }
  80. return pc;
  81. }
  82. async function waitGatheringComplete() {
  83. return new Promise((resolve) => {
  84. if (pc.iceGatheringState === 'complete') {
  85. resolve();
  86. } else {
  87. pc.addEventListener('icegatheringstatechange', () => {
  88. if (pc.iceGatheringState === 'complete') {
  89. resolve();
  90. }
  91. });
  92. }
  93. });
  94. }
  95. async function sendAnswer(pc) {
  96. await pc.setLocalDescription(await pc.createAnswer());
  97. await waitGatheringComplete();
  98. const answer = pc.localDescription;
  99. document.getElementById('answer-sdp').textContent = answer.sdp;
  100. websocket.send(JSON.stringify({
  101. id: "server",
  102. type: answer.type,
  103. sdp: answer.sdp,
  104. }));
  105. }
  106. async function handleOffer(offer) {
  107. pc = createPeerConnection();
  108. await pc.setRemoteDescription(offer);
  109. await sendAnswer(pc);
  110. }
  111. function sendRequest() {
  112. websocket.send(JSON.stringify({
  113. id: "server",
  114. type: "request",
  115. }));
  116. }
  117. function start() {
  118. document.getElementById('start').style.display = 'none';
  119. document.getElementById('stop').style.display = 'inline-block';
  120. document.getElementById('media').style.display = 'block';
  121. sendRequest();
  122. }
  123. function stop() {
  124. document.getElementById('stop').style.display = 'none';
  125. document.getElementById('media').style.display = 'none';
  126. document.getElementById('start').style.display = 'inline-block';
  127. // close data channel
  128. if (dc) {
  129. dc.close();
  130. dc = null;
  131. }
  132. // close transceivers
  133. if (pc.getTransceivers) {
  134. pc.getTransceivers().forEach((transceiver) => {
  135. if (transceiver.stop) {
  136. transceiver.stop();
  137. }
  138. });
  139. }
  140. // close local audio/video
  141. pc.getSenders().forEach((sender) => {
  142. const track = sender.track;
  143. if (track !== null) {
  144. sender.track.stop();
  145. }
  146. });
  147. // close peer connection
  148. setTimeout(() => {
  149. pc.close();
  150. pc = null;
  151. }, 500);
  152. }
  153. // Helper function to generate a random ID
  154. function randomId(length) {
  155. const characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  156. const pickRandom = () => characters.charAt(Math.floor(Math.random() * characters.length));
  157. return [...Array(length) ].map(pickRandom).join('');
  158. }
  159. // Helper function to generate a timestamp
  160. let startTime = null;
  161. function currentTimestamp() {
  162. if (startTime === null) {
  163. startTime = Date.now();
  164. return 0;
  165. } else {
  166. return Date.now() - startTime;
  167. }
  168. }