client.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /** @type {RTCPeerConnection} */
  2. let rtc;
  3. const iceConnectionLog = document.getElementById('ice-connection-state'),
  4. iceGatheringLog = document.getElementById('ice-gathering-state'),
  5. signalingLog = document.getElementById('signaling-state'),
  6. dataChannelLog = document.getElementById('data-channel');
  7. function randomString(len) {
  8. const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  9. let randomString = '';
  10. for (let i = 0; i < len; i++) {
  11. const randomPoz = Math.floor(Math.random() * charSet.length);
  12. randomString += charSet.substring(randomPoz, randomPoz + 1);
  13. }
  14. return randomString;
  15. }
  16. const receiveID = randomString(10);
  17. const websocket = new WebSocket('ws://127.0.0.1:8000/' + receiveID);
  18. websocket.onopen = function () {
  19. document.getElementById('start').disabled = false;
  20. }
  21. // data channel
  22. let dc = null, dcTimeout = null;
  23. function createPeerConnection() {
  24. const config = {
  25. sdpSemantics: 'unified-plan',
  26. bundlePolicy: "max-bundle",
  27. };
  28. if (document.getElementById('use-stun').checked) {
  29. config.iceServers = [{urls: ['stun:stun.l.google.com:19302']}];
  30. }
  31. let pc = new RTCPeerConnection(config);
  32. // register some listeners to help debugging
  33. pc.addEventListener('icegatheringstatechange', function () {
  34. iceGatheringLog.textContent += ' -> ' + pc.iceGatheringState;
  35. }, false);
  36. iceGatheringLog.textContent = pc.iceGatheringState;
  37. pc.addEventListener('iceconnectionstatechange', function () {
  38. iceConnectionLog.textContent += ' -> ' + pc.iceConnectionState;
  39. }, false);
  40. iceConnectionLog.textContent = pc.iceConnectionState;
  41. pc.addEventListener('signalingstatechange', function () {
  42. signalingLog.textContent += ' -> ' + pc.signalingState;
  43. }, false);
  44. signalingLog.textContent = pc.signalingState;
  45. // connect audio / video
  46. pc.addEventListener('track', function (evt) {
  47. document.getElementById('media').style.display = 'block';
  48. const videoTag = document.getElementById('video');
  49. videoTag.srcObject = evt.streams[0];
  50. videoTag.play();
  51. });
  52. let time_start = null;
  53. function current_stamp() {
  54. if (time_start === null) {
  55. time_start = new Date().getTime();
  56. return 0;
  57. } else {
  58. return new Date().getTime() - time_start;
  59. }
  60. }
  61. pc.ondatachannel = function (event) {
  62. dc = event.channel;
  63. dc.onopen = function () {
  64. dataChannelLog.textContent += '- open\n';
  65. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  66. };
  67. dc.onmessage = function (evt) {
  68. dataChannelLog.textContent += '< ' + evt.data + '\n';
  69. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  70. dcTimeout = setTimeout(function () {
  71. if (dc == null && dcTimeout != null) {
  72. dcTimeout = null;
  73. return
  74. }
  75. const message = 'Pong ' + current_stamp();
  76. dataChannelLog.textContent += '> ' + message + '\n';
  77. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  78. dc.send(message);
  79. }, 1000);
  80. }
  81. dc.onclose = function () {
  82. clearTimeout(dcTimeout);
  83. dcTimeout = null;
  84. dataChannelLog.textContent += '- close\n';
  85. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  86. };
  87. }
  88. return pc;
  89. }
  90. function sendAnswer(pc) {
  91. return pc.createAnswer()
  92. .then((answer) => rtc.setLocalDescription(answer))
  93. .then(function () {
  94. // wait for ICE gathering to complete
  95. return new Promise(function (resolve) {
  96. if (pc.iceGatheringState === 'complete') {
  97. resolve();
  98. } else {
  99. function checkState() {
  100. if (pc.iceGatheringState === 'complete') {
  101. pc.removeEventListener('icegatheringstatechange', checkState);
  102. resolve();
  103. }
  104. }
  105. pc.addEventListener('icegatheringstatechange', checkState);
  106. }
  107. });
  108. }).then(function () {
  109. const answer = pc.localDescription;
  110. document.getElementById('answer-sdp').textContent = answer.sdp;
  111. return websocket.send(JSON.stringify(
  112. {
  113. id: "server",
  114. type: answer.type,
  115. sdp: answer.sdp,
  116. }));
  117. }).catch(function (e) {
  118. alert(e);
  119. });
  120. }
  121. function handleOffer(offer) {
  122. rtc = createPeerConnection();
  123. return rtc.setRemoteDescription(offer)
  124. .then(() => sendAnswer(rtc));
  125. }
  126. function sendStreamRequest() {
  127. websocket.send(JSON.stringify(
  128. {
  129. id: "server",
  130. type: "streamRequest",
  131. receiver: receiveID,
  132. }));
  133. }
  134. async function start() {
  135. document.getElementById('start').style.display = 'none';
  136. document.getElementById('stop').style.display = 'inline-block';
  137. document.getElementById('media').style.display = 'block';
  138. sendStreamRequest();
  139. }
  140. function stop() {
  141. document.getElementById('stop').style.display = 'none';
  142. document.getElementById('media').style.display = 'none';
  143. document.getElementById('start').style.display = 'inline-block';
  144. // close data channel
  145. if (dc) {
  146. dc.close();
  147. dc = null;
  148. }
  149. // close transceivers
  150. if (rtc.getTransceivers) {
  151. rtc.getTransceivers().forEach(function (transceiver) {
  152. if (transceiver.stop) {
  153. transceiver.stop();
  154. }
  155. });
  156. }
  157. // close local audio / video
  158. rtc.getSenders().forEach(function (sender) {
  159. const track = sender.track;
  160. if (track !== null) {
  161. sender.track.stop();
  162. }
  163. });
  164. // close peer connection
  165. setTimeout(function () {
  166. rtc.close();
  167. rtc = null;
  168. }, 500);
  169. }
  170. websocket.onmessage = async function (evt) {
  171. const received_msg = evt.data;
  172. const object = JSON.parse(received_msg);
  173. if (object.type == "offer") {
  174. document.getElementById('offer-sdp').textContent = object.sdp;
  175. await handleOffer(object)
  176. }
  177. }