summaryrefslogtreecommitdiff
path: root/toxav
diff options
context:
space:
mode:
Diffstat (limited to 'toxav')
-rw-r--r--toxav/Makefile.inc137
-rwxr-xr-xtoxav/phone.c735
-rw-r--r--toxav/toxmedia.c819
-rw-r--r--toxav/toxmedia.h165
-rwxr-xr-xtoxav/toxmsi.c1337
-rwxr-xr-xtoxav/toxmsi.h231
-rwxr-xr-xtoxav/toxrtp.c876
-rwxr-xr-xtoxav/toxrtp.h215
8 files changed, 4515 insertions, 0 deletions
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
new file mode 100644
index 00000000..203d8a07
--- /dev/null
+++ b/toxav/Makefile.inc
@@ -0,0 +1,137 @@
1if BUILD_AV
2
3lib_LTLIBRARIES += libtoxrtp.la \
4 libtoxmsi.la \
5 libtoxmedia.la
6
7
8# ****** RTP ****** #
9
10libtoxrtp_la_include_HEADERS = \
11 ../toxav/toxrtp.h
12
13libtoxrtp_la_includedir = $(includedir)/tox
14
15libtoxrtp_la_SOURCES = ../toxav/toxrtp.h \
16 ../toxav/toxrtp.c
17
18libtoxrtp_la_CFLAGS = -I../toxcore \
19 -I../toxav \
20 $(NACL_CFLAGS)
21
22libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \
23 $(NACL_LDFLAGS) \
24 $(EXTRA_LT_LDFLAGS)
25
26libtoxrtp_la_LIBS = libtoxcore.la \
27 $(NACL_LIBS)
28
29
30
31
32
33# ****** MSI ****** #
34
35libtoxmsi_la_include_HEADERS = \
36 ../toxav/toxmsi.h
37
38libtoxmsi_la_includedir = $(includedir)/tox
39
40libtoxmsi_la_SOURCES = ../toxav/toxmsi.h \
41 ../toxav/toxmsi.c
42
43libtoxmsi_la_CFLAGS = -I../toxcore \
44 -I../toxav \
45 $(NACL_CFLAGS)
46
47libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
48 $(EXTRA_LT_LDFLAGS) \
49 $(NACL_LDFLAGS)
50
51libtoxmsi_la_LIBS = libtoxcore.la \
52 $(NACL_LIBS)
53
54
55
56
57
58# ****** MEDIA ****** #
59
60libtoxmedia_la_include_HEADERS = \
61 ../toxav/toxmedia.h
62
63libtoxmedia_la_includedir = $(includedir)/tox
64
65libtoxmedia_la_SOURCES = ../toxav/toxmedia.h \
66 ../toxav/toxmedia.c
67
68libtoxmedia_la_CFLAGS = -I../toxcore \
69 -I../toxav \
70 $(AVFORMAT_CFLAGS) \
71 $(AVCODEC_CFLAGS) \
72 $(AVUTIL_CFLAGS) \
73 $(AVDEVICE_CFLAGS) \
74 $(SWSCALE_CFLAGS) \
75 $(SDL_CFLAGS) \
76 $(OPENAL_CFLAGS) \
77 $(NACL_CFLAGS) \
78 $(OPUS_CFLAGS)
79
80
81libtoxmedia_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
82 $(TOXRTP_LT_LDFLAGS) \
83 $(EXTRA_LT_LDFLAGS) \
84 $(NACL_LDFLAGS)
85
86
87libtoxmedia_la_LIBS = libtoxcore.la \
88 $(NACL_LDFLAGS) \
89 $(AVFORMAT_LIBS) \
90 $(AVCODEC_LIBS) \
91 $(AVUTIL_LIBS) \
92 $(AVDEVICE_LIBS) \
93 $(SWSCALE_LIBS) \
94 $(SDL_LIBS) \
95 $(OPENAL_LIBS) \
96 $(NACL_LIBS) \
97 $(OPUS_LIBS)
98
99
100
101
102# ***** PHONE ***** #
103
104noinst_PROGRAMS += phone
105
106phone_SOURCES = ../toxav/phone.c
107
108phone_CFLAGS = -I../toxcore \
109 -I../toxav \
110 $(AVFORMAT_CFLAGS) \
111 $(AVCODEC_CFLAGS) \
112 $(AVUTIL_CFLAGS) \
113 $(AVDEVICE_CFLAGS) \
114 $(SWSCALE_CFLAGS) \
115 $(SDL_CFLAGS) \
116 $(OPENAL_CFLAGS) \
117 $(NACL_CFLAGS) \
118 $(OPUS_CFLAGS)
119
120
121phone_LDADD = libtoxrtp.la \
122 libtoxmsi.la \
123 libtoxmedia.la \
124 libtoxcore.la \
125 $(NACL_LDFLAGS) \
126 $(AVFORMAT_LIBS) \
127 $(AVCODEC_LIBS) \
128 $(AVUTIL_LIBS) \
129 $(AVDEVICE_LIBS) \
130 $(SWSCALE_LIBS) \
131 $(SDL_LIBS) \
132 $(OPENAL_LIBS) \
133 $(NACL_LIBS) \
134 $(OPUS_LIBS)
135
136
137endif \ No newline at end of file
diff --git a/toxav/phone.c b/toxav/phone.c
new file mode 100755
index 00000000..6d4b5e6b
--- /dev/null
+++ b/toxav/phone.c
@@ -0,0 +1,735 @@
1/** phone.c
2 *
3 * NOTE NOTE NOTE NOTE NOTE NOTE
4 *
5 * This file is for testing/reference purposes only, hence
6 * it is _poorly_ designed and it does not fully reflect the
7 * quaility of msi nor rtp. Although toxmsi* and toxrtp* are tested
8 * there is always possiblity of crashes. If crash occures,
9 * contact me ( mannol ) on either irc channel #tox-dev @ freenode.net:6667
10 * or eniz_vukovic@hotmail.com
11 *
12 * NOTE NOTE NOTE NOTE NOTE NOTE
13 *
14 * Copyright (C) 2013 Tox project All Rights Reserved.
15 *
16 * This file is part of Tox.
17 *
18 * Tox is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * Tox is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
30 *
31 */
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif /* HAVE_CONFIG_H */
36
37#define _BSD_SOURCE
38
39#include <stdio.h>
40#include <string.h>
41#include <stdlib.h>
42
43#include "toxmsi.h"
44#include "toxrtp.h"
45#include <stdarg.h>
46#include <unistd.h>
47#include <assert.h>
48
49#include "../toxcore/network.h"
50#include "../toxcore/event.h"
51#include "../toxcore/tox.h"
52
53/* Define client version */
54#define _USERAGENT "v.0.3.0"
55
56
57typedef struct av_friend_s {
58 int _id;
59 int _active; /* 0=false; 1=true; */
60} av_friend_t;
61
62typedef struct av_session_s {
63 MSISession* _msi;
64
65 RTPSession* _rtp_audio;
66 RTPSession* _rtp_video;
67
68 pthread_mutex_t _mutex;
69
70 Tox* _messenger;
71 av_friend_t* _friends;
72 int _friend_cout;
73 uint8_t _my_public_id[200];
74} av_session_t;
75
76
77void av_allocate_friend(av_session_t* _phone, int _id, int _active)
78{
79 static int _new_id = 0;
80
81 if ( !_phone->_friends ) {
82 _phone->_friends = calloc(sizeof(av_friend_t), 1);
83 _phone->_friend_cout = 1;
84 } else{
85 _phone->_friend_cout ++;
86 _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout);
87 }
88
89 if ( _id = -1 ) {
90 _phone->_friends->_id = _new_id;
91 _new_id ++;
92 } else _phone->_friends->_id = _id;
93
94 _phone->_friends->_active = _active;
95}
96av_friend_t* av_get_friend(av_session_t* _phone, int _id)
97{
98 av_friend_t* _friends = _phone->_friends;
99
100 if ( !_friends ) return NULL;
101
102 int _it = 0;
103 for (; _it < _phone->_friend_cout; _it ++)
104 if ( _friends[_it]._id == _id )
105 return _friends + _it;
106
107 return NULL;
108}
109
110
111/***************** MISC *****************/
112
113void INFO (const char* _format, ...)
114{
115 printf("\r[!] ");
116 va_list _arg;
117 va_start (_arg, _format);
118 vfprintf (stdout, _format, _arg);
119 va_end (_arg);
120 printf("\n\r >> ");
121 fflush(stdout);
122}
123
124unsigned char *hex_string_to_bin(char hex_string[])
125{
126 size_t i, len = strlen(hex_string);
127 unsigned char *val = calloc(sizeof(char), len);
128 char *pos = hex_string;
129
130 for (i = 0; i < len; ++i, pos += 2)
131 sscanf(pos, "%2hhx", &val[i]);
132
133 return val;
134}
135
136int getinput( char* _buff, size_t _limit, int* _len )
137{
138 if ( fgets(_buff, _limit, stdin) == NULL )
139 return -1;
140
141 *_len = strlen(_buff) - 1;
142
143 /* Get rid of newline */
144 _buff[*_len] = '\0';
145
146 return 0;
147}
148
149char* trim_spaces ( char* buff )
150{
151
152 int _i = 0, _len = strlen(buff);
153
154 char* container = calloc(sizeof(char), _len);
155 int _ci = 0;
156
157 for ( ; _i < _len; _i++ ) {
158 while ( _i < _len && buff[_i] == ' ' )
159 _i++;
160
161 if ( _i < _len ){
162 container[_ci] = buff[_i];
163 _ci ++;
164 }
165 }
166
167 memcpy( buff, container, _ci );
168 buff[_ci] = '\0';
169 free(container);
170 return buff;
171}
172
173#define FRADDR_TOSTR_CHUNK_LEN 8
174
175static void fraddr_to_str(uint8_t *id_bin, char *id_str)
176{
177 uint i, delta = 0, pos_extra, sum_extra = 0;
178
179 for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
180 sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
181
182 if ((i + 1) == TOX_CLIENT_ID_SIZE)
183 pos_extra = 2 * (i + 1) + delta;
184
185 if (i >= TOX_CLIENT_ID_SIZE)
186 sum_extra |= id_bin[i];
187
188 if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
189 id_str[2 * (i + 1) + delta] = ' ';
190 delta++;
191 }
192 }
193
194 id_str[2 * i + delta] = 0;
195
196 if (!sum_extra)
197 id_str[pos_extra] = 0;
198}
199
200void* phone_handle_media_transport_poll ( void* _hmtc_args_p )
201{
202 RTPMessage* _audio_msg, * _video_msg;
203 av_session_t* _phone = _hmtc_args_p;
204 MSISession* _session = _phone->_msi;
205
206 RTPSession* _rtp_audio = _phone->_rtp_audio;
207 RTPSession* _rtp_video = _phone->_rtp_video;
208
209
210 Tox* _messenger = _phone->_messenger;
211
212
213 while ( _session->call ) {
214
215 _audio_msg = rtp_recv_msg ( _rtp_audio );
216 _video_msg = rtp_recv_msg ( _rtp_video );
217
218 if ( _audio_msg ) {
219 /* Do whatever with msg
220 printf("%d - %s\n", _audio_msg->header->sequnum, _audio_msg->data);*/
221 rtp_free_msg ( _rtp_audio, _audio_msg );
222 }
223
224 if ( _video_msg ) {
225 /* Do whatever with msg
226 p rintf("%d - %s\n", _video_msg->header->sequnum, _video_msg->data);*/
227 rtp_free_msg ( _rtp_video, _video_msg );
228 }
229
230 /*
231 * Send test message to the 'remote'
232 */
233 rtp_send_msg ( _rtp_audio, _messenger, (const uint8_t*)"audio\0", 6 );
234
235 if ( _session->call->type_local == type_video ){ /* if local call send video */
236 rtp_send_msg ( _rtp_video, _messenger, (const uint8_t*)"video\0", 6 );
237 }
238
239 _audio_msg = _video_msg = NULL;
240
241
242 /* Send ~1k messages per second
243 * That _should_ be enough for both Audio and Video
244 */
245 usleep ( 1000 );
246 /* -------------------- */
247 }
248
249 if ( _audio_msg ) rtp_free_msg(_rtp_audio, _audio_msg);
250 rtp_release_session_recv(_rtp_audio);
251 rtp_terminate_session(_rtp_audio, _messenger);
252
253 if ( _video_msg ) rtp_free_msg(_rtp_video, _video_msg);
254 rtp_release_session_recv(_rtp_video);
255 rtp_terminate_session(_rtp_video, _messenger);
256
257 INFO("Media thread finished!");
258
259 pthread_exit ( NULL );
260}
261
262int phone_startmedia_loop ( av_session_t* _phone )
263{
264 if ( !_phone ){
265 return -1;
266 }
267
268 _phone->_rtp_audio = rtp_init_session (
269 type_audio,
270 _phone->_messenger,
271 _phone->_msi->call->peers[0],
272 _phone->_msi->call->key_peer,
273 _phone->_msi->call->key_local,
274 _phone->_msi->call->nonce_peer,
275 _phone->_msi->call->nonce_local
276 );
277
278 _phone->_rtp_audio = rtp_init_session (
279 type_video,
280 _phone->_messenger,
281 _phone->_msi->call->peers[0],
282 _phone->_msi->call->key_peer,
283 _phone->_msi->call->key_local,
284 _phone->_msi->call->nonce_peer,
285 _phone->_msi->call->nonce_local
286 );
287
288
289 if ( 0 > event.rise(phone_handle_media_transport_poll, _phone) )
290 {
291 printf("Error while starting phone_handle_media_transport_poll()\n");
292 return -1;
293 }
294 else return 0;
295}
296
297
298/* Some example callbacks */
299
300void* callback_recv_invite ( void* _arg )
301{
302 const char* _call_type;
303
304 MSISession* _msi = _arg;
305
306 switch ( _msi->call->type_peer[_msi->call->peer_count - 1] ){
307 case type_audio:
308 _call_type = "audio";
309 break;
310 case type_video:
311 _call_type = "video";
312 break;
313 }
314
315 INFO( "Incoming %s call!", _call_type );
316
317}
318void* callback_recv_ringing ( void* _arg )
319{
320 INFO ( "Ringing!" );
321}
322void* callback_recv_starting ( void* _arg )
323{
324 MSISession* _session = _arg;
325 if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
326 INFO("Starting call failed!");
327 } else {
328 INFO ("Call started! ( press h to hangup )");
329 }
330}
331void* callback_recv_ending ( void* _arg )
332{
333 INFO ( "Call ended!" );
334}
335
336void* callback_recv_error ( void* _arg )
337{
338 MSISession* _session = _arg;
339
340 INFO( "Error: %s", _session->last_error_str );
341}
342
343void* callback_call_started ( void* _arg )
344{
345 MSISession* _session = _arg;
346 if ( 0 != phone_startmedia_loop(_session->agent_handler) ){
347 INFO("Starting call failed!");
348 } else {
349 INFO ("Call started! ( press h to hangup )");
350 }
351
352}
353void* callback_call_canceled ( void* _arg )
354{
355 INFO ( "Call canceled!" );
356}
357void* callback_call_rejected ( void* _arg )
358{
359 INFO ( "Call rejected!" );
360}
361void* callback_call_ended ( void* _arg )
362{
363 INFO ( "Call ended!" );
364}
365
366void* callback_requ_timeout ( void* _arg )
367{
368 INFO( "No answer! " );
369}
370
371int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port)
372{
373 unsigned char *_binary_string = hex_string_to_bin(_dht_key);
374
375 uint16_t _port = htons(_dht_port);
376
377 int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string );
378
379 free(_binary_string);
380
381 return _if ? 0 : -1;
382}
383
384av_session_t* av_init_session()
385{
386 av_session_t* _retu = malloc(sizeof(av_session_t));
387
388 /* Initialize our mutex */
389 pthread_mutex_init ( &_retu->_mutex, NULL );
390
391 _retu->_messenger = tox_new(1);
392
393 if ( !_retu->_messenger ) {
394 fprintf ( stderr, "tox_new() failed!\n" );
395 return NULL;
396 }
397
398 _retu->_friends = NULL;
399
400 _retu->_rtp_audio = NULL;
401 _retu->_rtp_video = NULL;
402
403 uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
404 tox_get_address(_retu->_messenger, _byte_address );
405 fraddr_to_str( _byte_address, _retu->_my_public_id );
406
407
408 /* Initialize msi */
409 _retu->_msi = msi_init_session ( _retu->_messenger, _USERAGENT );
410
411 if ( !_retu->_msi ) {
412 fprintf ( stderr, "msi_init_session() failed\n" );
413 return NULL;
414 }
415
416 _retu->_msi->agent_handler = _retu;
417
418 /* ------------------ */
419 msi_register_callback(callback_call_started, cb_onstart);
420 msi_register_callback(callback_call_canceled, cb_oncancel);
421 msi_register_callback(callback_call_rejected, cb_onreject);
422 msi_register_callback(callback_call_ended, cb_onend);
423 msi_register_callback(callback_recv_invite, cb_oninvite);
424
425 msi_register_callback(callback_recv_ringing, cb_ringing);
426 msi_register_callback(callback_recv_starting, cb_starting);
427 msi_register_callback(callback_recv_ending, cb_ending);
428
429 msi_register_callback(callback_recv_error, cb_error);
430 msi_register_callback(callback_requ_timeout, cb_timeout);
431 /* ------------------ */
432
433 return _retu;
434}
435
436int av_terminate_session(av_session_t* _phone)
437{
438 if ( _phone->_msi->call ){
439 msi_hangup(_phone->_msi); /* Hangup the phone first */
440 }
441
442 free(_phone->_friends);
443 msi_terminate_session(_phone->_msi);
444 pthread_mutex_destroy ( &_phone->_mutex );
445
446 Tox* _p = _phone->_messenger;
447 _phone->_messenger = NULL; usleep(100000); /* Wait for tox_pool to end */
448 tox_kill(_p);
449
450 printf("\r[i] Quit!\n");
451 return 0;
452}
453
454/****** AV HELPER FUNCTIONS ******/
455
456/* Auto accept friend request */
457void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata)
458{
459 av_session_t* _phone = _userdata;
460 av_allocate_friend (_phone, -1, 0);
461
462 INFO("Got friend request with message: %s", _data);
463
464 tox_add_friend_norequest(_phone->_messenger, _public_key);
465
466 INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id );
467}
468
469void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata)
470{
471 av_session_t* _phone = _userdata;
472 INFO("Friend no. %d is online", _friendnumber);
473
474 av_friend_t* _this_friend = av_get_friend(_phone, _friendnumber);
475
476 if ( !_this_friend ) {
477 INFO("But it's not registered!");
478 return;
479 }
480
481 (*_this_friend)._active = 1;
482}
483
484int av_add_friend(av_session_t* _phone, char* _friend_hash)
485{
486 trim_spaces(_friend_hash);
487
488 unsigned char *_bin_string = hex_string_to_bin(_friend_hash);
489 int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT, sizeof("Tox phone "_USERAGENT));
490 free(_bin_string);
491
492 if ( _number >= 0) {
493 INFO("Added friend as %d", _number );
494 av_allocate_friend(_phone, _number, 0);
495 }
496 else
497 INFO("Unknown error %i", _number );
498
499 return _number;
500}
501/*********************************/
502
503void do_phone ( av_session_t* _phone )
504{
505 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
506 "Usage: \n"
507 "f [pubkey] (add friend)\n"
508 "c [a/v] (type) [friend] (friend id) (calls friend if online)\n"
509 "h (if call is active hang up)\n"
510 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
511 "r (reject incoming call)\n"
512 "q (quit)\n"
513 "================================================================================"
514 );
515
516 while ( 1 )
517 {
518 char _line [ 1500 ];
519 int _len;
520
521 if ( -1 == getinput(_line, 1500, &_len) ){
522 printf(" >> ");
523 fflush(stdout);
524 continue;
525 }
526
527 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){
528 INFO("Invalid input!");
529 continue;
530 }
531
532 switch (_line[0]){
533
534 case 'f':
535 {
536 char _id [128];
537 strncpy(_id, _line + 2, 128);
538
539 av_add_friend(_phone, _id);
540
541 } break;
542 case 'c':
543 {
544 if ( _phone->_msi->call ){
545 INFO("Already in a call");
546 break;
547 }
548
549 MSICallType _ctype;
550
551 if ( _len < 5 ){
552 INFO("Invalid input; usage: c a/v [friend]");
553 break;
554 }
555 else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
556 _ctype = type_audio;
557 }
558 else { /* video */
559 _ctype = type_video;
560 }
561
562 char* _end;
563 int _friend = strtol(_line + 4, &_end, 10);
564
565 if ( *_end ){
566 INFO("Friend num has to be numerical value");
567 break;
568 }
569
570 /* Set timeout */
571 msi_invite ( _phone->_msi, _ctype, 10 * 1000, _friend );
572 INFO("Calling friend: %d!", _friend);
573
574 } break;
575 case 'h':
576 {
577 if ( !_phone->_msi->call ){
578 INFO("No call!");
579 break;
580 }
581
582 msi_hangup(_phone->_msi);
583
584 INFO("Hung up...");
585
586 } break;
587 case 'a':
588 {
589
590 if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ) {
591 break;
592 }
593
594 if ( _len > 1 && _line[2] == 'v' )
595 msi_answer(_phone->_msi, type_video);
596 else
597 msi_answer(_phone->_msi, type_audio);
598
599 } break;
600 case 'r':
601 {
602 if ( _phone->_msi->call && _phone->_msi->call->state != call_starting ){
603 break;
604 }
605
606 msi_reject(_phone->_msi);
607
608 INFO("Call Rejected...");
609
610 } break;
611 case 'q':
612 {
613 INFO("Quitting!");
614 return;
615 }
616 default:
617 {
618 INFO("Invalid command!");
619 } break;
620
621 }
622
623 }
624}
625
626void* tox_poll (void* _messenger_p)
627{
628 Tox** _messenger = _messenger_p;
629 while( *_messenger ) {
630 tox_do(*_messenger);
631 usleep(10000);
632 }
633
634 pthread_exit(NULL);
635}
636
637int av_wait_dht(av_session_t* _phone, int _wait_seconds, const char* _ip, char* _key, unsigned short _port)
638{
639 if ( !_wait_seconds )
640 return -1;
641
642 int _waited = 0;
643
644 while( !tox_isconnected(_phone->_messenger) ) {
645
646 if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) )
647 {
648 INFO("Could not connect to: %s", _ip);
649 av_terminate_session(_phone);
650 return -1;
651 }
652
653 if ( _waited >= _wait_seconds ) return 0;
654
655 printf(".");
656 fflush(stdout);
657
658 _waited ++;
659 usleep(1000000);
660 }
661
662 int _r = _wait_seconds - _waited;
663 return _r ? _r : 1;
664}
665/* ---------------------- */
666
667int print_help ( const char* _name )
668{
669 printf ( "Usage: %s [IP] [PORT] [KEY]\n"
670 "\t[IP] (DHT ip)\n"
671 "\t[PORT] (DHT port)\n"
672 "\t[KEY] (DHT public key)\n"
673 ,_name );
674 return 1;
675}
676
677int main ( int argc, char* argv [] )
678{
679 if ( argc < 1 || argc < 4 )
680 return print_help(argv[0]);
681
682 char* _convertable;
683
684 int _wait_seconds = 5;
685
686 const char* _ip = argv[1];
687 char* _key = argv[3];
688 unsigned short _port = strtol(argv[2], &_convertable, 10);
689
690 if ( *_convertable ){
691 printf("Invalid port: cannot convert string to long: %s", _convertable);
692 return 1;
693 }
694
695 av_session_t* _phone = av_init_session();
696
697 tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
698 tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
699
700 system("clear");
701
702 INFO("\r================================================================================\n"
703 "[!] Trying dht@%s:%d"
704 , _ip, _port);
705
706 /* Start tox protocol */
707 event.rise( tox_poll, &_phone->_messenger );
708
709 /* Just clean one line */
710 printf("\r \r");
711 fflush(stdout);
712
713 int _r;
714 for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --;
715
716
717 if ( -1 == _r ) {
718 INFO("Error while connecting to dht: %s:%d", _ip, _port);
719 av_terminate_session(_phone);
720 return 1;
721 }
722
723 INFO("CONNECTED!\n"
724 "================================================================================\n"
725 "%s\n"
726 "================================================================================"
727 , _phone->_my_public_id );
728
729
730 do_phone (_phone);
731
732 av_terminate_session(_phone);
733
734 return 0;
735}
diff --git a/toxav/toxmedia.c b/toxav/toxmedia.c
new file mode 100644
index 00000000..aff3cf8c
--- /dev/null
+++ b/toxav/toxmedia.c
@@ -0,0 +1,819 @@
1/* AV_codec.c
2// *
3 * Audio and video codec intitialisation, encoding/decoding and playback
4 *
5 * Copyright (C) 2013 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24/*----------------------------------------------------------------------------------*/
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif /* HAVE_CONFIG_H */
29
30#include <stdio.h>
31#include <math.h>
32#include <libavcodec/avcodec.h>
33#include <libavformat/avformat.h>
34#include <libswscale/swscale.h>
35#include <libavdevice/avdevice.h>
36#include <libavutil/opt.h>
37#include <AL/al.h>
38#include <AL/alc.h>
39#include <SDL/SDL.h>
40#include <SDL/SDL_thread.h>
41#include <pthread.h>
42#include <opus/opus.h>
43
44#include "toxmsi.h"
45#include "toxrtp.h"
46#include "toxmedia.h"
47
48SDL_Surface *screen;
49
50int display_received_frame(codec_state *cs, AVFrame *r_video_frame)
51{
52 AVPicture pict;
53 SDL_LockYUVOverlay(cs->video_picture.bmp);
54
55 pict.data[0] = cs->video_picture.bmp->pixels[0];
56 pict.data[1] = cs->video_picture.bmp->pixels[2];
57 pict.data[2] = cs->video_picture.bmp->pixels[1];
58 pict.linesize[0] = cs->video_picture.bmp->pitches[0];
59 pict.linesize[1] = cs->video_picture.bmp->pitches[2];
60 pict.linesize[2] = cs->video_picture.bmp->pitches[1];
61
62 /* Convert the image into YUV format that SDL uses */
63 sws_scale(cs->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
64 cs->video_decoder_ctx->height, pict.data, pict.linesize );
65
66 SDL_UnlockYUVOverlay(cs->video_picture.bmp);
67 SDL_Rect rect;
68 rect.x = 0;
69 rect.y = 0;
70 rect.w = cs->video_decoder_ctx->width;
71 rect.h = cs->video_decoder_ctx->height;
72 SDL_DisplayYUVOverlay(cs->video_picture.bmp, &rect);
73 return 1;
74}
75
76struct jitter_buffer {
77 RTPMessage **queue;
78 uint16_t capacity;
79 uint16_t size;
80 uint16_t front;
81 uint16_t rear;
82 uint8_t queue_ready;
83 uint16_t current_id;
84 uint32_t current_ts;
85 uint8_t id_set;
86};
87
88struct jitter_buffer *create_queue(int capacity)
89{
90 struct jitter_buffer *q;
91 q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1);
92 q->queue = (RTPMessage **)calloc((sizeof(RTPMessage) * capacity),1);
93 int i = 0;
94
95 for (i = 0; i < capacity; ++i) {
96 q->queue[i] = NULL;
97 }
98
99 q->size = 0;
100 q->capacity = capacity;
101 q->front = 0;
102 q->rear = -1;
103 q->queue_ready = 0;
104 q->current_id = 0;
105 q->current_ts = 0;
106 q->id_set = 0;
107 return q;
108}
109
110/* returns 1 if 'a' has a higher sequence number than 'b' */
111uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b)
112{
113 /* should be stable enough */
114
115 /* TODO: There is already this kind of function in toxrtp.c.
116 * Maybe merge?
117 */
118 return (sn_a > sn_b || ts_a > ts_b);
119}
120
121/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
122RTPMessage *dequeue(struct jitter_buffer *q, int *success)
123{
124 if (q->size == 0 || q->queue_ready == 0) {
125 q->queue_ready = 0;
126 *success = 0;
127 return NULL;
128 }
129
130 int front = q->front;
131
132 if (q->id_set == 0) {
133 q->current_id = q->queue[front]->header->sequnum;
134 q->current_ts = q->queue[front]->header->timestamp;
135 q->id_set = 1;
136 } else {
137 int next_id = q->queue[front]->header->sequnum;
138 int next_ts = q->queue[front]->header->timestamp;
139
140 /* if this packet is indeed the expected packet */
141 if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) {
142 q->current_id = next_id;
143 q->current_ts = next_ts;
144 } else {
145 if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
146 printf("nextid: %d current: %d\n", next_id, q->current_id);
147 q->current_id = (q->current_id + 1) % MAX_SEQU_NUM;
148 *success = 2; /* tell the decoder the packet is lost */
149 return NULL;
150 } else {
151 /* packet too old */
152 printf("packet too old\n");
153 *success = 0;
154 return NULL;
155 }
156 }
157 }
158
159 q->size--;
160 q->front++;
161
162 if (q->front == q->capacity)
163 q->front = 0;
164
165 *success = 1;
166 q->current_id = q->queue[front]->header->sequnum;
167 q->current_ts = q->queue[front]->header->timestamp;
168 return q->queue[front];
169}
170
171int empty_queue(struct jitter_buffer *q)
172{
173 while (q->size > 0) {
174 q->size--;
175 /* FIXME: */
176 /* rtp_free_msg(cs->_rtp_video, q->queue[q->front]); */
177 q->front++;
178
179 if (q->front == q->capacity)
180 q->front = 0;
181 }
182
183 q->id_set = 0;
184 q->queue_ready = 0;
185 return 0;
186}
187
188int queue(struct jitter_buffer *q, RTPMessage *pk)
189{
190 if (q->size == q->capacity) {
191 printf("buffer full, emptying buffer...\n");
192 empty_queue(q);
193 return 0;
194 }
195
196 if (q->size > 8)
197 q->queue_ready = 1;
198
199 ++q->size;
200 ++q->rear;
201
202 if (q->rear == q->capacity)
203 q->rear = 0;
204
205 q->queue[q->rear] = pk;
206
207 int a;
208 int b;
209 int j;
210 a = q->rear;
211
212 for (j = 0; j < q->size - 1; ++j) {
213 b = a - 1;
214
215 if (b < 0)
216 b += q->capacity;
217
218 if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum,
219 q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) {
220 RTPMessage *temp;
221 temp = q->queue[a];
222 q->queue[a] = q->queue[b];
223 q->queue[b] = temp;
224 printf("had to swap\n");
225 } else {
226 break;
227 }
228
229 a -= 1;
230
231 if (a < 0)
232 a += q->capacity;
233 }
234
235 if (pk)
236 return 1;
237
238 return 0;
239}
240
241int init_receive_audio(codec_state *cs)
242{
243 int err = OPUS_OK;
244 cs->audio_decoder = opus_decoder_create(48000, 1, &err);
245 opus_decoder_init(cs->audio_decoder, 48000, 1);
246 printf("init audio decoder successful\n");
247 return 1;
248}
249
250int init_receive_video(codec_state *cs)
251{
252 cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC);
253
254 if (!cs->video_decoder) {
255 printf("init video_decoder failed\n");
256 return 0;
257 }
258
259 cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder);
260
261 if (!cs->video_decoder_ctx) {
262 printf("init video_decoder_ctx failed\n");
263 return 0;
264 }
265
266 if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) {
267 printf("opening video decoder failed\n");
268 return 0;
269 }
270
271 printf("init video decoder successful\n");
272 return 1;
273}
274
275int init_send_video(codec_state *cs)
276{
277 cs->video_input_format = av_find_input_format(VIDEO_DRIVER);
278
279 if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) {
280 printf("opening video_input_format failed\n");
281 return 0;
282 }
283
284 avformat_find_stream_info(cs->video_format_ctx, NULL);
285 av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
286
287 int i;
288
289 for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) {
290 if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
291 cs->video_stream = i;
292 break;
293 }
294 }
295
296 cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec;
297 cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id);
298
299 if (cs->webcam_decoder == NULL) {
300 printf("Unsupported codec\n");
301 return 0;
302 }
303
304 if (cs->webcam_decoder_ctx == NULL) {
305 printf("init webcam_decoder_ctx failed\n");
306 return 0;
307 }
308
309 if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) {
310 printf("opening webcam decoder failed\n");
311 return 0;
312 }
313
314 cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
315
316 if (!cs->video_encoder) {
317 printf("init video_encoder failed\n");
318 return 0;
319 }
320
321 cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
322
323 if (!cs->video_encoder_ctx) {
324 printf("init video_encoder_ctx failed\n");
325 return 0;
326 }
327
328 cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE;
329 cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
330 av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
331 av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
332
333 cs->video_encoder_ctx->thread_count = 4;
334 cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
335 cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6;
336 cs->video_encoder_ctx->profile = 3;
337 cs->video_encoder_ctx->qmax = 54;
338 cs->video_encoder_ctx->qmin = 4;
339 AVRational myrational = {1, 25};
340 cs->video_encoder_ctx->time_base = myrational;
341 cs->video_encoder_ctx->gop_size = 99999;
342 cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P;
343 cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width;
344 cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
345
346 if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
347 printf("opening video encoder failed\n");
348 return 0;
349 }
350
351 printf("init video encoder successful\n");
352 return 1;
353}
354
355int init_send_audio(codec_state *cs)
356{
357 cs->support_send_audio = 0;
358
359 const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
360 int i = 0;
361 const ALchar *device_names[20];
362
363 if (pDeviceList) {
364 printf("\nAvailable Capture Devices are:\n");
365
366 while (*pDeviceList) {
367 device_names[i] = pDeviceList;
368 printf("%d) %s\n", i, device_names[i]);
369 pDeviceList += strlen(pDeviceList) + 1;
370 ++i;
371 }
372 }
373
374 printf("enter capture device number: \n");
375 char dev[2];
376 fgets(dev, sizeof(dev), stdin);
377 cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16,
378 AUDIO_FRAME_SIZE * 4);
379
380 if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) {
381 printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device));
382 return 0;
383 }
384
385 int err = OPUS_OK;
386 cs->audio_bitrate = AUDIO_BITRATE;
387 cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err);
388 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
389 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
390 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
391
392 opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP);
393
394 int nfo;
395 err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo));
396 /* printf("Encoder lookahead delay : %d\n", nfo); */
397 printf("init audio encoder successful\n");
398
399 return 1;
400}
401
402int init_encoder(codec_state *cs)
403{
404 avdevice_register_all();
405 avcodec_register_all();
406 avdevice_register_all();
407 av_register_all();
408
409 pthread_mutex_init(&cs->rtp_msg_mutex_lock, NULL);
410 pthread_mutex_init(&cs->avcodec_mutex_lock, NULL);
411
412 cs->support_send_video = init_send_video(cs);
413 cs->support_send_audio = init_send_audio(cs);
414
415 cs->send_audio = 1;
416 cs->send_video = 1;
417
418 return 1;
419}
420
421int init_decoder(codec_state *cs)
422{
423 avdevice_register_all();
424 avcodec_register_all();
425 avdevice_register_all();
426 av_register_all();
427
428 cs->receive_video = 0;
429 cs->receive_audio = 0;
430
431 cs->support_receive_video = init_receive_video(cs);
432 cs->support_receive_audio = init_receive_audio(cs);
433
434 cs->receive_audio = 1;
435 cs->receive_video = 1;
436
437 return 1;
438}
439
440int video_encoder_refresh(codec_state *cs, int bps)
441{
442 if (cs->video_encoder_ctx)
443 avcodec_close(cs->video_encoder_ctx);
444
445 cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
446
447 if (!cs->video_encoder) {
448 printf("init video_encoder failed\n");
449 return -1;
450 }
451
452 cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
453
454 if (!cs->video_encoder_ctx) {
455 printf("init video_encoder_ctx failed\n");
456 return -1;
457 }
458
459 cs->video_encoder_ctx->bit_rate = bps;
460 cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
461 av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
462 av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
463
464 cs->video_encoder_ctx->thread_count = 4;
465 cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
466 cs->video_encoder_ctx->rc_buffer_size = bps * 6;
467 cs->video_encoder_ctx->profile = 0;
468 cs->video_encoder_ctx->qmax = 54;
469 cs->video_encoder_ctx->qmin = 4;
470 AVRational myrational = {1, 25};
471 cs->video_encoder_ctx->time_base = myrational;
472 cs->video_encoder_ctx->gop_size = 99999;
473 cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P;
474 cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width;
475 cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
476
477 if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
478 printf("opening video encoder failed\n");
479 return -1;
480 }
481 return 0;
482}
483
484void *encode_video_thread(void *arg)
485{
486 codec_state *cs = (codec_state *)arg;
487 AVPacket pkt1, *packet = &pkt1;
488 int p = 0;
489 int err;
490 int got_packet;
491 RTPMessage *s_video_msg;
492 int video_frame_finished;
493 AVFrame *s_video_frame;
494 AVFrame *webcam_frame;
495 s_video_frame = avcodec_alloc_frame();
496 webcam_frame = avcodec_alloc_frame();
497 AVPacket enc_video_packet;
498
499 uint8_t *buffer;
500 int numBytes;
501 /* Determine required buffer size and allocate buffer */
502 numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height);
503 buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1);
504 avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width,
505 cs->webcam_decoder_ctx->height);
506 cs->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height,
507 cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
508 SWS_BILINEAR, NULL, NULL, NULL);
509
510 while (!cs->quit && cs->send_video) {
511
512 if (av_read_frame(cs->video_format_ctx, packet) < 0) {
513 printf("error reading frame\n");
514
515 if (cs->video_format_ctx->pb->error != 0)
516 break;
517
518 continue;
519 }
520
521 if (packet->stream_index == cs->video_stream) {
522 if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
523 printf("couldn't decode\n");
524 continue;
525 }
526
527 av_free_packet(packet);
528 sws_scale(cs->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
529 cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
530 /* create a new I-frame every 60 frames */
531 ++p;
532
533 if (p == 60) {
534
535 s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
536 } else if (p == 61) {
537 s_video_frame->pict_type = AV_PICTURE_TYPE_I ;
538 p = 0;
539 } else {
540 s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
541 }
542
543 if (video_frame_finished) {
544 err = avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet);
545
546 if (err < 0) {
547 printf("could not encode video frame\n");
548 continue;
549 }
550
551 if (!got_packet) {
552 continue;
553 }
554
555 pthread_mutex_lock(&cs->rtp_msg_mutex_lock);
556
557 if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
558
559 if ( 0 > rtp_send_msg ( cs->_rtp_video, cs->_messenger, enc_video_packet.data, enc_video_packet.size) ) {
560 printf("invalid message\n");
561 }
562
563 pthread_mutex_unlock(&cs->rtp_msg_mutex_lock);
564 av_free_packet(&enc_video_packet);
565 }
566 } else {
567 av_free_packet(packet);
568 }
569 }
570
571 /* clean up codecs */
572 pthread_mutex_lock(&cs->avcodec_mutex_lock);
573 av_free(buffer);
574 av_free(webcam_frame);
575 av_free(s_video_frame);
576 sws_freeContext(cs->sws_ctx);
577 avcodec_close(cs->webcam_decoder_ctx);
578 avcodec_close(cs->video_encoder_ctx);
579 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
580 pthread_exit ( NULL );
581}
582
583void *encode_audio_thread(void *arg)
584{
585 codec_state *cs = (codec_state *)arg;
586 RTPMessage *s_audio_msg;
587 unsigned char encoded_data[4096];
588 int encoded_size = 0;
589 int16_t frame[4096];
590 int frame_size = AUDIO_FRAME_SIZE;
591 ALint sample = 0;
592 alcCaptureStart(cs->audio_capture_device);
593
594 while (!cs->quit && cs->send_audio) {
595 alcGetIntegerv(cs->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
596
597 if (sample >= frame_size) {
598 alcCaptureSamples(cs->audio_capture_device, frame, frame_size);
599 encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480);
600
601 if (encoded_size <= 0) {
602 printf("Could not encode audio packet\n");
603 } else {
604 pthread_mutex_lock(&cs->rtp_msg_mutex_lock);
605
606 rtp_send_msg ( cs->_rtp_audio, cs->_messenger, encoded_data, encoded_size );
607
608 pthread_mutex_unlock(&cs->rtp_msg_mutex_lock);
609
610 }
611 } else {
612 usleep(1000);
613 }
614 }
615
616 /* clean up codecs */
617 pthread_mutex_lock(&cs->avcodec_mutex_lock);
618 alcCaptureStop(cs->audio_capture_device);
619 alcCaptureCloseDevice(cs->audio_capture_device);
620
621 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
622 pthread_exit ( NULL );
623}
624
625
626int video_decoder_refresh(codec_state *cs, int width, int height)
627{
628 printf("need to refresh\n");
629 screen = SDL_SetVideoMode(width, height, 0, 0);
630
631 if (cs->video_picture.bmp)
632 SDL_FreeYUVOverlay(cs->video_picture.bmp);
633
634 cs->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
635 cs->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
636 SWS_BILINEAR, NULL, NULL, NULL);
637 return 1;
638}
639
640void *decode_video_thread(void *arg)
641{
642 codec_state *cs = (codec_state *)arg;
643 cs->video_stream = 0;
644 RTPMessage *r_msg;
645 int dec_frame_finished;
646 AVFrame *r_video_frame;
647 r_video_frame = avcodec_alloc_frame();
648 AVPacket dec_video_packet;
649 av_new_packet (&dec_video_packet, 65536);
650 int width = 0;
651 int height = 0;
652
653 while (!cs->quit && cs->receive_video) {
654 r_msg = rtp_recv_msg ( cs->_rtp_video );
655
656 if (r_msg) {
657 memcpy(dec_video_packet.data, r_msg->data, r_msg->length);
658 dec_video_packet.size = r_msg->length;
659 avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
660
661 if (dec_frame_finished) {
662 if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) {
663 width = cs->video_decoder_ctx->width;
664 height = cs->video_decoder_ctx->height;
665 printf("w: %d h%d \n", width, height);
666 video_decoder_refresh(cs, width, height);
667 }
668
669 display_received_frame(cs, r_video_frame);
670 } else {
671 /* TODO: request the sender to create a new i-frame immediatly */
672 printf("bad video packet\n");
673 }
674
675 rtp_free_msg(cs->_rtp_video, r_msg);
676 }
677
678 usleep(1000);
679 }
680
681 printf("vend\n");
682 /* clean up codecs */
683 pthread_mutex_lock(&cs->avcodec_mutex_lock);
684 av_free(r_video_frame);
685 avcodec_close(cs->video_decoder_ctx);
686 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
687 pthread_exit ( NULL );
688}
689
690void *decode_audio_thread(void *arg)
691{
692 codec_state *cs = (codec_state *)arg;
693 RTPMessage *r_msg;
694
695 int frame_size = AUDIO_FRAME_SIZE;
696 int data_size;
697
698 ALCdevice *dev;
699 ALCcontext *ctx;
700 ALuint source, *buffers;
701 dev = alcOpenDevice(NULL);
702 ctx = alcCreateContext(dev, NULL);
703 alcMakeContextCurrent(ctx);
704 int openal_buffers = 5;
705
706 buffers = calloc(sizeof(ALuint) * openal_buffers,1);
707 alGenBuffers(openal_buffers, buffers);
708 alGenSources((ALuint)1, &source);
709 alSourcei(source, AL_LOOPING, AL_FALSE);
710
711 ALuint buffer;
712 ALint val;
713
714 ALenum error;
715 uint16_t zeros[frame_size];
716 int i;
717
718 for (i = 0; i < frame_size; i++) {
719 zeros[i] = 0;
720 }
721
722 for (i = 0; i < openal_buffers; ++i) {
723 alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
724 }
725
726 alSourceQueueBuffers(source, openal_buffers, buffers);
727 alSourcePlay(source);
728
729 if (alGetError() != AL_NO_ERROR) {
730 fprintf(stderr, "Error starting audio\n");
731 cs->quit = 1;
732 }
733
734 struct jitter_buffer *j_buf = NULL;
735
736 j_buf = create_queue(20);
737
738 int success = 0;
739
740 int dec_frame_len;
741
742 opus_int16 PCM[frame_size];
743
744 while (!cs->quit && cs->receive_audio) {
745
746 r_msg = rtp_recv_msg ( cs->_rtp_audio );
747
748 if (r_msg) {
749 /* push the packet into the queue */
750 queue(j_buf, r_msg);
751 }
752
753 /* grab a packet from the queue */
754 success = 0;
755 alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
756
757 if (val > 0)
758 r_msg = dequeue(j_buf, &success);
759
760 if (success > 0) {
761 /* good packet */
762 if (success == 1) {
763 dec_frame_len = opus_decode(cs->audio_decoder, r_msg->data, r_msg->length, PCM, frame_size, 0);
764 rtp_free_msg(cs->_rtp_audio, r_msg);
765 }
766
767 /* lost packet */
768 if (success == 2) {
769 printf("lost packet\n");
770 dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1);
771 }
772
773 if (dec_frame_len > 0) {
774 alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
775
776 if (val <= 0)
777 continue;
778
779 alSourceUnqueueBuffers(source, 1, &buffer);
780 data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1);
781 alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000);
782 int error = alGetError();
783
784 if (error != AL_NO_ERROR) {
785 fprintf(stderr, "Error setting buffer %d\n", error);
786 break;
787 }
788
789 alSourceQueueBuffers(source, 1, &buffer);
790
791 if (alGetError() != AL_NO_ERROR) {
792 fprintf(stderr, "error: could not buffer audio\n");
793 break;
794 }
795
796 alGetSourcei(source, AL_SOURCE_STATE, &val);
797
798 if (val != AL_PLAYING)
799 alSourcePlay(source);
800
801
802 }
803 }
804
805 usleep(1000);
806 }
807
808 /* clean up codecs */
809 pthread_mutex_lock(&cs->avcodec_mutex_lock);
810
811 /* clean up openal */
812 alDeleteSources(1, &source);
813 alDeleteBuffers(openal_buffers, buffers);
814 alcMakeContextCurrent(NULL);
815 alcDestroyContext(ctx);
816 alcCloseDevice(dev);
817 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
818 pthread_exit ( NULL );
819}
diff --git a/toxav/toxmedia.h b/toxav/toxmedia.h
new file mode 100644
index 00000000..aad299bb
--- /dev/null
+++ b/toxav/toxmedia.h
@@ -0,0 +1,165 @@
1/* AV_codec.h
2 *
3 * Audio and video codec intitialisation, encoding/decoding and playback
4 *
5 * Copyright (C) 2013 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24/*----------------------------------------------------------------------------------*/
25#ifndef _AVCODEC_H_
26#define _AVCODEC_H_
27
28#include <stdio.h>
29#include <math.h>
30#include <libavcodec/avcodec.h>
31#include <libavformat/avformat.h>
32#include <libswscale/swscale.h>
33#include <libavdevice/avdevice.h>
34#include <libavutil/opt.h>
35#include <pthread.h>
36#include <AL/al.h>
37#include <AL/alc.h>
38#include "toxrtp.h"
39#include "tox.h"
40
41#include <SDL/SDL.h>
42#include <opus/opus.h>
43
44/* ffmpeg VP8 codec ID */
45#define VIDEO_CODEC AV_CODEC_ID_VP8
46
47/* ffmpeg Opus codec ID */
48#define AUDIO_CODEC AV_CODEC_ID_OPUS
49
50/* default video bitrate in bytes/s */
51#define VIDEO_BITRATE 10*1000
52
53/* default audio bitrate in bytes/s */
54#define AUDIO_BITRATE 64000
55
56/* audio frame duration in miliseconds */
57#define AUDIO_FRAME_DURATION 20
58
59/* audio sample rate recommended to be 48kHz for Opus */
60#define AUDIO_SAMPLE_RATE 48000
61
62/* the amount of samples in one audio frame */
63#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000
64
65/* the quit event for SDL */
66#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
67
68#ifdef __linux__
69#define VIDEO_DRIVER "video4linux2"
70#define DEFAULT_WEBCAM "/dev/video0"
71#endif
72
73#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
74#define VIDEO_DRIVER "vfwcap"
75#define DEFAULT_WEBCAM "0"
76#endif
77
78extern SDL_Surface *screen;
79
80typedef struct {
81 SDL_Overlay *bmp;
82 int width, height;
83} VideoPicture;
84
85
86typedef struct {
87 uint8_t send_audio;
88 uint8_t receive_audio;
89 uint8_t send_video;
90 uint8_t receive_video;
91
92 uint8_t support_send_audio;
93 uint8_t support_send_video;
94 uint8_t support_receive_audio;
95 uint8_t support_receive_video;
96
97 /* video encoding */
98 AVInputFormat *video_input_format;
99 AVFormatContext *video_format_ctx;
100 uint8_t video_stream;
101 AVCodecContext *webcam_decoder_ctx;
102 AVCodec *webcam_decoder;
103 AVCodecContext *video_encoder_ctx;
104 AVCodec *video_encoder;
105
106 /* video decoding */
107 AVCodecContext *video_decoder_ctx;
108 AVCodec *video_decoder;
109
110 /* audio encoding */
111 ALCdevice *audio_capture_device;
112 OpusEncoder *audio_encoder;
113 int audio_bitrate;
114
115 /* audio decoding */
116 OpusDecoder *audio_decoder;
117
118 uint8_t req_video_refresh;
119
120 /* context for converting image format to something SDL can use*/
121 struct SwsContext *sws_SDL_r_ctx;
122
123 /* context for converting webcam image format to something the video encoder can use */
124 struct SwsContext *sws_ctx;
125
126 /* rendered video picture, ready for display */
127 VideoPicture video_picture;
128
129 RTPSession *_rtp_video;
130 RTPSession *_rtp_audio;
131
132 Tox* _messenger;
133
134 pthread_t encode_audio_thread;
135 pthread_t encode_video_thread;
136
137 pthread_t decode_audio_thread;
138 pthread_t decode_video_thread;
139
140 pthread_mutex_t rtp_msg_mutex_lock;
141 pthread_mutex_t avcodec_mutex_lock;
142
143 uint8_t quit;
144 SDL_Event SDL_event;
145
146 MSISession *_msi;
147 uint32_t _frame_rate;
148
149} codec_state;
150
151int display_received_frame(codec_state *cs, AVFrame *r_video_frame);
152int init_receive_audio(codec_state *cs);
153int init_decoder(codec_state *cs);
154int init_send_video(codec_state *cs);
155int init_send_audio(codec_state *cs);
156int init_encoder(codec_state *cs);
157int video_encoder_refresh(codec_state *cs, int bps);
158void *encode_video_thread(void *arg);
159void *encode_audio_thread(void *arg);
160int video_decoder_refresh(codec_state *cs, int width, int height);
161int handle_rtp_video_packet(codec_state *cs, RTPMessage *r_msg);
162void *decode_video_thread(void *arg);
163void *decode_audio_thread(void *arg);
164
165#endif
diff --git a/toxav/toxmsi.c b/toxav/toxmsi.c
new file mode 100755
index 00000000..cf0914ab
--- /dev/null
+++ b/toxav/toxmsi.c
@@ -0,0 +1,1337 @@
1/** toxmsi.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 * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or
22 * my email: eniz_vukovic@hotmail.com
23 */
24
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif /* HAVE_CONFIG_H */
29
30#define _BSD_SOURCE
31
32#include "toxmsi.h"
33#include "../toxcore/util.h"
34#include "../toxcore/network.h"
35#include "../toxcore/event.h"
36#include "../toxcore/Messenger.h"
37
38#include <assert.h>
39#include <unistd.h>
40#include <string.h>
41#include <stdlib.h>
42
43#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
44
45#define MSI_MAXMSG_SIZE 1024
46
47#define TYPE_REQUEST 1
48#define TYPE_RESPONSE 2
49
50#define VERSION_STRING "0.3.1"
51#define VERSION_STRLEN 5
52
53#define CT_AUDIO_HEADER_VALUE "AUDIO"
54#define CT_VIDEO_HEADER_VALUE "VIDEO"
55
56
57/* Define default timeout for a request.
58 * There is no behavior specified by the msi on what will
59 * client do on timeout, but to call timeout callback.
60 */
61#define m_deftout 10000 /* in milliseconds */
62
63/**
64 * Protocol:
65 *
66 * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) |
67 *
68 * ie.
69 *
70 * | 0x1 | 0x0 0x7 | "version"
71 *
72 * Means: it's field value with length of 7 bytes and value of "version"
73 * It's similar to amp protocol
74 */
75
76
77#define GENERIC_HEADER(header) \
78typedef struct _MSIHeader##header { \
79uint8_t* header_value; \
80uint16_t size; \
81} MSIHeader##header;
82
83
84GENERIC_HEADER ( Version )
85GENERIC_HEADER ( Request )
86GENERIC_HEADER ( Response )
87GENERIC_HEADER ( CallType )
88GENERIC_HEADER ( UserAgent )
89GENERIC_HEADER ( CallId )
90GENERIC_HEADER ( Info )
91GENERIC_HEADER ( Reason )
92GENERIC_HEADER ( CryptoKey )
93GENERIC_HEADER ( Nonce )
94
95
96/**
97 * @brief This is the message structure. It contains all of the headers and
98 * destination/source of the message stored in friend_id.
99 *
100 */
101typedef struct _MSIMessage {
102
103 MSIHeaderVersion version;
104 MSIHeaderRequest request;
105 MSIHeaderResponse response;
106 MSIHeaderCallType calltype;
107 MSIHeaderUserAgent useragent;
108 MSIHeaderInfo info;
109 MSIHeaderReason reason;
110 MSIHeaderCallId callid;
111 MSIHeaderCryptoKey cryptokey;
112 MSIHeaderNonce nonce;
113
114 struct _MSIMessage* next;
115
116 int friend_id;
117
118} MSIMessage;
119
120
121
122static MSICallback callbacks[9] = {0};
123
124
125/* define strings for the identifiers */
126#define VERSION_FIELD "Version"
127#define REQUEST_FIELD "Request"
128#define RESPONSE_FIELD "Response"
129#define INFO_FIELD "INFO"
130#define REASON_FIELD "Reason"
131#define CALLTYPE_FIELD "Call-type"
132#define USERAGENT_FIELD "User-agent"
133#define CALLID_FIELD "Call-id"
134#define CRYPTOKEY_FIELD "Crypto-key"
135#define NONCE_FIELD "Nonce"
136
137/* protocol descriptors */
138#define end_byte 0x0
139#define field_byte 0x1
140#define value_byte 0x2
141
142
143typedef enum {
144 invite,
145 start,
146 cancel,
147 reject,
148 end,
149
150} MSIRequest;
151
152
153/**
154 * @brief Get string value for request.
155 *
156 * @param request The request.
157 * @return const uint8_t* The string
158 */
159static inline const uint8_t *stringify_request ( MSIRequest request ) {
160 static const uint8_t* strings[] = {
161 ( uint8_t* ) "INVITE",
162 ( uint8_t* ) "START",
163 ( uint8_t* ) "CANCEL",
164 ( uint8_t* ) "REJECT",
165 ( uint8_t* ) "END"
166 };
167
168 return strings[request];
169}
170
171
172typedef enum {
173 ringing,
174 starting,
175 ending,
176 error
177
178} MSIResponse;
179
180
181/**
182 * @brief Get string value for response.
183 *
184 * @param response The response.
185 * @return const uint8_t* The string
186 */
187static inline const uint8_t *stringify_response ( MSIResponse response ) {
188 static const uint8_t* strings[] = {
189 ( uint8_t* ) "ringing",
190 ( uint8_t* ) "starting",
191 ( uint8_t* ) "ending",
192 ( uint8_t* ) "error"
193 };
194
195 return strings[response];
196}
197
198
199#define ON_HEADER(iterator, header, descriptor, size_const) \
200( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \
201 iterator += size_const; /* Set iterator at begining of value part */ \
202 if ( *iterator != value_byte ) { assert(0); return -1; }\
203 iterator ++;\
204 uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \
205 (uint16_t) *(iterator + 1); \
206 header.header_value = calloc(sizeof(uint8_t), _value_size); \
207 header.size = _value_size; \
208 memcpy(header.header_value, iterator + 2, _value_size);\
209 iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \
210}
211
212/**
213 * @brief Parse raw 'data' received from socket into MSIMessage struct.
214 * Every message has to have end value of 'end_byte' or _undefined_ behavior
215 * occures. The best practice is to check the end of the message at the handle_packet.
216 *
217 * @param msg Container.
218 * @param data The data.
219 * @return int
220 * @retval -1 Error occured.
221 * @retval 0 Success.
222 */
223int parse_raw_data ( MSIMessage* msg, const uint8_t* data ) {
224 assert ( msg );
225
226 const uint8_t* _it = data;
227
228 while ( *_it ) {/* until end_byte is hit */
229
230 if ( *_it == field_byte ) {
231 uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 |
232 ( uint16_t ) * ( _it + 2 );
233
234 _it += 3; /*place it at the field value beginning*/
235
236 switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */
237
238 case 4: { /* INFO header */
239 if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 )
240 }
241 break;
242
243 case 5: { /* NONCE header */
244 if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 )
245 }
246 break;
247
248 case 6: { /* Reason header */
249 if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 )
250 }
251 break;
252
253 case 7: { /* Version, Request, Call-id headers */
254 if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 )
255 else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 )
256 else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 )
257 }
258 break;
259
260 case 8: { /* Response header */
261 if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 )
262 }
263 break;
264
265 case 9: { /* Call-type header */
266 if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 )
267 }
268 break;
269
270 case 10: { /* User-agent, Crypto-key headers */
271 if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 )
272 else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 )
273 }
274 break;
275
276 default:
277 return -1;
278 }
279 } else return -1;
280 /* If it's anything else return failure as the message is invalid */
281
282 }
283
284 return 0;
285}
286
287
288#define ALLOCATE_HEADER( var, mheader_value, t_size) \
289var.header_value = calloc(sizeof *mheader_value, t_size); \
290memcpy(var.header_value, mheader_value, t_size); \
291var.size = t_size;
292
293
294/**
295 * @brief Speaks for it self.
296 *
297 * @param msg The message.
298 * @return void
299 */
300void free_message ( MSIMessage* msg ) {
301 assert ( msg );
302
303 free ( msg->calltype.header_value );
304 free ( msg->request.header_value );
305 free ( msg->response.header_value );
306 free ( msg->useragent.header_value );
307 free ( msg->version.header_value );
308 free ( msg->info.header_value );
309 free ( msg->cryptokey.header_value );
310 free ( msg->nonce.header_value );
311 free ( msg->reason.header_value );
312 free ( msg->callid.header_value );
313
314 free ( msg );
315}
316
317
318/**
319 * @brief Create the message.
320 *
321 * @param type Request or response.
322 * @param type_id Type of request/response.
323 * @return MSIMessage* Created message.
324 * @retval NULL Error occured.
325 */
326MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) {
327 MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 );
328 assert ( _retu );
329
330 memset ( _retu, 0, sizeof ( MSIMessage ) );
331
332 if ( type == TYPE_REQUEST ) {
333 ALLOCATE_HEADER ( _retu->request, type_id, strlen ( type_id ) )
334
335 } else if ( type == TYPE_RESPONSE ) {
336 ALLOCATE_HEADER ( _retu->response, type_id, strlen ( type_id ) )
337
338 } else {
339 free_message ( _retu );
340 return NULL;
341 }
342
343 ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( VERSION_STRING ) )
344
345 return _retu;
346}
347
348
349/**
350 * @brief Parse data from handle_packet.
351 *
352 * @param data The data.
353 * @return MSIMessage* Parsed message.
354 * @retval NULL Error occured.
355 */
356MSIMessage* parse_message ( const uint8_t* data ) {
357 assert ( data );
358
359 MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 );
360 assert ( _retu );
361
362 memset ( _retu, 0, sizeof ( MSIMessage ) );
363
364 if ( parse_raw_data ( _retu, data ) == -1 ) {
365
366 free_message ( _retu );
367 return NULL;
368 }
369
370 if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size ||
371 memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) {
372
373 free_message ( _retu );
374 return NULL;
375 }
376
377 return _retu;
378}
379
380
381
382/**
383 * @brief Speaks for it self.
384 *
385 * @param dest Container.
386 * @param header_field Field.
387 * @param header_value Field value.
388 * @param value_len Length of field value.
389 * @param length Pointer to container length.
390 * @return uint8_t* Iterated container.
391 */
392uint8_t* append_header_to_string (
393 uint8_t* dest,
394 const uint8_t* header_field,
395 const uint8_t* header_value,
396 uint16_t value_len,
397 uint16_t* length )
398{
399 assert ( dest );
400 assert ( header_value );
401 assert ( header_field );
402
403 const uint8_t* _hvit = header_value;
404 uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/
405
406 *dest = field_byte; /* Set the first byte */
407
408 uint8_t* _getback_byte = dest + 1; /* remeber the byte we were on */
409 dest += 3; /* swith to 4th byte where field value starts */
410
411 /* Now set the field value and calculate it's length */
412 uint16_t _i = 0;
413 for ( ; header_field[_i]; ++_i ) {
414 *dest = header_field[_i];
415 ++dest;
416 };
417 _total += _i;
418
419 /* Now set the length of the field byte */
420 *_getback_byte = ( uint8_t ) _i >> 8;
421 _getback_byte++;
422 *_getback_byte = ( uint8_t ) _i;
423
424 /* for value part do it regulary */
425 *dest = value_byte;
426 dest++;
427
428 *dest = ( uint8_t ) value_len >> 8;
429 dest++;
430 *dest = ( uint8_t ) value_len;
431 dest++;
432
433 for ( _i = value_len; _i; --_i ) {
434 *dest = *_hvit;
435 ++_hvit;
436 ++dest;
437 }
438
439 *length += _total;
440 return dest;
441}
442
443
444#define CLEAN_ASSIGN(added, var, field, header)\
445if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); }
446
447
448/**
449 * @brief Convert MSIMessage struct to _sendable_ string.
450 *
451 * @param msg The message.
452 * @param dest Destination.
453 * @return uint16_t It's final size.
454 */
455uint16_t message_to_string ( MSIMessage* msg, uint8_t* dest ) {
456 assert ( msg );
457 assert ( dest );
458
459 uint8_t* _iterated = dest;
460 uint16_t _size = 0;
461
462 CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version );
463 CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request );
464 CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response );
465 CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype );
466 CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent );
467 CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info );
468 CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid );
469 CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason );
470 CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey );
471 CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce );
472
473 *_iterated = end_byte;
474 _size ++;
475
476 return _size;
477}
478
479
480#define GENERIC_SETTER_DEFINITION(header) \
481void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \
482{ assert(_msg); assert(header_value); \
483 free(_msg->header.header_value); \
484 ALLOCATE_HEADER( _msg->header, header_value, _size )}
485
486GENERIC_SETTER_DEFINITION ( calltype )
487GENERIC_SETTER_DEFINITION ( useragent )
488GENERIC_SETTER_DEFINITION ( reason )
489GENERIC_SETTER_DEFINITION ( info )
490GENERIC_SETTER_DEFINITION ( callid )
491GENERIC_SETTER_DEFINITION ( cryptokey )
492GENERIC_SETTER_DEFINITION ( nonce )
493
494
495/**
496 * @brief Generate _random_ alphanumerical string.
497 *
498 * @param str Destination.
499 * @param size Size of string.
500 * @return void
501 */
502void t_randomstr ( uint8_t* str, size_t size ) {
503 assert ( str );
504
505 static const uint8_t _bytes[] =
506 "0123456789"
507 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
508 "abcdefghijklmnopqrstuvwxyz";
509
510 int _it = 0;
511
512 for ( ; _it < size; _it++ ) {
513 str[_it] = _bytes[ randombytes_random() % 61 ];
514 }
515}
516
517
518typedef enum {
519 error_deadcall = 1, /* has call id but it's from old call */
520 error_id_mismatch, /* non-existing call */
521
522 error_no_callid, /* not having call id */
523 error_no_call, /* no call in session */
524 error_no_crypto_key, /* no crypto key */
525
526 error_busy,
527
528} MSICallError; /* Error codes */
529
530
531/**
532 * @brief Stringify error code.
533 *
534 * @param error_code The code.
535 * @return const uint8_t* The string.
536 */
537static inline const uint8_t *stringify_error ( MSICallError error_code ) {
538 static const uint8_t* strings[] = {
539 ( uint8_t* ) "",
540 ( uint8_t* ) "Using dead call",
541 ( uint8_t* ) "Call id not set to any call",
542 ( uint8_t* ) "Call id not available",
543 ( uint8_t* ) "No active call in session",
544 ( uint8_t* ) "No Crypto-key set",
545 ( uint8_t* ) "Callee busy"
546 };
547
548 return strings[error_code];
549}
550
551
552/**
553 * @brief Convert error_code into string.
554 *
555 * @param error_code The code.
556 * @return const uint8_t* The string.
557 */
558static inline const uint8_t *stringify_error_code ( MSICallError error_code ) {
559 static const uint8_t* strings[] = {
560 ( uint8_t* ) "",
561 ( uint8_t* ) "1",
562 ( uint8_t* ) "2",
563 ( uint8_t* ) "3",
564 ( uint8_t* ) "4",
565 ( uint8_t* ) "5",
566 ( uint8_t* ) "6"
567 };
568
569 return strings[error_code];
570}
571
572
573/**
574 * @brief Speaks for it self.
575 *
576 * @param session Control session.
577 * @param msg The message.
578 * @param to Where to.
579 * @return int
580 * @retval -1 Error occured.
581 * @retval 0 Success.
582 */
583int send_message ( MSISession* session, MSIMessage* msg, uint32_t to )
584{
585 msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN );
586
587 uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
588 uint16_t _length = message_to_string ( msg, _msg_string_final );
589
590 return m_msi_packet((struct Messenger*) session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
591}
592
593
594/**
595 * @brief Speaks for it self.
596 *
597 * @param session Control session.
598 * @param msg The message.
599 * @param peer_id The peer.
600 * @return void
601 */
602void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) {
603 if ( msg->calltype.header_value ) {
604 if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
605 session->call->type_peer[peer_id] = type_audio;
606
607 } else if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
608 session->call->type_peer[peer_id] = type_video;
609 } else {} /* Error */
610 } else {} /* Error */
611}
612
613
614
615/**
616 * @brief Sends error response to peer.
617 *
618 * @param session The session.
619 * @param errid The id.
620 * @param to Where to?
621 * @return int
622 * @retval 0 It's always success.
623 */
624int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) {
625 MSIMessage* _msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) );
626
627 const uint8_t* _error_code_str = stringify_error_code ( errid );
628
629 msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char* ) _error_code_str ) );
630 send_message ( session, _msg_error, to );
631 free_message ( _msg_error );
632
633 session->last_error_id = errid;
634 session->last_error_str = stringify_error ( errid );
635
636 event.rise ( callbacks[cb_error], session );
637
638 return 0;
639}
640
641
642/**
643 * @brief Determine the error if any.
644 *
645 * @param session Control session.
646 * @param msg The message.
647 * @return int
648 * @retval -1 No error.
649 * @retval 0 Error occured and response sent.
650 */
651int has_call_error ( MSISession* session, MSIMessage* msg ) {
652 if ( !msg->callid.header_value ) {
653 return handle_error ( session, error_no_callid, msg->friend_id );
654
655 } else if ( !session->call ) {
656 return handle_error ( session, error_no_call, msg->friend_id );
657
658 } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) {
659 return handle_error ( session, error_id_mismatch, msg->friend_id );
660
661 }
662
663 return -1;
664}
665
666
667/**
668 * @brief Function called at request timeout.
669 *
670 * @param arg Control session
671 * @return void*
672 */
673void* handle_timeout ( void* arg )
674{
675 /* Send hangup either way */
676 MSISession* _session = arg;
677
678 uint32_t* _peers = _session->call->peers;
679 uint16_t _peer_count = _session->call->peer_count;
680
681
682 /* Cancel all? */
683 uint16_t _it = 0;
684 for ( ; _it < _peer_count; _it++ )
685 msi_cancel ( arg, _peers[_it] );
686
687
688 ( *callbacks[cb_timeout] ) ( arg );
689 ( *callbacks[cb_ending ] ) ( arg );
690
691 return NULL;
692}
693
694
695/**
696 * @brief Add peer to peer list.
697 *
698 * @param call What call.
699 * @param peer_id Its id.
700 * @return void
701 */
702void add_peer( MSICall* call, int peer_id )
703{
704 if ( !call->peers ) {
705 call->peers = calloc(sizeof(int), 1);
706 call->peer_count = 1;
707 } else{
708 call->peer_count ++;
709 call->peers = realloc( call->peers, sizeof(int) * call->peer_count);
710 }
711
712 call->peers[call->peer_count - 1] = peer_id;
713}
714
715
716/**
717 * @brief BASIC call flow:
718 *
719 * ALICE BOB
720 * | invite --> |
721 * | |
722 * | <-- ringing |
723 * | |
724 * | <-- starting |
725 * | |
726 * | start --> |
727 * | |
728 * | <-- MEDIA TRANS --> |
729 * | |
730 * | end --> |
731 * | |
732 * | <-- ending |
733 *
734 * Alice calls Bob by sending invite packet.
735 * Bob recvs the packet and sends an ringing packet;
736 * which notifies Alice that her invite is acknowledged.
737 * Ringing screen shown on both sides.
738 * Bob accepts the invite for a call by sending starting packet.
739 * Alice recvs the starting packet and sends the started packet to
740 * inform Bob that she recved the starting packet.
741 * Now the media transmission is established ( i.e. RTP transmission ).
742 * Alice hangs up and sends end packet.
743 * Bob recves the end packet and sends ending packet
744 * as the acknowledgement that the call is ending.
745 *
746 *
747 */
748void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object )
749{
750 MSISession* _session = object;
751 MSIMessage* _msg;
752
753 _msg = parse_message ( data );
754
755 if ( !_msg ) return;
756
757 _msg->friend_id = source;
758
759
760 /* Now handle message */
761
762 if ( _msg->request.header_value ) { /* Handle request */
763
764 const uint8_t* _request_value = _msg->request.header_value;
765
766 if ( same ( _request_value, stringify_request ( invite ) ) ) {
767 handle_recv_invite ( _session, _msg );
768
769 } else if ( same ( _request_value, stringify_request ( start ) ) ) {
770 handle_recv_start ( _session, _msg );
771
772 } else if ( same ( _request_value, stringify_request ( cancel ) ) ) {
773 handle_recv_cancel ( _session, _msg );
774
775 } else if ( same ( _request_value, stringify_request ( reject ) ) ) {
776 handle_recv_reject ( _session, _msg );
777
778 } else if ( same ( _request_value, stringify_request ( end ) ) ) {
779 handle_recv_end ( _session, _msg );
780 }
781
782 else {
783 free_message ( _msg );
784 return;
785 }
786
787 } else if ( _msg->response.header_value ) { /* Handle response */
788
789 const uint8_t* _response_value = _msg->response.header_value;
790
791 if ( same ( _response_value, stringify_response ( ringing ) ) ) {
792 handle_recv_ringing ( _session, _msg );
793
794 } else if ( same ( _response_value, stringify_response ( starting ) ) ) {
795 handle_recv_starting ( _session, _msg );
796
797 } else if ( same ( _response_value, stringify_response ( ending ) ) ) {
798 handle_recv_ending ( _session, _msg );
799
800 } else if ( same ( _response_value, stringify_response ( error ) ) ) {
801 handle_recv_error ( _session, _msg );
802 } else {
803 free_message ( _msg );
804 return;
805 }
806
807 /* Got response so cancel timer */
808 if ( _session->call )
809 event.timer_release ( _session->call->request_timer_id );
810
811 }
812
813 free_message ( _msg );
814}
815
816
817/**
818 * @brief Speaks for it self.
819 *
820 * @param session Control session.
821 * @param peers Amount of peers. (Currently it only supports 1)
822 * @param ringing_timeout Ringing timeout.
823 * @return MSICall* The created call.
824 */
825MSICall* init_call ( MSISession* session, int peers, int ringing_timeout ) {
826 assert ( session );
827 assert ( peers );
828
829 MSICall* _call = calloc ( sizeof ( MSICall ), 1 );
830 _call->type_peer = calloc ( sizeof ( MSICallType ), peers );
831
832 assert ( _call );
833 assert ( _call->type_peer );
834
835 /*_call->_participant_count = _peers;*/
836
837 _call->request_timer_id = 0;
838 _call->ringing_timer_id = 0;
839
840 _call->key_local = NULL;
841 _call->key_peer = NULL;
842 _call->nonce_local = NULL;
843 _call->nonce_peer = NULL;
844
845 _call->ringing_tout_ms = ringing_timeout;
846
847 pthread_mutex_init ( &_call->mutex, NULL );
848
849 return _call;
850}
851
852
853/**
854 * @brief Terminate the call.
855 *
856 * @param session Control session.
857 * @return int
858 * @retval -1 Error occured.
859 * @retval 0 Success.
860 */
861int terminate_call ( MSISession* session ) {
862 assert ( session );
863
864 if ( !session->call )
865 return -1;
866
867
868 /* Check event loop and cancel timed events if there are any
869 * Notice: This has to be done before possibly
870 * locking the mutex the second time
871 */
872 event.timer_release ( session->call->request_timer_id );
873 event.timer_release ( session->call->ringing_timer_id );
874
875 /* Get a handle */
876 pthread_mutex_lock ( &session->call->mutex );
877
878 MSICall* _call = session->call;
879 session->call = NULL;
880
881 free ( _call->type_peer );
882 free ( _call->key_local );
883 free ( _call->key_peer );
884 free ( _call->peers);
885
886 /* Release handle */
887 pthread_mutex_unlock ( &_call->mutex );
888
889 pthread_mutex_destroy ( &_call->mutex );
890
891 free ( _call );
892
893 return 0;
894}
895
896
897/********** Request handlers **********/
898int handle_recv_invite ( MSISession* session, MSIMessage* msg ) {
899 assert ( session );
900
901 if ( session->call ) {
902 handle_error ( session, error_busy, msg->friend_id );
903 return 0;
904 }
905 if ( !msg->callid.header_value ) {
906 handle_error ( session, error_no_callid, msg->friend_id );
907 return 0;
908 }
909
910 session->call = init_call ( session, 1, 0 );
911 memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN );
912 session->call->state = call_starting;
913
914 add_peer( session->call, msg->friend_id);
915
916 flush_peer_type ( session, msg, 0 );
917
918 MSIMessage* _msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) );
919 send_message ( session, _msg_ringing, msg->friend_id );
920 free_message ( _msg_ringing );
921
922 event.rise ( callbacks[cb_oninvite], session );
923
924 return 1;
925}
926int handle_recv_start ( MSISession* session, MSIMessage* msg ) {
927 assert ( session );
928
929 if ( has_call_error ( session, msg ) == 0 )
930 return 0;
931
932 if ( !msg->cryptokey.header_value )
933 return handle_error ( session, error_no_crypto_key, msg->friend_id );
934
935 session->call->state = call_active;
936
937 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
938 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
939
940 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
941 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
942
943 flush_peer_type ( session, msg, 0 );
944
945 event.rise ( callbacks[cb_onstart], session );
946
947 return 1;
948}
949int handle_recv_reject ( MSISession* session, MSIMessage* msg ) {
950 assert ( session );
951
952 if ( has_call_error ( session, msg ) == 0 )
953 return 0;
954
955
956 MSIMessage* _msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
957 send_message ( session, _msg_end, msg->friend_id );
958 free_message ( _msg_end );
959
960 event.timer_release ( session->call->request_timer_id );
961 event.rise ( callbacks[cb_onreject], session );
962 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
963
964 return 1;
965}
966int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) {
967 assert ( session );
968
969 if ( has_call_error ( session, msg ) == 0 )
970 return 0;
971
972
973 terminate_call ( session );
974
975 event.rise ( callbacks[cb_oncancel], session );
976
977 return 1;
978}
979int handle_recv_end ( MSISession* session, MSIMessage* msg ) {
980 assert ( session );
981
982 if ( has_call_error ( session, msg ) == 0 )
983 return 0;
984
985
986 MSIMessage* _msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) );
987 send_message ( session, _msg_ending, msg->friend_id );
988 free_message ( _msg_ending );
989
990 terminate_call ( session );
991
992 event.rise ( callbacks[cb_onend], session );
993
994 return 1;
995}
996
997/********** Response handlers **********/
998int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) {
999 assert ( session );
1000
1001 if ( has_call_error ( session, msg ) == 0 )
1002 return 0;
1003
1004 session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
1005 event.rise ( callbacks[cb_ringing], session );
1006
1007 return 1;
1008}
1009int handle_recv_starting ( MSISession* session, MSIMessage* msg ) {
1010 assert ( session );
1011
1012 if ( has_call_error ( session, msg ) == 0 )
1013 return 0;
1014
1015 if ( !msg->cryptokey.header_value ) {
1016 return handle_error ( session, error_no_crypto_key, msg->friend_id );
1017 }
1018
1019 /* Generate local key/nonce to send */
1020 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1021 new_symmetric_key ( session->call->key_local );
1022
1023 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1024 new_nonce ( session->call->nonce_local );
1025
1026 /* Save peer key/nonce */
1027 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1028 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
1029
1030 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1031 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
1032
1033 session->call->state = call_active;
1034
1035 MSIMessage* _msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) );
1036 msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES );
1037 msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES );
1038 send_message ( session, _msg_start, msg->friend_id );
1039 free_message ( _msg_start );
1040
1041 flush_peer_type ( session, msg, 0 );
1042
1043 event.rise ( callbacks[cb_starting], session );
1044 event.timer_release ( session->call->ringing_timer_id );
1045
1046 return 1;
1047}
1048int handle_recv_ending ( MSISession* session, MSIMessage* msg ) {
1049 assert ( session );
1050
1051 if ( has_call_error ( session, msg ) == 0 )
1052 return 0;
1053
1054
1055 terminate_call ( session );
1056
1057 event.rise ( callbacks[cb_ending], session );
1058
1059 return 1;
1060}
1061int handle_recv_error ( MSISession* session, MSIMessage* msg ) {
1062 assert ( session );
1063 assert ( session->call );
1064
1065 /* Handle error accordingly */
1066 if ( msg->reason.header_value ) {
1067 session->last_error_id = atoi ( ( const char* ) msg->reason.header_value );
1068 session->last_error_str = stringify_error ( session->last_error_id );
1069 }
1070
1071 terminate_call ( session );
1072
1073 event.rise ( callbacks[cb_ending], session );
1074
1075 return 1;
1076}
1077
1078
1079/********************************************************************************************************************
1080 * *******************************************************************************************************************
1081 ********************************************************************************************************************
1082 ********************************************************************************************************************
1083 ********************************************************************************************************************
1084 *
1085 *
1086 *
1087 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
1088 *
1089 *
1090 *
1091 ********************************************************************************************************************
1092 ********************************************************************************************************************
1093 ********************************************************************************************************************
1094 ********************************************************************************************************************
1095 ********************************************************************************************************************/
1096
1097
1098
1099
1100
1101
1102
1103
1104/**
1105 * @brief Callback setter.
1106 *
1107 * @param callback The callback.
1108 * @param id The id.
1109 * @return void
1110 */
1111void msi_register_callback ( MSICallback callback, MSICallbackID id )
1112{
1113 callbacks[id] = callback;
1114}
1115
1116
1117/**
1118 * @brief Start the control session.
1119 *
1120 * @param messenger Tox* object.
1121 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
1122 * @return MSISession* The created session.
1123 * @retval NULL Error occured.
1124 */
1125MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent ) {
1126 assert ( messenger );
1127 assert ( user_agent );
1128
1129 MSISession* _retu = calloc ( sizeof ( MSISession ), 1 );
1130 assert ( _retu );
1131
1132 _retu->user_agent = user_agent;
1133 _retu->messenger_handle = messenger;
1134 _retu->agent_handler = NULL;
1135
1136 _retu->call = NULL;
1137
1138 _retu->frequ = 10000; /* default value? */
1139 _retu->call_timeout = 30000; /* default value? */
1140
1141
1142 m_callback_msi_packet((struct Messenger*) messenger, msi_handle_packet, _retu );
1143
1144
1145 return _retu;
1146}
1147
1148
1149/**
1150 * @brief Terminate control session.
1151 *
1152 * @param session The session
1153 * @return int
1154 */
1155int msi_terminate_session ( MSISession* session ) {
1156 assert ( session );
1157
1158 int _status = 0;
1159
1160 terminate_call ( session );
1161
1162 /* TODO: Clean it up more? */
1163
1164 free ( session );
1165 return _status;
1166}
1167
1168
1169/**
1170 * @brief Send invite request to friend_id.
1171 *
1172 * @param session Control session.
1173 * @param call_type Type of the call. Audio or Video(both audio and video)
1174 * @param rngsec Ringing timeout.
1175 * @param friend_id The friend.
1176 * @return int
1177 */
1178int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ) {
1179 assert ( session );
1180
1181 MSIMessage* _msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) );
1182
1183 session->call = init_call ( session, 1, rngsec ); /* Just one for now */
1184 t_randomstr ( session->call->id, CALL_ID_LEN );
1185
1186 add_peer(session->call, friend_id );
1187
1188 session->call->type_local = call_type;
1189 /* Do whatever with message */
1190
1191 if ( call_type == type_audio ) {
1192 msi_msg_set_calltype
1193 ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1194 } else {
1195 msi_msg_set_calltype
1196 ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1197 }
1198
1199 send_message ( session, _msg_invite, friend_id );
1200 free_message ( _msg_invite );
1201
1202 session->call->state = call_inviting;
1203
1204 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1205
1206 return 0;
1207}
1208
1209
1210/**
1211 * @brief Hangup active call.
1212 *
1213 * @param session Control session.
1214 * @return int
1215 * @retval -1 Error occured.
1216 * @retval 0 Success.
1217 */
1218int msi_hangup ( MSISession* session ) {
1219 assert ( session );
1220
1221 if ( !session->call && session->call->state != call_active )
1222 return -1;
1223
1224 MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
1225
1226 /* hangup for each peer */
1227 int _it = 0;
1228 for ( ; _it < session->call->peer_count; _it ++ )
1229 send_message ( session, _msg_ending, session->call->peers[_it] );
1230
1231
1232 free_message ( _msg_ending );
1233
1234 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1235
1236 return 0;
1237}
1238
1239
1240/**
1241 * @brief Answer active call request.
1242 *
1243 * @param session Control session.
1244 * @param call_type Answer with Audio or Video(both).
1245 * @return int
1246 */
1247int msi_answer ( MSISession* session, MSICallType call_type ) {
1248 assert ( session );
1249
1250 MSIMessage* _msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) );
1251 session->call->type_local = call_type;
1252
1253 if ( call_type == type_audio ) {
1254 msi_msg_set_calltype
1255 ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1256 } else {
1257 msi_msg_set_calltype
1258 ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1259 }
1260
1261 /* Now set the local encryption key and pass it with STARTING message */
1262
1263 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1264 new_symmetric_key ( session->call->key_local );
1265
1266 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1267 new_nonce ( session->call->nonce_local );
1268
1269 msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES );
1270 msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES );
1271
1272 send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] );
1273 free_message ( _msg_starting );
1274
1275 session->call->state = call_active;
1276
1277 return 0;
1278}
1279
1280
1281/**
1282 * @brief Cancel request.
1283 *
1284 * @param session Control session.
1285 * @param friend_id The friend.
1286 * @return int
1287 */
1288int msi_cancel ( MSISession* session, int friend_id ) {
1289 assert ( session );
1290
1291 MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
1292 send_message ( session, _msg_cancel, friend_id );
1293 free_message ( _msg_cancel );
1294
1295 terminate_call ( session );
1296
1297 return 0;
1298}
1299
1300
1301/**
1302 * @brief Reject request.
1303 *
1304 * @param session Control session.
1305 * @return int
1306 */
1307int msi_reject ( MSISession* session ) {
1308 assert ( session );
1309
1310 MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) );
1311 send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] );
1312 free_message ( _msg_reject );
1313
1314 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1315
1316 return 0;
1317}
1318
1319
1320/**
1321 * @brief Terminate the current call.
1322 *
1323 * @param session Control session.
1324 * @return int
1325 */
1326int msi_stopcall ( MSISession* session ) {
1327 assert ( session );
1328
1329 if ( !session->call )
1330 return -1;
1331
1332 /* just terminate it */
1333
1334 terminate_call ( session );
1335
1336 return 0;
1337} \ No newline at end of file
diff --git a/toxav/toxmsi.h b/toxav/toxmsi.h
new file mode 100755
index 00000000..c45662a6
--- /dev/null
+++ b/toxav/toxmsi.h
@@ -0,0 +1,231 @@
1/** toxmsi.h
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 * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or
22 * my email: eniz_vukovic@hotmail.com
23 */
24
25#ifndef __TOXMSI
26#define __TOXMSI
27
28#include <inttypes.h>
29#include "../toxcore/tox.h"
30#include <pthread.h>
31
32/* define size for call_id */
33#define CALL_ID_LEN 12
34
35
36typedef void* ( *MSICallback ) ( void* arg );
37
38
39/**
40 * @brief Call type identifier. Also used as rtp callback prefix.
41 */
42typedef enum {
43 type_audio = 70,
44 type_video,
45} MSICallType;
46
47
48/**
49 * @brief Call state identifiers.
50 */
51typedef enum {
52 call_inviting, /* when sending call invite */
53 call_starting, /* when getting call invite */
54 call_active,
55 call_hold
56
57} MSICallState;
58
59
60
61/**
62 * @brief The call struct.
63 *
64 */
65typedef struct _MSICall { /* Call info structure */
66 MSICallState state;
67
68 MSICallType type_local; /* Type of payload user is ending */
69 MSICallType* type_peer; /* Type of payload others are sending */
70
71 uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */
72
73 uint8_t* key_local; /* The key for encryption */
74 uint8_t* key_peer; /* The key for decryption */
75
76 uint8_t* nonce_local; /* Local nonce */
77 uint8_t* nonce_peer; /* Peer nonce */
78
79 int ringing_tout_ms; /* Ringing timeout in ms */
80
81 int request_timer_id; /* Timer id for outgoing request/action */
82 int ringing_timer_id; /* Timer id for ringing timeout */
83
84 pthread_mutex_t mutex; /* It's to be assumed that call will have
85 * seperate thread so add mutex
86 */
87 uint32_t* peers;
88 uint16_t peer_count;
89
90
91} MSICall;
92
93
94/**
95 * @brief Control session struct
96 *
97 */
98typedef struct _MSISession {
99
100 /* Call handler */
101 struct _MSICall* call;
102
103 int last_error_id; /* Determine the last error */
104 const uint8_t* last_error_str;
105
106 const uint8_t* user_agent;
107
108 void* agent_handler; /* Pointer to an object that is handling msi */
109 Tox* messenger_handle;
110
111 uint32_t frequ;
112 uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
113
114
115} MSISession;
116
117
118/**
119 * @brief Callbacks ids that handle the states
120 */
121typedef enum {
122 /* Requests */
123 cb_oninvite,
124 cb_onstart,
125 cb_oncancel,
126 cb_onreject,
127 cb_onend,
128
129 /* Responses */
130 cb_ringing,
131 cb_starting,
132 cb_ending,
133
134 /* Protocol */
135 cb_error,
136 cb_timeout,
137
138} MSICallbackID;
139
140
141/**
142 * @brief Callback setter.
143 *
144 * @param callback The callback.
145 * @param id The id.
146 * @return void
147 */
148void msi_register_callback(MSICallback callback, MSICallbackID id);
149
150
151/**
152 * @brief Start the control session.
153 *
154 * @param messenger Tox* object.
155 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
156 * @return MSISession* The created session.
157 * @retval NULL Error occured.
158 */
159MSISession* msi_init_session ( Tox* messenger, const uint8_t* user_agent );
160
161
162/**
163 * @brief Terminate control session.
164 *
165 * @param session The session
166 * @return int
167 */
168int msi_terminate_session ( MSISession* session );
169
170
171/**
172 * @brief Send invite request to friend_id.
173 *
174 * @param session Control session.
175 * @param call_type Type of the call. Audio or Video(both audio and video)
176 * @param rngsec Ringing timeout.
177 * @param friend_id The friend.
178 * @return int
179 */
180int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id );
181
182
183/**
184 * @brief Hangup active call.
185 *
186 * @param session Control session.
187 * @return int
188 * @retval -1 Error occured.
189 * @retval 0 Success.
190 */
191int msi_hangup ( MSISession* session );
192
193
194/**
195 * @brief Answer active call request.
196 *
197 * @param session Control session.
198 * @param call_type Answer with Audio or Video(both).
199 * @return int
200 */
201int msi_answer ( MSISession* session, MSICallType call_type );
202
203
204/**
205 * @brief Cancel request.
206 *
207 * @param session Control session.
208 * @param friend_id The friend.
209 * @return int
210 */
211int msi_cancel ( MSISession* session, int friend_id );
212
213
214/**
215 * @brief Reject request.
216 *
217 * @param session Control session.
218 * @return int
219 */
220int msi_reject ( MSISession* session );
221
222
223/**
224 * @brief Terminate the current call.
225 *
226 * @param session Control session.
227 * @return int
228 */
229int msi_stopcall ( MSISession* session );
230
231#endif /* __TOXMSI */
diff --git a/toxav/toxrtp.c b/toxav/toxrtp.c
new file mode 100755
index 00000000..03d20363
--- /dev/null
+++ b/toxav/toxrtp.c
@@ -0,0 +1,876 @@
1/** toxrtp.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 * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or
22 * my email: eniz_vukovic@hotmail.com
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include "toxrtp.h"
30#include <assert.h>
31#include <limits.h>
32#include <pthread.h>
33
34#include "../toxcore/util.h"
35#include "../toxcore/network.h"
36#include "../toxcore/net_crypto.h"
37#include "../toxcore/Messenger.h"
38
39#define PAYLOAD_ID_VALUE_OPUS 1
40#define PAYLOAD_ID_VALUE_VP8 2
41
42#define size_32 4
43
44#define inline__ inline __attribute__((always_inline))
45
46
47#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
48#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
49#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
50#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
51#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
52#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
53
54#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
55#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
56#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
57#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
58#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
59#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
60
61
62
63/**
64 * @brief Checks if message came in late.
65 *
66 * @param session Control session.
67 * @param msg The message.
68 * @return int
69 * @retval -1 The message came in order.
70 * @retval 0 The message came late.
71 */
72inline__ int check_late_message (RTPSession* session, RTPMessage* msg)
73{
74 /*
75 * Check Sequence number. If this new msg has lesser number then the session->rsequnum
76 * it shows that the message came in late. Also check timestamp to be 100% certain.
77 *
78 */
79 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
80}
81
82
83/**
84 * @brief Increases nonce value by 'target'
85 *
86 * @param nonce The nonce
87 * @param target The target
88 * @return void
89 */
90inline__ void increase_nonce(uint8_t* nonce, uint16_t target)
91{
92 uint16_t _nonce_counter = ((uint16_t)(
93 (((uint16_t) nonce [crypto_box_NONCEBYTES - 1]) << 8 ) |
94 (((uint16_t) nonce [crypto_box_NONCEBYTES - 2]) )));
95
96 /* Check overflow */
97 if (_nonce_counter > USHRT_MAX - target ) { /* 2 bytes are not long enough */
98 int _it = 3;
99 while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1;
100
101 _nonce_counter = _nonce_counter - (USHRT_MAX - target ); /* Assign the rest of it */
102 } else { /* Increase nonce */
103
104 _nonce_counter+= target;
105 }
106
107 /* Assign the 8 last bytes */
108
109 nonce [crypto_box_NONCEBYTES - 1] = (uint8_t) (_nonce_counter >> 8);
110 nonce [crypto_box_NONCEBYTES - 2] = (uint8_t) (_nonce_counter);
111}
112
113
114/**
115 * @brief Speaks for it self.
116 *
117 */
118static const uint32_t payload_table[] =
119{
120 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */
121 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */
122 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */
123 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */
127 PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
132 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */
133};
134
135
136/**
137 * @brief Extracts header from payload.
138 *
139 * @param payload The payload.
140 * @param length The size of payload.
141 * @return RTPHeader* Extracted header.
142 * @retval NULL Error occurred while extracting header.
143 */
144RTPHeader* extract_header ( const uint8_t* payload, size_t length )
145{
146 if ( !payload ) {
147 return NULL;
148 }
149
150 const uint8_t* _it = payload;
151
152 RTPHeader* _retu = calloc(sizeof(RTPHeader), 1);
153 assert(_retu);
154
155 _retu->flags = *_it; ++_it;
156
157 /* This indicates if the first 2 bytes are valid.
158 * Now it my happen that this is out of order but
159 * it cuts down chances of parsing some invalid value
160 */
161 if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){
162 /* Deallocate */
163 free(_retu);
164 return NULL;
165 }
166
167 /*
168 * Added a check for the size of the header little sooner so
169 * I don't need to parse the other stuff if it's bad
170 */
171 uint8_t _cc = GET_FLAG_CSRCC ( _retu );
172 uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 );
173
174 if ( length < _length ) {
175 /* Deallocate */
176 free(_retu);
177 return NULL;
178 }
179
180 if ( _cc > 0 ) {
181 _retu->csrc = calloc ( sizeof ( uint32_t ), _cc );
182 assert(_retu->csrc);
183
184 } else { /* But this should not happen ever */
185 /* Deallocate */
186 free(_retu);
187 return NULL;
188 }
189
190
191 _retu->marker_payloadt = *_it; ++_it;
192 _retu->length = _length;
193
194 _retu->timestamp = ( ( uint32_t ) * _it << 24 ) |
195 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
196 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
197 ( * ( _it + 3 ) ) ;
198
199 _it += 4;
200
201 _retu->ssrc = ( ( uint32_t ) * _it << 24 ) |
202 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
203 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
204 ( ( uint32_t ) * ( _it + 3 ) ) ;
205
206
207 size_t _x;
208 for ( _x = 0; _x < _cc; _x++ ) {
209 _it += 4;
210 _retu->csrc[_x] = ( ( uint32_t ) * _it << 24 ) |
211 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
212 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
213 ( ( uint32_t ) * ( _it + 3 ) ) ;
214 }
215
216 return _retu;
217}
218
219/**
220 * @brief Extracts external header from payload. Must be called AFTER extract_header()!
221 *
222 * @param payload The ITERATED payload.
223 * @param length The size of payload.
224 * @return RTPExtHeader* Extracted extension header.
225 * @retval NULL Error occurred while extracting extension header.
226 */
227RTPExtHeader* extract_ext_header ( const uint8_t* payload, size_t length )
228{
229 const uint8_t* _it = payload;
230
231 RTPExtHeader* _retu = calloc(sizeof(RTPExtHeader), 1);
232 assert(_retu);
233
234 uint16_t _ext_length = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2;
235
236 if ( length < ( _ext_length * sizeof(uint32_t) ) ) {
237 return NULL;
238 }
239
240 _retu->length = _ext_length;
241 _retu->type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2;
242
243 _retu->table = calloc(sizeof(uint32_t), _ext_length );
244 assert(_retu->table);
245
246 uint32_t* _table = _retu->table;
247 size_t _i;
248 for ( _i = 0; _i < _ext_length; _i++ ) {
249 _it += 4;
250 _table[_i] = ( ( uint32_t ) * _it << 24 ) |
251 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
252 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
253 ( ( uint32_t ) * ( _it + 3 ) ) ;
254 }
255
256 return _retu;
257}
258
259/**
260 * @brief Adds header to payload. Make sure _payload_ has enough space.
261 *
262 * @param header The header.
263 * @param payload The payload.
264 * @return uint8_t* Iterated position.
265 */
266uint8_t* add_header ( RTPHeader* header, uint8_t* payload )
267{
268 uint8_t _cc = GET_FLAG_CSRCC ( header );
269
270 uint8_t* _it = payload;
271
272
273 /* Add sequence number first */
274 *_it = ( header->sequnum >> 8 ); ++_it;
275 *_it = ( header->sequnum ); ++_it;
276
277 *_it = header->flags; ++_it;
278 *_it = header->marker_payloadt; ++_it;
279
280
281 uint32_t _timestamp = header->timestamp;
282 *_it = ( _timestamp >> 24 ); ++_it;
283 *_it = ( _timestamp >> 16 ); ++_it;
284 *_it = ( _timestamp >> 8 ); ++_it;
285 *_it = ( _timestamp ); ++_it;
286
287 uint32_t _ssrc = header->ssrc;
288 *_it = ( _ssrc >> 24 ); ++_it;
289 *_it = ( _ssrc >> 16 ); ++_it;
290 *_it = ( _ssrc >> 8 ); ++_it;
291 *_it = ( _ssrc );
292
293 uint32_t *_csrc = header->csrc;
294 size_t _x;
295 for ( _x = 0; _x < _cc; _x++ ) {
296 ++_it;
297 *_it = ( _csrc[_x] >> 24 ); ++_it;
298 *_it = ( _csrc[_x] >> 16 ); ++_it;
299 *_it = ( _csrc[_x] >> 8 ); ++_it;
300 *_it = ( _csrc[_x] );
301 }
302
303 return _it;
304}
305
306/**
307 * @brief Adds extension header to payload. Make sure _payload_ has enough space.
308 *
309 * @param header The header.
310 * @param payload The payload.
311 * @return uint8_t* Iterated position.
312 */
313uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload )
314{
315 uint8_t* _it = payload;
316
317 *_it = ( header->length >> 8 ); _it++;
318 *_it = ( header->length ); _it++;
319
320 *_it = ( header->type >> 8 ); ++_it;
321 *_it = ( header->type );
322
323 size_t x;
324
325 uint32_t* _hd_ext = header->table;
326 for ( x = 0; x < header->length; x++ ) {
327 ++_it;
328 *_it = ( _hd_ext[x] >> 24 ); ++_it;
329 *_it = ( _hd_ext[x] >> 16 ); ++_it;
330 *_it = ( _hd_ext[x] >> 8 ); ++_it;
331 *_it = ( _hd_ext[x] );
332 }
333
334 return _it;
335}
336
337/**
338 * @brief Builds header from control session values.
339 *
340 * @param session Control session.
341 * @return RTPHeader* Created header.
342 */
343RTPHeader* build_header ( RTPSession* session )
344{
345 RTPHeader* _retu;
346 _retu = calloc ( sizeof * _retu, 1 );
347 assert(_retu);
348
349 ADD_FLAG_VERSION ( _retu, session->version );
350 ADD_FLAG_PADDING ( _retu, session->padding );
351 ADD_FLAG_EXTENSION ( _retu, session->extension );
352 ADD_FLAG_CSRCC ( _retu, session->cc );
353 ADD_SETTING_MARKER ( _retu, session->marker );
354 ADD_SETTING_PAYLOAD ( _retu, session->payload_type );
355
356 _retu->sequnum = session->sequnum;
357 _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */
358 _retu->ssrc = session->ssrc;
359
360 if ( session->cc > 0 ) {
361 _retu->csrc = calloc(sizeof(uint32_t), session->cc);
362 assert(_retu->csrc);
363
364 int i;
365
366 for ( i = 0; i < session->cc; i++ ) {
367 _retu->csrc[i] = session->csrc[i];
368 }
369 } else {
370 _retu->csrc = NULL;
371 }
372
373 _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
374
375 return _retu;
376}
377
378
379/**
380 * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data
381 * and so the length variable is set accordingly. _sequnum_ argument is
382 * passed by the handle_packet() since it's parsed already.
383 *
384 * @param session Control session.
385 * @param sequnum Sequence number that's parsed from payload in handle_packet()
386 * @param data Payload data.
387 * @param length Payload size.
388 * @return RTPMessage*
389 * @retval NULL Error occurred.
390 */
391RTPMessage* msg_parse ( RTPSession* session, uint16_t sequnum, const uint8_t* data, uint32_t length )
392{
393 assert( length != -1);
394
395 RTPMessage* _retu = calloc(sizeof(RTPMessage), 1);
396 assert(_retu);
397
398 _retu->header = extract_header ( data, length ); /* It allocates memory and all */
399
400 if ( !_retu->header ){
401 free(_retu);
402 return NULL;
403 }
404 _retu->header->sequnum = sequnum;
405
406 _retu->length = length - _retu->header->length;
407
408 uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ;
409
410
411 if ( GET_FLAG_EXTENSION ( _retu->header ) ) {
412 _retu->ext_header = extract_ext_header ( data + _from_pos, length );
413 if ( _retu->ext_header ){
414 _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
415 _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
416 } else {
417 free (_retu->ext_header);
418 free (_retu->header);
419 free (_retu);
420 return NULL;
421 }
422 } else {
423 _retu->ext_header = NULL;
424 }
425
426 /* Get the payload */
427 _retu->data = calloc ( sizeof ( uint8_t ), _retu->length );
428 assert(_retu->data);
429
430 memcpy ( _retu->data, data + _from_pos, length - _from_pos );
431
432 _retu->next = NULL;
433
434
435 if ( session && check_late_message ( session, _retu) < 0 ){
436 session->rsequnum = _retu->header->sequnum;
437 session->timestamp = _retu->header->timestamp;
438 }
439
440 return _retu;
441}
442
443/**
444 * @brief Callback for networking core.
445 *
446 * @param object RTPSession object.
447 * @param ip_port Where the message comes from.
448 * @param data Message data.
449 * @param length Message length.
450 * @return int
451 * @retval -1 Error occurred.
452 * @retval 0 Success.
453 */
454int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t length )
455{
456 RTPSession* _session = object;
457 RTPMessage* _msg;
458
459 if ( !_session )
460 return -1;
461
462 uint8_t _plain[MAX_UDP_PACKET_SIZE];
463
464 uint16_t _sequnum = ( ( uint16_t ) data[1] << 8 ) | data[2];
465
466 /* Clculate the right nonce */
467 uint8_t _calculated[crypto_box_NONCEBYTES];
468 memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES);
469 increase_nonce ( _calculated, _sequnum );
470
471 /* Decrypt message */
472 int _decrypted_length = decrypt_data_symmetric(
473 (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
474
475 /* This packet is either not encrypted properly or late
476 */
477 if ( -1 == _decrypted_length ){
478
479 /* If this is the case, then the packet is most likely late.
480 * Try with old nonce cycle.
481 */
482 if ( _session->rsequnum < _sequnum ) {
483 _decrypted_length = decrypt_data_symmetric(
484 (uint8_t*)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain );
485
486 if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */
487
488 /* Otherwise, if decryption is ok with new cycle, set new cycle
489 */
490 } else {
491 increase_nonce ( _calculated, MAX_SEQU_NUM );
492 _decrypted_length = decrypt_data_symmetric(
493 (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
494
495 if ( !_decrypted_length ) return -1; /* This is just an error */
496
497 /* A new cycle setting. */
498 memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES);
499 memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES);
500 }
501 }
502
503 _msg = msg_parse ( NULL, _sequnum, _plain, _decrypted_length );
504
505 if ( !_msg )
506 return -1;
507
508 /* Hopefully this goes well
509 * NOTE: Is this even used?
510 */
511 memcpy(&_msg->from, &ip_port, sizeof(tox_IP_Port));
512
513 /* Check if message came in late */
514 if ( check_late_message(_session, _msg) < 0 ) { /* Not late */
515 _session->rsequnum = _msg->header->sequnum;
516 _session->timestamp = _msg->header->timestamp;
517 }
518
519 pthread_mutex_lock(&_session->mutex);
520
521 if ( _session->last_msg ) {
522 _session->last_msg->next = _msg;
523 _session->last_msg = _msg;
524 } else {
525 _session->last_msg = _session->oldest_msg = _msg;
526 }
527
528 pthread_mutex_unlock(&_session->mutex);
529
530 return 0;
531}
532
533
534
535/**
536 * @brief Stores headers and payload data in one container ( data )
537 * and the length is set accordingly. Returned message is used for sending _only_.
538 *
539 * @param session The control session.
540 * @param data Payload data to send ( This is what you pass ).
541 * @param length Size of the payload data.
542 * @return RTPMessage* Created message.
543 * @retval NULL Error occurred.
544 */
545RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t length )
546{
547 if ( !session )
548 return NULL;
549
550 uint8_t* _from_pos;
551 RTPMessage* _retu = calloc(sizeof(RTPMessage), 1);
552 assert(_retu);
553
554 /* Sets header values and copies the extension header in _retu */
555 _retu->header = build_header ( session ); /* It allocates memory and all */
556 _retu->ext_header = session->ext_header;
557
558
559 uint32_t _total_length = length + _retu->header->length;
560
561 if ( _retu->ext_header ) {
562 _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
563 /* Allocate Memory for _retu->_data */
564 _retu->data = calloc ( sizeof _retu->data, _total_length );
565 assert(_retu->data);
566
567 _from_pos = add_header ( _retu->header, _retu->data );
568 _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 );
569 } else {
570 /* Allocate Memory for _retu->_data */
571 _retu->data = calloc ( sizeof _retu->data, _total_length );
572 assert(_retu->data);
573
574 _from_pos = add_header ( _retu->header, _retu->data );
575 }
576
577 /*
578 * Parses the extension header into the message
579 * Of course if any
580 */
581
582 /* Appends _data on to _retu->_data */
583 memcpy ( _from_pos + 1, data, length );
584
585 _retu->length = _total_length;
586
587 _retu->next = NULL;
588
589 return _retu;
590}
591
592
593/********************************************************************************************************************
594 ********************************************************************************************************************
595 ********************************************************************************************************************
596 ********************************************************************************************************************
597 ********************************************************************************************************************
598 *
599 *
600 *
601 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
602 *
603 *
604 *
605 ********************************************************************************************************************
606 ********************************************************************************************************************
607 ********************************************************************************************************************
608 ********************************************************************************************************************
609 ********************************************************************************************************************/
610
611
612
613
614
615
616
617
618
619/**
620 * @brief Release all messages held by session.
621 *
622 * @param session The session.
623 * @return int
624 * @retval -1 Error occurred.
625 * @retval 0 Success.
626 */
627int rtp_release_session_recv ( RTPSession* session )
628{
629 if ( !session ){
630 return -1;
631 }
632
633 RTPMessage* _tmp,* _it;
634
635 pthread_mutex_lock(&session->mutex);
636
637 for ( _it = session->oldest_msg; _it; _it = _tmp ){
638 _tmp = _it->next;
639 rtp_free_msg( session, _it);
640 }
641
642 session->last_msg = session->oldest_msg = NULL;
643
644 pthread_mutex_unlock(&session->mutex);
645
646 return 0;
647}
648
649
650/**
651 * @brief Get's oldes message in the list.
652 *
653 * @param session Where the list is.
654 * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it.
655 * @retval NULL No messages in the list, or no list.
656 */
657RTPMessage* rtp_recv_msg ( RTPSession* session )
658{
659 if ( !session )
660 return NULL;
661
662 RTPMessage* _retu = session->oldest_msg;
663
664 pthread_mutex_lock(&session->mutex);
665
666 if ( _retu )
667 session->oldest_msg = _retu->next;
668
669 if ( !session->oldest_msg )
670 session->last_msg = NULL;
671
672 pthread_mutex_unlock(&session->mutex);
673
674 return _retu;
675}
676
677
678/**
679 * @brief Sends data to _RTPSession::dest
680 *
681 * @param session The session.
682 * @param messenger Tox* object.
683 * @param data The payload.
684 * @param length Size of the payload.
685 * @return int
686 * @retval -1 On error.
687 * @retval 0 On success.
688 */
689int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length )
690{
691 RTPMessage* msg = rtp_new_message (session, data, length);
692
693 if ( !msg ) return -1;
694
695 uint8_t _send_data [ MAX_UDP_PACKET_SIZE ];
696
697 _send_data[0] = session->prefix;
698
699 /* Generate the right nonce */
700 uint8_t _calculated[crypto_box_NONCEBYTES];
701 memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES);
702 increase_nonce ( _calculated, msg->header->sequnum );
703
704 /* Need to skip 2 bytes that are for sequnum */
705 int encrypted_length = encrypt_data_symmetric(
706 (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length - 2, _send_data + 3 );
707
708 int full_length = encrypted_length + 3;
709
710 _send_data[1] = msg->data[0];
711 _send_data[2] = msg->data[1];
712
713
714 if ( full_length != sendpacket ( ((Messenger*)messenger)->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {
715 printf("Rtp error: %s\n", strerror(errno));
716 return -1;
717 }
718
719
720 /* Set sequ number */
721 if ( session->sequnum >= MAX_SEQU_NUM ) {
722 session->sequnum = 0;
723 memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES);
724 } else {
725 session->sequnum++;
726 }
727
728 rtp_free_msg ( session, msg );
729 return 0;
730}
731
732
733/**
734 * @brief Speaks for it self.
735 *
736 * @param session The control session msg belongs to. It can be NULL.
737 * @param msg The message.
738 * @return void
739 */
740void rtp_free_msg ( RTPSession* session, RTPMessage* msg )
741{
742 free ( msg->data );
743
744 if ( !session ){
745 free ( msg->header->csrc );
746 if ( msg->ext_header ){
747 free ( msg->ext_header->table );
748 free ( msg->ext_header );
749 }
750 } else {
751 if ( session->csrc != msg->header->csrc )
752 free ( msg->header->csrc );
753 if ( msg->ext_header && session->ext_header != msg->ext_header ) {
754 free ( msg->ext_header->table );
755 free ( msg->ext_header );
756 }
757 }
758
759 free ( msg->header );
760 free ( msg );
761}
762
763
764/**
765 * @brief Must be called before calling any other rtp function. It's used
766 * to initialize RTP control session.
767 *
768 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
769 * @param messenger Tox* object.
770 * @param friend_num Friend id.
771 * @param encrypt_key Speaks for it self.
772 * @param decrypt_key Speaks for it self.
773 * @param encrypt_nonce Speaks for it self.
774 * @param decrypt_nonce Speaks for it self.
775 * @return RTPSession* Created control session.
776 * @retval NULL Error occurred.
777 */
778RTPSession* rtp_init_session ( int payload_type,
779 Tox* messenger,
780 int friend_num,
781 const uint8_t* encrypt_key,
782 const uint8_t* decrypt_key,
783 const uint8_t* encrypt_nonce,
784 const uint8_t* decrypt_nonce
785)
786{
787 Messenger* _messenger_casted = (Messenger*) messenger;
788
789 IP_Port _dest = get_friend_ipport(_messenger_casted, friend_num );
790
791 /* This should be enough eh? */
792 if ( _dest.port == 0) {
793 return NULL;
794 }
795
796 RTPSession* _retu = calloc(sizeof(RTPSession), 1);
797 assert(_retu);
798
799 networking_registerhandler(_messenger_casted->net, payload_type, rtp_handle_packet, _retu);
800
801 _retu->version = RTP_VERSION; /* It's always 2 */
802 _retu->padding = 0; /* If some additional data is needed about the packet */
803 _retu->extension = 0; /* If extension to header is needed */
804 _retu->cc = 1; /* Amount of contributors */
805 _retu->csrc = NULL; /* Container */
806 _retu->ssrc = randombytes_random();
807 _retu->marker = 0;
808 _retu->payload_type = payload_table[payload_type];
809
810 _retu->dest = *((tox_IP_Port*)&_dest);
811
812 _retu->rsequnum = _retu->sequnum = 1;
813
814 _retu->ext_header = NULL; /* When needed allocate */
815 _retu->framerate = -1;
816 _retu->resolution = -1;
817
818 _retu->encrypt_key = encrypt_key;
819 _retu->decrypt_key = decrypt_key;
820
821 /* Need to allocate new memory */
822 _retu->encrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->encrypt_nonce);
823 _retu->decrypt_nonce = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->decrypt_nonce);
824 _retu->nonce_cycle = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES ); assert(_retu->nonce_cycle);
825
826 memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES);
827 memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES);
828 memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES);
829
830 _retu->csrc = calloc(sizeof(uint32_t), 1);
831 assert(_retu->csrc);
832
833 _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */
834
835 /* Also set payload type as prefix */
836 _retu->prefix = payload_type;
837
838 _retu->oldest_msg = _retu->last_msg = NULL;
839
840 pthread_mutex_init(&_retu->mutex, NULL);
841 /*
842 *
843 */
844 return _retu;
845}
846
847
848/**
849 * @brief Terminate the session.
850 *
851 * @param session The session.
852 * @param messenger The messenger who owns the session
853 * @return int
854 * @retval -1 Error occurred.
855 * @retval 0 Success.
856 */
857int rtp_terminate_session ( RTPSession* session, Tox* messenger )
858{
859 if ( !session )
860 return -1;
861
862 networking_registerhandler(((Messenger*)messenger)->net, session->prefix, NULL, NULL);
863
864 free ( session->ext_header );
865 free ( session->csrc );
866 free ( session->decrypt_nonce );
867 free ( session->encrypt_nonce );
868 free ( session->nonce_cycle );
869
870 pthread_mutex_destroy(&session->mutex);
871
872 /* And finally free session */
873 free ( session );
874
875 return 0;
876} \ No newline at end of file
diff --git a/toxav/toxrtp.h b/toxav/toxrtp.h
new file mode 100755
index 00000000..32234ebe
--- /dev/null
+++ b/toxav/toxrtp.h
@@ -0,0 +1,215 @@
1/** toxrtp.h
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 * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or
22 * my email: eniz_vukovic@hotmail.com
23 */
24
25#ifndef __TOXRTP
26#define __TOXRTP
27
28#define RTP_VERSION 2
29#include <inttypes.h>
30#include "../toxcore/tox.h"
31
32#define MAX_SEQU_NUM 65535
33
34
35/**
36 * @brief Standard rtp header.
37 *
38 */
39
40typedef struct _RTPHeader {
41 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
42 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
43 uint16_t sequnum; /* Sequence Number */
44 uint32_t timestamp; /* Timestamp */
45 uint32_t ssrc; /* SSRC */
46 uint32_t* csrc; /* CSRC's table */
47 uint32_t length; /* Length of the header in payload string. */
48
49} RTPHeader;
50
51
52/**
53 * @brief Standard rtp extension header.
54 *
55 */
56typedef struct _RTPExtHeader {
57 uint16_t type; /* Extension profile */
58 uint16_t length; /* Number of extensions */
59 uint32_t* table; /* Extension's table */
60
61} RTPExtHeader;
62
63
64/**
65 * @brief Standard rtp message.
66 *
67 */
68typedef struct _RTPMessage {
69 RTPHeader* header;
70 RTPExtHeader* ext_header;
71
72 uint8_t* data;
73 uint32_t length;
74 tox_IP_Port from;
75
76 struct _RTPMessage* next;
77} RTPMessage;
78
79
80/**
81 * @brief Our main session descriptor.
82 * It measures the session variables and controls
83 * the entire session. There are functions for manipulating
84 * the session so tend to use those instead of directly modifying
85 * session parameters.
86 *
87 */
88typedef struct _RTPSession {
89 uint8_t version;
90 uint8_t padding;
91 uint8_t extension;
92 uint8_t cc;
93 uint8_t marker;
94 uint8_t payload_type;
95 uint16_t sequnum; /* Set when sending */
96 uint16_t rsequnum; /* Check when recving msg */
97 uint32_t timestamp;
98 uint32_t ssrc;
99 uint32_t* csrc;
100
101 /* If some additional data must be sent via message
102 * apply it here. Only by allocating this member you will be
103 * automatically placing it within a message.
104 */
105 RTPExtHeader* ext_header;
106
107 /* External header identifiers */
108 int resolution;
109 int framerate;
110
111
112 /* Since these are only references of the
113 * call structure don't allocate or free
114 */
115
116 const uint8_t* encrypt_key;
117 const uint8_t* decrypt_key;
118 uint8_t* encrypt_nonce;
119 uint8_t* decrypt_nonce;
120
121 uint8_t* nonce_cycle;
122
123 RTPMessage* oldest_msg;
124 RTPMessage* last_msg; /* tail */
125
126 /* Msg prefix for core to know when recving */
127 uint8_t prefix;
128
129 pthread_mutex_t mutex;
130 tox_IP_Port dest;
131
132} RTPSession;
133
134
135/**
136 * @brief Release all messages held by session.
137 *
138 * @param session The session.
139 * @return int
140 * @retval -1 Error occurred.
141 * @retval 0 Success.
142 */
143int rtp_release_session_recv ( RTPSession* session );
144
145
146/**
147 * @brief Get's oldest message in the list.
148 *
149 * @param session Where the list is.
150 * @return RTPMessage* The message. You need to call rtp_msg_free() to free it.
151 * @retval NULL No messages in the list, or no list.
152 */
153RTPMessage* rtp_recv_msg ( RTPSession* session );
154
155
156/**
157 * @brief Sends msg to _RTPSession::dest
158 *
159 * @param session The session.
160 * @param msg The message
161 * @param messenger Tox* object.
162 * @return int
163 * @retval -1 On error.
164 * @retval 0 On success.
165 */
166int rtp_send_msg ( RTPSession* session, Tox* messenger, const uint8_t* data, uint16_t length );
167
168
169/**
170 * @brief Speaks for it self.
171 *
172 * @param session The control session msg belongs to. It can be NULL.
173 * @param msg The message.
174 * @return void
175 */
176void rtp_free_msg ( RTPSession* session, RTPMessage* msg );
177
178
179/**
180 * @brief Must be called before calling any other rtp function. It's used
181 * to initialize RTP control session.
182 *
183 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
184 * @param messenger Tox* object.
185 * @param friend_num Friend id.
186 * @param encrypt_key Speaks for it self.
187 * @param decrypt_key Speaks for it self.
188 * @param encrypt_nonce Speaks for it self.
189 * @param decrypt_nonce Speaks for it self.
190 * @return RTPSession* Created control session.
191 * @retval NULL Error occurred.
192 */
193RTPSession* rtp_init_session ( int payload_type,
194 Tox* messenger,
195 int friend_num,
196 const uint8_t* encrypt_key,
197 const uint8_t* decrypt_key,
198 const uint8_t* encrypt_nonce,
199 const uint8_t* decrypt_nonce );
200
201
202/**
203 * @brief Terminate the session.
204 *
205 * @param session The session.
206 * @param messenger The messenger who owns the session
207 * @return int
208 * @retval -1 Error occurred.
209 * @retval 0 Success.
210 */
211int rtp_terminate_session ( RTPSession* session, Tox* messenger );
212
213
214
215#endif /* __TOXRTP */