summaryrefslogtreecommitdiff
path: root/toxav/toxav_new_1.c
diff options
context:
space:
mode:
authormannol <eniz_vukovic@hotmail.com>2015-02-14 23:37:52 +0100
committermannol <eniz_vukovic@hotmail.com>2015-02-14 23:37:52 +0100
commitaad857527cd63b5f79786df0c1aab50f4de87774 (patch)
treec94463c21fe2d73b1ec514b223a5e0166e1e8693 /toxav/toxav_new_1.c
parent39680f31d0121cef2358507fcea84cacad69893a (diff)
Control part of new api already kind of works
Diffstat (limited to 'toxav/toxav_new_1.c')
-rw-r--r--toxav/toxav_new_1.c689
1 files changed, 689 insertions, 0 deletions
diff --git a/toxav/toxav_new_1.c b/toxav/toxav_new_1.c
new file mode 100644
index 00000000..ee7f49a6
--- /dev/null
+++ b/toxav/toxav_new_1.c
@@ -0,0 +1,689 @@
1/** toxav.c
2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#define __TOX_DEFINED__
27typedef struct Messenger Tox;
28
29#define _GNU_SOURCE /* implicit declaration warning */
30
31#include "codec.h"
32#include "msi.h"
33#include "group.h"
34
35#include "../toxcore/logger.h"
36#include "../toxcore/util.h"
37
38#include <assert.h>
39#include <stdlib.h>
40#include <string.h>
41
42/* Assume 24 fps*/
43#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
44
45/* true if invalid call index */
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max)
47
48const ToxAvCSettings av_DefaultSettings = {
49 av_TypeAudio,
50
51 500,
52 1280,
53 720,
54
55 32000,
56 20,
57 48000,
58 1
59};
60
61static const uint32_t jbuf_capacity = 6;
62static const uint8_t audio_index = 0, video_index = 1;
63
64typedef struct _ToxAvCall {
65 pthread_mutex_t mutex_control[1];
66 pthread_mutex_t mutex_encoding_audio[1];
67 pthread_mutex_t mutex_encoding_video[1];
68 pthread_mutex_t mutex_do[1];
69 RTPSession *crtps[2]; /** Audio is first and video is second */
70 CSSession *cs;
71 _Bool active;
72} ToxAvCall;
73
74struct _ToxAv {
75 Messenger *messenger;
76 MSISession *msi_session; /** Main msi session */
77 ToxAvCall *calls; /** Per-call params */
78 uint32_t max_calls;
79
80 PAIR(ToxAvAudioCallback, void *) acb;
81 PAIR(ToxAvVideoCallback, void *) vcb;
82
83 /* Decode time measure */
84 int32_t dectmsscount; /** Measure count */
85 int32_t dectmsstotal; /** Last cycle total */
86 int32_t avgdectms; /** Average decoding time in ms */
87};
88
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from)
90{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
92 return (const MSICSettings *) from;
93}
94
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings));
98 return (const ToxAvCSettings *) from;
99
100}
101
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
103{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1);
105
106 if (av == NULL) {
107 LOGGER_WARNING("Allocation failed!");
108 return NULL;
109 }
110
111 av->messenger = (Messenger *)messenger;
112 av->msi_session = msi_new(av->messenger, max_calls);
113 av->msi_session->agent_handler = av;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls);
115 av->max_calls = max_calls;
116
117 unsigned int i;
118
119 for (i = 0; i < max_calls; ++i) {
120 if (create_recursive_mutex(av->calls[i].mutex_control) != 0 ) {
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i);
122 msi_kill(av->msi_session);
123
124 free(av->calls);
125 free(av);
126 return NULL;
127 }
128 }
129
130 return av;
131}
132
133void toxav_kill ( ToxAv *av )
134{
135 uint32_t i;
136
137 for (i = 0; i < av->max_calls; i ++) {
138 if ( av->calls[i].crtps[audio_index] )
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
140
141
142 if ( av->calls[i].crtps[video_index] )
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
144
145 if ( av->calls[i].cs )
146 cs_kill(av->calls[i].cs);
147
148 pthread_mutex_destroy(av->calls[i].mutex_control);
149 }
150
151 msi_kill(av->msi_session);
152
153 free(av->calls);
154 free(av);
155}
156
157uint32_t toxav_do_interval(ToxAv *av)
158{
159 int i = 0;
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */
161
162 for (; i < av->max_calls; i ++) {
163 pthread_mutex_lock(av->calls[i].mutex_control);
164
165 if (av->calls[i].active) {
166 /* This should work. Video payload will always come in greater intervals */
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
168 }
169
170 pthread_mutex_unlock(av->calls[i].mutex_control);
171 }
172
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms;
174}
175
176void toxav_do(ToxAv *av)
177{
178 msi_do(av->msi_session);
179
180 uint64_t start = current_time_monotonic();
181
182 uint32_t i = 0;
183
184 for (; i < av->max_calls; i ++) {
185 pthread_mutex_lock(av->calls[i].mutex_control);
186
187 if (av->calls[i].active) {
188 pthread_mutex_lock(av->calls[i].mutex_do);
189 pthread_mutex_unlock(av->calls[i].mutex_control);
190 cs_do(av->calls[i].cs);
191 pthread_mutex_unlock(av->calls[i].mutex_do);
192 } else {
193 pthread_mutex_unlock(av->calls[i].mutex_control);
194 }
195 }
196
197 uint64_t end = current_time_monotonic();
198
199 /* TODO maybe use variable for sizes */
200 av->dectmsstotal += end - start;
201
202 if (++av->dectmsscount == 3) {
203 av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */;
204 av->dectmsscount = 0;
205 av->dectmsstotal = 0;
206 }
207}
208
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata )
210{
211 msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata);
212}
213
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata)
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata)
221{
222 av->vcb.first = cb;
223 av->vcb.second = userdata;
224}
225
226int toxav_call (ToxAv *av,
227 int32_t *call_index,
228 int user,
229 const ToxAvCSettings *csettings,
230 int ringing_seconds )
231{
232 return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user);
233}
234
235int toxav_hangup ( ToxAv *av, int32_t call_index )
236{
237 return msi_hangup(av->msi_session, call_index);
238}
239
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings )
241{
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
243}
244
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason )
246{
247 return msi_reject(av->msi_session, call_index, reason);
248}
249
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason )
251{
252 return msi_cancel(av->msi_session, call_index, peer_id, reason);
253}
254
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings)
256{
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings));
258}
259
260int toxav_stop_call ( ToxAv *av, int32_t call_index )
261{
262 return msi_stopcall(av->msi_session, call_index);
263}
264
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video )
266{
267 if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) ||
268 !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) {
269 LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
270 return av_ErrorNoCall;
271 }
272
273 ToxAvCall *call = &av->calls[call_index];
274
275 pthread_mutex_lock(call->mutex_control);
276
277 if (call->active) {
278 pthread_mutex_unlock(call->mutex_control);
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n");
280 return av_ErrorAlreadyInCallWithPeer;
281 }
282
283 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0
284 || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) {
285 pthread_mutex_unlock(call->mutex_control);
286 LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n");
287 return av_ErrorUnknown;
288 }
289
290 const ToxAvCSettings *c_peer = toxavcsettings_cast
291 (&av->msi_session->calls[call_index]->csettings_peer[0]);
292 const ToxAvCSettings *c_self = toxavcsettings_cast
293 (&av->msi_session->calls[call_index]->csettings_local);
294
295 LOGGER_DEBUG(
296 "Type: %u(s) %u(p)\n"
297 "Video bitrate: %u(s) %u(p)\n"
298 "Video height: %u(s) %u(p)\n"
299 "Video width: %u(s) %u(p)\n"
300 "Audio bitrate: %u(s) %u(p)\n"
301 "Audio framedur: %u(s) %u(p)\n"
302 "Audio sample rate: %u(s) %u(p)\n"
303 "Audio channels: %u(s) %u(p)\n",
304 c_self->call_type, c_peer->call_type,
305 c_self->video_bitrate, c_peer->video_bitrate,
306 c_self->max_video_height, c_peer->max_video_height,
307 c_self->max_video_width, c_peer->max_video_width,
308 c_self->audio_bitrate, c_peer->audio_bitrate,
309 c_self->audio_frame_duration, c_peer->audio_frame_duration,
310 c_self->audio_sample_rate, c_peer->audio_sample_rate,
311 c_self->audio_channels, c_peer->audio_channels );
312
313 if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) {
314 LOGGER_ERROR("Error while starting Codec State!\n");
315 pthread_mutex_unlock(call->mutex_control);
316 return av_ErrorInitializingCodecs;
317 }
318
319 call->cs->agent = av;
320 call->cs->call_idx = call_index;
321
322 call->cs->acb.first = av->acb.first;
323 call->cs->acb.second = av->acb.second;
324
325 call->cs->vcb.first = av->vcb.first;
326 call->cs->vcb.second = av->vcb.second;
327
328
329 call->crtps[audio_index] =
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]);
331
332 if ( !call->crtps[audio_index] ) {
333 LOGGER_ERROR("Error while starting audio RTP session!\n");
334 goto error;
335 }
336
337 call->crtps[audio_index]->cs = call->cs;
338
339 if ( support_video ) {
340 call->crtps[video_index] =
341 rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]);
342
343 if ( !call->crtps[video_index] ) {
344 LOGGER_ERROR("Error while starting video RTP session!\n");
345 goto error;
346 }
347
348 call->crtps[video_index]->cs = call->cs;
349 }
350
351 call->active = 1;
352 pthread_mutex_unlock(call->mutex_control);
353 return av_ErrorNone;
354error:
355 rtp_kill(call->crtps[audio_index], av->messenger);
356 call->crtps[audio_index] = NULL;
357 rtp_kill(call->crtps[video_index], av->messenger);
358 call->crtps[video_index] = NULL;
359 cs_kill(call->cs);
360 call->cs = NULL;
361 call->active = 0;
362 pthread_mutex_destroy(call->mutex_encoding_audio);
363 pthread_mutex_destroy(call->mutex_encoding_video);
364 pthread_mutex_destroy(call->mutex_do);
365
366 pthread_mutex_unlock(call->mutex_control);
367 return av_ErrorCreatingRtpSessions;
368}
369
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index )
371{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
373 LOGGER_WARNING("Invalid call index: %d", call_index);
374 return av_ErrorNoCall;
375 }
376
377 ToxAvCall *call = &av->calls[call_index];
378
379 pthread_mutex_lock(call->mutex_control);
380
381 if (!call->active) {
382 pthread_mutex_unlock(call->mutex_control);
383 LOGGER_WARNING("Action on inactive call: %d", call_index);
384 return av_ErrorInvalidState;
385 }
386
387 call->active = 0;
388
389 pthread_mutex_lock(call->mutex_encoding_audio);
390 pthread_mutex_unlock(call->mutex_encoding_audio);
391 pthread_mutex_lock(call->mutex_encoding_video);
392 pthread_mutex_unlock(call->mutex_encoding_video);
393 pthread_mutex_lock(call->mutex_do);
394 pthread_mutex_unlock(call->mutex_do);
395
396 rtp_kill(call->crtps[audio_index], av->messenger);
397 call->crtps[audio_index] = NULL;
398 rtp_kill(call->crtps[video_index], av->messenger);
399 call->crtps[video_index] = NULL;
400 cs_kill(call->cs);
401 call->cs = NULL;
402
403 pthread_mutex_destroy(call->mutex_encoding_audio);
404 pthread_mutex_destroy(call->mutex_encoding_video);
405 pthread_mutex_destroy(call->mutex_do);
406
407 pthread_mutex_unlock(call->mutex_control);
408
409 return av_ErrorNone;
410}
411
412static int toxav_send_rtp_payload(ToxAv *av,
413 ToxAvCall *call,
414 ToxAvCallType type,
415 const uint8_t *payload,
416 unsigned int length)
417{
418 if (call->crtps[type - av_TypeAudio]) {
419
420 /* Audio */
421 if (type == av_TypeAudio)
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
423
424 /* Video */
425 int parts = cs_split_video_payload(call->cs, payload, length);
426
427 if (parts < 0) return parts;
428
429 uint16_t part_size;
430 const uint8_t *iter;
431
432 int i;
433
434 for (i = 0; i < parts; i++) {
435 iter = cs_iterate_split_video_frame(call->cs, &part_size);
436
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0)
438 return av_ErrorSendingPayload;
439 }
440
441 return av_ErrorNone;
442
443 } else return av_ErrorNoRtpSession;
444}
445
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input)
447{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
449 LOGGER_WARNING("Invalid call index: %d", call_index);
450 return av_ErrorNoCall;
451 }
452
453
454 ToxAvCall *call = &av->calls[call_index];
455 pthread_mutex_lock(call->mutex_control);
456
457 if (!call->active) {
458 pthread_mutex_unlock(call->mutex_control);
459 LOGGER_WARNING("Action on inactive call: %d", call_index);
460 return av_ErrorInvalidState;
461 }
462
463 if (cs_set_sending_video_resolution(call->cs, input->d_w, input->d_h) < 0) {
464 pthread_mutex_unlock(call->mutex_control);
465 return av_ErrorSettingVideoResolution;
466 }
467
468 pthread_mutex_lock(call->mutex_encoding_video);
469 pthread_mutex_unlock(call->mutex_control);
470
471 int rc = vpx_codec_encode(call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
472
473 if ( rc != VPX_CODEC_OK) {
474 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc));
475 pthread_mutex_unlock(call->mutex_encoding_video);
476 return av_ErrorEncodingVideo;
477 }
478
479 ++call->cs->frame_counter;
480
481 vpx_codec_iter_t iter = NULL;
482 const vpx_codec_cx_pkt_t *pkt;
483 int copied = 0;
484
485 while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) {
486 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
487 if ( copied + pkt->data.frame.sz > dest_max ) {
488 pthread_mutex_unlock(call->mutex_encoding_video);
489 return av_ErrorPacketTooLarge;
490 }
491
492 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz);
493 copied += pkt->data.frame.sz;
494 }
495 }
496
497 pthread_mutex_unlock(call->mutex_encoding_video);
498 return copied;
499}
500
501int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size)
502{
503
504 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
505 LOGGER_WARNING("Invalid call index: %d", call_index);
506 return av_ErrorNoCall;
507 }
508
509 ToxAvCall *call = &av->calls[call_index];
510 pthread_mutex_lock(call->mutex_control);
511
512
513 if (!call->active) {
514 pthread_mutex_unlock(call->mutex_control);
515 LOGGER_WARNING("Action on inactive call: %d", call_index);
516 return av_ErrorInvalidState;
517 }
518
519 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size);
520 pthread_mutex_unlock(call->mutex_control);
521
522 return rc;
523}
524
525int toxav_prepare_audio_frame ( ToxAv *av,
526 int32_t call_index,
527 uint8_t *dest,
528 int dest_max,
529 const int16_t *frame,
530 int frame_size)
531{
532 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
533 LOGGER_WARNING("Action on nonexisting call: %d", call_index);
534 return av_ErrorNoCall;
535 }
536
537 ToxAvCall *call = &av->calls[call_index];
538 pthread_mutex_lock(call->mutex_control);
539
540 if (!call->active) {
541 pthread_mutex_unlock(call->mutex_control);
542 LOGGER_WARNING("Action on inactive call: %d", call_index);
543 return av_ErrorInvalidState;
544 }
545
546 pthread_mutex_lock(call->mutex_encoding_audio);
547 pthread_mutex_unlock(call->mutex_control);
548 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max);
549 pthread_mutex_unlock(call->mutex_encoding_audio);
550
551 if (rc < 0) {
552 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc));
553 return av_ErrorEncodingAudio;
554 }
555
556 return rc;
557}
558
559int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
560{
561 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
562 LOGGER_WARNING("Action on nonexisting call: %d", call_index);
563 return av_ErrorNoCall;
564 }
565
566 ToxAvCall *call = &av->calls[call_index];
567 pthread_mutex_lock(call->mutex_control);
568
569
570 if (!call->active) {
571 pthread_mutex_unlock(call->mutex_control);
572 LOGGER_WARNING("Action on inactive call: %d", call_index);
573 return av_ErrorInvalidState;
574 }
575
576 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size);
577 pthread_mutex_unlock(call->mutex_control);
578 return rc;
579}
580
581int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest )
582{
583 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) ||
584 !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer )
585 return av_ErrorNoCall;
586
587 *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]);
588 return av_ErrorNone;
589}
590
591int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer )
592{
593 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index]
594 || av->msi_session->calls[call_index]->peer_count <= peer )
595 return av_ErrorNoCall;
596
597 return av->msi_session->calls[call_index]->peers[peer];
598}
599
600ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
601{
602 if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] )
603 return av_CallNonExistent;
604
605 return av->msi_session->calls[call_index]->state;
606
607}
608
609int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability )
610{
611}
612
613Tox *toxav_get_tox(ToxAv *av)
614{
615 return (Tox *)av->messenger;
616}
617
618int toxav_get_active_count(ToxAv *av)
619{
620 if (!av) return -1;
621
622 int rc = 0, i = 0;
623
624 for (; i < av->max_calls; i++) {
625 pthread_mutex_lock(av->calls[i].mutex_control);
626
627 if (av->calls[i].active) rc++;
628
629 pthread_mutex_unlock(av->calls[i].mutex_control);
630 }
631
632 return rc;
633}
634
635/* Create a new toxav group.
636 *
637 * return group number on success.
638 * return -1 on failure.
639 *
640 * Audio data callback format:
641 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
642 *
643 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
644 */
645int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int,
646 uint8_t, unsigned int, void *), void *userdata)
647{
648 Messenger *m = tox;
649 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
650}
651
652/* Join a AV group (you need to have been invited first.)
653 *
654 * returns group number on success
655 * returns -1 on failure.
656 *
657 * Audio data callback format (same as the one for toxav_add_av_groupchat()):
658 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
659 *
660 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
661 */
662int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
663 void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
664 void *userdata)
665{
666 Messenger *m = tox;
667 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata);
668}
669
670/* Send audio to the group chat.
671 *
672 * return 0 on success.
673 * return -1 on failure.
674 *
675 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
676 *
677 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
678 * Valid number of channels are 1 or 2.
679 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
680 *
681 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
682 */
683int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
684 unsigned int sample_rate)
685{
686 Messenger *m = tox;
687 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate);
688}
689