Coverage Report

Created: 2024-01-26 01:52

/work/toxav/toxav.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2018 The TokTok team.
3
 * Copyright © 2013-2015 Tox project.
4
 */
5
#include "toxav.h"
6
7
#include <assert.h>
8
#include <errno.h>
9
#include <limits.h>
10
#include <stdlib.h>
11
#include <string.h>
12
13
#include "msi.h"
14
#include "rtp.h"
15
16
#include "../toxcore/Messenger.h"
17
#include "../toxcore/ccompat.h"
18
#include "../toxcore/logger.h"
19
#include "../toxcore/mono_time.h"
20
#include "../toxcore/tox_struct.h"
21
#include "../toxcore/util.h"
22
23
// TODO(zoff99): don't hardcode this, let the application choose it
24
// VPX Info: Time to spend encoding, in microseconds (it's a *soft* deadline)
25
142
#define WANTED_MAX_ENCODER_FPS 40
26
142
#define MAX_ENCODE_TIME_US (1000000 / WANTED_MAX_ENCODER_FPS) // to allow x fps
27
28
242
#define VIDEO_SEND_X_KEYFRAMES_FIRST 7 // force the first n frames to be keyframes!
29
30
/*
31
 * VPX_DL_REALTIME       (1)       deadline parameter analogous to VPx REALTIME mode.
32
 * VPX_DL_GOOD_QUALITY   (1000000) deadline parameter analogous to VPx GOOD QUALITY mode.
33
 * VPX_DL_BEST_QUALITY   (0)       deadline parameter analogous to VPx BEST QUALITY mode.
34
 */
35
36
// iteration interval that is used when no call is active
37
352
#define IDLE_ITERATION_INTERVAL_MS 200
38
39
typedef struct ToxAVCall {
40
    ToxAV *av;
41
42
    pthread_mutex_t mutex_audio[1];
43
    RTPSession *audio_rtp;
44
    ACSession *audio;
45
46
    pthread_mutex_t mutex_video[1];
47
    RTPSession *video_rtp;
48
    VCSession *video;
49
50
    BWController *bwc;
51
52
    bool active;
53
    MSICall *msi_call;
54
    uint32_t friend_number;
55
56
    uint32_t audio_bit_rate; /* Sending audio bit rate */
57
    uint32_t video_bit_rate; /* Sending video bit rate */
58
59
    /** Required for monitoring changes in states */
60
    uint8_t previous_self_capabilities;
61
62
    pthread_mutex_t toxav_call_mutex[1];
63
64
    struct ToxAVCall *prev;
65
    struct ToxAVCall *next;
66
} ToxAVCall;
67
68
69
/** Decode time statistics */
70
typedef struct DecodeTimeStats {
71
    /** Measure count */
72
    int32_t count;
73
    /** Last cycle total */
74
    int32_t total;
75
    /** Average decoding time in ms */
76
    int32_t average;
77
78
    /** Calculated iteration interval */
79
    uint32_t interval;
80
} DecodeTimeStats;
81
82
struct ToxAV {
83
    Tox *tox;
84
    Messenger *m;
85
    MSISession *msi;
86
87
    /* Two-way storage: first is array of calls and second is list of calls with head and tail */
88
    ToxAVCall **calls;
89
    uint32_t calls_tail;
90
    uint32_t calls_head;
91
    pthread_mutex_t mutex[1];
92
93
    /* Call callback */
94
    toxav_call_cb *ccb;
95
    void *ccb_user_data;
96
    /* Call state callback */
97
    toxav_call_state_cb *scb;
98
    void *scb_user_data;
99
    /* Audio frame receive callback */
100
    toxav_audio_receive_frame_cb *acb;
101
    void *acb_user_data;
102
    /* Video frame receive callback */
103
    toxav_video_receive_frame_cb *vcb;
104
    void *vcb_user_data;
105
    /* Bit rate control callback */
106
    toxav_audio_bit_rate_cb *abcb;
107
    void *abcb_user_data;
108
    /* Bit rate control callback */
109
    toxav_video_bit_rate_cb *vbcb;
110
    void *vbcb_user_data;
111
112
    /* keep track of decode times for audio and video */
113
    DecodeTimeStats audio_stats;
114
    DecodeTimeStats video_stats;
115
    /** ToxAV's own mono_time instance */
116
    Mono_Time *toxav_mono_time;
117
};
118
119
static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *user_data);
120
121
static int callback_invite(void *object, MSICall *call);
122
static int callback_start(void *object, MSICall *call);
123
static int callback_end(void *object, MSICall *call);
124
static int callback_error(void *object, MSICall *call);
125
static int callback_capabilites(void *object, MSICall *call);
126
127
static bool audio_bit_rate_invalid(uint32_t bit_rate);
128
static bool video_bit_rate_invalid(uint32_t bit_rate);
129
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
130
static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *error);
131
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
132
static ToxAVCall *call_remove(ToxAVCall *call);
133
static bool call_prepare_transmission(ToxAVCall *call);
134
static void call_kill_transmission(ToxAVCall *call);
135
136
/**
137
 * @brief initialize d with default values
138
 * @param d struct to be initialized, must not be nullptr
139
 */
140
static void init_decode_time_stats(DecodeTimeStats *d)
141
12
{
142
12
    assert(d != nullptr);
143
12
    d->count = 0;
144
12
    d->total = 0;
145
12
    d->average = 0;
146
12
    d->interval = IDLE_ITERATION_INTERVAL_MS;
147
12
}
148
149
ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
150
6
{
151
6
    Toxav_Err_New rc = TOXAV_ERR_NEW_OK;
152
6
    ToxAV *av = nullptr;
153
154
6
    if (tox == nullptr) {
155
0
        rc = TOXAV_ERR_NEW_NULL;
156
0
        goto RETURN;
157
0
    }
158
159
    // TODO(iphydf): Don't rely on toxcore internals.
160
6
    Messenger *m;
161
6
    m = tox->m;
162
163
6
    if (m->msi_packet != nullptr) {
164
0
        rc = TOXAV_ERR_NEW_MULTIPLE;
165
0
        goto RETURN;
166
0
    }
167
168
6
    av = (ToxAV *)calloc(1, sizeof(ToxAV));
169
170
6
    if (av == nullptr) {
171
0
        LOGGER_WARNING(m->log, "Allocation failed!");
172
0
        rc = TOXAV_ERR_NEW_MALLOC;
173
0
        goto RETURN;
174
0
    }
175
176
6
    if (create_recursive_mutex(av->mutex) != 0) {
177
0
        LOGGER_WARNING(m->log, "Mutex creation failed!");
178
0
        rc = TOXAV_ERR_NEW_MALLOC;
179
0
        goto RETURN;
180
0
    }
181
182
6
    av->tox = tox;
183
6
    av->m = m;
184
6
    av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr);
185
6
    av->msi = msi_new(av->m);
186
187
6
    if (av->msi == nullptr) {
188
0
        pthread_mutex_destroy(av->mutex);
189
0
        rc = TOXAV_ERR_NEW_MALLOC;
190
0
        goto RETURN;
191
0
    }
192
193
6
    init_decode_time_stats(&av->audio_stats);
194
6
    init_decode_time_stats(&av->video_stats);
195
6
    av->msi->av = av;
196
197
6
    msi_callback_invite(av->msi, callback_invite);
198
6
    msi_callback_start(av->msi, callback_start);
199
6
    msi_callback_end(av->msi, callback_end);
200
6
    msi_callback_error(av->msi, callback_error);
201
6
    msi_callback_peertimeout(av->msi, callback_error);
202
6
    msi_callback_capabilities(av->msi, callback_capabilites);
203
204
6
RETURN:
205
206
6
    if (error != nullptr) {
207
6
        *error = rc;
208
6
    }
209
210
6
    if (rc != TOXAV_ERR_NEW_OK) {
211
0
        free(av);
212
0
        av = nullptr;
213
0
    }
214
215
6
    return av;
216
6
}
217
void toxav_kill(ToxAV *av)
218
6
{
219
6
    if (av == nullptr) {
220
0
        return;
221
0
    }
222
223
6
    pthread_mutex_lock(av->mutex);
224
225
    /* To avoid possible deadlocks */
226
6
    while (av->msi != nullptr && msi_kill(av->msi, av->m->log) != 0) {
227
0
        pthread_mutex_unlock(av->mutex);
228
0
        pthread_mutex_lock(av->mutex);
229
0
    }
230
231
    /* Msi kill will hang up all calls so just clean these calls */
232
6
    if (av->calls != nullptr) {
233
0
        ToxAVCall *it = call_get(av, av->calls_head);
234
235
0
        while (it != nullptr) {
236
0
            call_kill_transmission(it);
237
0
            it->msi_call = nullptr; /* msi_kill() frees the call's msi_call handle; which causes #278 */
238
0
            it = call_remove(it); /* This will eventually free av->calls */
239
0
        }
240
0
    }
241
242
6
    mono_time_free(av->tox->sys.mem, av->toxav_mono_time);
243
244
6
    pthread_mutex_unlock(av->mutex);
245
6
    pthread_mutex_destroy(av->mutex);
246
247
6
    free(av);
248
6
}
249
Tox *toxav_get_tox(const ToxAV *av)
250
0
{
251
0
    return av->tox;
252
0
}
253
254
uint32_t toxav_audio_iteration_interval(const ToxAV *av)
255
0
{
256
0
    return av->calls != nullptr ? av->audio_stats.interval : IDLE_ITERATION_INTERVAL_MS;
257
0
}
258
259
uint32_t toxav_video_iteration_interval(const ToxAV *av)
260
0
{
261
0
    return av->calls != nullptr ? av->video_stats.interval : IDLE_ITERATION_INTERVAL_MS;
262
0
}
263
264
uint32_t toxav_iteration_interval(const ToxAV *av)
265
0
{
266
0
    return min_u32(toxav_audio_iteration_interval(av),
267
0
                   toxav_video_iteration_interval(av));
268
0
}
269
270
/**
271
 * @brief calc_interval Calculates the needed iteration interval based on previous decode times
272
 * @param av ToxAV struct to work on
273
 * @param stats Statistics to update
274
 * @param frame_time the duration of the current frame in ms
275
 * @param start_time the timestamp when decoding of this frame started
276
 */
277
static void calc_interval(ToxAV *av, DecodeTimeStats *stats, int32_t frame_time, uint64_t start_time)
278
340
{
279
340
    stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average);
280
340
    stats->total += current_time_monotonic(av->m->mono_time) - start_time;
281
282
340
    if (++stats->count == 3) {
283
110
        stats->average = stats->total / 3 + 5; /* NOTE: Magic Offset for precision */
284
110
        stats->count = 0;
285
110
        stats->total = 0;
286
110
    }
287
340
}
288
289
/**
290
 * @brief common iterator function for audio and video calls
291
 * @param av pointer to ToxAV structure of current instance
292
 * @param audio if true, iterate audio, video else
293
 */
294
static void iterate_common(ToxAV *av, bool audio)
295
2.02k
{
296
2.02k
    pthread_mutex_lock(av->mutex);
297
298
2.02k
    if (av->calls == nullptr) {
299
1.68k
        pthread_mutex_unlock(av->mutex);
300
1.68k
        return;
301
1.68k
    }
302
303
340
    const uint64_t start = current_time_monotonic(av->toxav_mono_time);
304
    // time until the first audio or video frame is over
305
340
    int32_t frame_time = IDLE_ITERATION_INTERVAL_MS;
306
307
1.01k
    for (ToxAVCall *i = av->calls[av->calls_head]; i != nullptr; i = i->next) {
308
676
        if (!i->active) {
309
116
            continue;
310
116
        }
311
312
560
        pthread_mutex_lock(i->toxav_call_mutex);
313
560
        pthread_mutex_unlock(av->mutex);
314
315
560
        if (audio) {
316
280
            ac_iterate(i->audio);
317
318
280
            if ((i->msi_call->self_capabilities & MSI_CAP_R_AUDIO) != 0 &&
319
280
                    (i->msi_call->peer_capabilities & MSI_CAP_S_AUDIO) != 0) {
320
280
                frame_time = min_s32(i->audio->lp_frame_duration, frame_time);
321
280
            }
322
280
        } else {
323
280
            vc_iterate(i->video);
324
325
280
            if ((i->msi_call->self_capabilities & MSI_CAP_R_VIDEO) != 0 &&
326
280
                    (i->msi_call->peer_capabilities & MSI_CAP_S_VIDEO) != 0) {
327
280
                pthread_mutex_lock(i->video->queue_mutex);
328
280
                frame_time = min_s32(i->video->lcfd, frame_time);
329
280
                pthread_mutex_unlock(i->video->queue_mutex);
330
280
            }
331
280
        }
332
333
560
        const uint32_t fid = i->friend_number;
334
335
560
        pthread_mutex_unlock(i->toxav_call_mutex);
336
560
        pthread_mutex_lock(av->mutex);
337
338
        /* In case this call is popped from container stop iteration */
339
560
        if (call_get(av, fid) != i) {
340
1
            break;
341
1
        }
342
560
    }
343
344
340
    DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats;
345
340
    calc_interval(av, stats, frame_time, start);
346
340
    pthread_mutex_unlock(av->mutex);
347
340
}
348
void toxav_audio_iterate(ToxAV *av)
349
1.01k
{
350
1.01k
    iterate_common(av, true);
351
1.01k
}
352
353
void toxav_video_iterate(ToxAV *av)
354
1.01k
{
355
1.01k
    iterate_common(av, false);
356
1.01k
}
357
358
void toxav_iterate(ToxAV *av)
359
1.01k
{
360
1.01k
    toxav_audio_iterate(av);
361
1.01k
    toxav_video_iterate(av);
362
1.01k
}
363
364
bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
365
                Toxav_Err_Call *error)
366
11
{
367
11
    Toxav_Err_Call rc = TOXAV_ERR_CALL_OK;
368
11
    ToxAVCall *call;
369
370
11
    pthread_mutex_lock(av->mutex);
371
372
11
    if ((audio_bit_rate != 0 && audio_bit_rate_invalid(audio_bit_rate))
373
11
            || (video_bit_rate != 0 && video_bit_rate_invalid(video_bit_rate))) {
374
0
        rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
375
0
        goto RETURN;
376
0
    }
377
378
11
    call = call_new(av, friend_number, &rc);
379
380
11
    if (call == nullptr) {
381
0
        goto RETURN;
382
0
    }
383
384
11
    call->audio_bit_rate = audio_bit_rate;
385
11
    call->video_bit_rate = video_bit_rate;
386
387
11
    call->previous_self_capabilities = MSI_CAP_R_AUDIO | MSI_CAP_R_VIDEO;
388
389
11
    call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
390
11
    call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
391
392
11
    if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
393
0
        call_remove(call);
394
0
        rc = TOXAV_ERR_CALL_SYNC;
395
0
        goto RETURN;
396
0
    }
397
398
11
    call->msi_call->av_call = call;
399
400
11
RETURN:
401
11
    pthread_mutex_unlock(av->mutex);
402
403
11
    if (error != nullptr) {
404
11
        *error = rc;
405
11
    }
406
407
11
    return rc == TOXAV_ERR_CALL_OK;
408
11
}
409
void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data)
410
6
{
411
6
    pthread_mutex_lock(av->mutex);
412
6
    av->ccb = callback;
413
6
    av->ccb_user_data = user_data;
414
6
    pthread_mutex_unlock(av->mutex);
415
6
}
416
bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
417
                  Toxav_Err_Answer *error)
418
9
{
419
9
    pthread_mutex_lock(av->mutex);
420
421
9
    Toxav_Err_Answer rc = TOXAV_ERR_ANSWER_OK;
422
9
    ToxAVCall *call;
423
424
9
    if (!m_friend_exists(av->m, friend_number)) {
425
0
        rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
426
0
        goto RETURN;
427
0
    }
428
429
9
    if ((audio_bit_rate != 0 && audio_bit_rate_invalid(audio_bit_rate))
430
9
            || (video_bit_rate != 0 && video_bit_rate_invalid(video_bit_rate))
431
9
       ) {
432
0
        rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
433
0
        goto RETURN;
434
0
    }
435
436
9
    call = call_get(av, friend_number);
437
438
9
    if (call == nullptr) {
439
0
        rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
440
0
        goto RETURN;
441
0
    }
442
443
9
    if (!call_prepare_transmission(call)) {
444
0
        rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
445
0
        goto RETURN;
446
0
    }
447
448
9
    call->audio_bit_rate = audio_bit_rate;
449
9
    call->video_bit_rate = video_bit_rate;
450
451
9
    call->previous_self_capabilities = MSI_CAP_R_AUDIO | MSI_CAP_R_VIDEO;
452
453
9
    call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
454
9
    call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
455
456
9
    if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) {
457
0
        rc = TOXAV_ERR_ANSWER_SYNC;
458
0
    }
459
460
9
RETURN:
461
9
    pthread_mutex_unlock(av->mutex);
462
463
9
    if (error != nullptr) {
464
9
        *error = rc;
465
9
    }
466
467
9
    return rc == TOXAV_ERR_ANSWER_OK;
468
9
}
469
void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *user_data)
470
6
{
471
6
    pthread_mutex_lock(av->mutex);
472
6
    av->scb = callback;
473
6
    av->scb_user_data = user_data;
474
6
    pthread_mutex_unlock(av->mutex);
475
6
}
476
static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call)
477
2
{
478
    /* Only act if paused and had media transfer active before */
479
2
    if (call->msi_call->self_capabilities != 0 || call->previous_self_capabilities == 0) {
480
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
481
0
    }
482
483
2
    if (msi_change_capabilities(call->msi_call, call->previous_self_capabilities) == -1) {
484
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
485
0
    }
486
487
2
    rtp_allow_receiving(call->audio_rtp);
488
2
    rtp_allow_receiving(call->video_rtp);
489
490
2
    return TOXAV_ERR_CALL_CONTROL_OK;
491
2
}
492
static Toxav_Err_Call_Control call_control_handle_pause(ToxAVCall *call)
493
2
{
494
    /* Only act if not already paused */
495
2
    if (call->msi_call->self_capabilities == 0) {
496
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
497
0
    }
498
499
2
    call->previous_self_capabilities = call->msi_call->self_capabilities;
500
501
2
    if (msi_change_capabilities(call->msi_call, 0) == -1) {
502
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
503
0
    }
504
505
2
    rtp_stop_receiving(call->audio_rtp);
506
2
    rtp_stop_receiving(call->video_rtp);
507
508
2
    return TOXAV_ERR_CALL_CONTROL_OK;
509
2
}
510
static Toxav_Err_Call_Control call_control_handle_cancel(ToxAVCall *call)
511
11
{
512
    /* Hang up */
513
11
    pthread_mutex_lock(call->toxav_call_mutex);
514
515
11
    if (msi_hangup(call->msi_call) != 0) {
516
0
        pthread_mutex_unlock(call->toxav_call_mutex);
517
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
518
0
    }
519
520
11
    call->msi_call = nullptr;
521
11
    pthread_mutex_unlock(call->toxav_call_mutex);
522
523
    /* No matter the case, terminate the call */
524
11
    call_kill_transmission(call);
525
11
    call_remove(call);
526
527
11
    return TOXAV_ERR_CALL_CONTROL_OK;
528
11
}
529
static Toxav_Err_Call_Control call_control_handle_mute_audio(const ToxAVCall *call)
530
2
{
531
2
    if ((call->msi_call->self_capabilities & MSI_CAP_R_AUDIO) == 0) {
532
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
533
0
    }
534
535
2
    if (msi_change_capabilities(call->msi_call, call->
536
2
                                msi_call->self_capabilities ^ MSI_CAP_R_AUDIO) == -1) {
537
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
538
539
0
    }
540
541
2
    rtp_stop_receiving(call->audio_rtp);
542
2
    return TOXAV_ERR_CALL_CONTROL_OK;
543
2
}
544
static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *call)
545
2
{
546
2
    if ((call->msi_call->self_capabilities ^ MSI_CAP_R_AUDIO) == 0) {
547
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
548
0
    }
549
550
2
    if (msi_change_capabilities(call->msi_call, call->
551
2
                                msi_call->self_capabilities | MSI_CAP_R_AUDIO) == -1) {
552
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
553
0
    }
554
555
2
    rtp_allow_receiving(call->audio_rtp);
556
2
    return TOXAV_ERR_CALL_CONTROL_OK;
557
2
}
558
static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *call)
559
1
{
560
1
    if ((call->msi_call->self_capabilities & MSI_CAP_R_VIDEO) == 0) {
561
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
562
0
    }
563
564
1
    if (msi_change_capabilities(call->msi_call, call->
565
1
                                msi_call->self_capabilities ^ MSI_CAP_R_VIDEO) == -1) {
566
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
567
0
    }
568
569
1
    rtp_stop_receiving(call->video_rtp);
570
1
    return TOXAV_ERR_CALL_CONTROL_OK;
571
1
}
572
static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *call)
573
1
{
574
1
    if ((call->msi_call->self_capabilities ^ MSI_CAP_R_VIDEO) == 0) {
575
0
        return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
576
0
    }
577
578
1
    if (msi_change_capabilities(call->msi_call, call->
579
1
                                msi_call->self_capabilities | MSI_CAP_R_VIDEO) == -1) {
580
0
        return TOXAV_ERR_CALL_CONTROL_SYNC;
581
0
    }
582
583
1
    rtp_allow_receiving(call->video_rtp);
584
1
    return TOXAV_ERR_CALL_CONTROL_OK;
585
1
}
586
static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Control control)
587
21
{
588
21
    switch (control) {
589
2
        case TOXAV_CALL_CONTROL_RESUME:
590
2
            return call_control_handle_resume(call);
591
592
2
        case TOXAV_CALL_CONTROL_PAUSE:
593
2
            return call_control_handle_pause(call);
594
595
11
        case TOXAV_CALL_CONTROL_CANCEL:
596
11
            return call_control_handle_cancel(call);
597
598
2
        case TOXAV_CALL_CONTROL_MUTE_AUDIO:
599
2
            return call_control_handle_mute_audio(call);
600
601
2
        case TOXAV_CALL_CONTROL_UNMUTE_AUDIO:
602
2
            return call_control_handle_unmute_audio(call);
603
604
1
        case TOXAV_CALL_CONTROL_HIDE_VIDEO:
605
1
            return call_control_handle_hide_video(call);
606
607
1
        case TOXAV_CALL_CONTROL_SHOW_VIDEO:
608
1
            return call_control_handle_show_video(call);
609
21
    }
610
611
0
    return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
612
21
}
613
static Toxav_Err_Call_Control call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control control)
614
27
{
615
27
    if (!m_friend_exists(av->m, friend_number)) {
616
0
        return TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
617
0
    }
618
619
27
    ToxAVCall *call = call_get(av, friend_number);
620
621
27
    if (call == nullptr || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
622
6
        return TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
623
6
    }
624
625
21
    return call_control_handle(call, control);
626
27
}
627
bool toxav_call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control control, Toxav_Err_Call_Control *error)
628
27
{
629
27
    pthread_mutex_lock(av->mutex);
630
631
27
    const Toxav_Err_Call_Control rc = call_control(av, friend_number, control);
632
633
27
    pthread_mutex_unlock(av->mutex);
634
635
27
    if (error != nullptr) {
636
21
        *error = rc;
637
21
    }
638
639
27
    return rc == TOXAV_ERR_CALL_CONTROL_OK;
640
27
}
641
bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
642
                              Toxav_Err_Bit_Rate_Set *error)
643
1
{
644
1
    Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
645
1
    ToxAVCall *call;
646
647
1
    if (!m_friend_exists(av->m, friend_number)) {
648
0
        rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
649
0
        goto RETURN;
650
0
    }
651
652
1
    if (bit_rate > 0 && audio_bit_rate_invalid(bit_rate)) {
653
0
        rc = TOXAV_ERR_BIT_RATE_SET_INVALID_BIT_RATE;
654
0
        goto RETURN;
655
0
    }
656
657
1
    pthread_mutex_lock(av->mutex);
658
1
    call = call_get(av, friend_number);
659
660
1
    if (call == nullptr || !call->active || call->msi_call->state != MSI_CALL_ACTIVE) {
661
0
        pthread_mutex_unlock(av->mutex);
662
0
        rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
663
0
        goto RETURN;
664
0
    }
665
666
1
    LOGGER_DEBUG(av->m->log, "Setting new audio bitrate to: %d", bit_rate);
667
668
1
    if (call->audio_bit_rate == bit_rate) {
669
0
        LOGGER_DEBUG(av->m->log, "Audio bitrate already set to: %d", bit_rate);
670
1
    } else if (bit_rate == 0) {
671
1
        LOGGER_DEBUG(av->m->log, "Turned off audio sending");
672
673
1
        if (msi_change_capabilities(call->msi_call, call->msi_call->
674
1
                                    self_capabilities ^ MSI_CAP_S_AUDIO) != 0) {
675
0
            pthread_mutex_unlock(av->mutex);
676
0
            rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
677
0
            goto RETURN;
678
0
        }
679
680
        /* Audio sending is turned off; notify peer */
681
1
        call->audio_bit_rate = 0;
682
1
    } else {
683
0
        pthread_mutex_lock(call->toxav_call_mutex);
684
685
0
        if (call->audio_bit_rate == 0) {
686
0
            LOGGER_DEBUG(av->m->log, "Turned on audio sending");
687
688
            /* The audio has been turned off before this */
689
0
            if (msi_change_capabilities(call->msi_call, call->
690
0
                                        msi_call->self_capabilities | MSI_CAP_S_AUDIO) != 0) {
691
0
                pthread_mutex_unlock(call->toxav_call_mutex);
692
0
                pthread_mutex_unlock(av->mutex);
693
0
                rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
694
0
                goto RETURN;
695
0
            }
696
0
        } else {
697
0
            LOGGER_DEBUG(av->m->log, "Set new audio bit rate %d", bit_rate);
698
0
        }
699
700
0
        call->audio_bit_rate = bit_rate;
701
0
        pthread_mutex_unlock(call->toxav_call_mutex);
702
0
    }
703
704
1
    pthread_mutex_unlock(av->mutex);
705
1
RETURN:
706
707
1
    if (error != nullptr) {
708
0
        *error = rc;
709
0
    }
710
711
1
    return rc == TOXAV_ERR_BIT_RATE_SET_OK;
712
1
}
713
bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
714
                              Toxav_Err_Bit_Rate_Set *error)
715
2
{
716
2
    Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
717
2
    ToxAVCall *call;
718
719
2
    if (!m_friend_exists(av->m, friend_number)) {
720
0
        rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
721
0
        goto RETURN;
722
0
    }
723
724
2
    if (bit_rate > 0 && video_bit_rate_invalid(bit_rate)) {
725
0
        rc = TOXAV_ERR_BIT_RATE_SET_INVALID_BIT_RATE;
726
0
        goto RETURN;
727
0
    }
728
729
2
    pthread_mutex_lock(av->mutex);
730
2
    call = call_get(av, friend_number);
731
732
2
    if (call == nullptr || !call->active || call->msi_call->state != MSI_CALL_ACTIVE) {
733
0
        pthread_mutex_unlock(av->mutex);
734
0
        rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
735
0
        goto RETURN;
736
0
    }
737
738
2
    LOGGER_DEBUG(av->m->log, "Setting new video bitrate to: %d", bit_rate);
739
740
2
    if (call->video_bit_rate == bit_rate) {
741
0
        LOGGER_DEBUG(av->m->log, "Video bitrate already set to: %d", bit_rate);
742
2
    } else if (bit_rate == 0) {
743
1
        LOGGER_DEBUG(av->m->log, "Turned off video sending");
744
745
        /* Video sending is turned off; notify peer */
746
1
        if (msi_change_capabilities(call->msi_call, call->msi_call->
747
1
                                    self_capabilities ^ MSI_CAP_S_VIDEO) != 0) {
748
0
            pthread_mutex_unlock(av->mutex);
749
0
            rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
750
0
            goto RETURN;
751
0
        }
752
753
1
        call->video_bit_rate = 0;
754
1
    } else {
755
1
        pthread_mutex_lock(call->toxav_call_mutex);
756
757
1
        if (call->video_bit_rate == 0) {
758
1
            LOGGER_DEBUG(av->m->log, "Turned on video sending");
759
760
            /* The video has been turned off before this */
761
1
            if (msi_change_capabilities(call->msi_call, call->
762
1
                                        msi_call->self_capabilities | MSI_CAP_S_VIDEO) != 0) {
763
0
                pthread_mutex_unlock(call->toxav_call_mutex);
764
0
                pthread_mutex_unlock(av->mutex);
765
0
                rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
766
0
                goto RETURN;
767
0
            }
768
1
        } else {
769
0
            LOGGER_DEBUG(av->m->log, "Set new video bit rate %d", bit_rate);
770
0
        }
771
772
1
        call->video_bit_rate = bit_rate;
773
1
        pthread_mutex_unlock(call->toxav_call_mutex);
774
1
    }
775
776
2
    pthread_mutex_unlock(av->mutex);
777
2
RETURN:
778
779
2
    if (error != nullptr) {
780
0
        *error = rc;
781
0
    }
782
783
2
    return rc == TOXAV_ERR_BIT_RATE_SET_OK;
784
2
}
785
void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback, void *user_data)
786
0
{
787
0
    pthread_mutex_lock(av->mutex);
788
0
    av->abcb = callback;
789
0
    av->abcb_user_data = user_data;
790
0
    pthread_mutex_unlock(av->mutex);
791
0
}
792
void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback, void *user_data)
793
0
{
794
0
    pthread_mutex_lock(av->mutex);
795
0
    av->vbcb = callback;
796
0
    av->vbcb_user_data = user_data;
797
0
    pthread_mutex_unlock(av->mutex);
798
0
}
799
bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
800
                            uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error)
801
1.01k
{
802
1.01k
    Toxav_Err_Send_Frame rc = TOXAV_ERR_SEND_FRAME_OK;
803
1.01k
    ToxAVCall *call;
804
805
1.01k
    if (!m_friend_exists(av->m, friend_number)) {
806
0
        rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
807
0
        goto RETURN;
808
0
    }
809
810
1.01k
    if (pthread_mutex_trylock(av->mutex) != 0) {
811
0
        rc = TOXAV_ERR_SEND_FRAME_SYNC;
812
0
        goto RETURN;
813
0
    }
814
815
1.01k
    call = call_get(av, friend_number);
816
817
1.01k
    if (call == nullptr || !call->active || call->msi_call->state != MSI_CALL_ACTIVE) {
818
871
        pthread_mutex_unlock(av->mutex);
819
871
        rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
820
871
        goto RETURN;
821
871
    }
822
823
147
    if (call->audio_bit_rate == 0 ||
824
147
            (call->msi_call->self_capabilities & MSI_CAP_S_AUDIO) == 0 ||
825
147
            (call->msi_call->peer_capabilities & MSI_CAP_R_AUDIO) == 0) {
826
2
        pthread_mutex_unlock(av->mutex);
827
2
        rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
828
2
        goto RETURN;
829
2
    }
830
831
145
    pthread_mutex_lock(call->mutex_audio);
832
145
    pthread_mutex_unlock(av->mutex);
833
834
145
    if (pcm == nullptr) {
835
0
        pthread_mutex_unlock(call->mutex_audio);
836
0
        rc = TOXAV_ERR_SEND_FRAME_NULL;
837
0
        goto RETURN;
838
0
    }
839
840
145
    if (channels > 2) {
841
0
        pthread_mutex_unlock(call->mutex_audio);
842
0
        rc = TOXAV_ERR_SEND_FRAME_INVALID;
843
0
        goto RETURN;
844
0
    }
845
846
145
    {   /* Encode and send */
847
145
        if (ac_reconfigure_encoder(call->audio, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
848
0
            pthread_mutex_unlock(call->mutex_audio);
849
0
            rc = TOXAV_ERR_SEND_FRAME_INVALID;
850
0
            goto RETURN;
851
0
        }
852
853
        /* This is more than enough always */
854
145
        const uint16_t dest_size = sample_count + sizeof(sampling_rate);
855
145
        VLA(uint8_t, dest, dest_size);
856
857
145
        sampling_rate = net_htonl(sampling_rate);
858
145
        memcpy(dest, &sampling_rate, sizeof(sampling_rate));
859
145
        const int vrc = opus_encode(call->audio->encoder, pcm, sample_count,
860
145
                              dest + sizeof(sampling_rate), dest_size - sizeof(sampling_rate));
861
862
145
        if (vrc < 0) {
863
0
            LOGGER_WARNING(av->m->log, "Failed to encode frame %s", opus_strerror(vrc));
864
0
            pthread_mutex_unlock(call->mutex_audio);
865
0
            rc = TOXAV_ERR_SEND_FRAME_INVALID;
866
0
            goto RETURN;
867
0
        }
868
869
145
        if (rtp_send_data(call->audio_rtp, dest, vrc + sizeof(sampling_rate), false, av->m->log) != 0) {
870
0
            LOGGER_WARNING(av->m->log, "Failed to send audio packet");
871
0
            rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
872
0
        }
873
145
    }
874
875
0
    pthread_mutex_unlock(call->mutex_audio);
876
877
1.01k
RETURN:
878
879
1.01k
    if (error != nullptr) {
880
0
        *error = rc;
881
0
    }
882
883
1.01k
    return rc == TOXAV_ERR_SEND_FRAME_OK;
884
145
}
885
886
static Toxav_Err_Send_Frame send_frames(const Logger *log, ToxAVCall *call)
887
142
{
888
142
    vpx_codec_iter_t iter = nullptr;
889
890
142
    for (const vpx_codec_cx_pkt_t *pkt = vpx_codec_get_cx_data(call->video->encoder, &iter);
891
284
            pkt != nullptr;
892
142
            pkt = vpx_codec_get_cx_data(call->video->encoder, &iter)) {
893
142
        if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) {
894
0
            continue;
895
0
        }
896
897
142
        const bool is_keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
898
899
        // https://www.webmproject.org/docs/webm-sdk/structvpx__codec__cx__pkt.html
900
        // pkt->data.frame.sz -> size_t
901
142
        const uint32_t frame_length_in_bytes = pkt->data.frame.sz;
902
903
142
        const int res = rtp_send_data(
904
142
                            call->video_rtp,
905
142
                            (const uint8_t *)pkt->data.frame.buf,
906
142
                            frame_length_in_bytes,
907
142
                            is_keyframe,
908
142
                            log);
909
910
142
        LOGGER_DEBUG(log, "+ _sending_FRAME_TYPE_==%s bytes=%d frame_len=%d", is_keyframe ? "K" : ".",
911
142
                     (int)pkt->data.frame.sz, (int)frame_length_in_bytes);
912
142
        const uint8_t *const buf = (const uint8_t *)pkt->data.frame.buf;
913
142
        LOGGER_DEBUG(log, "+ _sending_FRAME_ b0=%d b1=%d", buf[0], buf[1]);
914
915
142
        if (res < 0) {
916
0
            char *netstrerror = net_new_strerror(net_error());
917
0
            LOGGER_WARNING(log, "Could not send video frame: %s", netstrerror);
918
0
            net_kill_strerror(netstrerror);
919
0
            return TOXAV_ERR_SEND_FRAME_RTP_FAILED;
920
0
        }
921
142
    }
922
923
142
    return TOXAV_ERR_SEND_FRAME_OK;
924
142
}
925
926
bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
927
                            const uint8_t *u, const uint8_t *v, Toxav_Err_Send_Frame *error)
928
1.01k
{
929
1.01k
    Toxav_Err_Send_Frame rc = TOXAV_ERR_SEND_FRAME_OK;
930
1.01k
    ToxAVCall *call;
931
932
1.01k
    int vpx_encode_flags = 0;
933
934
1.01k
    if (!m_friend_exists(av->m, friend_number)) {
935
0
        rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
936
0
        goto RETURN;
937
0
    }
938
939
1.01k
    if (pthread_mutex_trylock(av->mutex) != 0) {
940
1
        rc = TOXAV_ERR_SEND_FRAME_SYNC;
941
1
        goto RETURN;
942
1
    }
943
944
1.01k
    call = call_get(av, friend_number);
945
946
1.01k
    if (call == nullptr || !call->active || call->msi_call->state != MSI_CALL_ACTIVE) {
947
871
        pthread_mutex_unlock(av->mutex);
948
871
        rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
949
871
        goto RETURN;
950
871
    }
951
952
142
    if (call->video_bit_rate == 0 ||
953
142
            (call->msi_call->self_capabilities & MSI_CAP_S_VIDEO) == 0 ||
954
142
            (call->msi_call->peer_capabilities & MSI_CAP_R_VIDEO) == 0) {
955
0
        pthread_mutex_unlock(av->mutex);
956
0
        rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
957
0
        goto RETURN;
958
0
    }
959
960
142
    pthread_mutex_lock(call->mutex_video);
961
142
    pthread_mutex_unlock(av->mutex);
962
963
142
    if (y == nullptr || u == nullptr || v == nullptr) {
964
0
        pthread_mutex_unlock(call->mutex_video);
965
0
        rc = TOXAV_ERR_SEND_FRAME_NULL;
966
0
        goto RETURN;
967
0
    }
968
969
142
    if (vc_reconfigure_encoder(call->video, call->video_bit_rate * 1000, width, height, -1) != 0) {
970
0
        pthread_mutex_unlock(call->mutex_video);
971
0
        rc = TOXAV_ERR_SEND_FRAME_INVALID;
972
0
        goto RETURN;
973
0
    }
974
975
142
    if (call->video_rtp->ssrc < VIDEO_SEND_X_KEYFRAMES_FIRST) {
976
        // Key frame flag for first frames
977
42
        vpx_encode_flags = VPX_EFLAG_FORCE_KF;
978
42
        LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video_rtp->ssrc);
979
980
42
        ++call->video_rtp->ssrc;
981
100
    } else if (call->video_rtp->ssrc == VIDEO_SEND_X_KEYFRAMES_FIRST) {
982
        // normal keyframe placement
983
6
        vpx_encode_flags = 0;
984
6
        LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d normal mode", call->video_rtp->ssrc);
985
986
6
        ++call->video_rtp->ssrc;
987
6
    }
988
989
    // we start with I-frames (full frames) and then switch to normal mode later
990
991
142
    {   /* Encode */
992
142
        vpx_image_t img;
993
142
        img.w = 0;
994
142
        img.h = 0;
995
142
        img.d_w = 0;
996
142
        img.d_h = 0;
997
142
        vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
998
999
        /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
1000
         * http://fourcc.org/yuv.php#IYUV
1001
         */
1002
142
        memcpy(img.planes[VPX_PLANE_Y], y, width * height);
1003
142
        memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
1004
142
        memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
1005
1006
142
        const vpx_codec_err_t vrc = vpx_codec_encode(call->video->encoder, &img,
1007
142
                                               call->video->frame_counter, 1, vpx_encode_flags, MAX_ENCODE_TIME_US);
1008
1009
142
        vpx_img_free(&img);
1010
1011
142
        if (vrc != VPX_CODEC_OK) {
1012
0
            pthread_mutex_unlock(call->mutex_video);
1013
0
            LOGGER_ERROR(av->m->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc));
1014
0
            rc = TOXAV_ERR_SEND_FRAME_INVALID;
1015
0
            goto RETURN;
1016
0
        }
1017
142
    }
1018
1019
142
    ++call->video->frame_counter;
1020
1021
142
    rc = send_frames(av->m->log, call);
1022
1023
142
    pthread_mutex_unlock(call->mutex_video);
1024
1025
1.01k
RETURN:
1026
1027
1.01k
    if (error != nullptr) {
1028
0
        *error = rc;
1029
0
    }
1030
1031
1.01k
    return rc == TOXAV_ERR_SEND_FRAME_OK;
1032
142
}
1033
1034
void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *callback, void *user_data)
1035
6
{
1036
6
    pthread_mutex_lock(av->mutex);
1037
6
    av->acb = callback;
1038
6
    av->acb_user_data = user_data;
1039
6
    pthread_mutex_unlock(av->mutex);
1040
6
}
1041
1042
void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *callback, void *user_data)
1043
6
{
1044
6
    pthread_mutex_lock(av->mutex);
1045
6
    av->vcb = callback;
1046
6
    av->vcb_user_data = user_data;
1047
6
    pthread_mutex_unlock(av->mutex);
1048
6
}
1049
1050
/*******************************************************************************
1051
 *
1052
 * :: Internal
1053
 *
1054
 ******************************************************************************/
1055
static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *user_data)
1056
0
{
1057
    /* Callback which is called when the internal measure mechanism reported packet loss.
1058
     * We report suggested lowered bitrate to an app. If app is sending both audio and video,
1059
     * we will report lowered bitrate for video only because in that case video probably
1060
     * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
1061
     * The application may choose to disable video totally if the stream is too bad.
1062
     */
1063
1064
0
    ToxAVCall *call = (ToxAVCall *)user_data;
1065
0
    assert(call != nullptr);
1066
1067
0
    LOGGER_DEBUG(call->av->m->log, "Reported loss of %f%%", (double)loss * 100);
1068
1069
    /* if less than 10% data loss we do nothing! */
1070
0
    if (loss < 0.1F) {
1071
0
        return;
1072
0
    }
1073
1074
0
    pthread_mutex_lock(call->av->mutex);
1075
1076
0
    if (call->video_bit_rate != 0) {
1077
0
        if (call->av->vbcb == nullptr) {
1078
0
            pthread_mutex_unlock(call->av->mutex);
1079
0
            LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
1080
0
            return;
1081
0
        }
1082
1083
0
        call->av->vbcb(call->av, friend_number,
1084
0
                       call->video_bit_rate - (call->video_bit_rate * loss),
1085
0
                       call->av->vbcb_user_data);
1086
0
    } else if (call->audio_bit_rate != 0) {
1087
0
        if (call->av->abcb == nullptr) {
1088
0
            pthread_mutex_unlock(call->av->mutex);
1089
0
            LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
1090
0
            return;
1091
0
        }
1092
1093
0
        call->av->abcb(call->av, friend_number,
1094
0
                       call->audio_bit_rate - (call->audio_bit_rate * loss),
1095
0
                       call->av->abcb_user_data);
1096
0
    }
1097
1098
0
    pthread_mutex_unlock(call->av->mutex);
1099
0
}
1100
static int callback_invite(void *object, MSICall *call)
1101
11
{
1102
11
    ToxAV *toxav = (ToxAV *)object;
1103
11
    pthread_mutex_lock(toxav->mutex);
1104
1105
11
    ToxAVCall *av_call = call_new(toxav, call->friend_number, nullptr);
1106
1107
11
    if (av_call == nullptr) {
1108
0
        LOGGER_WARNING(toxav->m->log, "Failed to initialize call...");
1109
0
        pthread_mutex_unlock(toxav->mutex);
1110
0
        return -1;
1111
0
    }
1112
1113
11
    call->av_call = av_call;
1114
11
    av_call->msi_call = call;
1115
1116
11
    if (toxav->ccb != nullptr) {
1117
11
        toxav->ccb(toxav, call->friend_number, call->peer_capabilities & MSI_CAP_S_AUDIO,
1118
11
                   call->peer_capabilities & MSI_CAP_S_VIDEO, toxav->ccb_user_data);
1119
11
    } else {
1120
        /* No handler to capture the call request, send failure */
1121
0
        pthread_mutex_unlock(toxav->mutex);
1122
0
        return -1;
1123
0
    }
1124
1125
11
    pthread_mutex_unlock(toxav->mutex);
1126
11
    return 0;
1127
11
}
1128
static int callback_start(void *object, MSICall *call)
1129
9
{
1130
9
    ToxAV *toxav = (ToxAV *)object;
1131
9
    pthread_mutex_lock(toxav->mutex);
1132
1133
9
    ToxAVCall *av_call = call_get(toxav, call->friend_number);
1134
1135
9
    if (av_call == nullptr) {
1136
        /* Should this ever happen? */
1137
0
        pthread_mutex_unlock(toxav->mutex);
1138
0
        return -1;
1139
0
    }
1140
1141
9
    if (!call_prepare_transmission(av_call)) {
1142
0
        callback_error(toxav, call);
1143
0
        pthread_mutex_unlock(toxav->mutex);
1144
0
        return -1;
1145
0
    }
1146
1147
9
    if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
1148
0
        callback_error(toxav, call);
1149
0
        pthread_mutex_unlock(toxav->mutex);
1150
0
        return -1;
1151
0
    }
1152
1153
9
    pthread_mutex_unlock(toxav->mutex);
1154
9
    return 0;
1155
9
}
1156
static int callback_end(void *object, MSICall *call)
1157
11
{
1158
11
    ToxAV *toxav = (ToxAV *)object;
1159
11
    pthread_mutex_lock(toxav->mutex);
1160
1161
11
    invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
1162
1163
11
    if (call->av_call != nullptr) {
1164
11
        call_kill_transmission(call->av_call);
1165
11
        call_remove(call->av_call);
1166
11
    }
1167
1168
11
    pthread_mutex_unlock(toxav->mutex);
1169
11
    return 0;
1170
11
}
1171
static int callback_error(void *object, MSICall *call)
1172
0
{
1173
0
    ToxAV *toxav = (ToxAV *)object;
1174
0
    pthread_mutex_lock(toxav->mutex);
1175
1176
0
    invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR);
1177
1178
0
    if (call->av_call != nullptr) {
1179
0
        call_kill_transmission(call->av_call);
1180
0
        call_remove(call->av_call);
1181
0
    }
1182
1183
0
    pthread_mutex_unlock(toxav->mutex);
1184
0
    return 0;
1185
0
}
1186
static int callback_capabilites(void *object, MSICall *call)
1187
13
{
1188
13
    ToxAV *toxav = (ToxAV *)object;
1189
13
    pthread_mutex_lock(toxav->mutex);
1190
1191
13
    if ((call->peer_capabilities & MSI_CAP_S_AUDIO) != 0) {
1192
10
        rtp_allow_receiving(call->av_call->audio_rtp);
1193
10
    } else {
1194
3
        rtp_stop_receiving(call->av_call->audio_rtp);
1195
3
    }
1196
1197
13
    if ((call->peer_capabilities & MSI_CAP_S_VIDEO) != 0) {
1198
8
        rtp_allow_receiving(call->av_call->video_rtp);
1199
8
    } else {
1200
5
        rtp_stop_receiving(call->av_call->video_rtp);
1201
5
    }
1202
1203
13
    invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
1204
1205
13
    pthread_mutex_unlock(toxav->mutex);
1206
13
    return 0;
1207
13
}
1208
static bool audio_bit_rate_invalid(uint32_t bit_rate)
1209
18
{
1210
    /* Opus RFC 6716 section-2.1.1 dictates the following:
1211
     * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
1212
     */
1213
18
    return bit_rate < 6 || bit_rate > 510;
1214
18
}
1215
static bool video_bit_rate_invalid(uint32_t bit_rate)
1216
13
{
1217
    /* https://www.webmproject.org/docs/webm-sdk/structvpx__codec__enc__cfg.html shows the following:
1218
     * unsigned int rc_target_bitrate
1219
     * the range of uint varies from platform to platform
1220
     * though, uint32_t should be large enough to store bitrates,
1221
     * we may want to prevent from passing overflowed bitrates to libvpx
1222
     * more in detail, it's the case where bit_rate is larger than uint, but smaller than uint32_t
1223
     */
1224
13
    return bit_rate > UINT32_MAX;
1225
13
}
1226
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1227
33
{
1228
33
    if (av->scb != nullptr) {
1229
33
        av->scb(av, friend_number, state, av->scb_user_data);
1230
33
    } else {
1231
0
        return false;
1232
0
    }
1233
1234
33
    return true;
1235
33
}
1236
1237
static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *error)
1238
22
{
1239
    /* Assumes mutex locked */
1240
22
    Toxav_Err_Call rc = TOXAV_ERR_CALL_OK;
1241
22
    ToxAVCall *call = nullptr;
1242
1243
22
    if (!m_friend_exists(av->m, friend_number)) {
1244
0
        rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1245
0
        goto RETURN;
1246
0
    }
1247
1248
22
    if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
1249
0
        rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1250
0
        goto RETURN;
1251
0
    }
1252
1253
22
    if (call_get(av, friend_number) != nullptr) {
1254
0
        rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1255
0
        goto RETURN;
1256
0
    }
1257
1258
22
    call = (ToxAVCall *)calloc(1, sizeof(ToxAVCall));
1259
1260
22
    if (call == nullptr) {
1261
0
        rc = TOXAV_ERR_CALL_MALLOC;
1262
0
        goto RETURN;
1263
0
    }
1264
1265
22
    call->av = av;
1266
22
    call->friend_number = friend_number;
1267
1268
22
    if (create_recursive_mutex(call->toxav_call_mutex) != 0) {
1269
0
        free(call);
1270
0
        call = nullptr;
1271
0
        rc = TOXAV_ERR_CALL_MALLOC;
1272
0
        goto RETURN;
1273
0
    }
1274
1275
22
    if (av->calls == nullptr) { /* Creating */
1276
20
        av->calls = (ToxAVCall **)calloc(friend_number + 1, sizeof(ToxAVCall *));
1277
1278
20
        if (av->calls == nullptr) {
1279
0
            pthread_mutex_destroy(call->toxav_call_mutex);
1280
0
            free(call);
1281
0
            call = nullptr;
1282
0
            rc = TOXAV_ERR_CALL_MALLOC;
1283
0
            goto RETURN;
1284
0
        }
1285
1286
20
        av->calls_tail = friend_number;
1287
20
        av->calls_head = friend_number;
1288
20
    } else if (av->calls_tail < friend_number) { /* Appending */
1289
2
        ToxAVCall **tmp = (ToxAVCall **)realloc(av->calls, (friend_number + 1) * sizeof(ToxAVCall *));
1290
1291
2
        if (tmp == nullptr) {
1292
0
            pthread_mutex_destroy(call->toxav_call_mutex);
1293
0
            free(call);
1294
0
            call = nullptr;
1295
0
            rc = TOXAV_ERR_CALL_MALLOC;
1296
0
            goto RETURN;
1297
0
        }
1298
1299
2
        av->calls = tmp;
1300
1301
        /* Set fields in between to null */
1302
2
        for (uint32_t i = av->calls_tail + 1; i < friend_number; ++i) {
1303
0
            av->calls[i] = nullptr;
1304
0
        }
1305
1306
2
        call->prev = av->calls[av->calls_tail];
1307
2
        av->calls[av->calls_tail]->next = call;
1308
1309
2
        av->calls_tail = friend_number;
1310
2
    } else if (av->calls_head > friend_number) { /* Inserting at front */
1311
0
        call->next = av->calls[av->calls_head];
1312
0
        av->calls[av->calls_head]->prev = call;
1313
0
        av->calls_head = friend_number;
1314
0
    }
1315
1316
22
    av->calls[friend_number] = call;
1317
1318
22
RETURN:
1319
1320
22
    if (error != nullptr) {
1321
11
        *error = rc;
1322
11
    }
1323
1324
22
    return call;
1325
22
}
1326
1327
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
1328
2.66k
{
1329
    /* Assumes mutex locked */
1330
2.66k
    if (av->calls == nullptr || av->calls_tail < friend_number) {
1331
1.71k
        return nullptr;
1332
1.71k
    }
1333
1334
948
    return av->calls[friend_number];
1335
2.66k
}
1336
1337
static ToxAVCall *call_remove(ToxAVCall *call)
1338
22
{
1339
22
    if (call == nullptr) {
1340
0
        return nullptr;
1341
0
    }
1342
1343
22
    const uint32_t friend_number = call->friend_number;
1344
22
    ToxAV *av = call->av;
1345
1346
22
    ToxAVCall *prev = call->prev;
1347
22
    ToxAVCall *next = call->next;
1348
1349
    /* Set av call in msi to NULL in order to know if call if ToxAVCall is
1350
     * removed from the msi call.
1351
     */
1352
22
    if (call->msi_call != nullptr) {
1353
11
        call->msi_call->av_call = nullptr;
1354
11
    }
1355
1356
22
    pthread_mutex_destroy(call->toxav_call_mutex);
1357
22
    free(call);
1358
1359
22
    if (prev != nullptr) {
1360
0
        prev->next = next;
1361
22
    } else if (next != nullptr) {
1362
2
        av->calls_head = next->friend_number;
1363
20
    } else {
1364
20
        goto CLEAR;
1365
20
    }
1366
1367
2
    if (next != nullptr) {
1368
2
        next->prev = prev;
1369
2
    } else if (prev != nullptr) {
1370
0
        av->calls_tail = prev->friend_number;
1371
0
    } else {
1372
0
        goto CLEAR;
1373
0
    }
1374
1375
2
    av->calls[friend_number] = nullptr;
1376
2
    return next;
1377
1378
20
CLEAR:
1379
20
    av->calls_head = 0;
1380
20
    av->calls_tail = 0;
1381
20
    free(av->calls);
1382
20
    av->calls = nullptr;
1383
1384
20
    return nullptr;
1385
2
}
1386
1387
static bool call_prepare_transmission(ToxAVCall *call)
1388
18
{
1389
    /* Assumes mutex locked */
1390
1391
18
    if (call == nullptr) {
1392
0
        return false;
1393
0
    }
1394
1395
18
    ToxAV *av = call->av;
1396
1397
18
    if (av->acb == nullptr && av->vcb == nullptr) {
1398
        /* It makes no sense to have CSession without callbacks */
1399
0
        return false;
1400
0
    }
1401
1402
18
    if (call->active) {
1403
0
        LOGGER_WARNING(av->m->log, "Call already active!");
1404
0
        return true;
1405
0
    }
1406
1407
18
    if (create_recursive_mutex(call->mutex_audio) != 0) {
1408
0
        return false;
1409
0
    }
1410
1411
18
    if (create_recursive_mutex(call->mutex_video) != 0) {
1412
0
        goto FAILURE_2;
1413
0
    }
1414
1415
    /* Prepare bwc */
1416
18
    call->bwc = bwc_new(av->m, av->tox, call->friend_number, callback_bwc, call, av->toxav_mono_time);
1417
1418
18
    if (call->bwc == nullptr) {
1419
0
        LOGGER_ERROR(av->m->log, "Failed to create new bwc");
1420
0
        goto FAILURE;
1421
0
    }
1422
1423
18
    {   /* Prepare audio */
1424
18
        call->audio = ac_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->acb, av->acb_user_data);
1425
1426
18
        if (call->audio == nullptr) {
1427
0
            LOGGER_ERROR(av->m->log, "Failed to create audio codec session");
1428
0
            goto FAILURE;
1429
0
        }
1430
1431
18
        call->audio_rtp = rtp_new(RTP_TYPE_AUDIO, av->m, av->tox, call->friend_number, call->bwc,
1432
18
                                  call->audio, ac_queue_message);
1433
1434
18
        if (call->audio_rtp == nullptr) {
1435
0
            LOGGER_ERROR(av->m->log, "Failed to create audio rtp session");
1436
0
            goto FAILURE;
1437
0
        }
1438
18
    }
1439
1440
18
    {   /* Prepare video */
1441
18
        call->video = vc_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->vcb, av->vcb_user_data);
1442
1443
18
        if (call->video == nullptr) {
1444
0
            LOGGER_ERROR(av->m->log, "Failed to create video codec session");
1445
0
            goto FAILURE;
1446
0
        }
1447
1448
18
        call->video_rtp = rtp_new(RTP_TYPE_VIDEO, av->m, av->tox, call->friend_number, call->bwc,
1449
18
                                  call->video, vc_queue_message);
1450
1451
18
        if (call->video_rtp == nullptr) {
1452
0
            LOGGER_ERROR(av->m->log, "Failed to create video rtp session");
1453
0
            goto FAILURE;
1454
0
        }
1455
18
    }
1456
1457
18
    call->active = true;
1458
18
    return true;
1459
1460
0
FAILURE:
1461
0
    bwc_kill(call->bwc);
1462
0
    rtp_kill(call->audio_rtp);
1463
0
    ac_kill(call->audio);
1464
0
    call->audio_rtp = nullptr;
1465
0
    call->audio = nullptr;
1466
0
    rtp_kill(call->video_rtp);
1467
0
    vc_kill(call->video);
1468
0
    call->video_rtp = nullptr;
1469
0
    call->video = nullptr;
1470
0
    pthread_mutex_destroy(call->mutex_video);
1471
0
FAILURE_2:
1472
0
    pthread_mutex_destroy(call->mutex_audio);
1473
0
    return false;
1474
0
}
1475
1476
static void call_kill_transmission(ToxAVCall *call)
1477
22
{
1478
22
    if (call == nullptr || !call->active) {
1479
4
        return;
1480
4
    }
1481
1482
18
    call->active = false;
1483
1484
18
    pthread_mutex_lock(call->mutex_audio);
1485
18
    pthread_mutex_unlock(call->mutex_audio);
1486
18
    pthread_mutex_lock(call->mutex_video);
1487
18
    pthread_mutex_unlock(call->mutex_video);
1488
18
    pthread_mutex_lock(call->toxav_call_mutex);
1489
18
    pthread_mutex_unlock(call->toxav_call_mutex);
1490
1491
18
    bwc_kill(call->bwc);
1492
1493
18
    rtp_kill(call->audio_rtp);
1494
18
    ac_kill(call->audio);
1495
18
    call->audio_rtp = nullptr;
1496
18
    call->audio = nullptr;
1497
1498
18
    rtp_kill(call->video_rtp);
1499
18
    vc_kill(call->video);
1500
18
    call->video_rtp = nullptr;
1501
18
    call->video = nullptr;
1502
1503
18
    pthread_mutex_destroy(call->mutex_audio);
1504
18
    pthread_mutex_destroy(call->mutex_video);
1505
18
}