library_godot_webrtc.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*************************************************************************/
  2. /* library_godot_webrtc.js */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. const GodotRTCDataChannel = {
  31. // Our socket implementation that forwards events to C++.
  32. $GodotRTCDataChannel__deps: ['$IDHandler', '$GodotRuntime'],
  33. $GodotRTCDataChannel: {
  34. connect: function (p_id, p_on_open, p_on_message, p_on_error, p_on_close) {
  35. const ref = IDHandler.get(p_id);
  36. if (!ref) {
  37. return;
  38. }
  39. ref.binaryType = 'arraybuffer';
  40. ref.onopen = function (event) {
  41. p_on_open();
  42. };
  43. ref.onclose = function (event) {
  44. p_on_close();
  45. };
  46. ref.onerror = function (event) {
  47. p_on_error();
  48. };
  49. ref.onmessage = function (event) {
  50. let buffer;
  51. let is_string = 0;
  52. if (event.data instanceof ArrayBuffer) {
  53. buffer = new Uint8Array(event.data);
  54. } else if (event.data instanceof Blob) {
  55. GodotRuntime.error('Blob type not supported');
  56. return;
  57. } else if (typeof event.data === 'string') {
  58. is_string = 1;
  59. const enc = new TextEncoder('utf-8');
  60. buffer = new Uint8Array(enc.encode(event.data));
  61. } else {
  62. GodotRuntime.error('Unknown message type');
  63. return;
  64. }
  65. const len = buffer.length * buffer.BYTES_PER_ELEMENT;
  66. const out = GodotRuntime.malloc(len);
  67. HEAPU8.set(buffer, out);
  68. p_on_message(out, len, is_string);
  69. GodotRuntime.free(out);
  70. };
  71. },
  72. close: function (p_id) {
  73. const ref = IDHandler.get(p_id);
  74. if (!ref) {
  75. return;
  76. }
  77. ref.onopen = null;
  78. ref.onmessage = null;
  79. ref.onerror = null;
  80. ref.onclose = null;
  81. ref.close();
  82. },
  83. get_prop: function (p_id, p_prop, p_def) {
  84. const ref = IDHandler.get(p_id);
  85. return (ref && ref[p_prop] !== undefined) ? ref[p_prop] : p_def;
  86. },
  87. },
  88. godot_js_rtc_datachannel_ready_state_get__sig: 'ii',
  89. godot_js_rtc_datachannel_ready_state_get: function (p_id) {
  90. const ref = IDHandler.get(p_id);
  91. if (!ref) {
  92. return 3; // CLOSED
  93. }
  94. switch (ref.readyState) {
  95. case 'connecting':
  96. return 0;
  97. case 'open':
  98. return 1;
  99. case 'closing':
  100. return 2;
  101. case 'closed':
  102. default:
  103. return 3;
  104. }
  105. },
  106. godot_js_rtc_datachannel_send__sig: 'iiiii',
  107. godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) {
  108. const ref = IDHandler.get(p_id);
  109. if (!ref) {
  110. return 1;
  111. }
  112. const bytes_array = new Uint8Array(p_length);
  113. for (let i = 0; i < p_length; i++) {
  114. bytes_array[i] = GodotRuntime.getHeapValue(p_buffer + i, 'i8');
  115. }
  116. if (p_raw) {
  117. ref.send(bytes_array.buffer);
  118. } else {
  119. const string = new TextDecoder('utf-8').decode(bytes_array);
  120. ref.send(string);
  121. }
  122. return 0;
  123. },
  124. godot_js_rtc_datachannel_is_ordered__sig: 'ii',
  125. godot_js_rtc_datachannel_is_ordered: function (p_id) {
  126. return IDHandler.get_prop(p_id, 'ordered', true);
  127. },
  128. godot_js_rtc_datachannel_id_get__sig: 'ii',
  129. godot_js_rtc_datachannel_id_get: function (p_id) {
  130. return IDHandler.get_prop(p_id, 'id', 65535);
  131. },
  132. godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii',
  133. godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) {
  134. const ref = IDHandler.get(p_id);
  135. if (!ref) {
  136. return 65535;
  137. }
  138. if (ref['maxPacketLifeTime'] !== undefined) {
  139. return ref['maxPacketLifeTime'];
  140. } else if (ref['maxRetransmitTime'] !== undefined) {
  141. // Guess someone didn't appreciate the standardization process.
  142. return ref['maxRetransmitTime'];
  143. }
  144. return 65535;
  145. },
  146. godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii',
  147. godot_js_rtc_datachannel_max_retransmits_get: function (p_id) {
  148. return IDHandler.get_prop(p_id, 'maxRetransmits', 65535);
  149. },
  150. godot_js_rtc_datachannel_is_negotiated__sig: 'ii',
  151. godot_js_rtc_datachannel_is_negotiated: function (p_id) {
  152. return IDHandler.get_prop(p_id, 'negotiated', 65535);
  153. },
  154. godot_js_rtc_datachannel_label_get__sig: 'ii',
  155. godot_js_rtc_datachannel_label_get: function (p_id) {
  156. const ref = IDHandler.get(p_id);
  157. if (!ref || !ref.label) {
  158. return 0;
  159. }
  160. return GodotRuntime.allocString(ref.label);
  161. },
  162. godot_js_rtc_datachannel_protocol_get__sig: 'ii',
  163. godot_js_rtc_datachannel_protocol_get: function (p_id) {
  164. const ref = IDHandler.get(p_id);
  165. if (!ref || !ref.protocol) {
  166. return 0;
  167. }
  168. return GodotRuntime.allocString(ref.protocol);
  169. },
  170. godot_js_rtc_datachannel_destroy__sig: 'vi',
  171. godot_js_rtc_datachannel_destroy: function (p_id) {
  172. GodotRTCDataChannel.close(p_id);
  173. IDHandler.remove(p_id);
  174. },
  175. godot_js_rtc_datachannel_connect__sig: 'viiiiii',
  176. godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
  177. const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
  178. const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
  179. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_ref);
  180. const onclose = GodotRuntime.get_func(p_on_close).bind(null, p_ref);
  181. GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
  182. },
  183. godot_js_rtc_datachannel_close__sig: 'vi',
  184. godot_js_rtc_datachannel_close: function (p_id) {
  185. const ref = IDHandler.get(p_id);
  186. if (!ref) {
  187. return;
  188. }
  189. GodotRTCDataChannel.close(p_id);
  190. },
  191. };
  192. autoAddDeps(GodotRTCDataChannel, '$GodotRTCDataChannel');
  193. mergeInto(LibraryManager.library, GodotRTCDataChannel);
  194. const GodotRTCPeerConnection = {
  195. $GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
  196. $GodotRTCPeerConnection: {
  197. onstatechange: function (p_id, p_conn, callback, event) {
  198. const ref = IDHandler.get(p_id);
  199. if (!ref) {
  200. return;
  201. }
  202. let state;
  203. switch (p_conn.iceConnectionState) {
  204. case 'new':
  205. state = 0;
  206. break;
  207. case 'checking':
  208. state = 1;
  209. break;
  210. case 'connected':
  211. case 'completed':
  212. state = 2;
  213. break;
  214. case 'disconnected':
  215. state = 3;
  216. break;
  217. case 'failed':
  218. state = 4;
  219. break;
  220. case 'closed':
  221. default:
  222. state = 5;
  223. break;
  224. }
  225. callback(state);
  226. },
  227. onicecandidate: function (p_id, callback, event) {
  228. const ref = IDHandler.get(p_id);
  229. if (!ref || !event.candidate) {
  230. return;
  231. }
  232. const c = event.candidate;
  233. const candidate_str = GodotRuntime.allocString(c.candidate);
  234. const mid_str = GodotRuntime.allocString(c.sdpMid);
  235. callback(mid_str, c.sdpMLineIndex, candidate_str);
  236. GodotRuntime.free(candidate_str);
  237. GodotRuntime.free(mid_str);
  238. },
  239. ondatachannel: function (p_id, callback, event) {
  240. const ref = IDHandler.get(p_id);
  241. if (!ref) {
  242. return;
  243. }
  244. const cid = IDHandler.add(event.channel);
  245. callback(cid);
  246. },
  247. onsession: function (p_id, callback, session) {
  248. const ref = IDHandler.get(p_id);
  249. if (!ref) {
  250. return;
  251. }
  252. const type_str = GodotRuntime.allocString(session.type);
  253. const sdp_str = GodotRuntime.allocString(session.sdp);
  254. callback(type_str, sdp_str);
  255. GodotRuntime.free(type_str);
  256. GodotRuntime.free(sdp_str);
  257. },
  258. onerror: function (p_id, callback, error) {
  259. const ref = IDHandler.get(p_id);
  260. if (!ref) {
  261. return;
  262. }
  263. GodotRuntime.error(error);
  264. callback();
  265. },
  266. },
  267. godot_js_rtc_pc_create__sig: 'iiiiii',
  268. godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
  269. const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
  270. const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
  271. const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
  272. const config = JSON.parse(GodotRuntime.parseString(p_config));
  273. let conn = null;
  274. try {
  275. conn = new RTCPeerConnection(config);
  276. } catch (e) {
  277. GodotRuntime.error(e);
  278. return 0;
  279. }
  280. const base = GodotRTCPeerConnection;
  281. const id = IDHandler.add(conn);
  282. conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
  283. conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
  284. conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
  285. return id;
  286. },
  287. godot_js_rtc_pc_close__sig: 'vi',
  288. godot_js_rtc_pc_close: function (p_id) {
  289. const ref = IDHandler.get(p_id);
  290. if (!ref) {
  291. return;
  292. }
  293. ref.close();
  294. },
  295. godot_js_rtc_pc_destroy__sig: 'vi',
  296. godot_js_rtc_pc_destroy: function (p_id) {
  297. const ref = IDHandler.get(p_id);
  298. if (!ref) {
  299. return;
  300. }
  301. ref.oniceconnectionstatechange = null;
  302. ref.onicecandidate = null;
  303. ref.ondatachannel = null;
  304. IDHandler.remove(p_id);
  305. },
  306. godot_js_rtc_pc_offer_create__sig: 'viiii',
  307. godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
  308. const ref = IDHandler.get(p_id);
  309. if (!ref) {
  310. return;
  311. }
  312. const onsession = GodotRuntime.get_func(p_on_session).bind(null, p_obj);
  313. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  314. ref.createOffer().then(function (session) {
  315. GodotRTCPeerConnection.onsession(p_id, onsession, session);
  316. }).catch(function (error) {
  317. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  318. });
  319. },
  320. godot_js_rtc_pc_local_description_set__sig: 'viiiii',
  321. godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
  322. const ref = IDHandler.get(p_id);
  323. if (!ref) {
  324. return;
  325. }
  326. const type = GodotRuntime.parseString(p_type);
  327. const sdp = GodotRuntime.parseString(p_sdp);
  328. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  329. ref.setLocalDescription({
  330. 'sdp': sdp,
  331. 'type': type,
  332. }).catch(function (error) {
  333. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  334. });
  335. },
  336. godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
  337. godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
  338. const ref = IDHandler.get(p_id);
  339. if (!ref) {
  340. return;
  341. }
  342. const type = GodotRuntime.parseString(p_type);
  343. const sdp = GodotRuntime.parseString(p_sdp);
  344. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  345. const onsession = GodotRuntime.get_func(p_session_created).bind(null, p_obj);
  346. ref.setRemoteDescription({
  347. 'sdp': sdp,
  348. 'type': type,
  349. }).then(function () {
  350. if (type !== 'offer') {
  351. return Promise.resolve();
  352. }
  353. return ref.createAnswer().then(function (session) {
  354. GodotRTCPeerConnection.onsession(p_id, onsession, session);
  355. });
  356. }).catch(function (error) {
  357. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  358. });
  359. },
  360. godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
  361. godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
  362. const ref = IDHandler.get(p_id);
  363. if (!ref) {
  364. return;
  365. }
  366. const sdpMidName = GodotRuntime.parseString(p_mid_name);
  367. const sdpName = GodotRuntime.parseString(p_sdp);
  368. ref.addIceCandidate(new RTCIceCandidate({
  369. 'candidate': sdpName,
  370. 'sdpMid': sdpMidName,
  371. 'sdpMlineIndex': p_mline_idx,
  372. }));
  373. },
  374. godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
  375. godot_js_rtc_pc_datachannel_create__sig: 'iiii',
  376. godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
  377. try {
  378. const ref = IDHandler.get(p_id);
  379. if (!ref) {
  380. return 0;
  381. }
  382. const label = GodotRuntime.parseString(p_label);
  383. const config = JSON.parse(GodotRuntime.parseString(p_config));
  384. const channel = ref.createDataChannel(label, config);
  385. return IDHandler.add(channel);
  386. } catch (e) {
  387. GodotRuntime.error(e);
  388. return 0;
  389. }
  390. },
  391. };
  392. autoAddDeps(GodotRTCPeerConnection, '$GodotRTCPeerConnection');
  393. mergeInto(LibraryManager.library, GodotRTCPeerConnection);