Coverage Report

Created: 2026-10-24 08:16

/work/toxav/groupav.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 © 2014 Tox project.
4
 */
5
#include "groupav.h"
6
7
#include <stdlib.h>
8
#include <string.h>
9
10
#include "../toxcore/ccompat.h"
11
#include "../toxcore/logger.h"
12
#include "../toxcore/mono_time.h"
13
#include "../toxcore/tox_struct.h"
14
#include "../toxcore/util.h"
15
16
16
#define GROUP_JBUF_SIZE 6
17
451
#define GROUP_JBUF_DEAD_SECONDS 4
18
19
typedef struct Group_Audio_Packet {
20
    uint16_t sequnum;
21
    uint16_t length;
22
    uint8_t *_Nonnull data;
23
} Group_Audio_Packet;
24
25
typedef struct Group_JitterBuffer {
26
    Group_Audio_Packet *_Nullable *_Nonnull queue;
27
    uint32_t size;
28
    uint32_t capacity;
29
    uint16_t bottom;
30
    uint16_t top;
31
    uint64_t last_queued_time;
32
} Group_JitterBuffer;
33
34
static void free_audio_packet(Group_Audio_Packet *_Nullable pk)
35
451
{
36
451
    if (pk == nullptr) {
37
0
        return;
38
0
    }
39
40
451
    free(pk->data);
41
451
    free(pk);
42
451
}
43
44
static Group_JitterBuffer *_Nullable create_queue(unsigned int capacity)
45
16
{
46
16
    unsigned int size = 1;
47
48
64
    while (size <= capacity) {
49
48
        size *= 2;
50
48
    }
51
52
16
    Group_JitterBuffer *q = (Group_JitterBuffer *)calloc(1, sizeof(Group_JitterBuffer));
53
54
16
    if (q == nullptr) {
55
0
        return nullptr;
56
0
    }
57
58
16
    q->queue = (Group_Audio_Packet **)calloc(size, sizeof(Group_Audio_Packet *));
59
60
16
    if (q->queue == nullptr) {
61
0
        free(q);
62
0
        return nullptr;
63
0
    }
64
65
16
    q->size = size;
66
16
    q->capacity = capacity;
67
16
    return q;
68
16
}
69
70
static void clear_queue(Group_JitterBuffer *_Nonnull q)
71
16
{
72
16
    while (q->bottom != q->top) {
73
0
        const size_t idx = q->bottom % q->size;
74
0
        free_audio_packet(q->queue[idx]);
75
0
        q->queue[idx] = nullptr;
76
0
        ++q->bottom;
77
0
    }
78
16
}
79
80
static void terminate_queue(Group_JitterBuffer *_Nullable q)
81
16
{
82
16
    if (q == nullptr) {
83
0
        return;
84
0
    }
85
86
16
    clear_queue(q);
87
16
    free(q->queue);
88
16
    free(q);
89
16
}
90
91
/** @retval 0 if packet was queued
92
 * @retval -1 if it wasn't.
93
 */
94
static int queue(Group_JitterBuffer *_Nonnull q, const Mono_Time *_Nonnull mono_time, Group_Audio_Packet *_Nonnull pk)
95
451
{
96
451
    const uint16_t sequnum = pk->sequnum;
97
98
451
    const unsigned int num = sequnum % q->size;
99
100
451
    if (!mono_time_is_timeout(mono_time, q->last_queued_time, GROUP_JBUF_DEAD_SECONDS)) {
101
439
        if ((uint32_t)(sequnum - q->bottom) > (1 << 15)) {
102
            /* Drop old packet. */
103
0
            return -1;
104
0
        }
105
439
    }
106
107
451
    if ((uint32_t)(sequnum - q->bottom) > q->size) {
108
0
        clear_queue(q);
109
0
        q->bottom = sequnum - q->capacity;
110
0
        q->queue[num] = pk;
111
0
        q->top = sequnum + 1;
112
0
        q->last_queued_time = mono_time_get(mono_time);
113
0
        return 0;
114
0
    }
115
116
451
    if (q->queue[num] != nullptr) {
117
0
        return -1;
118
0
    }
119
120
451
    q->queue[num] = pk;
121
122
451
    if ((sequnum - q->bottom) >= (q->top - q->bottom)) {
123
450
        q->top = sequnum + 1;
124
450
    }
125
126
451
    q->last_queued_time = mono_time_get(mono_time);
127
451
    return 0;
128
451
}
129
130
/**
131
 * success is:
132
 * - 0 when there is nothing to dequeue
133
 * - 1 when there's a good packet
134
 * - 2 when there's a lost packet
135
 */
136
static Group_Audio_Packet *_Nullable dequeue(Group_JitterBuffer *_Nonnull q, int *_Nonnull success)
137
903
{
138
903
    if (q->top == q->bottom) {
139
420
        *success = 0;
140
420
        return nullptr;
141
420
    }
142
143
483
    const unsigned int num = q->bottom % q->size;
144
145
483
    if (q->queue[num] != nullptr) {
146
451
        Group_Audio_Packet *ret = q->queue[num];
147
451
        q->queue[num] = nullptr;
148
451
        ++q->bottom;
149
451
        *success = 1;
150
451
        return ret;
151
451
    }
152
153
32
    if ((uint32_t)(q->top - q->bottom) > q->capacity) {
154
27
        ++q->bottom;
155
27
        *success = 2;
156
27
        return nullptr;
157
27
    }
158
159
5
    *success = 0;
160
5
    return nullptr;
161
32
}
162
163
typedef struct Group_AV {
164
    const Logger *_Nonnull log;
165
    Tox *_Nonnull tox;
166
    Group_Chats *_Nonnull g_c;
167
    OpusEncoder *_Nullable audio_encoder;
168
169
    unsigned int audio_channels;
170
    unsigned int audio_sample_rate;
171
    unsigned int audio_bitrate;
172
173
    uint16_t audio_sequnum;
174
175
    audio_data_cb *_Nullable audio_data;
176
    void *_Nullable userdata;
177
} Group_AV;
178
179
typedef struct Group_Peer_AV {
180
    const Mono_Time *_Nonnull mono_time;
181
    Group_JitterBuffer *_Nonnull buffer;
182
183
    OpusDecoder *_Nullable audio_decoder;
184
    int decoder_channels;
185
    unsigned int last_packet_samples;
186
} Group_Peer_AV;
187
188
static void kill_group_av(Group_AV *_Nonnull group_av)
189
4
{
190
4
    if (group_av->audio_encoder != nullptr) {
191
4
        opus_encoder_destroy(group_av->audio_encoder);
192
4
    }
193
194
4
    free(group_av);
195
4
}
196
197
static int recreate_encoder(Group_AV *_Nonnull group_av)
198
4
{
199
4
    if (group_av->audio_encoder != nullptr) {
200
0
        opus_encoder_destroy(group_av->audio_encoder);
201
0
        group_av->audio_encoder = nullptr;
202
0
    }
203
204
4
    int rc = OPUS_OK;
205
4
    group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
206
4
                              OPUS_APPLICATION_AUDIO, &rc);
207
208
4
    if (rc != OPUS_OK) {
209
0
        LOGGER_ERROR(group_av->log, "Error while starting audio encoder: %s", opus_strerror(rc));
210
0
        group_av->audio_encoder = nullptr;
211
0
        return -1;
212
0
    }
213
214
4
    rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
215
216
4
    if (rc != OPUS_OK) {
217
0
        LOGGER_ERROR(group_av->log, "Error while setting encoder ctl: %s", opus_strerror(rc));
218
0
        opus_encoder_destroy(group_av->audio_encoder);
219
0
        group_av->audio_encoder = nullptr;
220
0
        return -1;
221
0
    }
222
223
4
    rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
224
225
4
    if (rc != OPUS_OK) {
226
0
        LOGGER_ERROR(group_av->log, "Error while setting encoder ctl: %s", opus_strerror(rc));
227
0
        opus_encoder_destroy(group_av->audio_encoder);
228
0
        group_av->audio_encoder = nullptr;
229
0
        return -1;
230
0
    }
231
232
4
    return 0;
233
4
}
234
235
static Group_AV *_Nullable new_group_av(const Logger *_Nonnull log, Tox *_Nonnull tox, Group_Chats *_Nonnull g_c, audio_data_cb *_Nullable audio_callback,
236
                                        void *_Nullable userdata)
237
4
{
238
4
    if (g_c == nullptr) {
239
0
        return nullptr;
240
0
    }
241
242
4
    Group_AV *group_av = (Group_AV *)calloc(1, sizeof(Group_AV));
243
244
4
    if (group_av == nullptr) {
245
0
        return nullptr;
246
0
    }
247
248
4
    group_av->log = log;
249
4
    group_av->tox = tox;
250
4
    group_av->g_c = g_c;
251
252
4
    group_av->audio_data = audio_callback;
253
4
    group_av->userdata = userdata;
254
255
4
    return group_av;
256
4
}
257
258
static void group_av_peer_new(void *_Nonnull object, Tox_Conference_Number conference_number, Tox_Conference_Peer_Number peer_number)
259
16
{
260
16
    const Group_AV *group_av = (const Group_AV *)object;
261
16
    Group_Peer_AV *peer_av = (Group_Peer_AV *)calloc(1, sizeof(Group_Peer_AV));
262
263
16
    if (peer_av == nullptr) {
264
0
        return;
265
0
    }
266
267
16
    peer_av->mono_time = g_mono_time(group_av->g_c);
268
16
    peer_av->buffer = create_queue(GROUP_JBUF_SIZE);
269
270
16
    if (group_peer_set_object(group_av->g_c, conference_number, peer_number, peer_av) == -1) {
271
0
        free(peer_av);
272
0
    }
273
16
}
274
275
static void group_av_peer_delete(void *_Nullable object, Tox_Conference_Number conference_number, void *_Nullable peer_object)
276
16
{
277
16
    Group_Peer_AV *peer_av = (Group_Peer_AV *)peer_object;
278
279
16
    if (peer_av == nullptr) {
280
0
        return;
281
0
    }
282
283
16
    if (peer_av->audio_decoder != nullptr) {
284
12
        opus_decoder_destroy(peer_av->audio_decoder);
285
12
    }
286
287
16
    terminate_queue(peer_av->buffer);
288
16
    free(peer_object);
289
16
}
290
291
static void group_av_groupchat_delete(void *_Nullable object, Tox_Conference_Number conference_number)
292
4
{
293
4
    Group_AV *group_av = (Group_AV *)object;
294
4
    if (group_av != nullptr) {
295
4
        kill_group_av(group_av);
296
4
    }
297
4
}
298
299
static int decode_audio_packet(Group_AV *_Nonnull group_av, Group_Peer_AV *_Nonnull peer_av, Tox_Conference_Number conference_number,
300
                               Tox_Conference_Peer_Number peer_number)
301
903
{
302
903
    if (group_av == nullptr || peer_av == nullptr) {
303
0
        return -1;
304
0
    }
305
306
903
    int success;
307
903
    Group_Audio_Packet *pk = dequeue(peer_av->buffer, &success);
308
309
903
    if (success == 0) {
310
425
        return -1;
311
425
    }
312
313
478
    int16_t *out_audio = nullptr;
314
478
    int out_audio_samples = 0;
315
316
478
    const uint32_t sample_rate = 48000;
317
318
478
    if (success == 1) {
319
451
        const int channels = opus_packet_get_nb_channels(pk->data);
320
321
451
        if (channels == OPUS_INVALID_PACKET) {
322
0
            free_audio_packet(pk);
323
0
            return -1;
324
0
        }
325
326
451
        if (channels != 1 && channels != 2) {
327
0
            free_audio_packet(pk);
328
0
            return -1;
329
0
        }
330
331
451
        if (channels != peer_av->decoder_channels) {
332
12
            if (peer_av->audio_decoder != nullptr) {
333
0
                opus_decoder_destroy(peer_av->audio_decoder);
334
0
                peer_av->audio_decoder = nullptr;
335
0
            }
336
337
12
            int rc;
338
12
            peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
339
340
12
            if (rc != OPUS_OK) {
341
0
                LOGGER_ERROR(group_av->log, "Error while starting audio decoder: %s", opus_strerror(rc));
342
0
                free_audio_packet(pk);
343
0
                return -1;
344
0
            }
345
346
12
            peer_av->decoder_channels = channels;
347
12
        }
348
349
451
        const int num_samples = opus_decoder_get_nb_samples(peer_av->audio_decoder, pk->data, pk->length);
350
351
451
        out_audio = (int16_t *)malloc(num_samples * peer_av->decoder_channels * sizeof(int16_t));
352
353
451
        if (out_audio == nullptr) {
354
0
            free_audio_packet(pk);
355
0
            return -1;
356
0
        }
357
358
451
        out_audio_samples = opus_decode(peer_av->audio_decoder, pk->data, pk->length, out_audio, num_samples, 0);
359
451
        free_audio_packet(pk);
360
361
451
        if (out_audio_samples <= 0) {
362
0
            free(out_audio);
363
0
            return -1;
364
0
        }
365
366
451
        peer_av->last_packet_samples = (unsigned int)out_audio_samples;
367
451
    } else {
368
27
        if (peer_av->audio_decoder == nullptr) {
369
26
            return -1;
370
26
        }
371
372
1
        if (peer_av->last_packet_samples == 0) {
373
0
            return -1;
374
0
        }
375
376
1
        out_audio = (int16_t *)malloc(peer_av->last_packet_samples * peer_av->decoder_channels * sizeof(int16_t));
377
378
1
        if (out_audio == nullptr) {
379
0
            free_audio_packet(pk);
380
0
            return -1;
381
0
        }
382
383
1
        out_audio_samples = opus_decode(peer_av->audio_decoder, nullptr, 0, out_audio, peer_av->last_packet_samples, 1);
384
385
1
        if (out_audio_samples <= 0) {
386
0
            free(out_audio);
387
0
            return -1;
388
0
        }
389
1
    }
390
391
452
    if (out_audio != nullptr) {
392
393
452
        if (group_av->audio_data != nullptr) {
394
452
            group_av->audio_data(group_av->tox, conference_number, peer_number, out_audio, (uint32_t)out_audio_samples,
395
452
                                 (uint8_t)peer_av->decoder_channels, sample_rate, group_av->userdata);
396
452
        }
397
398
452
        free(out_audio);
399
452
        return 0;
400
452
    }
401
402
0
    return -1;
403
452
}
404
405
static int handle_group_audio_packet(void *_Nonnull object, Tox_Conference_Number conference_number, Tox_Conference_Peer_Number peer_number, void *_Nonnull peer_object,
406
                                     const uint8_t *_Nonnull packet, uint16_t length)
407
451
{
408
451
    Group_AV *group_av = (Group_AV *)object;
409
451
    Group_Peer_AV *peer_av = (Group_Peer_AV *)peer_object;
410
411
451
    if (group_av == nullptr || peer_av == nullptr || length <= sizeof(uint16_t)) {
412
0
        return -1;
413
0
    }
414
415
451
    Group_Audio_Packet *pk = (Group_Audio_Packet *)calloc(1, sizeof(Group_Audio_Packet));
416
417
451
    if (pk == nullptr) {
418
0
        return -1;
419
0
    }
420
421
451
    net_unpack_u16(packet, &pk->sequnum);
422
451
    pk->length = length - sizeof(uint16_t);
423
424
451
    pk->data = (uint8_t *)malloc(pk->length);
425
426
451
    if (pk->data == nullptr) {
427
0
        free_audio_packet(pk);
428
0
        return -1;
429
0
    }
430
431
451
    memcpy(pk->data, packet + sizeof(uint16_t), pk->length);
432
433
451
    if (queue(peer_av->buffer, peer_av->mono_time, pk) == -1) {
434
0
        free_audio_packet(pk);
435
0
        return -1;
436
0
    }
437
438
903
    while (decode_audio_packet(group_av, peer_av, conference_number, peer_number) == 0) {
439
        /* Continue. */
440
452
    }
441
442
451
    return 0;
443
451
}
444
445
/** @brief Enable A/V in a conference.
446
 *
447
 * @retval 0 on success.
448
 * @retval -1 on failure.
449
 */
450
int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, Tox_Conference_Number conference_number,
451
                        audio_data_cb *audio_callback, void *userdata)
452
4
{
453
4
    if (group_get_type(g_c, conference_number) != GROUPCHAT_TYPE_AV
454
4
            || group_get_object(g_c, conference_number) != nullptr) {
455
0
        return -1;
456
0
    }
457
458
4
    Group_AV *group_av = new_group_av(log, tox, g_c, audio_callback, userdata);
459
460
4
    if (group_av == nullptr) {
461
0
        return -1;
462
0
    }
463
464
4
    if (group_set_object(g_c, conference_number, group_av) == -1
465
4
            || callback_groupchat_peer_new(g_c, conference_number, group_av_peer_new) == -1
466
4
            || callback_groupchat_peer_delete(g_c, conference_number, group_av_peer_delete) == -1
467
4
            || callback_groupchat_delete(g_c, conference_number, group_av_groupchat_delete) == -1) {
468
0
        kill_group_av(group_av);
469
0
        return -1;
470
0
    }
471
472
4
    const int numpeers = group_number_peers(g_c, conference_number, false);
473
474
4
    if (numpeers < 0) {
475
0
        kill_group_av(group_av);
476
0
        return -1;
477
0
    }
478
479
5
    for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) {
480
1
        group_av_peer_new(group_av, conference_number, i);
481
1
    }
482
483
4
    group_lossy_packet_registerhandler(g_c, GROUP_AUDIO_PACKET_ID, &handle_group_audio_packet);
484
4
    return 0;
485
4
}
486
487
/** @brief Disable A/V in a conference.
488
 *
489
 * @retval 0 on success.
490
 * @retval -1 on failure.
491
 */
492
int groupchat_disable_av(const Group_Chats *g_c, Tox_Conference_Number conference_number)
493
0
{
494
0
    if (group_get_type(g_c, conference_number) != GROUPCHAT_TYPE_AV) {
495
0
        return -1;
496
0
    }
497
498
0
    Group_AV *group_av = (Group_AV *)group_get_object(g_c, conference_number);
499
500
0
    if (group_av == nullptr) {
501
0
        return -1;
502
0
    }
503
504
0
    const int numpeers = group_number_peers(g_c, conference_number, false);
505
506
0
    if (numpeers < 0) {
507
0
        kill_group_av(group_av);
508
0
        return -1;
509
0
    }
510
511
0
    for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) {
512
0
        group_av_peer_delete(group_av, conference_number, group_peer_get_object(g_c, conference_number, i));
513
0
        group_peer_set_object(g_c, conference_number, i, nullptr);
514
0
    }
515
516
0
    kill_group_av(group_av);
517
518
0
    if (group_set_object(g_c, conference_number, nullptr) == -1
519
0
            || callback_groupchat_peer_new(g_c, conference_number, nullptr) == -1
520
0
            || callback_groupchat_peer_delete(g_c, conference_number, nullptr) == -1
521
0
            || callback_groupchat_delete(g_c, conference_number, nullptr) == -1) {
522
0
        return -1;
523
0
    }
524
525
0
    return 0;
526
0
}
527
528
/** Return whether A/V is enabled in the conference. */
529
bool groupchat_av_enabled(const Group_Chats *g_c, Tox_Conference_Number conference_number)
530
0
{
531
0
    return group_get_object(g_c, conference_number) != nullptr;
532
0
}
533
534
/** @brief Create and connect to a new toxav group.
535
 *
536
 * @return conference number on success.
537
 * @retval -1 on failure.
538
 */
539
int add_av_groupchat(const Logger *log, Tox *tox, Group_Chats *g_c, audio_data_cb *audio_callback, void *userdata)
540
1
{
541
1
    const int conference_number = add_groupchat(g_c, tox->sys.rng, GROUPCHAT_TYPE_AV);
542
543
1
    if (conference_number == -1) {
544
0
        return -1;
545
0
    }
546
547
1
    if (groupchat_enable_av(log, tox, g_c, (Tox_Conference_Number)conference_number, audio_callback, userdata) == -1) {
548
0
        del_groupchat(g_c, (Tox_Conference_Number)conference_number, true);
549
0
        return -1;
550
0
    }
551
552
1
    return conference_number;
553
1
}
554
555
/** @brief Join a AV group (you need to have been invited first).
556
 *
557
 * @return conference number on success
558
 * @retval -1 on failure.
559
 */
560
int join_av_groupchat(const Logger *log, Tox *tox, Group_Chats *g_c, Tox_Friend_Number friend_number, const uint8_t *data,
561
                      uint16_t length, audio_data_cb *audio_callback, void *userdata)
562
3
{
563
3
    const int conference_number = join_groupchat(g_c, friend_number, GROUPCHAT_TYPE_AV, data, length);
564
565
3
    if (conference_number == -1) {
566
0
        return -1;
567
0
    }
568
569
3
    if (groupchat_enable_av(log, tox, g_c, (Tox_Conference_Number)conference_number, audio_callback, userdata) == -1) {
570
0
        del_groupchat(g_c, (Tox_Conference_Number)conference_number, true);
571
0
        return -1;
572
0
    }
573
574
3
    return conference_number;
575
3
}
576
577
/** @brief Send an encoded audio packet to the conference.
578
 *
579
 * @retval 0 on success.
580
 * @retval -1 on failure.
581
 */
582
static int send_audio_packet(const Group_Chats *_Nonnull g_c, Tox_Conference_Number conference_number, const uint8_t *_Nonnull packet, uint16_t length)
583
160
{
584
160
    if (length == 0 || length > MAX_CRYPTO_DATA_SIZE - 1 - sizeof(uint16_t)) {
585
0
        return -1;
586
0
    }
587
588
160
    const uint16_t plen = 1 + sizeof(uint16_t) + length;
589
590
160
    Group_AV *const group_av = (Group_AV *)group_get_object(g_c, conference_number);
591
592
160
    if (group_av == nullptr) {
593
0
        return -1;
594
0
    }
595
596
160
    uint8_t data[MAX_CRYPTO_DATA_SIZE];
597
160
    uint8_t *ptr = data;
598
160
    *ptr = GROUP_AUDIO_PACKET_ID;
599
160
    ++ptr;
600
601
160
    ptr += net_pack_u16(ptr, group_av->audio_sequnum);
602
160
    memcpy(ptr, packet, length);
603
604
160
    if (send_group_lossy_packet(g_c, conference_number, data, plen) == -1) {
605
0
        return -1;
606
0
    }
607
608
160
    ++group_av->audio_sequnum;
609
160
    return 0;
610
160
}
611
612
/** @brief Send audio to the conference.
613
 *
614
 * @retval 0 on success.
615
 * @retval -1 on failure.
616
 */
617
int group_send_audio(const Group_Chats *g_c, Tox_Conference_Number conference_number, const int16_t pcm[], uint32_t samples, uint8_t channels,
618
                     uint32_t sample_rate)
619
160
{
620
160
    Group_AV *group_av = (Group_AV *)group_get_object(g_c, conference_number);
621
622
160
    if (group_av == nullptr) {
623
0
        return -1;
624
0
    }
625
626
160
    if (channels != 1 && channels != 2) {
627
0
        return -1;
628
0
    }
629
630
160
    if (sample_rate != 8000 && sample_rate != 12000 && sample_rate != 16000 && sample_rate != 24000
631
160
            && sample_rate != 48000) {
632
0
        return -1;
633
0
    }
634
635
160
    if (group_av->audio_encoder == nullptr || group_av->audio_channels != channels
636
160
            || group_av->audio_sample_rate != sample_rate) {
637
4
        group_av->audio_channels = channels;
638
4
        group_av->audio_sample_rate = sample_rate;
639
640
4
        if (channels == 1) {
641
4
            group_av->audio_bitrate = 32000; // TODO(mannol): add way of adjusting bitrate
642
4
        } else {
643
0
            group_av->audio_bitrate = 64000; // TODO(mannol): add way of adjusting bitrate
644
0
        }
645
646
4
        if (recreate_encoder(group_av) == -1) {
647
0
            return -1;
648
0
        }
649
4
    }
650
651
160
    uint8_t encoded[1024];
652
160
    const int32_t size = opus_encode(group_av->audio_encoder, pcm, samples, encoded, sizeof(encoded));
653
654
160
    if (size <= 0) {
655
0
        return -1;
656
0
    }
657
658
160
    return send_audio_packet(g_c, conference_number, encoded, size);
659
160
}