Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2026 The TokTok team. |
3 | | * Copyright © 2013-2015 Tox project. |
4 | | */ |
5 | | #include "rtp.h" |
6 | | |
7 | | #include <assert.h> |
8 | | #include <stdlib.h> |
9 | | #include <string.h> |
10 | | |
11 | | #include <sodium.h> |
12 | | |
13 | | #include "../toxcore/ccompat.h" |
14 | | #include "../toxcore/logger.h" |
15 | | #include "../toxcore/mono_time.h" |
16 | | #include "../toxcore/net_crypto.h" |
17 | | #include "../toxcore/network.h" |
18 | | #include "../toxcore/util.h" |
19 | | |
20 | | /** |
21 | | * Maximum size of a single RTP frame in bytes. |
22 | | * This limit prevents memory exhaustion attacks where a malicious peer sends |
23 | | * a header indicating a very large frame size, causing the receiver to allocate |
24 | | * excessive memory. |
25 | | */ |
26 | 140 | #define MAX_RTP_FRAME_SIZE (32 * 1024 * 1024) |
27 | | |
28 | | struct RTPHeader { |
29 | | /* Standard RTP header */ |
30 | | unsigned ve: 2; /* Version has only 2 bits! */ |
31 | | unsigned pe: 1; /* Padding */ |
32 | | unsigned xe: 1; /* Extra header */ |
33 | | unsigned cc: 4; /* Contributing sources count */ |
34 | | |
35 | | unsigned ma: 1; /* Marker */ |
36 | | unsigned pt: 7; /* Payload type */ |
37 | | |
38 | | uint16_t sequnum; |
39 | | uint32_t timestamp; |
40 | | uint32_t ssrc; |
41 | | |
42 | | /* Non-standard Tox-specific fields */ |
43 | | |
44 | | /** |
45 | | * Bit mask of `RTPFlags` setting features of the current frame. |
46 | | */ |
47 | | uint64_t flags; |
48 | | |
49 | | /** |
50 | | * The full 32 bit data offset of the current data chunk. The |
51 | | * @ref offset_lower data member contains the lower 16 bits of this value. |
52 | | * For frames smaller than 64KiB, @ref offset_full and @ref offset_lower are |
53 | | * equal. |
54 | | */ |
55 | | uint32_t offset_full; |
56 | | /** |
57 | | * The full 32 bit payload length without header and packet id. |
58 | | */ |
59 | | uint32_t data_length_full; |
60 | | /** |
61 | | * Only the receiver uses this field (why do we have this?). |
62 | | */ |
63 | | uint32_t received_length_full; |
64 | | |
65 | | /** |
66 | | * Data offset of the current part (lower bits). |
67 | | */ |
68 | | uint16_t offset_lower; |
69 | | /** |
70 | | * Total message length (lower bits). |
71 | | */ |
72 | | uint16_t data_length_lower; |
73 | | }; |
74 | | |
75 | | struct RTPMessage { |
76 | | /** |
77 | | * This is used in the old code that doesn't deal with large frames, i.e. |
78 | | * the audio code or receiving code for old 16 bit messages. We use it to |
79 | | * record the number of bytes received so far in a multi-part message. The |
80 | | * multi-part message in the old code is stored in `RTPSession::mp`. |
81 | | */ |
82 | | uint32_t len; |
83 | | |
84 | | struct RTPHeader header; |
85 | | uint8_t data[]; |
86 | | }; |
87 | | |
88 | | /** |
89 | | * One slot in the work buffer list. Represents one frame that is currently |
90 | | * being assembled. |
91 | | */ |
92 | | struct RTPWorkBuffer { |
93 | | /** |
94 | | * Whether this slot contains a key frame. This is true iff |
95 | | * `buf->header.flags & RTP_KEY_FRAME`. |
96 | | */ |
97 | | bool is_keyframe; |
98 | | /** |
99 | | * The number of bytes received so far, regardless of which pieces. I.e. we |
100 | | * could have received the first 1000 bytes and the last 1000 bytes with |
101 | | * 4000 bytes in the middle still to come, and this number would be 2000. |
102 | | */ |
103 | | uint32_t received_len; |
104 | | /** |
105 | | * The message currently being assembled. |
106 | | */ |
107 | | struct RTPMessage *_Nullable buf; |
108 | | }; |
109 | | |
110 | | struct RTPWorkBufferList { |
111 | | int8_t next_free_entry; |
112 | | struct RTPWorkBuffer work_buffer[USED_RTP_WORKBUFFER_COUNT]; |
113 | | }; |
114 | | |
115 | | /** |
116 | | * RTP control session. |
117 | | */ |
118 | | struct RTPSession { |
119 | | uint8_t payload_type; |
120 | | uint16_t sequnum; /* Sending sequence number */ |
121 | | uint16_t rsequnum; /* Receiving sequence number */ |
122 | | uint32_t rtimestamp; |
123 | | uint32_t ssrc; // this seems to be unused!? |
124 | | struct RTPMessage *_Nullable mp; /* Expected parted message */ |
125 | | struct RTPWorkBufferList *_Nonnull work_buffer_list; |
126 | | uint8_t first_packets_counter; /* dismiss first few lost video packets */ |
127 | | const Logger *_Nonnull log; |
128 | | Mono_Time *_Nonnull mono_time; |
129 | | bool rtp_receive_active; /* if this is set to false then incoming rtp packets will not be processed by rtp_receive_packet() */ |
130 | | |
131 | | rtp_send_packet_cb *_Nullable send_packet; |
132 | | void *_Nullable send_packet_user_data; |
133 | | |
134 | | rtp_add_recv_cb *_Nullable add_recv; |
135 | | rtp_add_lost_cb *_Nullable add_lost; |
136 | | void *_Nullable bwc_user_data; |
137 | | |
138 | | void *_Nonnull cs; |
139 | | rtp_m_cb *_Nonnull mcb; |
140 | | }; |
141 | | |
142 | | const uint8_t *rtp_message_data(const RTPMessage *msg) |
143 | 66.0k | { |
144 | 66.0k | return msg->data; |
145 | 66.0k | } |
146 | | |
147 | | uint32_t rtp_message_len(const RTPMessage *msg) |
148 | 65.9k | { |
149 | 65.9k | return msg->len; |
150 | 65.9k | } |
151 | | |
152 | | uint8_t rtp_message_pt(const RTPMessage *msg) |
153 | 131k | { |
154 | 131k | return msg->header.pt; |
155 | 131k | } |
156 | | |
157 | | uint16_t rtp_message_sequnum(const RTPMessage *msg) |
158 | 65.7k | { |
159 | 65.7k | return msg->header.sequnum; |
160 | 65.7k | } |
161 | | |
162 | | uint64_t rtp_message_flags(const RTPMessage *msg) |
163 | 250 | { |
164 | 250 | return msg->header.flags; |
165 | 250 | } |
166 | | |
167 | | uint32_t rtp_message_data_length_full(const RTPMessage *msg) |
168 | 250 | { |
169 | 250 | return msg->header.data_length_full; |
170 | 250 | } |
171 | | |
172 | | bool rtp_session_is_receiving_active(const RTPSession *session) |
173 | 121 | { |
174 | 121 | if (session == nullptr) { |
175 | 1 | return false; |
176 | 1 | } |
177 | 120 | return session->rtp_receive_active; |
178 | 121 | } |
179 | | |
180 | | uint32_t rtp_session_get_ssrc(const RTPSession *session) |
181 | 124 | { |
182 | 124 | return session->ssrc; |
183 | 124 | } |
184 | | |
185 | | void rtp_session_set_ssrc(RTPSession *session, uint32_t ssrc) |
186 | 25 | { |
187 | 25 | session->ssrc = ssrc; |
188 | 25 | } |
189 | | |
190 | | /** |
191 | | * The number of milliseconds we want to keep a keyframe in the buffer for, |
192 | | * even though there are no free slots for incoming frames. |
193 | | */ |
194 | 2 | #define VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS 15 |
195 | | |
196 | | // allocate_len is NOT including header! |
197 | | static struct RTPMessage *_Nullable new_message(const Logger *_Nonnull log, const struct RTPHeader *_Nonnull header, size_t allocate_len, |
198 | | const uint8_t *_Nonnull data, uint16_t data_length) |
199 | 65.7k | { |
200 | 65.7k | if (allocate_len < data_length) { |
201 | 0 | LOGGER_WARNING(log, "new_message: allocate_len (%zu) < data_length (%u)", allocate_len, data_length); |
202 | 0 | return nullptr; |
203 | 0 | } |
204 | | |
205 | 65.7k | struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + allocate_len); |
206 | | |
207 | 65.7k | if (msg == nullptr) { |
208 | 0 | LOGGER_WARNING(log, "Could not allocate RTPMessage buffer"); |
209 | 0 | return nullptr; |
210 | 0 | } |
211 | | |
212 | 65.7k | msg->len = data_length; // result without header |
213 | 65.7k | msg->header = *header; |
214 | 65.7k | memcpy(msg->data, data, msg->len); |
215 | 65.7k | return msg; |
216 | 65.7k | } |
217 | | |
218 | | /** |
219 | | * Instruct the caller to clear slot 0. |
220 | | */ |
221 | 283 | #define GET_SLOT_RESULT_DROP_OLDEST_SLOT (-1) |
222 | | |
223 | | /** |
224 | | * Instruct the caller to drop the incoming packet. |
225 | | */ |
226 | 285 | #define GET_SLOT_RESULT_DROP_INCOMING (-2) |
227 | | |
228 | | /** |
229 | | * Find the next free slot in work_buffer for the incoming data packet. |
230 | | * |
231 | | * - If the data packet belongs to a frame that's already in the work_buffer then |
232 | | * use that slot. |
233 | | * - If there is no free slot return GET_SLOT_RESULT_DROP_OLDEST_SLOT. |
234 | | * - If the data packet is too old return GET_SLOT_RESULT_DROP_INCOMING. |
235 | | * |
236 | | * If there is a keyframe being assembled in slot 0, keep it a bit longer and |
237 | | * do not kick it out right away if all slots are full instead kick out the new |
238 | | * incoming interframe. |
239 | | */ |
240 | | static int8_t get_slot(const Logger *_Nonnull log, struct RTPWorkBufferList *_Nonnull wkbl, bool is_keyframe, |
241 | | const struct RTPHeader *_Nonnull header, bool is_multipart) |
242 | 284 | { |
243 | 284 | if (is_multipart) { |
244 | | // This RTP message is part of a multipart frame, so we try to find an |
245 | | // existing slot with the previous parts of the frame in it. |
246 | 217 | for (uint8_t i = 0; i < wkbl->next_free_entry; ++i) { |
247 | 152 | const struct RTPWorkBuffer *slot = &wkbl->work_buffer[i]; |
248 | | |
249 | 152 | assert(slot->buf != nullptr); |
250 | | |
251 | 152 | if ((slot->buf->header.sequnum == header->sequnum) && (slot->buf->header.timestamp == header->timestamp)) { |
252 | | // Sequence number and timestamp match, so this slot belongs to |
253 | | // the same frame. |
254 | | // |
255 | | // In reality, these will almost certainly either both match or |
256 | | // both not match. Only if somehow there were 65535 frames |
257 | | // between, the timestamp will matter. |
258 | 142 | return i; |
259 | 142 | } |
260 | 152 | } |
261 | 207 | } |
262 | | |
263 | | // The message may or may not be part of a multipart frame. |
264 | | // |
265 | | // If it is part of a multipart frame, then this is an entirely new frame |
266 | | // for which we did not have a slot *or* the frame is so old that its slot |
267 | | // has been evicted by now. |
268 | | // |
269 | | // |----------- time -----------> |
270 | | // _________________ |
271 | | // slot 0 | | |
272 | | // ----------------- |
273 | | // _________________ |
274 | | // slot 1 | | |
275 | | // ----------------- |
276 | | // ____________ |
277 | | // slot 2 | | -> frame too old, drop |
278 | | // ------------ |
279 | | // |
280 | | // |
281 | | // |
282 | | // |----------- time -----------> |
283 | | // _________________ |
284 | | // slot 0 | | |
285 | | // ----------------- |
286 | | // _________________ |
287 | | // slot 1 | | |
288 | | // ----------------- |
289 | | // ____________ |
290 | | // slot 2 | | -> ok, start filling in a new slot |
291 | | // ------------ |
292 | | |
293 | | // If there is a free slot: |
294 | 142 | if (wkbl->next_free_entry < USED_RTP_WORKBUFFER_COUNT) { |
295 | | // If there is at least one filled slot: |
296 | 140 | if (wkbl->next_free_entry > 0) { |
297 | | // Get the most recently filled slot. |
298 | 5 | const struct RTPWorkBuffer *slot = &wkbl->work_buffer[wkbl->next_free_entry - 1]; |
299 | | |
300 | | // If the incoming packet is older than our newest slot, drop it. |
301 | | // This is the first situation in the above diagram. |
302 | 5 | if (slot->buf->header.timestamp > header->timestamp) { |
303 | 0 | LOGGER_DEBUG(log, "workbuffer:2:timestamp too old"); |
304 | 0 | return GET_SLOT_RESULT_DROP_INCOMING; |
305 | 0 | } |
306 | 5 | } |
307 | | |
308 | | // Not all slots are filled, and the packet is newer than our most |
309 | | // recent slot, so it's a new frame we want to start assembling. This is |
310 | | // the second situation in the above diagram. |
311 | 140 | return wkbl->next_free_entry; |
312 | 140 | } |
313 | | |
314 | | // If the incoming frame is a key frame, then stop assembling the oldest |
315 | | // slot, regardless of whether there was a keyframe in that or not. |
316 | 2 | if (is_keyframe) { |
317 | 0 | return GET_SLOT_RESULT_DROP_OLDEST_SLOT; |
318 | 0 | } |
319 | | |
320 | | // The incoming slot is not a key frame, so we look at slot 0 to see what to |
321 | | // do next. |
322 | 2 | const struct RTPWorkBuffer *slot = &wkbl->work_buffer[0]; |
323 | | |
324 | | // The incoming frame is not a key frame, but the existing slot 0 is also |
325 | | // not a keyframe, so we stop assembling the existing frame and make space |
326 | | // for the new one. |
327 | 2 | if (!slot->is_keyframe) { |
328 | 0 | return GET_SLOT_RESULT_DROP_OLDEST_SLOT; |
329 | 0 | } |
330 | | |
331 | | // If this key frame is fully received, we also stop assembling and clear |
332 | | // slot 0. This also means sending the frame to the decoder. |
333 | 2 | if (slot->received_len == slot->buf->header.data_length_full) { |
334 | 0 | return GET_SLOT_RESULT_DROP_OLDEST_SLOT; |
335 | 0 | } |
336 | | |
337 | | // This is a key frame, not fully received yet, but it's already much older |
338 | | // than the incoming frame, so we stop assembling it and send whatever part |
339 | | // we did receive to the decoder. |
340 | 2 | if (slot->buf->header.timestamp + VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS <= header->timestamp) { |
341 | 1 | return GET_SLOT_RESULT_DROP_OLDEST_SLOT; |
342 | 1 | } |
343 | | |
344 | | // This is a key frame, it's not too old yet, so we keep it in its slot for |
345 | | // a little longer. |
346 | 1 | LOGGER_INFO(log, "keep KEYFRAME in workbuffer"); |
347 | 1 | return GET_SLOT_RESULT_DROP_INCOMING; |
348 | 2 | } |
349 | | |
350 | | /** |
351 | | * Returns an assembled frame (as much data as we currently have for this frame, |
352 | | * some pieces may be missing) |
353 | | * |
354 | | * If there are no frames ready, we return NULL. If this function returns |
355 | | * non-NULL, it transfers ownership of the message to the caller, i.e. the |
356 | | * caller is responsible for storing it elsewhere or calling `free()`. |
357 | | */ |
358 | | static struct RTPMessage *_Nullable process_frame(const Logger *_Nonnull log, struct RTPWorkBufferList *_Nonnull wkbl, uint8_t slot_id) |
359 | 132 | { |
360 | 132 | assert(wkbl->next_free_entry >= 0); |
361 | | |
362 | 132 | if (wkbl->next_free_entry == 0) { |
363 | | // There are no frames in any slot. |
364 | 0 | return nullptr; |
365 | 0 | } |
366 | | |
367 | | // Slot 0 contains a key frame, slot_id points at an interframe that is |
368 | | // relative to that key frame, so we don't use it yet. |
369 | 132 | if (wkbl->work_buffer[0].is_keyframe && slot_id != 0) { |
370 | 1 | LOGGER_DEBUG(log, "process_frame:KEYFRAME waiting in slot 0"); |
371 | 1 | return nullptr; |
372 | 1 | } |
373 | | |
374 | | // Either slot_id is 0 and slot 0 is a key frame, or there is no key frame |
375 | | // in slot 0 (and slot_id is anything). |
376 | 131 | struct RTPWorkBuffer *const slot = &wkbl->work_buffer[slot_id]; |
377 | | |
378 | | // Move ownership of the frame out of the slot into m_new. |
379 | 131 | struct RTPMessage *msg = slot->buf; |
380 | 131 | msg->len = msg->header.data_length_full; |
381 | 131 | slot->buf = nullptr; |
382 | | |
383 | 131 | assert(wkbl->next_free_entry >= 1 && wkbl->next_free_entry <= USED_RTP_WORKBUFFER_COUNT); |
384 | | |
385 | 131 | if (slot_id != wkbl->next_free_entry - 1) { |
386 | | // The slot is not the last slot, so we created a gap. We move all the |
387 | | // entries after it one step up. |
388 | 3 | for (uint8_t i = slot_id; i < wkbl->next_free_entry - 1; ++i) { |
389 | | // Move entry (i+1) into entry (i). |
390 | 2 | wkbl->work_buffer[i] = wkbl->work_buffer[i + 1]; |
391 | 2 | } |
392 | 1 | } |
393 | | |
394 | | // We now have a free entry at the end of the array. |
395 | 131 | --wkbl->next_free_entry; |
396 | | |
397 | | // Clear the newly freed entry. |
398 | 131 | const struct RTPWorkBuffer empty = {false}; |
399 | 131 | wkbl->work_buffer[wkbl->next_free_entry] = empty; |
400 | | |
401 | | // Move ownership of the frame to the caller. |
402 | 131 | return msg; |
403 | 131 | } |
404 | | |
405 | | /** |
406 | | * @param log A pointer to the Logger object. |
407 | | * @param wkbl The list of in-progress frames, i.e. all the slots. |
408 | | * @param slot_id The slot we want to fill the data into. |
409 | | * @param is_keyframe Whether the data is part of a key frame. |
410 | | * @param header The RTP header from the incoming packet. |
411 | | * @param incoming_data The pure payload without header. |
412 | | * @param incoming_data_length The length in bytes of the incoming data payload. |
413 | | */ |
414 | | static bool fill_data_into_slot(const Logger *_Nonnull log, struct RTPWorkBufferList *_Nonnull wkbl, const uint8_t slot_id, |
415 | | bool is_keyframe, const struct RTPHeader *_Nonnull header, |
416 | | const uint8_t *_Nonnull incoming_data, uint16_t incoming_data_length) |
417 | 282 | { |
418 | | // We're either filling the data into an existing slot, or in a new one that |
419 | | // is the next free entry. |
420 | 282 | assert(slot_id <= wkbl->next_free_entry); |
421 | 282 | assert(slot_id < USED_RTP_WORKBUFFER_COUNT); |
422 | 282 | struct RTPWorkBuffer *const slot = &wkbl->work_buffer[slot_id]; |
423 | | |
424 | 282 | assert(header != nullptr); |
425 | 282 | assert(is_keyframe == (bool)((header->flags & RTP_KEY_FRAME) != 0)); |
426 | | |
427 | 282 | if (slot->buf == nullptr) { |
428 | 140 | if (header->data_length_full > MAX_RTP_FRAME_SIZE) { |
429 | 0 | LOGGER_WARNING(log, "RTP frame too large: %u > %u", (unsigned)header->data_length_full, (unsigned)MAX_RTP_FRAME_SIZE); |
430 | 0 | return false; |
431 | 0 | } |
432 | | |
433 | | // No data for this slot has been received, yet, so we create a new |
434 | | // message for it with enough memory for the entire frame. |
435 | 140 | struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + header->data_length_full); |
436 | | |
437 | 140 | if (msg == nullptr) { |
438 | 0 | LOGGER_ERROR(log, "Out of memory while trying to allocate for frame of size %u", |
439 | 0 | (unsigned)header->data_length_full); |
440 | | // Out of memory: throw away the incoming data. |
441 | 0 | return false; |
442 | 0 | } |
443 | | |
444 | | // Unused in the new video receiving code, as it's 16 bit and can't hold |
445 | | // the full length of large frames. Instead, we use slot->received_len. |
446 | 140 | msg->len = 0; |
447 | 140 | msg->header = *header; |
448 | | |
449 | 140 | slot->buf = msg; |
450 | 140 | slot->is_keyframe = is_keyframe; |
451 | 140 | slot->received_len = 0; |
452 | | |
453 | 140 | assert(wkbl->next_free_entry < USED_RTP_WORKBUFFER_COUNT); |
454 | 140 | ++wkbl->next_free_entry; |
455 | 142 | } else { |
456 | 142 | if (slot->buf->header.data_length_full != header->data_length_full) { |
457 | 1 | LOGGER_WARNING(log, "Received packet with different length than previous packets in same frame: %u != %u", |
458 | 1 | header->data_length_full, slot->buf->header.data_length_full); |
459 | 1 | return false; |
460 | 1 | } |
461 | 142 | } |
462 | | |
463 | | // We already checked this when we received the packet, but we rely on it |
464 | | // here, so assert again. |
465 | 281 | assert(header->offset_full < header->data_length_full); |
466 | | |
467 | 281 | if (header->data_length_full - header->offset_full < incoming_data_length) { |
468 | 0 | LOGGER_ERROR(log, "Packet too long for buffer: offset %u + len %u > total %u", |
469 | 0 | (unsigned)header->offset_full, (unsigned)incoming_data_length, (unsigned)header->data_length_full); |
470 | 0 | return false; |
471 | 0 | } |
472 | | |
473 | | // Copy the incoming chunk of data into the correct position in the full |
474 | | // frame data array. |
475 | 281 | memcpy( |
476 | 281 | slot->buf->data + header->offset_full, |
477 | 281 | incoming_data, |
478 | 281 | incoming_data_length |
479 | 281 | ); |
480 | | |
481 | | // Update the total received length of this slot. |
482 | 281 | slot->received_len += incoming_data_length; |
483 | | |
484 | | // Update received length also in the header of the message, for later use. |
485 | 281 | slot->buf->header.received_length_full = slot->received_len; |
486 | | |
487 | 281 | return slot->received_len == header->data_length_full; |
488 | 281 | } |
489 | | |
490 | | static void update_bwc_values(RTPSession *_Nonnull session, const struct RTPMessage *_Nonnull msg) |
491 | 131 | { |
492 | 131 | if (session->first_packets_counter < DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT) { |
493 | 89 | ++session->first_packets_counter; |
494 | 89 | } else { |
495 | 42 | const uint32_t data_length_full = msg->header.data_length_full; // without header |
496 | 42 | const uint32_t received_length_full = msg->header.received_length_full; // without header |
497 | 42 | if (session->add_recv != nullptr) { |
498 | 22 | session->add_recv(session->bwc_user_data, data_length_full); |
499 | 22 | } |
500 | | |
501 | 42 | if (received_length_full < data_length_full) { |
502 | 0 | LOGGER_DEBUG(session->log, "BWC: full length=%u received length=%u", data_length_full, received_length_full); |
503 | 0 | if (session->add_lost != nullptr) { |
504 | 0 | session->add_lost(session->bwc_user_data, data_length_full - received_length_full); |
505 | 0 | } |
506 | 0 | } |
507 | 42 | } |
508 | 131 | } |
509 | | |
510 | | /** |
511 | | * Handle a single RTP video packet. |
512 | | * |
513 | | * The packet may or may not be part of a multipart frame. This function will |
514 | | * find out and handle it appropriately. |
515 | | * |
516 | | * @param session The current RTP session |
517 | | * @param header The RTP header deserialised from the packet. |
518 | | * @param incoming_data The packet data *not* header, i.e. this is the actual |
519 | | * payload. |
520 | | * @param incoming_data_length The packet length *not* including header, i.e. |
521 | | * this is the actual payload length. |
522 | | * @param log A logger. |
523 | | * |
524 | | * @retval -1 on error. |
525 | | * @retval 0 on success. |
526 | | */ |
527 | | static int handle_video_packet(const Logger *_Nonnull log, RTPSession *_Nonnull session, const struct RTPHeader *_Nonnull header, |
528 | | const uint8_t *_Nonnull incoming_data, uint16_t incoming_data_length) |
529 | 283 | { |
530 | 283 | if (incoming_data_length == 0) { |
531 | | // video always has a payload, ignore |
532 | 0 | return -1; |
533 | 0 | } |
534 | | |
535 | | // Full frame length in bytes. The frame may be split into multiple packets, |
536 | | // but this value is the complete assembled frame size. |
537 | 283 | const uint32_t full_frame_length = header->data_length_full; |
538 | | |
539 | | // The sender tells us whether this is a key frame. |
540 | 283 | const bool is_keyframe = (header->flags & RTP_KEY_FRAME) != 0; |
541 | | |
542 | 283 | LOGGER_DEBUG(log, "wkbl->next_free_entry:003=%d", session->work_buffer_list->next_free_entry); |
543 | | |
544 | 283 | const bool is_multipart = full_frame_length != incoming_data_length; |
545 | | |
546 | | /* The message was sent in single part */ |
547 | 283 | int8_t slot_id = get_slot(log, session->work_buffer_list, is_keyframe, header, is_multipart); |
548 | 283 | LOGGER_DEBUG(log, "slot num=%d", slot_id); |
549 | | |
550 | | // get_slot told us to drop the packet, so we ignore it. |
551 | 283 | if (slot_id == GET_SLOT_RESULT_DROP_INCOMING) { |
552 | 1 | return -1; |
553 | 1 | } |
554 | | |
555 | | // get_slot said there is no free slot. |
556 | 282 | if (slot_id == GET_SLOT_RESULT_DROP_OLDEST_SLOT) { |
557 | 1 | LOGGER_DEBUG(log, "there was no free slot, so we process the oldest frame"); |
558 | | // We now own the frame. |
559 | 1 | struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, 0); |
560 | | |
561 | | // The process_frame function returns NULL if there is no slot 0, i.e. |
562 | | // the work buffer list is completely empty. It can't be empty, because |
563 | | // get_slot just told us it's full, so process_frame must return non-null. |
564 | 1 | assert(m_new != nullptr); |
565 | | |
566 | 1 | if (m_new->len >= 2) { |
567 | 1 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0], |
568 | 1 | (int)m_new->data[1]); |
569 | 1 | } else if (m_new->len == 1) { |
570 | 0 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d", (int)m_new->data[0]); |
571 | 0 | } else { |
572 | 0 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a (empty)"); |
573 | 0 | } |
574 | 1 | update_bwc_values(session, m_new); |
575 | | // Pass ownership of m_new to the callback. |
576 | 1 | session->mcb(session->mono_time, session->cs, m_new); |
577 | | // Now we no longer own m_new. |
578 | 1 | m_new = nullptr; |
579 | | |
580 | | // Now we must have a free slot, so we either get that slot, i.e. >= 0, |
581 | | // or get told to drop the incoming packet if it's too old. |
582 | 1 | slot_id = get_slot(log, session->work_buffer_list, is_keyframe, header, /* is_multipart */false); |
583 | | |
584 | 1 | if (slot_id == GET_SLOT_RESULT_DROP_INCOMING) { |
585 | | // The incoming frame is too old, so we drop it. |
586 | 0 | return -1; |
587 | 0 | } |
588 | 1 | } |
589 | | |
590 | | // We must have a valid slot here. |
591 | 282 | assert(slot_id >= 0); |
592 | | |
593 | 282 | LOGGER_DEBUG(log, "fill_data_into_slot.1"); |
594 | | |
595 | | // fill in this part into the slot buffer at the correct offset |
596 | 282 | if (!fill_data_into_slot( |
597 | 282 | log, |
598 | 282 | session->work_buffer_list, |
599 | 282 | slot_id, |
600 | 282 | is_keyframe, |
601 | 282 | header, |
602 | 282 | incoming_data, |
603 | 282 | incoming_data_length)) { |
604 | | // Memory allocation failed. Return error. |
605 | 151 | return -1; |
606 | 151 | } |
607 | | |
608 | 131 | struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, slot_id); |
609 | | |
610 | 131 | if (m_new != nullptr) { |
611 | 130 | if (m_new->len >= 2) { |
612 | 129 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0], |
613 | 129 | (int)m_new->data[1]); |
614 | 129 | } else if (m_new->len == 1) { |
615 | 1 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d", (int)m_new->data[0]); |
616 | 1 | } else { |
617 | 0 | LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a (empty)"); |
618 | 0 | } |
619 | 130 | update_bwc_values(session, m_new); |
620 | 130 | session->mcb(session->mono_time, session->cs, m_new); |
621 | | |
622 | 130 | m_new = nullptr; |
623 | 130 | } |
624 | | |
625 | 131 | return 0; |
626 | 282 | } |
627 | | |
628 | | /** |
629 | | * receive custom lossypackets and process them. they can be incoming audio or video packets |
630 | | */ |
631 | | void rtp_receive_packet(RTPSession *session, const uint8_t *data, size_t length) |
632 | 66.0k | { |
633 | 66.0k | const Logger *log = session->log; |
634 | | |
635 | 66.0k | if (length < RTP_HEADER_SIZE + 1) { |
636 | 2 | LOGGER_WARNING(log, "Invalid length of received buffer!"); |
637 | 2 | return; |
638 | 2 | } |
639 | | |
640 | | // Get the packet type. |
641 | 66.0k | const uint8_t packet_type = data[0]; |
642 | 66.0k | const uint8_t *payload = &data[1]; |
643 | 66.0k | assert(length - 1 < UINT16_MAX); |
644 | 66.0k | const uint16_t payload_size = (uint16_t)length - 1; |
645 | | |
646 | | // Unpack the header. |
647 | 66.0k | struct RTPHeader header; |
648 | 66.0k | rtp_header_unpack(payload, &header); |
649 | | |
650 | 66.0k | if (header.pt != packet_type % 128) { |
651 | 2 | LOGGER_WARNING(log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d", |
652 | 2 | header.pt, packet_type % 128); |
653 | 2 | return; |
654 | 2 | } |
655 | | |
656 | 66.0k | if (header.pt != session->payload_type % 128) { |
657 | 4 | LOGGER_WARNING(log, "RTPHeader packet type does not match this session's payload type: %d != %d", |
658 | 4 | header.pt, session->payload_type % 128); |
659 | 4 | return; |
660 | 4 | } |
661 | | |
662 | 66.0k | if ((header.flags & RTP_LARGE_FRAME) != 0 && header.offset_full >= header.data_length_full) { |
663 | 1 | LOGGER_ERROR(log, "Invalid video packet: frame offset (%u) >= full frame length (%u)", |
664 | 1 | (unsigned)header.offset_full, (unsigned)header.data_length_full); |
665 | 1 | return; |
666 | 1 | } |
667 | | |
668 | 66.0k | if (header.offset_lower >= header.data_length_lower) { |
669 | 3 | LOGGER_ERROR(log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)", |
670 | 3 | (unsigned)header.offset_lower, (unsigned)header.data_length_lower); |
671 | 3 | return; |
672 | 3 | } |
673 | | |
674 | 65.9k | LOGGER_DEBUG(log, "header.pt %d, video %d", (uint8_t)header.pt, RTP_TYPE_VIDEO % 128); |
675 | | |
676 | | // The sender uses the new large-frame capable protocol and is sending a |
677 | | // video packet. |
678 | 65.9k | if ((header.flags & RTP_LARGE_FRAME) != 0 && header.pt == (RTP_TYPE_VIDEO % 128)) { |
679 | 283 | handle_video_packet(log, session, &header, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); |
680 | 283 | return; |
681 | 283 | } |
682 | | |
683 | | // everything below here is for the old 16 bit protocol ------------------ |
684 | | |
685 | 65.7k | if (header.data_length_lower == payload_size - RTP_HEADER_SIZE) { |
686 | | /* The message is sent in single part */ |
687 | | |
688 | | /* Message is not late; pick up the latest parameters */ |
689 | 65.7k | session->rsequnum = header.sequnum; |
690 | 65.7k | session->rtimestamp = header.timestamp; |
691 | 65.7k | if (session->add_recv != nullptr) { |
692 | 49 | session->add_recv(session->bwc_user_data, payload_size); |
693 | 49 | } |
694 | | |
695 | | /* Invoke processing of active multiparted message */ |
696 | 65.7k | if (session->mp != nullptr) { |
697 | 1 | session->mcb(session->mono_time, session->cs, session->mp); |
698 | 1 | session->mp = nullptr; |
699 | 1 | } |
700 | | |
701 | | /* The message came in the allowed time; |
702 | | */ |
703 | | |
704 | 65.7k | session->mp = new_message(log, &header, payload_size - RTP_HEADER_SIZE, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); |
705 | 65.7k | session->mcb(session->mono_time, session->cs, session->mp); |
706 | 65.7k | session->mp = nullptr; |
707 | 65.7k | return; |
708 | 65.7k | } |
709 | | |
710 | | /* The message is sent in multiple parts */ |
711 | | |
712 | 9 | if (session->mp != nullptr) { |
713 | | /* There are 2 possible situations in this case: |
714 | | * 1) being that we got the part of already processing message. |
715 | | * 2) being that we got the part of a new/old message. |
716 | | * |
717 | | * We handle them differently as we only allow a single multiparted |
718 | | * processing message |
719 | | */ |
720 | 2 | if (session->mp->header.sequnum == header.sequnum && |
721 | 2 | session->mp->header.timestamp == header.timestamp) { |
722 | | /* First case */ |
723 | | |
724 | | /* Make sure we have enough allocated memory */ |
725 | 2 | if (session->mp->header.data_length_lower - session->mp->len < payload_size - RTP_HEADER_SIZE || |
726 | 2 | session->mp->header.data_length_lower <= header.offset_lower || |
727 | 2 | session->mp->header.data_length_lower - header.offset_lower < payload_size - RTP_HEADER_SIZE) { |
728 | 1 | LOGGER_WARNING(log, "Corruption on the stream: multipart audio packet does not fit"); |
729 | 1 | return; |
730 | 1 | } |
731 | | |
732 | 1 | memcpy(session->mp->data + header.offset_lower, &payload[RTP_HEADER_SIZE], |
733 | 1 | payload_size - RTP_HEADER_SIZE); |
734 | 1 | session->mp->len += payload_size - RTP_HEADER_SIZE; |
735 | 1 | if (session->add_recv != nullptr) { |
736 | 1 | session->add_recv(session->bwc_user_data, payload_size); |
737 | 1 | } |
738 | | |
739 | 1 | if (session->mp->len == session->mp->header.data_length_lower) { |
740 | | /* Received a full message; now push it for the further |
741 | | * processing. |
742 | | */ |
743 | 1 | session->mcb(session->mono_time, session->cs, session->mp); |
744 | 1 | session->mp = nullptr; |
745 | 1 | } |
746 | 1 | } else { |
747 | | /* Second case */ |
748 | 0 | if (session->mp->header.timestamp > header.timestamp) { |
749 | | /* The received message part is from the old message; |
750 | | * discard it. |
751 | | */ |
752 | 0 | return; |
753 | 0 | } |
754 | | |
755 | | /* Push the previous message for processing */ |
756 | 0 | session->mcb(session->mono_time, session->cs, session->mp); |
757 | |
|
758 | 0 | session->mp = nullptr; |
759 | 0 | goto NEW_MULTIPARTED; |
760 | 0 | } |
761 | 7 | } else { |
762 | | /* In this case treat the message as if it was received in order |
763 | | */ |
764 | | /* This is also a point for new multiparted messages */ |
765 | 7 | NEW_MULTIPARTED: |
766 | | |
767 | 7 | if (header.data_length_lower - header.offset_lower < payload_size - RTP_HEADER_SIZE) { |
768 | 2 | LOGGER_WARNING(log, "Packet too long for buffer: offset %u + len %u > total %u", |
769 | 2 | (unsigned)header.offset_lower, (unsigned)(payload_size - RTP_HEADER_SIZE), |
770 | 2 | (unsigned)header.data_length_lower); |
771 | 2 | return; |
772 | 2 | } |
773 | | |
774 | | /* Message is not late; pick up the latest parameters */ |
775 | 5 | session->rsequnum = header.sequnum; |
776 | 5 | session->rtimestamp = header.timestamp; |
777 | 5 | if (session->add_recv != nullptr) { |
778 | 1 | session->add_recv(session->bwc_user_data, payload_size); |
779 | 1 | } |
780 | | |
781 | | /* Store message. |
782 | | */ |
783 | 5 | session->mp = new_message(log, &header, header.data_length_lower, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); |
784 | | |
785 | 5 | if (session->mp != nullptr) { |
786 | 5 | memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len); |
787 | 5 | } else { |
788 | 0 | LOGGER_WARNING(log, "new_message() returned a null pointer"); |
789 | 0 | return; |
790 | 0 | } |
791 | 5 | } |
792 | | |
793 | 6 | return; |
794 | 9 | } |
795 | | |
796 | | size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header) |
797 | 66.0k | { |
798 | 66.0k | uint8_t *p = rdata; |
799 | 66.0k | *p = (header->ve & 3) << 6 |
800 | 66.0k | | (header->pe & 1) << 5 |
801 | 66.0k | | (header->xe & 1) << 4 |
802 | 66.0k | | (header->cc & 0xf); |
803 | 66.0k | ++p; |
804 | 66.0k | *p = (header->ma & 1) << 7 |
805 | 66.0k | | (header->pt & 0x7f); |
806 | 66.0k | ++p; |
807 | | |
808 | 66.0k | p += net_pack_u16(p, header->sequnum); |
809 | 66.0k | p += net_pack_u32(p, header->timestamp); |
810 | 66.0k | p += net_pack_u32(p, header->ssrc); |
811 | 66.0k | p += net_pack_u64(p, header->flags); |
812 | 66.0k | p += net_pack_u32(p, header->offset_full); |
813 | 66.0k | p += net_pack_u32(p, header->data_length_full); |
814 | 66.0k | p += net_pack_u32(p, header->received_length_full); |
815 | | |
816 | 792k | for (size_t i = 0; i < RTP_PADDING_FIELDS; ++i) { |
817 | 726k | p += net_pack_u32(p, 0); |
818 | 726k | } |
819 | | |
820 | 66.0k | p += net_pack_u16(p, header->offset_lower); |
821 | 66.0k | p += net_pack_u16(p, header->data_length_lower); |
822 | 66.0k | assert(p == rdata + RTP_HEADER_SIZE); |
823 | 66.0k | return p - rdata; |
824 | 66.0k | } |
825 | | |
826 | | size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header) |
827 | 66.0k | { |
828 | 66.0k | const uint8_t *p = data; |
829 | 66.0k | header->ve = (*p >> 6) & 3; |
830 | 66.0k | header->pe = (*p >> 5) & 1; |
831 | 66.0k | header->xe = (*p >> 4) & 1; |
832 | 66.0k | header->cc = *p & 0xf; |
833 | 66.0k | ++p; |
834 | | |
835 | 66.0k | header->ma = (*p >> 7) & 1; |
836 | 66.0k | header->pt = *p & 0x7f; |
837 | 66.0k | ++p; |
838 | | |
839 | 66.0k | p += net_unpack_u16(p, &header->sequnum); |
840 | 66.0k | p += net_unpack_u32(p, &header->timestamp); |
841 | 66.0k | p += net_unpack_u32(p, &header->ssrc); |
842 | 66.0k | p += net_unpack_u64(p, &header->flags); |
843 | 66.0k | p += net_unpack_u32(p, &header->offset_full); |
844 | 66.0k | p += net_unpack_u32(p, &header->data_length_full); |
845 | 66.0k | p += net_unpack_u32(p, &header->received_length_full); |
846 | | |
847 | 66.0k | p += sizeof(uint32_t) * RTP_PADDING_FIELDS; |
848 | | |
849 | 66.0k | p += net_unpack_u16(p, &header->offset_lower); |
850 | 66.0k | p += net_unpack_u16(p, &header->data_length_lower); |
851 | 66.0k | assert(p == data + RTP_HEADER_SIZE); |
852 | 66.0k | return p - data; |
853 | 66.0k | } |
854 | | |
855 | | static uint32_t rtp_random_u32(void) |
856 | 47 | { |
857 | | // HINT: uses libsodium function |
858 | 47 | return randombytes_random(); |
859 | 47 | } |
860 | | |
861 | | RTPSession *rtp_new(const Logger *log, int payload_type, Mono_Time *mono_time, |
862 | | rtp_send_packet_cb *send_packet, void *send_packet_user_data, |
863 | | rtp_add_recv_cb *add_recv, rtp_add_lost_cb *add_lost, void *bwc_user_data, |
864 | | void *cs, rtp_m_cb *mcb) |
865 | 83 | { |
866 | 83 | assert(mcb != nullptr); |
867 | 83 | assert(cs != nullptr); |
868 | | |
869 | 83 | RTPSession *session = (RTPSession *)calloc(1, sizeof(RTPSession)); |
870 | | |
871 | 83 | if (session == nullptr) { |
872 | 0 | LOGGER_WARNING(log, "Alloc failed! Program might misbehave!"); |
873 | 0 | return nullptr; |
874 | 0 | } |
875 | | |
876 | 83 | session->work_buffer_list = (struct RTPWorkBufferList *)calloc(1, sizeof(struct RTPWorkBufferList)); |
877 | | |
878 | 83 | if (session->work_buffer_list == nullptr) { |
879 | 0 | LOGGER_ERROR(log, "out of memory while allocating work buffer list"); |
880 | 0 | free(session); |
881 | 0 | return nullptr; |
882 | 0 | } |
883 | | |
884 | | // First entry is free. |
885 | 83 | session->work_buffer_list->next_free_entry = 0; |
886 | | |
887 | 83 | session->ssrc = payload_type == RTP_TYPE_VIDEO ? 0 : rtp_random_u32(); // Zoff: what is this?? |
888 | 83 | session->payload_type = payload_type; |
889 | 83 | session->log = log; |
890 | 83 | session->mono_time = mono_time; |
891 | 83 | session->rtp_receive_active = true; |
892 | | |
893 | 83 | session->send_packet = send_packet; |
894 | 83 | session->send_packet_user_data = send_packet_user_data; |
895 | 83 | session->add_recv = add_recv; |
896 | 83 | session->add_lost = add_lost; |
897 | 83 | session->bwc_user_data = bwc_user_data; |
898 | | |
899 | | // set NULL just in case |
900 | 83 | session->mp = nullptr; |
901 | 83 | session->first_packets_counter = 1; |
902 | | |
903 | | /* Also set payload type as prefix */ |
904 | 83 | session->cs = cs; |
905 | 83 | session->mcb = mcb; |
906 | | |
907 | 83 | return session; |
908 | 83 | } |
909 | | |
910 | | void rtp_kill(const Logger *log, RTPSession *session) |
911 | 83 | { |
912 | 83 | if (session == nullptr) { |
913 | 0 | LOGGER_WARNING(log, "No session"); |
914 | 0 | return; |
915 | 0 | } |
916 | | |
917 | 83 | LOGGER_DEBUG(log, "Terminated RTP session: %p", (void *)session); |
918 | 83 | LOGGER_DEBUG(log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d", |
919 | 83 | (int)session->work_buffer_list->next_free_entry); |
920 | | |
921 | 83 | if (session->work_buffer_list != nullptr) { |
922 | 92 | for (int8_t i = 0; i < session->work_buffer_list->next_free_entry; ++i) { |
923 | 9 | free(session->work_buffer_list->work_buffer[i].buf); |
924 | 9 | } |
925 | 83 | free(session->work_buffer_list); |
926 | 83 | } |
927 | 83 | free(session->mp); |
928 | 83 | free(session); |
929 | 83 | } |
930 | | |
931 | | void rtp_allow_receiving_mark(RTPSession *session) |
932 | 1 | { |
933 | 1 | if (session != nullptr) { |
934 | 1 | session->rtp_receive_active = true; |
935 | 1 | } |
936 | 1 | } |
937 | | |
938 | | void rtp_stop_receiving_mark(RTPSession *session) |
939 | 1 | { |
940 | 1 | if (session != nullptr) { |
941 | 1 | session->rtp_receive_active = false; |
942 | 1 | } |
943 | 1 | } |
944 | | |
945 | | static void rtp_send_piece(RTPSession *_Nonnull session, const struct RTPHeader *_Nonnull header, |
946 | | const uint8_t *_Nonnull data, uint8_t *_Nonnull rdata, uint16_t length) |
947 | 66.0k | { |
948 | 66.0k | rtp_header_pack(rdata + 1, header); |
949 | 66.0k | memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length); |
950 | | |
951 | 66.0k | const uint16_t rdata_size = length + RTP_HEADER_SIZE + 1; |
952 | | |
953 | 66.0k | if (session->send_packet != nullptr) { |
954 | 66.0k | session->send_packet(session->send_packet_user_data, rdata, rdata_size); |
955 | 66.0k | } |
956 | 66.0k | } |
957 | | |
958 | | static struct RTPHeader rtp_default_header(const RTPSession *_Nonnull session, uint32_t length, bool is_keyframe) |
959 | 65.8k | { |
960 | 65.8k | uint16_t length_safe = (uint16_t)length; |
961 | | |
962 | 65.8k | if (length > UINT16_MAX) { |
963 | 1 | length_safe = UINT16_MAX; |
964 | 1 | } |
965 | | |
966 | 65.8k | struct RTPHeader header = {0}; |
967 | | |
968 | 65.8k | if (is_keyframe) { |
969 | 35 | header.flags |= RTP_KEY_FRAME; |
970 | 35 | } |
971 | | |
972 | 65.8k | if (session->payload_type == RTP_TYPE_VIDEO) { |
973 | 140 | header.flags |= RTP_LARGE_FRAME; |
974 | 140 | } |
975 | | |
976 | 65.8k | header.ve = 2; // this is unused in toxav |
977 | 65.8k | header.pe = 0; |
978 | 65.8k | header.xe = 0; |
979 | 65.8k | header.cc = 0; |
980 | 65.8k | header.ma = 0; |
981 | 65.8k | header.pt = session->payload_type % 128; |
982 | 65.8k | header.sequnum = session->sequnum; |
983 | 65.8k | if (session->mono_time != nullptr) { |
984 | 65.8k | header.timestamp = current_time_monotonic(session->mono_time); |
985 | 65.8k | } else { |
986 | 0 | header.timestamp = 0; |
987 | 0 | } |
988 | 65.8k | header.ssrc = session->ssrc; |
989 | 65.8k | header.offset_lower = 0; |
990 | 65.8k | header.data_length_lower = length_safe; |
991 | 65.8k | header.data_length_full = length; // without header |
992 | 65.8k | header.offset_full = 0; |
993 | | |
994 | 65.8k | return header; |
995 | 65.8k | } |
996 | | |
997 | | /** |
998 | | * @brief Send a frame of audio or video data, chunked in @ref RTPMessage instances. |
999 | | * |
1000 | | * @param session The A/V session to send the data for. |
1001 | | * @param data A byte array of length @p length. |
1002 | | * @param length The number of bytes to send from @p data. |
1003 | | * @param is_keyframe Whether this video frame is a key frame. If it is an |
1004 | | * audio frame, this parameter is ignored. |
1005 | | */ |
1006 | | int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, uint32_t length, |
1007 | | bool is_keyframe) |
1008 | 65.8k | { |
1009 | 65.8k | if (session == nullptr) { |
1010 | 0 | return -1; |
1011 | 0 | } |
1012 | | |
1013 | 65.8k | const uint16_t rdata_size = min_u32(length + RTP_HEADER_SIZE + 1, MAX_CRYPTO_DATA_SIZE); |
1014 | 65.8k | VLA(uint8_t, rdata, rdata_size); |
1015 | 65.8k | memset(rdata, 0, rdata_size); |
1016 | 65.8k | rdata[0] = session->payload_type; // packet id == payload_type |
1017 | | |
1018 | 65.8k | struct RTPHeader header = rtp_default_header(session, length, is_keyframe); |
1019 | | |
1020 | 65.8k | if (MAX_CRYPTO_DATA_SIZE > (length + RTP_HEADER_SIZE + 1)) { |
1021 | | /* |
1022 | | * The length is lesser than the maximum allowed length (including header) |
1023 | | * Send the packet in single piece. |
1024 | | */ |
1025 | 65.8k | assert(length < UINT16_MAX); |
1026 | 65.8k | rtp_send_piece(session, &header, data, rdata, (uint16_t)length); |
1027 | 65.8k | } else { |
1028 | | /* |
1029 | | * The length is greater than the maximum allowed length (including header) |
1030 | | * Send the packet in multiple pieces. |
1031 | | */ |
1032 | 66 | uint32_t sent = 0; |
1033 | 66 | uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1); |
1034 | | |
1035 | 221 | while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) { |
1036 | 155 | rtp_send_piece(session, &header, data + sent, rdata, piece); |
1037 | | |
1038 | 155 | sent += piece; |
1039 | 155 | header.offset_lower = (uint16_t)sent; |
1040 | 155 | header.offset_full = sent; // raw data offset, without any header |
1041 | 155 | } |
1042 | | |
1043 | | /* Send remaining */ |
1044 | 66 | piece = (uint16_t)(length - sent); |
1045 | | |
1046 | 66 | if (piece != 0) { |
1047 | 66 | rtp_send_piece(session, &header, data + sent, rdata, piece); |
1048 | 66 | } |
1049 | 66 | } |
1050 | | |
1051 | 65.8k | ++session->sequnum; |
1052 | 65.8k | return 0; |
1053 | 65.8k | } |