Coverage Report

Created: 2026-10-24 08:16

/work/toxav/rtp.c
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
}