1 | /**************************************************************************/ |
2 | /* webrtc_data_channel_js.cpp */ |
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 | |
31 | #include "webrtc_data_channel_js.h" |
32 | |
33 | #ifdef WEB_ENABLED |
34 | |
35 | #include <emscripten.h> |
36 | |
37 | extern "C" { |
38 | typedef void (*RTCChOnOpen)(void *p_obj); |
39 | typedef void (*RTCChOnMessage)(void *p_obj, const uint8_t *p_buffer, int p_size, int p_is_string); |
40 | typedef void (*RTCChOnClose)(void *p_obj); |
41 | typedef void (*RTCChOnError)(void *p_obj); |
42 | |
43 | extern int godot_js_rtc_datachannel_ready_state_get(int p_id); |
44 | extern int godot_js_rtc_datachannel_send(int p_id, const uint8_t *p_buffer, int p_length, int p_raw); |
45 | extern int godot_js_rtc_datachannel_is_ordered(int p_id); |
46 | extern int godot_js_rtc_datachannel_id_get(int p_id); |
47 | extern int godot_js_rtc_datachannel_max_packet_lifetime_get(int p_id); |
48 | extern int godot_js_rtc_datachannel_max_retransmits_get(int p_id); |
49 | extern int godot_js_rtc_datachannel_is_negotiated(int p_id); |
50 | extern int godot_js_rtc_datachannel_get_buffered_amount(int p_id); |
51 | extern char *godot_js_rtc_datachannel_label_get(int p_id); // Must free the returned string. |
52 | extern char *godot_js_rtc_datachannel_protocol_get(int p_id); // Must free the returned string. |
53 | extern void godot_js_rtc_datachannel_destroy(int p_id); |
54 | extern void godot_js_rtc_datachannel_connect(int p_id, void *p_obj, RTCChOnOpen p_on_open, RTCChOnMessage p_on_message, RTCChOnError p_on_error, RTCChOnClose p_on_close); |
55 | extern void godot_js_rtc_datachannel_close(int p_id); |
56 | } |
57 | |
58 | void WebRTCDataChannelJS::_on_open(void *p_obj) { |
59 | WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); |
60 | peer->in_buffer.resize(peer->_in_buffer_shift); |
61 | } |
62 | |
63 | void WebRTCDataChannelJS::_on_close(void *p_obj) { |
64 | WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); |
65 | peer->close(); |
66 | } |
67 | |
68 | void WebRTCDataChannelJS::_on_error(void *p_obj) { |
69 | WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); |
70 | peer->close(); |
71 | } |
72 | |
73 | void WebRTCDataChannelJS::_on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string) { |
74 | WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); |
75 | RingBuffer<uint8_t> &in_buffer = peer->in_buffer; |
76 | |
77 | ERR_FAIL_COND_MSG(in_buffer.space_left() < (int)(p_size + 5), "Buffer full! Dropping data." ); |
78 | |
79 | uint8_t is_string = p_is_string ? 1 : 0; |
80 | in_buffer.write((uint8_t *)&p_size, 4); |
81 | in_buffer.write((uint8_t *)&is_string, 1); |
82 | in_buffer.write(p_data, p_size); |
83 | peer->queue_count++; |
84 | } |
85 | |
86 | void WebRTCDataChannelJS::close() { |
87 | in_buffer.resize(0); |
88 | queue_count = 0; |
89 | _was_string = false; |
90 | godot_js_rtc_datachannel_close(_js_id); |
91 | } |
92 | |
93 | Error WebRTCDataChannelJS::poll() { |
94 | return OK; |
95 | } |
96 | |
97 | WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const { |
98 | return (ChannelState)godot_js_rtc_datachannel_ready_state_get(_js_id); |
99 | } |
100 | |
101 | int WebRTCDataChannelJS::get_available_packet_count() const { |
102 | return queue_count; |
103 | } |
104 | |
105 | Error WebRTCDataChannelJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { |
106 | ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED); |
107 | |
108 | if (queue_count == 0) { |
109 | return ERR_UNAVAILABLE; |
110 | } |
111 | |
112 | uint32_t to_read = 0; |
113 | uint32_t left = 0; |
114 | uint8_t is_string = 0; |
115 | r_buffer_size = 0; |
116 | |
117 | in_buffer.read((uint8_t *)&to_read, 4); |
118 | --queue_count; |
119 | left = in_buffer.data_left(); |
120 | |
121 | if (left < to_read + 1) { |
122 | in_buffer.advance_read(left); |
123 | return FAILED; |
124 | } |
125 | |
126 | in_buffer.read(&is_string, 1); |
127 | _was_string = is_string == 1; |
128 | in_buffer.read(packet_buffer, to_read); |
129 | *r_buffer = packet_buffer; |
130 | r_buffer_size = to_read; |
131 | |
132 | return OK; |
133 | } |
134 | |
135 | Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) { |
136 | ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED); |
137 | |
138 | int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0; |
139 | godot_js_rtc_datachannel_send(_js_id, p_buffer, p_buffer_size, is_bin); |
140 | return OK; |
141 | } |
142 | |
143 | int WebRTCDataChannelJS::get_max_packet_size() const { |
144 | return 1200; |
145 | } |
146 | |
147 | void WebRTCDataChannelJS::set_write_mode(WriteMode p_mode) { |
148 | _write_mode = p_mode; |
149 | } |
150 | |
151 | WebRTCDataChannel::WriteMode WebRTCDataChannelJS::get_write_mode() const { |
152 | return _write_mode; |
153 | } |
154 | |
155 | bool WebRTCDataChannelJS::was_string_packet() const { |
156 | return _was_string; |
157 | } |
158 | |
159 | String WebRTCDataChannelJS::get_label() const { |
160 | return _label; |
161 | } |
162 | |
163 | bool WebRTCDataChannelJS::is_ordered() const { |
164 | return godot_js_rtc_datachannel_is_ordered(_js_id); |
165 | } |
166 | |
167 | int WebRTCDataChannelJS::get_id() const { |
168 | return godot_js_rtc_datachannel_id_get(_js_id); |
169 | } |
170 | |
171 | int WebRTCDataChannelJS::get_max_packet_life_time() const { |
172 | return godot_js_rtc_datachannel_max_packet_lifetime_get(_js_id); |
173 | } |
174 | |
175 | int WebRTCDataChannelJS::get_max_retransmits() const { |
176 | return godot_js_rtc_datachannel_max_retransmits_get(_js_id); |
177 | } |
178 | |
179 | String WebRTCDataChannelJS::get_protocol() const { |
180 | return _protocol; |
181 | } |
182 | |
183 | bool WebRTCDataChannelJS::is_negotiated() const { |
184 | return godot_js_rtc_datachannel_is_negotiated(_js_id); |
185 | } |
186 | |
187 | int WebRTCDataChannelJS::get_buffered_amount() const { |
188 | return godot_js_rtc_datachannel_get_buffered_amount(_js_id); |
189 | } |
190 | |
191 | WebRTCDataChannelJS::WebRTCDataChannelJS() { |
192 | } |
193 | |
194 | WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) { |
195 | _js_id = js_id; |
196 | |
197 | godot_js_rtc_datachannel_connect(js_id, this, &_on_open, &_on_message, &_on_error, &_on_close); |
198 | // Parse label |
199 | char *label = godot_js_rtc_datachannel_label_get(js_id); |
200 | if (label) { |
201 | _label.parse_utf8(label); |
202 | free(label); |
203 | } |
204 | char *protocol = godot_js_rtc_datachannel_protocol_get(js_id); |
205 | if (protocol) { |
206 | _protocol.parse_utf8(protocol); |
207 | free(protocol); |
208 | } |
209 | } |
210 | |
211 | WebRTCDataChannelJS::~WebRTCDataChannelJS() { |
212 | close(); |
213 | godot_js_rtc_datachannel_destroy(_js_id); |
214 | } |
215 | #endif |
216 | |