library_godot_webrtc.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /**************************************************************************/
  2. /* library_godot_webrtc.js */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  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 GodotRTCDataChannel.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 GodotRTCDataChannel.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 GodotRTCDataChannel.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 GodotRTCDataChannel.get_prop(p_id, 'negotiated', 65535);
  153. },
  154. godot_js_rtc_datachannel_get_buffered_amount__sig: 'ii',
  155. godot_js_rtc_datachannel_get_buffered_amount: function (p_id) {
  156. return GodotRTCDataChannel.get_prop(p_id, 'bufferedAmount', 0);
  157. },
  158. godot_js_rtc_datachannel_label_get__sig: 'ii',
  159. godot_js_rtc_datachannel_label_get: function (p_id) {
  160. const ref = IDHandler.get(p_id);
  161. if (!ref || !ref.label) {
  162. return 0;
  163. }
  164. return GodotRuntime.allocString(ref.label);
  165. },
  166. godot_js_rtc_datachannel_protocol_get__sig: 'ii',
  167. godot_js_rtc_datachannel_protocol_get: function (p_id) {
  168. const ref = IDHandler.get(p_id);
  169. if (!ref || !ref.protocol) {
  170. return 0;
  171. }
  172. return GodotRuntime.allocString(ref.protocol);
  173. },
  174. godot_js_rtc_datachannel_destroy__sig: 'vi',
  175. godot_js_rtc_datachannel_destroy: function (p_id) {
  176. GodotRTCDataChannel.close(p_id);
  177. IDHandler.remove(p_id);
  178. },
  179. godot_js_rtc_datachannel_connect__sig: 'viiiiii',
  180. godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
  181. const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
  182. const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
  183. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_ref);
  184. const onclose = GodotRuntime.get_func(p_on_close).bind(null, p_ref);
  185. GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
  186. },
  187. godot_js_rtc_datachannel_close__sig: 'vi',
  188. godot_js_rtc_datachannel_close: function (p_id) {
  189. const ref = IDHandler.get(p_id);
  190. if (!ref) {
  191. return;
  192. }
  193. GodotRTCDataChannel.close(p_id);
  194. },
  195. };
  196. autoAddDeps(GodotRTCDataChannel, '$GodotRTCDataChannel');
  197. mergeInto(LibraryManager.library, GodotRTCDataChannel);
  198. const GodotRTCPeerConnection = {
  199. $GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
  200. $GodotRTCPeerConnection: {
  201. onstatechange: function (p_id, p_conn, callback, event) {
  202. const ref = IDHandler.get(p_id);
  203. if (!ref) {
  204. return;
  205. }
  206. let state;
  207. switch (p_conn.iceConnectionState) {
  208. case 'new':
  209. state = 0;
  210. break;
  211. case 'checking':
  212. state = 1;
  213. break;
  214. case 'connected':
  215. case 'completed':
  216. state = 2;
  217. break;
  218. case 'disconnected':
  219. state = 3;
  220. break;
  221. case 'failed':
  222. state = 4;
  223. break;
  224. case 'closed':
  225. default:
  226. state = 5;
  227. break;
  228. }
  229. callback(state);
  230. },
  231. onicecandidate: function (p_id, callback, event) {
  232. const ref = IDHandler.get(p_id);
  233. if (!ref || !event.candidate) {
  234. return;
  235. }
  236. const c = event.candidate;
  237. const candidate_str = GodotRuntime.allocString(c.candidate);
  238. const mid_str = GodotRuntime.allocString(c.sdpMid);
  239. callback(mid_str, c.sdpMLineIndex, candidate_str);
  240. GodotRuntime.free(candidate_str);
  241. GodotRuntime.free(mid_str);
  242. },
  243. ondatachannel: function (p_id, callback, event) {
  244. const ref = IDHandler.get(p_id);
  245. if (!ref) {
  246. return;
  247. }
  248. const cid = IDHandler.add(event.channel);
  249. callback(cid);
  250. },
  251. onsession: function (p_id, callback, session) {
  252. const ref = IDHandler.get(p_id);
  253. if (!ref) {
  254. return;
  255. }
  256. const type_str = GodotRuntime.allocString(session.type);
  257. const sdp_str = GodotRuntime.allocString(session.sdp);
  258. callback(type_str, sdp_str);
  259. GodotRuntime.free(type_str);
  260. GodotRuntime.free(sdp_str);
  261. },
  262. onerror: function (p_id, callback, error) {
  263. const ref = IDHandler.get(p_id);
  264. if (!ref) {
  265. return;
  266. }
  267. GodotRuntime.error(error);
  268. callback();
  269. },
  270. },
  271. godot_js_rtc_pc_create__sig: 'iiiiii',
  272. godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
  273. const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
  274. const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
  275. const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
  276. const config = JSON.parse(GodotRuntime.parseString(p_config));
  277. let conn = null;
  278. try {
  279. conn = new RTCPeerConnection(config);
  280. } catch (e) {
  281. GodotRuntime.error(e);
  282. return 0;
  283. }
  284. const base = GodotRTCPeerConnection;
  285. const id = IDHandler.add(conn);
  286. conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
  287. conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
  288. conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
  289. return id;
  290. },
  291. godot_js_rtc_pc_close__sig: 'vi',
  292. godot_js_rtc_pc_close: function (p_id) {
  293. const ref = IDHandler.get(p_id);
  294. if (!ref) {
  295. return;
  296. }
  297. ref.close();
  298. },
  299. godot_js_rtc_pc_destroy__sig: 'vi',
  300. godot_js_rtc_pc_destroy: function (p_id) {
  301. const ref = IDHandler.get(p_id);
  302. if (!ref) {
  303. return;
  304. }
  305. ref.oniceconnectionstatechange = null;
  306. ref.onicecandidate = null;
  307. ref.ondatachannel = null;
  308. IDHandler.remove(p_id);
  309. },
  310. godot_js_rtc_pc_offer_create__sig: 'viiii',
  311. godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
  312. const ref = IDHandler.get(p_id);
  313. if (!ref) {
  314. return;
  315. }
  316. const onsession = GodotRuntime.get_func(p_on_session).bind(null, p_obj);
  317. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  318. ref.createOffer().then(function (session) {
  319. GodotRTCPeerConnection.onsession(p_id, onsession, session);
  320. }).catch(function (error) {
  321. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  322. });
  323. },
  324. godot_js_rtc_pc_local_description_set__sig: 'viiiii',
  325. godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
  326. const ref = IDHandler.get(p_id);
  327. if (!ref) {
  328. return;
  329. }
  330. const type = GodotRuntime.parseString(p_type);
  331. const sdp = GodotRuntime.parseString(p_sdp);
  332. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  333. ref.setLocalDescription({
  334. 'sdp': sdp,
  335. 'type': type,
  336. }).catch(function (error) {
  337. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  338. });
  339. },
  340. godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
  341. godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
  342. const ref = IDHandler.get(p_id);
  343. if (!ref) {
  344. return;
  345. }
  346. const type = GodotRuntime.parseString(p_type);
  347. const sdp = GodotRuntime.parseString(p_sdp);
  348. const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
  349. const onsession = GodotRuntime.get_func(p_session_created).bind(null, p_obj);
  350. ref.setRemoteDescription({
  351. 'sdp': sdp,
  352. 'type': type,
  353. }).then(function () {
  354. if (type !== 'offer') {
  355. return Promise.resolve();
  356. }
  357. return ref.createAnswer().then(function (session) {
  358. GodotRTCPeerConnection.onsession(p_id, onsession, session);
  359. });
  360. }).catch(function (error) {
  361. GodotRTCPeerConnection.onerror(p_id, onerror, error);
  362. });
  363. },
  364. godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
  365. godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
  366. const ref = IDHandler.get(p_id);
  367. if (!ref) {
  368. return;
  369. }
  370. const sdpMidName = GodotRuntime.parseString(p_mid_name);
  371. const sdpName = GodotRuntime.parseString(p_sdp);
  372. ref.addIceCandidate(new RTCIceCandidate({
  373. 'candidate': sdpName,
  374. 'sdpMid': sdpMidName,
  375. 'sdpMlineIndex': p_mline_idx,
  376. }));
  377. },
  378. godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
  379. godot_js_rtc_pc_datachannel_create__sig: 'iiii',
  380. godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
  381. try {
  382. const ref = IDHandler.get(p_id);
  383. if (!ref) {
  384. return 0;
  385. }
  386. const label = GodotRuntime.parseString(p_label);
  387. const config = JSON.parse(GodotRuntime.parseString(p_config));
  388. const channel = ref.createDataChannel(label, config);
  389. return IDHandler.add(channel);
  390. } catch (e) {
  391. GodotRuntime.error(e);
  392. return 0;
  393. }
  394. },
  395. };
  396. autoAddDeps(GodotRTCPeerConnection, '$GodotRTCPeerConnection');
  397. mergeInto(LibraryManager.library, GodotRTCPeerConnection);