client.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. if (evt.track.kind == 'video') {
  48. document.getElementById('media').style.display = 'block';
  49. document.getElementById('video').srcObject = evt.streams[0];
  50. } else {
  51. document.getElementById('audio').srcObject = evt.streams[0];
  52. }
  53. });
  54. let time_start = null;
  55. function current_stamp() {
  56. if (time_start === null) {
  57. time_start = new Date().getTime();
  58. return 0;
  59. } else {
  60. return new Date().getTime() - time_start;
  61. }
  62. }
  63. pc.ondatachannel = function (event) {
  64. dc = event.channel;
  65. dc.onopen = function () {
  66. dataChannelLog.textContent += '- open\n';
  67. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  68. };
  69. dc.onmessage = function (evt) {
  70. dataChannelLog.textContent += '< ' + evt.data + '\n';
  71. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  72. dcTimeout = setTimeout(function () {
  73. if (dc == null && dcTimeout != null) {
  74. dcTimeout = null;
  75. return
  76. }
  77. const message = 'Pong ' + current_stamp();
  78. dataChannelLog.textContent += '> ' + message + '\n';
  79. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  80. dc.send(message);
  81. }, 1000);
  82. }
  83. dc.onclose = function () {
  84. clearTimeout(dcTimeout);
  85. dcTimeout = null;
  86. dataChannelLog.textContent += '- close\n';
  87. dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
  88. };
  89. }
  90. return pc;
  91. }
  92. function sendAnswer(pc) {
  93. return pc.createAnswer()
  94. .then((answer) => rtc.setLocalDescription(answer))
  95. .then(function () {
  96. // wait for ICE gathering to complete
  97. return new Promise(function (resolve) {
  98. if (pc.iceGatheringState === 'complete') {
  99. resolve();
  100. } else {
  101. function checkState() {
  102. if (pc.iceGatheringState === 'complete') {
  103. pc.removeEventListener('icegatheringstatechange', checkState);
  104. resolve();
  105. }
  106. }
  107. pc.addEventListener('icegatheringstatechange', checkState);
  108. }
  109. });
  110. }).then(function () {
  111. const answer = pc.localDescription;
  112. document.getElementById('answer-sdp').textContent = answer.sdp;
  113. return websocket.send(JSON.stringify(
  114. {
  115. id: "server",
  116. type: answer.type,
  117. sdp: answer.sdp,
  118. }));
  119. }).catch(function (e) {
  120. alert(e);
  121. });
  122. }
  123. function handleOffer(offer) {
  124. rtc = createPeerConnection();
  125. return rtc.setRemoteDescription(offer)
  126. .then(() => sendAnswer(rtc));
  127. }
  128. function sendStreamRequest() {
  129. websocket.send(JSON.stringify(
  130. {
  131. id: "server",
  132. type: "streamRequest",
  133. receiver: receiveID,
  134. }));
  135. }
  136. async function start() {
  137. document.getElementById('start').style.display = 'none';
  138. document.getElementById('stop').style.display = 'inline-block';
  139. document.getElementById('media').style.display = 'block';
  140. sendStreamRequest();
  141. }
  142. function stop() {
  143. document.getElementById('stop').style.display = 'none';
  144. document.getElementById('media').style.display = 'none';
  145. document.getElementById('start').style.display = 'inline-block';
  146. // close data channel
  147. if (dc) {
  148. dc.close();
  149. dc = null;
  150. }
  151. // close transceivers
  152. if (rtc.getTransceivers) {
  153. rtc.getTransceivers().forEach(function (transceiver) {
  154. if (transceiver.stop) {
  155. transceiver.stop();
  156. }
  157. });
  158. }
  159. // close local audio / video
  160. rtc.getSenders().forEach(function (sender) {
  161. const track = sender.track;
  162. if (track !== null) {
  163. sender.track.stop();
  164. }
  165. });
  166. // close peer connection
  167. setTimeout(function () {
  168. rtc.close();
  169. rtc = null;
  170. }, 500);
  171. }
  172. websocket.onmessage = async function (evt) {
  173. const received_msg = evt.data;
  174. const object = JSON.parse(received_msg);
  175. if (object.type == "offer") {
  176. document.getElementById('offer-sdp').textContent = object.sdp;
  177. await handleOffer(object)
  178. }
  179. }