Coverage Report

Created: 2025-10-08 19:34

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