Rtcp.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import {logger} from "./Logger.js"
  2. import Workers from "./Workers.js"
  3. export default class Rtcp extends EventEmitter {
  4. constructor(e) {
  5. super();
  6. this.connection = null
  7. this.inputChannel = null
  8. this.mediaStream = null
  9. this.socket = null
  10. this.connected = !1
  11. this.candidates = []
  12. this.isAnswered = !1
  13. this.isFlushing = !1
  14. this.inputReady = !1
  15. this.workers = null
  16. this.actived = !0
  17. this.heartbeat = null
  18. this.network = e;
  19. //this.workers = new Workers(this,new Logger("decode")),
  20. //this.workers.registerLogger(new Logger("decode")),
  21. this.workers = new Workers(this);
  22. this.workers.registerLogger();
  23. this.workers.registerFunction("data", t=>{
  24. this.emit("data", t)
  25. }
  26. ),
  27. this.heartbeat = new Heartbeat({
  28. ping: t=>{
  29. e.room.actionsHandler.echo(t)
  30. }
  31. ,
  32. pong(t, r) {
  33. var n;
  34. r && t > 500 && logger.warn(`high hb value ${t}, traceId:` + r),
  35. (n = e.room.stats) == null || n.assign({
  36. hb: t
  37. })
  38. }
  39. })
  40. }
  41. onIcecandidate(e){
  42. if (e.candidate != null) {
  43. const t = JSON.stringify(e.candidate);
  44. logger.debug(`Got ice candidate: ${t}`),
  45. this.network.socket.send({
  46. id: "ice_candidate",
  47. data: btoa(t)
  48. })
  49. }
  50. }
  51. onIcecandidateerror(e){
  52. logger.error("onicecandidateerror", e.errorCode, e.errorText, e)
  53. }
  54. onIceStateChange(e){
  55. switch (e.target.iceGatheringState) {
  56. case "gathering":
  57. logger.info("ice gathering");
  58. break;
  59. case "complete":
  60. logger.info("Ice gathering completed")
  61. }
  62. }
  63. onIceConnectionStateChange(){
  64. if (!!this.connection)
  65. switch (logger.info(`iceConnectionState: ${this.connection.iceConnectionState}`),
  66. this.connection.iceConnectionState) {
  67. case "connected":
  68. {
  69. this.connected = !0;
  70. break
  71. }
  72. case "disconnected":
  73. {
  74. this.connected = !1,
  75. this.emit("rtcDisconnected");
  76. break
  77. }
  78. case "failed":
  79. {
  80. this.emit("rtcDisconnected"),
  81. this.connected = !1;
  82. break
  83. }
  84. }
  85. }
  86. async setRemoteDescription(e,t){
  87. var a, s, l;
  88. if (!this.connection)
  89. return;
  90. const r = JSON.parse(atob(e))
  91. , n = new RTCSessionDescription(r);
  92. await this.connection.setRemoteDescription(n);
  93. const o = await this.connection.createAnswer();
  94. if (o.sdp = (a = o.sdp) == null ? void 0 : a.replace(/(a=fmtp:111 .*)/g, "$1;stereo=1;sprop-stereo=1"),
  95. ((l = (s = o.sdp) == null ? void 0 : s.match(/a=mid:1/g)) == null ? void 0 : l.length) == 2) {
  96. const u = o.sdp.lastIndexOf("a=mid:1");
  97. o.sdp = o.sdp.slice(0, u) + "a=mid:2" + o.sdp.slice(u + 7)
  98. }
  99. try {
  100. await this.connection.setLocalDescription(o)
  101. } catch (u) {
  102. logger.error("error", u)
  103. }
  104. this.isAnswered = !0,
  105. this.network.rtcp.flushCandidate(),
  106. this.network.socket.send({
  107. id: "answer",
  108. data: btoa(JSON.stringify(o))
  109. }),
  110. t.srcObject = this.mediaStream
  111. }
  112. flushCandidate(){
  113. this.isFlushing || !this.isAnswered || (this.isFlushing = !0,
  114. this.candidates.forEach(e=>{
  115. const t = atob(e)
  116. , r = JSON.parse(t);
  117. if (/172\./.test(r.candidate))
  118. return;
  119. const n = new RTCIceCandidate(r);
  120. this.connection && this.connection.addIceCandidate(n).then(()=>{}
  121. , o=>{
  122. logger.info("add candidate failed", o)
  123. }
  124. )
  125. }
  126. ),
  127. this.isFlushing = !1)
  128. }
  129. input(e){
  130. var t;
  131. !this.actived || !this.inputChannel || this.inputChannel.readyState === "open" && ((t = this.inputChannel) == null || t.send(e))
  132. }
  133. start() {
  134. this.connection = new RTCPeerConnection;
  135. const e = Date.now();
  136. this.connection.ondatachannel = t=>{
  137. logger.info(`ondatachannel: ${t.channel.label}`),
  138. this.inputChannel = t.channel,
  139. this.inputChannel.onopen = ()=>{
  140. var r;
  141. logger.info("The input channel has opened, id:", (r = this.inputChannel) == null ? void 0 : r.id),
  142. this.inputReady = !0,
  143. this.emit("rtcConnected"),
  144. this.network.room.currentNetworkOptions.reconnect || (logger.infoAndReportMeasurement({
  145. metric: "datachannelOpenedAt",
  146. startTime: this.network.room._startTime,
  147. group: "joinRoom"
  148. }),
  149. logger.infoAndReportMeasurement({
  150. metric: "datachannelOpenedCost",
  151. startTime: e,
  152. group: "joinRoom"
  153. }))
  154. }
  155. ,
  156. this.inputChannel.onclose = ()=>{
  157. var r;
  158. return logger.info("The input channel has closed, id:", (r = this.inputChannel) == null ? void 0 : r.id)
  159. }
  160. ,
  161. this.inputChannel.onmessage = r=>{
  162. this.workers.dataHandle(r.data)
  163. }
  164. }
  165. ,
  166. this.connection.oniceconnectionstatechange = this.onIceConnectionStateChange,
  167. this.connection.onicegatheringstatechange = this.onIceStateChange,
  168. this.connection.onicecandidate = this.onIcecandidate,
  169. this.connection.onicecandidateerror = this.onIcecandidateerror,
  170. this.network.socket.send({
  171. id: "init_webrtc",
  172. data: JSON.stringify({
  173. is_mobile: !0
  174. })
  175. })
  176. }
  177. addCandidate(e) {
  178. e === "" ? this.network.rtcp.flushCandidate() : this.candidates.push(e)
  179. }
  180. disconnect() {
  181. var e, t, r;
  182. this.heartbeat.stop(),
  183. logger.info("ready to close datachannel, id", (e = this.inputChannel) == null ? void 0 : e.id),
  184. (t = this.inputChannel) == null || t.close(),
  185. (r = this.connection) == null || r.close(),
  186. this.connection = null,
  187. this.inputChannel = null
  188. }
  189. sendStringData(e) {
  190. this.input(e)
  191. }
  192. sendData(e) {
  193. let t = "";
  194. try {
  195. t = JSON.stringify(e)
  196. } catch (r) {
  197. logger.error(r);
  198. return
  199. }
  200. this.input(t)
  201. }
  202. }