Coverage Report

Created: 2026-10-24 08:16

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