summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--INSTALL.md4
-rw-r--r--build/Makefile.am3
-rw-r--r--configure.ac79
-rw-r--r--toxav/Makefile.inc75
-rw-r--r--toxav/event.c384
-rw-r--r--toxav/event.h49
-rw-r--r--toxav/media.c314
-rw-r--r--toxav/media.h80
-rw-r--r--toxav/msi.c1424
-rw-r--r--toxav/msi.h233
-rw-r--r--toxav/phone.c1352
-rw-r--r--toxav/rtp.c934
-rw-r--r--toxav/rtp.h219
-rw-r--r--toxav/toxav.c551
-rw-r--r--toxav/toxav.h300
-rw-r--r--toxcore/LAN_discovery.h2
-rw-r--r--toxcore/group_chats.c29
-rw-r--r--toxcore/group_chats.h3
-rw-r--r--toxmsi/Makefile.inc69
-rw-r--r--toxmsi/phone.c668
-rw-r--r--toxmsi/phone.h62
-rw-r--r--toxmsi/toxmedia.c825
-rw-r--r--toxmsi/toxmedia.h168
-rw-r--r--toxmsi/toxmsi.c835
-rw-r--r--toxmsi/toxmsi.h145
-rw-r--r--toxmsi/toxmsi_event.c214
-rw-r--r--toxmsi/toxmsi_event.h46
-rw-r--r--toxmsi/toxmsi_header.c181
-rw-r--r--toxmsi/toxmsi_header.h99
-rw-r--r--toxmsi/toxmsi_message.c267
-rw-r--r--toxmsi/toxmsi_message.h120
-rw-r--r--toxrtp/Makefile.inc34
-rw-r--r--toxrtp/tests/Makefile.inc1
-rw-r--r--toxrtp/tests/test_bidirect.c109
-rw-r--r--toxrtp/tests/test_headers.c316
-rw-r--r--toxrtp/tests/test_helper.c83
-rw-r--r--toxrtp/tests/test_helper.h61
-rw-r--r--toxrtp/toxrtp.c693
-rw-r--r--toxrtp/toxrtp.h188
-rw-r--r--toxrtp/toxrtp_error.c68
-rw-r--r--toxrtp/toxrtp_error.h25
-rw-r--r--toxrtp/toxrtp_error_id.h32
-rw-r--r--toxrtp/toxrtp_helper.c208
-rw-r--r--toxrtp/toxrtp_helper.h77
-rw-r--r--toxrtp/toxrtp_message.c351
-rw-r--r--toxrtp/toxrtp_message.h111
47 files changed, 6008 insertions, 6090 deletions
diff --git a/.travis.yml b/.travis.yml
index 879f3a22..9d175564 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,6 +49,13 @@ before_script:
49 - make -j3 >/dev/null 49 - make -j3 >/dev/null
50 - sudo make install > /dev/null 50 - sudo make install > /dev/null
51 - cd .. 51 - cd ..
52 #installing vpx
53 - git clone http://git.chromium.org/webm/libvpx.git > /dev/null
54 - cd libvpx
55 - ./configure > /dev/null
56 - make -j3 >/dev/null
57 - sudo make install > /dev/null
58 - cd ..
52 #creating libraries links and updating cache 59 #creating libraries links and updating cache
53 - sudo ldconfig > /dev/null 60 - sudo ldconfig > /dev/null
54 #installing check, needed for unit tests 61 #installing check, needed for unit tests
diff --git a/INSTALL.md b/INSTALL.md
index 76a2623b..e82fe5bf 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,7 +1,7 @@
1#Install Instructions 1#Install Instructions
2 2
3- [Installation](#installation) 3- [Installation](#installation)
4 - [Unix like (Linux)](#unix) 4 - [Unix like](#unix)
5 - [OS X](#osx) 5 - [OS X](#osx)
6 - [Homebrew](#homebrew) 6 - [Homebrew](#homebrew)
7 - [Non-Homebrew](#non-homebrew) 7 - [Non-Homebrew](#non-homebrew)
@@ -11,7 +11,7 @@
11##Installation 11##Installation
12 12
13<a name="unix" /> 13<a name="unix" />
14###Most Unix like OSes (Linux): 14###Most Unix like OSes:
15 15
16Build dependencies: 16Build dependencies:
17 17
diff --git a/build/Makefile.am b/build/Makefile.am
index 9530e1a7..5514a4d5 100644
--- a/build/Makefile.am
+++ b/build/Makefile.am
@@ -5,8 +5,7 @@ noinst_bindir = $(top_builddir)/build
5EXTRA_DIST= 5EXTRA_DIST=
6 6
7include ../toxcore/Makefile.inc 7include ../toxcore/Makefile.inc
8include ../toxrtp/Makefile.inc 8include ../toxav/Makefile.inc
9include ../toxmsi/Makefile.inc
10include ../other/Makefile.inc 9include ../other/Makefile.inc
11include ../testing/Makefile.inc 10include ../testing/Makefile.inc
12include ../other/bootstrap_serverdaemon/Makefile.inc 11include ../other/bootstrap_serverdaemon/Makefile.inc
diff --git a/configure.ac b/configure.ac
index 62bb91bd..2b4fad78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,8 +13,7 @@ AC_CONFIG_MACRO_DIR([m4])
13EXTRA_LT_LDFLAGS= 13EXTRA_LT_LDFLAGS=
14 14
15LIBTOXCORE_LT_VERSION=0:0:0 15LIBTOXCORE_LT_VERSION=0:0:0
16LIBTOXMSI_LT_VERSION=0:0:0 16LIBTOXAV_LT_VERSION=0:0:0
17LIBTOXRTP_LT_VERSION=0:0:0
18dnl 17dnl
19dnl current:revision:age 18dnl current:revision:age
20dnl 19dnl
@@ -24,12 +23,10 @@ dnl incremented
24dnl age: increment if interfaces have been added, set to zero if 23dnl age: increment if interfaces have been added, set to zero if
25dnl interfaces have been removed or changed 24dnl interfaces have been removed or changed
26TOXCORE_LT_LDFLAGS="-version-info $LIBTOXCORE_LT_VERSION" 25TOXCORE_LT_LDFLAGS="-version-info $LIBTOXCORE_LT_VERSION"
27TOXMSI_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION" 26TOXAV_LT_LDFLAGS="-version-info $LIBTOXAV_LT_VERSION"
28TOXRTP_LT_LDFLAGS="-version-info $LIBTOXMSI_LT_VERSION"
29 27
30AC_SUBST(TOXCORE_LT_LDFLAGS) 28AC_SUBST(TOXCORE_LT_LDFLAGS)
31AC_SUBST(TOXMSI_LT_LDFLAGS) 29AC_SUBST(TOXAV_LT_LDFLAGS)
32AC_SUBST(TOXRTP_LT_LDFLAGS)
33 30
34if test "x${prefix}" = "xNONE"; then 31if test "x${prefix}" = "xNONE"; then
35 prefix="${ac_default_prefix}" 32 prefix="${ac_default_prefix}"
@@ -39,6 +36,7 @@ BUILD_DHT_BOOTSTRAP_DAEMON="yes"
39BUILD_NTOX="yes" 36BUILD_NTOX="yes"
40BUILD_TESTS="yes" 37BUILD_TESTS="yes"
41BUILD_AV="yes" 38BUILD_AV="yes"
39BUILD_PHONE="yes"
42BUILD_TESTING="yes" 40BUILD_TESTING="yes"
43 41
44NCURSES_FOUND="no" 42NCURSES_FOUND="no"
@@ -64,12 +62,24 @@ AC_ARG_ENABLE([av],
64 [ 62 [
65 if test "x$enableval" = "xno"; then 63 if test "x$enableval" = "xno"; then
66 BUILD_AV="no" 64 BUILD_AV="no"
65 BUILD_PHONE="no"
67 elif test "x$enableval" = "xyes"; then 66 elif test "x$enableval" = "xyes"; then
68 BUILD_AV="yes" 67 BUILD_AV="yes"
69 fi 68 fi
70 ] 69 ]
71) 70)
72 71
72AC_ARG_ENABLE([phone],
73 [AC_HELP_STRING([--disable-phone], [build test phone (default: auto)]) ],
74 [
75 if test "x$enableval" = "xno"; then
76 BUILD_PHONE="no"
77 elif test "x$enableval" = "xyes"; then
78 BUILD_PHONE="yes"
79 fi
80 ]
81)
82
73AC_ARG_ENABLE([tests], 83AC_ARG_ENABLE([tests],
74 [AC_HELP_STRING([--disable-tests], [build unit tests (default: auto)]) ], 84 [AC_HELP_STRING([--disable-tests], [build unit tests (default: auto)]) ],
75 [ 85 [
@@ -333,6 +343,7 @@ AC_TYPE_UINT16_T
333AC_TYPE_UINT32_T 343AC_TYPE_UINT32_T
334AC_TYPE_UINT64_T 344AC_TYPE_UINT64_T
335AC_TYPE_UINT8_T 345AC_TYPE_UINT8_T
346AC_C_BIGENDIAN
336 347
337# Checks for library functions. 348# Checks for library functions.
338AC_FUNC_FORK 349AC_FUNC_FORK
@@ -344,76 +355,77 @@ if test "x$BUILD_AV" = "xyes"; then
344 [ 355 [
345 AC_MSG_WARN([disabling AV support: required pthread library not found]) 356 AC_MSG_WARN([disabling AV support: required pthread library not found])
346 BUILD_AV="no" 357 BUILD_AV="no"
358 BUILD_PHONE="no"
347 ] 359 ]
348 ) 360 )
349fi 361fi
350 362
351if test "x$BUILD_AV" = "xyes"; then 363if test "x$BUILD_PHONE" = "xyes"; then
352 PKG_CHECK_MODULES([AVFORMAT], [libavformat], 364 PKG_CHECK_MODULES([AVFORMAT], [libavformat],
353 [], 365 [],
354 [ 366 [
355 AC_MSG_WARN([disabling AV support $AVFORMAT_PKG_ERRORS]) 367 AC_MSG_WARN([disabling phone $AVFORMAT_PKG_ERRORS])
356 BUILD_AV="no" 368 BUILD_PHONE="no"
357 ] 369 ]
358 ) 370 )
359fi 371fi
360 372
361if test "x$BUILD_AV" = "xyes"; then 373if test "x$BUILD_PHONE" = "xyes"; then
362 PKG_CHECK_MODULES([AVCODEC], [libavcodec], 374 PKG_CHECK_MODULES([AVCODEC], [libavcodec],
363 [], 375 [],
364 [ 376 [
365 AC_MSG_WARN([disabling AV support $AVCODEC_PKG_ERRORS]) 377 AC_MSG_WARN([disabling phone $AVCODEC_PKG_ERRORS])
366 BUILD_AV="no" 378 BUILD_PHONE="no"
367 ] 379 ]
368 ) 380 )
369fi 381fi
370 382
371if test "x$BUILD_AV" = "xyes"; then 383if test "x$BUILD_PHONE" = "xyes"; then
372 PKG_CHECK_MODULES([AVUTIL], [libavutil], 384 PKG_CHECK_MODULES([AVUTIL], [libavutil],
373 [], 385 [],
374 [ 386 [
375 AC_MSG_WARN([disabling AV support $AVUTIL_PKG_ERRORS]) 387 AC_MSG_WARN([disabling phone $AVUTIL_PKG_ERRORS])
376 BUILD_AV="no" 388 BUILD_PHONE="no"
377 ] 389 ]
378 ) 390 )
379fi 391fi
380 392
381if test "x$BUILD_AV" = "xyes"; then 393if test "x$BUILD_PHONE" = "xyes"; then
382 PKG_CHECK_MODULES([AVDEVICE], [libavdevice], 394 PKG_CHECK_MODULES([AVDEVICE], [libavdevice],
383 [], 395 [],
384 [ 396 [
385 AC_MSG_WARN([disabling AV support $AVDEVICE_PKG_ERRORS]) 397 AC_MSG_WARN([disabling phone $AVDEVICE_PKG_ERRORS])
386 BUILD_AV="no" 398 BUILD_PHONE="no"
387 ] 399 ]
388 ) 400 )
389fi 401fi
390 402
391if test "x$BUILD_AV" = "xyes"; then 403if test "x$BUILD_PHONE" = "xyes"; then
392 PKG_CHECK_MODULES([SWSCALE], [libswscale], 404 PKG_CHECK_MODULES([SWSCALE], [libswscale],
393 [], 405 [],
394 [ 406 [
395 AC_MSG_WARN([disabling AV support $SWSCALE_PKG_ERRORS]) 407 AC_MSG_WARN([disabling phone $SWSCALE_PKG_ERRORS])
396 BUILD_AV="no" 408 BUILD_PHONE="no"
397 ] 409 ]
398 ) 410 )
399fi 411fi
400 412
401if test "x$BUILD_AV" = "xyes"; then 413if test "x$BUILD_PHONE" = "xyes"; then
402 PKG_CHECK_MODULES([SDL], [sdl], 414 PKG_CHECK_MODULES([SDL], [sdl],
403 [], 415 [],
404 [ 416 [
405 AC_MSG_WARN([disabling AV support $SDL_PKG_ERRORS]) 417 AC_MSG_WARN([disabling phone $SDL_PKG_ERRORS])
406 BUILD_AV="no" 418 BUILD_PHONE="no"
407 ] 419 ]
408 ) 420 )
409fi 421fi
410 422
411if test "x$BUILD_AV" = "xyes"; then 423if test "x$BUILD_PHONE" = "xyes"; then
412 PKG_CHECK_MODULES([OPENAL], [openal], 424 PKG_CHECK_MODULES([OPENAL], [openal],
413 [], 425 [],
414 [ 426 [
415 AC_MSG_WARN([disabling AV support $OPENAL_PKG_ERRORS]) 427 AC_MSG_WARN([disabling phone $OPENAL_PKG_ERRORS])
416 BUILD_AV="no" 428 BUILD_PHONE="no"
417 ] 429 ]
418 ) 430 )
419fi 431fi
@@ -424,6 +436,18 @@ if test "x$BUILD_AV" = "xyes"; then
424 [ 436 [
425 AC_MSG_WARN([disabling AV support $OPUS_PKG_ERRORS]) 437 AC_MSG_WARN([disabling AV support $OPUS_PKG_ERRORS])
426 BUILD_AV="no" 438 BUILD_AV="no"
439 BUILD_PHONE="no"
440 ]
441 )
442fi
443
444if test "x$BUILD_AV" = "xyes"; then
445 PKG_CHECK_MODULES([VPX], [vpx],
446 [],
447 [
448 AC_MSG_WARN([disabling AV support $VPX_PKG_ERRORS])
449 BUILD_AV="no"
450 BUILD_PHONE="no"
427 ] 451 ]
428 ) 452 )
429fi 453fi
@@ -586,6 +610,7 @@ AM_CONDITIONAL(BUILD_DHT_BOOTSTRAP_DAEMON, test "x$BUILD_DHT_BOOTSTRAP_DAEMON" =
586AM_CONDITIONAL(BUILD_TESTS, test "x$BUILD_TESTS" = "xyes") 610AM_CONDITIONAL(BUILD_TESTS, test "x$BUILD_TESTS" = "xyes")
587AM_CONDITIONAL(BUILD_NTOX, test "x$BUILD_NTOX" = "xyes") 611AM_CONDITIONAL(BUILD_NTOX, test "x$BUILD_NTOX" = "xyes")
588AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes") 612AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
613AM_CONDITIONAL(BUILD_PHONE, test "x$BUILD_PHONE" = "xyes")
589AM_CONDITIONAL(BUILD_TESTING, test "x$BUILD_TESTING" = "xyes") 614AM_CONDITIONAL(BUILD_TESTING, test "x$BUILD_TESTING" = "xyes")
590AM_CONDITIONAL(WIN32, test "x$WIN32" = "xyes") 615AM_CONDITIONAL(WIN32, test "x$WIN32" = "xyes")
591 616
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
new file mode 100644
index 00000000..30879f65
--- /dev/null
+++ b/toxav/Makefile.inc
@@ -0,0 +1,75 @@
1if BUILD_AV
2
3lib_LTLIBRARIES += libtoxav.la
4libtoxav_la_include_HEADERS = ../toxav/toxav.h
5libtoxav_la_includedir = $(includedir)/tox
6
7libtoxav_la_SOURCES = ../toxav/event.h \
8 ../toxav/event.c \
9 ../toxav/rtp.h \
10 ../toxav/rtp.c \
11 ../toxav/msi.h \
12 ../toxav/msi.c \
13 ../toxav/media.h \
14 ../toxav/media.c \
15 ../toxav/toxav.h \
16 ../toxav/toxav.c
17
18
19libtoxav_la_CFLAGS = -I../toxcore \
20 -I../toxav \
21 $(NACL_CFLAGS) \
22 $(OPUS_CFLAGS) \
23 $(VPX_CFLAGS) \
24 $(PTHREAD_CFLAGS)
25
26libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
27 $(NACL_LDFLAGS) \
28 $(EXTRA_LT_LDFLAGS)
29
30libtoxav_la_LIBS = $(NACL_LIBS) \
31 $(OPUS_LIBS) \
32 $(VPX_LIBS) \
33 $(PTHREAD_LIBS)
34
35
36endif
37
38
39
40
41
42
43if BUILD_PHONE
44
45
46noinst_PROGRAMS += phone
47
48phone_SOURCES = ../toxav/phone.c
49
50phone_CFLAGS = -I../toxcore \
51 -I../toxav \
52 $(AVFORMAT_CFLAGS) \
53 $(AVCODEC_CFLAGS) \
54 $(AVUTIL_CFLAGS) \
55 $(AVDEVICE_CFLAGS) \
56 $(SWSCALE_CFLAGS) \
57 $(SDL_CFLAGS) \
58 $(OPENAL_CFLAGS)
59
60phone_LDADD = libtoxav.la \
61 libtoxcore.la \
62 $(AVFORMAT_LIBS) \
63 $(AVCODEC_LIBS) \
64 $(AVUTIL_LIBS) \
65 $(AVDEVICE_LIBS) \
66 $(SWSCALE_LIBS) \
67 $(SDL_LIBS) \
68 $(OPENAL_LIBS) \
69 $(OPUS_LIBS) \
70 $(VPX_LIBS)\
71 $(PTHREAD_LIBS)\
72 $(NACL_LIBS)
73
74
75endif \ No newline at end of file
diff --git a/toxav/event.c b/toxav/event.c
new file mode 100644
index 00000000..f79fd60e
--- /dev/null
+++ b/toxav/event.c
@@ -0,0 +1,384 @@
1/** event.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 at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include <stdlib.h>
30#include "event.h"
31
32#include "../toxcore/util.h"
33#include "../toxcore/network.h"
34
35#define _GNU_SOURCE
36
37#include <assert.h>
38#include <unistd.h>
39#include <stddef.h>
40#include <inttypes.h>
41#include <pthread.h>
42#include <stdio.h>
43
44#define RUN_IN_THREAD(func, args) { pthread_t _tid; \
45pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); }
46
47#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex)
48#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex)
49
50#define FREQUENCY 10000
51
52#define inline__ inline __attribute__((always_inline))
53
54
55typedef struct _EventContainer {
56 void *(*func)(void *);
57 void *func_args;
58 unsigned timeout;
59 long long id;
60
61} EventContainer;
62
63typedef struct _EventHandler {
64 EventContainer *timed_events;
65 size_t timed_events_count;
66
67 int running;
68
69 pthread_mutex_t mutex;
70
71} EventHandler;
72
73int throw_event( void * (func)(void *), void *arg );
74int reset_timer_event ( int id, uint32_t timeout );
75int throw_timer_event ( void * (func)(void *), void *arg, unsigned timeout);
76int cancel_timer_event ( int id );
77int execute_timer_event ( int id );
78
79struct _Event event = {
80 throw_event,
81 /* reset_timer_event */ NULL,
82 throw_timer_event,
83 cancel_timer_event,
84 /*execute_timer_event*/ NULL
85};
86
87/*
88 * Random functions used by this file
89 */
90void clear_events (EventContainer **event_container, size_t *counter)
91{
92 free(*event_container );
93
94 *event_container = NULL;
95 *counter = 0;
96}
97
98int pop_id ( EventContainer **event_container, size_t *counter, int id )
99{
100 if ( !*event_container || !*counter || !id )
101 return -1;
102
103 EventContainer *_it = *event_container;
104 int i;
105
106 for ( i = *counter; i; -- i ) {
107 if ( _it->id == id ) { /* Hit! */
108 break;
109 }
110
111 ++_it;
112 }
113
114 if ( i ) {
115 for ( ; i; -- i ) {
116 *_it = *(_it + 1);
117 ++_it;
118 }
119
120 -- (*counter );
121
122 if ( !(*counter)) { /* Free and set to NULL */
123 free(*event_container);
124 *event_container = NULL;
125 } else {
126 void *_result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */
127
128
129 if ( _result != NULL ) {
130 *event_container = _result;
131 return 0;
132 } else {
133 /* Not sure what would happen next so abort execution.
134 */
135 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
136 abort();
137 return -1;
138 }
139 }
140 }
141
142 /* not found here */
143
144 return -1;
145}
146
147void push_event ( EventContainer **container, size_t *counter, void * (func)(void *), void *arg )
148{
149 EventContainer *_new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1));
150
151 if ( _new == NULL ) {
152 /* Not sure what would happen next so abort execution.
153 * TODO: This could notice the calling function
154 * about realloc failing.
155 */
156 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
157 abort();
158 }
159
160 _new[*counter].func = func;
161 _new[*counter].func_args = arg;
162 _new[*counter].timeout = 0;
163 _new[*counter].id = 0;
164
165 (*container) = _new;
166
167 (*counter )++;
168}
169
170void reorder_events ( size_t counter, EventContainer *container, unsigned timeout )
171{
172 if ( counter > 1 ) {
173
174 int i = counter - 1;
175
176 /* start from behind excluding last added member */
177 EventContainer *_it = &container[i - 1];
178
179 EventContainer _last_added = container[i];
180
181 for ( ; i; --i ) {
182 if ( _it->timeout > timeout ) {
183 *(_it + 1) = *_it;
184 *_it = _last_added;
185 -- _it;
186 }
187 }
188
189 }
190}
191
192/* ============================================= */
193
194/* main poll for event execution */
195void *event_poll( void *arg )
196{
197 EventHandler *_event_handler = arg;
198
199 while ( _event_handler->running ) {
200
201 LOCK( _event_handler );
202
203 if ( _event_handler->timed_events ) {
204
205 uint32_t _time = ((uint32_t)(current_time() / 1000));
206
207 if ( _event_handler->timed_events[0].timeout < _time ) {
208
209 RUN_IN_THREAD ( _event_handler->timed_events[0].func,
210 _event_handler->timed_events[0].func_args );
211
212 pop_id(&_event_handler->timed_events,
213 &_event_handler->timed_events_count,
214 _event_handler->timed_events[0].id);
215
216 }
217
218 }
219
220 UNLOCK( _event_handler );
221
222 usleep(FREQUENCY);
223 }
224
225 LOCK( _event_handler );
226
227 clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count);
228
229 UNLOCK( _event_handler );
230
231 _event_handler->running = -1;
232 pthread_exit(NULL);
233}
234
235int throw_event( void * (func)(void *), void *arg )
236{
237 pthread_t _tid;
238 int _rc =
239 pthread_create(&_tid, NULL, func, arg );
240
241 return (0 != _rc ) ? _rc : pthread_detach(_tid);
242}
243
244EventHandler event_handler;
245
246/* Place and order array of timers */
247int throw_timer_event ( void * (func)(void *), void *arg, unsigned timeout)
248{
249 static int _unique_id = 1;
250
251 push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg );
252
253 size_t _counter = event_handler.timed_events_count;
254
255 event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000));
256 event_handler.timed_events[_counter - 1].id = _unique_id;
257 ++_unique_id;
258
259
260 /* reorder */
261
262 reorder_events(_counter, event_handler.timed_events, timeout );
263
264 return _unique_id - 1;
265}
266
267int execute_timer_event ( int id )
268{
269 int _status;
270
271 LOCK((&event_handler));
272 EventContainer *_it = event_handler.timed_events;
273
274 int _i = event_handler.timed_events_count;
275
276 /* Find it and execute */
277 for ( ; _i; _i-- ) {
278 if ( _it->id == id ) {
279 RUN_IN_THREAD ( _it->func, _it->func_args );
280 break;
281 }
282
283 ++_it;
284 }
285
286 /* Now remove it from the queue */
287
288 if ( _i ) {
289 for ( ; _i; -- _i ) {
290 *_it = *(_it + 1);
291 ++_it;
292 }
293
294 -- event_handler.timed_events_count;
295
296 if ( !event_handler.timed_events_count ) { /* Free and set to null */
297 free(event_handler.timed_events);
298 event_handler.timed_events = NULL;
299 } else {
300 void *_result = realloc(event_handler.timed_events,
301 sizeof(EventContainer) * event_handler.timed_events_count); /* resize */
302
303 if ( _result != NULL ) {
304 event_handler.timed_events = _result;
305 } else {
306 /* Not sure what would happen next so abort execution.
307 */
308 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
309 abort();
310 return -1;
311 }
312 }
313
314 _status = 0;
315
316 } else _status = -1;
317
318 UNLOCK((&event_handler));
319
320 return _status;
321}
322
323int reset_timer_event ( int id, uint32_t timeout )
324{
325 int _status;
326
327 LOCK((&event_handler));
328
329 EventContainer *_it = event_handler.timed_events;
330
331 int _i = event_handler.timed_events_count;
332
333 /* Find it and change */
334 for ( ; _i; _i-- ) {
335 if ( _it->id == id ) {
336 _it->timeout = timeout + ((uint32_t)(current_time() / 1000));
337 break;
338 }
339
340 ++_it;
341 }
342
343 _status = _i ? -1 : 0;
344
345 UNLOCK((&event_handler));
346
347 return _status;
348}
349
350/* Remove timer from array */
351inline__ int cancel_timer_event ( int id )
352{
353 return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id );
354}
355
356
357/* Initialization and termination of event polls
358 * This will be run at the beginning and the end of the program execution.
359 * I think that's the best way to do it.
360 */
361
362void __attribute__((constructor)) init_event_poll ()
363{
364 event_handler.timed_events = NULL;
365 event_handler.timed_events_count = 0;
366
367 event_handler.running = 1;
368
369 pthread_mutex_init(&event_handler.mutex, NULL);
370
371 RUN_IN_THREAD(event_poll, &event_handler);
372}
373
374/* NOTE: Do we need this? */
375void __attribute__((destructor)) terminate_event_poll()
376{
377 /* Exit thread */
378 event_handler.running = 0;
379
380 /* Give it enought time to exit */
381 usleep(FREQUENCY * 2);
382
383 pthread_mutex_destroy( &event_handler.mutex );
384} \ No newline at end of file
diff --git a/toxav/event.h b/toxav/event.h
new file mode 100644
index 00000000..0fb2d120
--- /dev/null
+++ b/toxav/event.h
@@ -0,0 +1,49 @@
1/** event.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 at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifndef __TOXEVENT
26#define __TOXEVENT
27
28
29/**
30 * - Events are, in fact, ran in their own threads upon execution.
31 * - Event handler is initialized at the start, before the main() function
32 * and terminated after it's execution.
33 * - Timers are checked for timeout every ~10000 ns.
34 * - Timers can be canceled or ran immediately via
35 * timer_release() or timer_now() functions.
36 * - Timeout is measured in milliseconds.
37 *
38 * NOTE: timer_reset () and timer_now() are not tested nor usable atm
39 *
40 */
41extern struct _Event {
42 int (*rise) (void * ( func ) ( void * ), void *arg);
43 int (*timer_reset ) ( int id, unsigned timeout );
44 int (*timer_alloc) (void * ( func ) ( void * ), void *arg, unsigned timeout);
45 int (*timer_release) (int id);
46 int (*timer_now) ( int id );
47} event;
48
49#endif /* _MSI__EVENT_H_ */
diff --git a/toxav/media.c b/toxav/media.c
new file mode 100644
index 00000000..2594f99a
--- /dev/null
+++ b/toxav/media.c
@@ -0,0 +1,314 @@
1/** media.c
2 *
3 * Audio and video codec intitialization, 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#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <math.h>
32#include <assert.h>
33
34#include "rtp.h"
35#include "media.h"
36
37struct jitter_buffer {
38 RTPMessage **queue;
39 uint16_t capacity;
40 uint16_t size;
41 uint16_t front;
42 uint16_t rear;
43 uint8_t queue_ready;
44 uint16_t current_id;
45 uint32_t current_ts;
46 uint8_t id_set;
47};
48
49
50struct jitter_buffer *create_queue(int capacity)
51{
52 struct jitter_buffer *q;
53 q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer), 1);
54 q->queue = (RTPMessage **)calloc(sizeof(RTPMessage *), capacity);
55 int i = 0;
56
57 for (i = 0; i < capacity; ++i) {
58 q->queue[i] = NULL;
59 }
60
61 q->size = 0;
62 q->capacity = capacity;
63 q->front = 0;
64 q->rear = -1;
65 q->queue_ready = 0;
66 q->current_id = 0;
67 q->current_ts = 0;
68 q->id_set = 0;
69 return q;
70}
71
72/* returns 1 if 'a' has a higher sequence number than 'b' */
73uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b)
74{
75 /* TODO: There is already this kind of function in toxrtp.c.
76 * Maybe merge?
77 */
78 return (sn_a > sn_b || ts_a > ts_b);
79}
80
81/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
82RTPMessage *dequeue(struct jitter_buffer *q, int *success)
83{
84 if (q->size == 0 || q->queue_ready == 0) {
85 q->queue_ready = 0;
86 *success = 0;
87 return NULL;
88 }
89
90 int front = q->front;
91
92 if (q->id_set == 0) {
93 q->current_id = q->queue[front]->header->sequnum;
94 q->current_ts = q->queue[front]->header->timestamp;
95 q->id_set = 1;
96 } else {
97 int next_id = q->queue[front]->header->sequnum;
98 int next_ts = q->queue[front]->header->timestamp;
99
100 /* if this packet is indeed the expected packet */
101 if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) {
102 q->current_id = next_id;
103 q->current_ts = next_ts;
104 } else {
105 if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
106 printf("nextid: %d current: %d\n", next_id, q->current_id);
107 q->current_id = (q->current_id + 1) % MAX_SEQU_NUM;
108 *success = 2; /* tell the decoder the packet is lost */
109 return NULL;
110 } else {
111 /* packet too old */
112 printf("packet too old\n");
113 *success = 0;
114 return NULL;
115 }
116 }
117 }
118
119 q->size--;
120 q->front++;
121
122 if (q->front == q->capacity)
123 q->front = 0;
124
125 *success = 1;
126 q->current_id = q->queue[front]->header->sequnum;
127 q->current_ts = q->queue[front]->header->timestamp;
128 return q->queue[front];
129}
130
131int empty_queue(struct jitter_buffer *q)
132{
133 while (q->size > 0) {
134 q->size--;
135 rtp_free_msg(NULL, q->queue[q->front]);
136 q->front++;
137
138 if (q->front == q->capacity)
139 q->front = 0;
140 }
141
142 q->id_set = 0;
143 q->queue_ready = 0;
144 return 0;
145}
146
147int queue(struct jitter_buffer *q, RTPMessage *pk)
148{
149 if (q->size == q->capacity) {
150 printf("buffer full, emptying buffer...\n");
151 empty_queue(q);
152 return 0;
153 }
154
155 if (q->size > 8)
156 q->queue_ready = 1;
157
158 ++q->size;
159 ++q->rear;
160
161 if (q->rear == q->capacity)
162 q->rear = 0;
163
164 q->queue[q->rear] = pk;
165
166 int a;
167 int b;
168 int j;
169 a = q->rear;
170
171 for (j = 0; j < q->size - 1; ++j) {
172 b = a - 1;
173
174 if (b < 0)
175 b += q->capacity;
176
177 if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum,
178 q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) {
179 RTPMessage *temp;
180 temp = q->queue[a];
181 q->queue[a] = q->queue[b];
182 q->queue[b] = temp;
183 printf("had to swap\n");
184 } else {
185 break;
186 }
187
188 a -= 1;
189
190 if (a < 0)
191 a += q->capacity;
192 }
193
194 if (pk)
195 return 1;
196
197 return 0;
198}
199
200
201int init_video_decoder(CodecState *cs)
202{
203 if (vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0,
204 VPX_DECODER_ABI_VERSION) != VPX_CODEC_OK) {
205 fprintf(stderr, "Init video_decoder failed!\n");
206 return -1;
207 }
208
209 return 0;
210}
211
212int init_audio_decoder(CodecState *cs, uint32_t audio_channels)
213{
214 int rc;
215 cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc );
216
217 if ( rc != OPUS_OK ) {
218 fprintf(stderr, "Error while starting audio decoder!\n");
219 return -1;
220 }
221
222 return 0;
223}
224
225
226int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t video_bitrate)
227{
228 vpx_codec_enc_cfg_t cfg;
229 int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
230
231 if (res) {
232 printf("Failed to get config: %s\n", vpx_codec_err_to_string(res));
233 return -1;
234 }
235
236 cfg.rc_target_bitrate = video_bitrate;
237 cfg.g_w = width;
238 cfg.g_h = height;
239
240 if (vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0,
241 VPX_ENCODER_ABI_VERSION) != VPX_CODEC_OK) {
242 fprintf(stderr, "Failed to initialize encoder\n");
243 return -1;
244 }
245
246 return 0;
247}
248
249int init_audio_encoder(CodecState *cs, uint32_t audio_channels)
250{
251 int err = OPUS_OK;
252 cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &err);
253 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
254 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
255
256
257 return err == OPUS_OK ? 0 : -1;
258}
259
260
261CodecState *codec_init_session ( uint32_t audio_bitrate,
262 uint16_t audio_frame_duration,
263 uint32_t audio_sample_rate,
264 uint32_t audio_channels,
265 uint16_t video_width,
266 uint16_t video_height,
267 uint32_t video_bitrate )
268{
269 CodecState *_retu = calloc(sizeof(CodecState), 1);
270 assert(_retu);
271
272 _retu->audio_bitrate = audio_bitrate;
273 _retu->audio_sample_rate = audio_sample_rate;
274
275 /* Encoders */
276 if (!video_width || !video_height) {
277 video_width = 320;
278 video_height = 240;
279 }
280
281 if ( 0 == init_video_encoder(_retu, video_width, video_height, video_bitrate) )
282 printf("Video encoder initialized!\n");
283
284 if ( 0 == init_audio_encoder(_retu, audio_channels) )
285 printf("Audio encoder initialized!\n");
286
287
288 /* Decoders */
289 if ( 0 == init_video_decoder(_retu) )
290 printf("Video decoder initialized!\n");
291
292 if ( 0 == init_audio_decoder(_retu, audio_channels) )
293 printf("Audio decoder initialized!\n");
294
295
296 return _retu;
297}
298
299void codec_terminate_session ( CodecState *cs )
300{
301 if ( cs->audio_encoder ) {
302 opus_encoder_destroy(cs->audio_encoder);
303 printf("Terminated encoder!\n");
304 }
305
306 if ( cs->audio_decoder ) {
307 opus_decoder_destroy(cs->audio_decoder);
308 printf("Terminated decoder!\n");
309 }
310
311 /* TODO: Terminate video */
312 vpx_codec_destroy(&cs->v_decoder);
313 vpx_codec_destroy(&cs->v_encoder);
314}
diff --git a/toxav/media.h b/toxav/media.h
new file mode 100644
index 00000000..aed57ea2
--- /dev/null
+++ b/toxav/media.h
@@ -0,0 +1,80 @@
1/** media.h
2 *
3 * Audio and video codec intitialization, 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#ifndef _AVCODEC_H_
25#define _AVCODEC_H_
26
27#include <stdio.h>
28#include <math.h>
29#include <pthread.h>
30
31#include <vpx/vpx_decoder.h>
32#include <vpx/vpx_encoder.h>
33#include <vpx/vp8dx.h>
34#include <vpx/vp8cx.h>
35#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
36#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
37
38/* Audio encoding/decoding */
39#include <opus/opus.h>
40
41
42typedef struct _CodecState {
43
44 /* video encoding */
45 vpx_codec_ctx_t v_encoder;
46 uint32_t frame_counter;
47
48 /* video decoding */
49 vpx_codec_ctx_t v_decoder;
50
51 /* audio encoding */
52 OpusEncoder *audio_encoder;
53 int audio_bitrate;
54 int audio_sample_rate;
55
56 /* audio decoding */
57 OpusDecoder *audio_decoder;
58
59} CodecState;
60
61typedef struct _RTPMessage RTPMessage;
62
63struct jitter_buffer *create_queue(int capacity);
64int empty_queue(struct jitter_buffer *q);
65
66int queue(struct jitter_buffer *q, RTPMessage *pk);
67RTPMessage *dequeue(struct jitter_buffer *q, int *success);
68
69
70CodecState *codec_init_session ( uint32_t audio_bitrate,
71 uint16_t audio_frame_duration,
72 uint32_t audio_sample_rate,
73 uint32_t audio_channels,
74 uint16_t video_width,
75 uint16_t video_height,
76 uint32_t video_bitrate );
77
78void codec_terminate_session(CodecState *cs);
79
80#endif
diff --git a/toxav/msi.c b/toxav/msi.c
new file mode 100644
index 00000000..8f69d942
--- /dev/null
+++ b/toxav/msi.c
@@ -0,0 +1,1424 @@
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 at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#define _BSD_SOURCE
30
31#include "msi.h"
32#include "event.h"
33#include "../toxcore/util.h"
34#include "../toxcore/network.h"
35#include "../toxcore/Messenger.h"
36
37#include <assert.h>
38#include <unistd.h>
39#include <string.h>
40#include <stdlib.h>
41
42#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
43
44#define MSI_MAXMSG_SIZE 1024
45
46#define TYPE_REQUEST 1
47#define TYPE_RESPONSE 2
48
49unsigned char *VERSION_STRING = (unsigned char *)"0.3.1";
50#define VERSION_STRLEN 5
51
52#define CT_AUDIO_HEADER_VALUE "AUDIO"
53#define CT_VIDEO_HEADER_VALUE "VIDEO"
54
55
56/* Define default timeout for a request.
57 * There is no behavior specified by the msi on what will
58 * client do on timeout, but to call timeout callback.
59 */
60#define m_deftout 10000 /* in milliseconds */
61
62/**
63 * Protocol:
64 *
65 * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) |
66 *
67 * ie.
68 *
69 * | 0x1 | 0x0 0x7 | "version"
70 *
71 * Means: it's field value with length of 7 bytes and value of "version"
72 * It's similar to amp protocol
73 */
74
75
76#define GENERIC_HEADER(header) \
77typedef struct _MSIHeader##header { \
78uint8_t* header_value; \
79uint16_t size; \
80} MSIHeader##header;
81
82
83GENERIC_HEADER ( Version )
84GENERIC_HEADER ( Request )
85GENERIC_HEADER ( Response )
86GENERIC_HEADER ( CallType )
87GENERIC_HEADER ( UserAgent )
88GENERIC_HEADER ( CallId )
89GENERIC_HEADER ( Info )
90GENERIC_HEADER ( Reason )
91GENERIC_HEADER ( CryptoKey )
92GENERIC_HEADER ( Nonce )
93
94
95/**
96 * @brief This is the message structure. It contains all of the headers and
97 * destination/source of the message stored in friend_id.
98 *
99 */
100typedef struct _MSIMessage {
101
102 MSIHeaderVersion version;
103 MSIHeaderRequest request;
104 MSIHeaderResponse response;
105 MSIHeaderCallType calltype;
106 MSIHeaderUserAgent useragent;
107 MSIHeaderInfo info;
108 MSIHeaderReason reason;
109 MSIHeaderCallId callid;
110 MSIHeaderCryptoKey cryptokey;
111 MSIHeaderNonce nonce;
112
113 struct _MSIMessage *next;
114
115 int friend_id;
116
117} MSIMessage;
118
119
120
121static MSICallback callbacks[10] = {0};
122
123
124/* define strings for the identifiers */
125#define VERSION_FIELD "Version"
126#define REQUEST_FIELD "Request"
127#define RESPONSE_FIELD "Response"
128#define INFO_FIELD "INFO"
129#define REASON_FIELD "Reason"
130#define CALLTYPE_FIELD "Call-type"
131#define USERAGENT_FIELD "User-agent"
132#define CALLID_FIELD "Call-id"
133#define CRYPTOKEY_FIELD "Crypto-key"
134#define NONCE_FIELD "Nonce"
135
136/* protocol descriptors */
137#define end_byte 0x0
138#define field_byte 0x1
139#define value_byte 0x2
140
141
142typedef enum {
143 invite,
144 start,
145 cancel,
146 reject,
147 end,
148
149} MSIRequest;
150
151
152/**
153 * @brief Get string value for request.
154 *
155 * @param request The request.
156 * @return const uint8_t* The string
157 */
158static inline const uint8_t *stringify_request ( MSIRequest request )
159{
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{
189 static const uint8_t *strings[] = {
190 ( uint8_t * ) "ringing",
191 ( uint8_t * ) "starting",
192 ( uint8_t * ) "ending",
193 ( uint8_t * ) "error"
194 };
195
196 return strings[response];
197}
198
199
200#define ON_HEADER(iterator, header, descriptor, size_const) \
201( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \
202 iterator += size_const; /* Set iterator at begining of value part */ \
203 if ( *iterator != value_byte ) { assert(0); return -1; }\
204 iterator ++;\
205 uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \
206 (uint16_t) *(iterator + 1); \
207 header.header_value = calloc(sizeof(uint8_t), _value_size); \
208 header.size = _value_size; \
209 memcpy(header.header_value, iterator + 2, _value_size);\
210 iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \
211}
212
213/**
214 * @brief Parse raw 'data' received from socket into MSIMessage struct.
215 * Every message has to have end value of 'end_byte' or _undefined_ behavior
216 * occures. The best practice is to check the end of the message at the handle_packet.
217 *
218 * @param msg Container.
219 * @param data The data.
220 * @return int
221 * @retval -1 Error occured.
222 * @retval 0 Success.
223 */
224int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length )
225{
226 assert ( msg );
227
228 if ( data[length - 1] ) /* End byte must have value 0 */
229 return -1;
230
231 const uint8_t *_it = data;
232
233 while ( *_it ) {/* until end_byte is hit */
234
235 uint16_t itedlen = (_it - data) + 2;
236
237 if ( *_it == field_byte && itedlen < length ) {
238
239 uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 |
240 ( uint16_t ) * ( _it + 2 );
241
242 if ( itedlen + _size > length ) return -1;
243
244 _it += 3; /* place it at the field value beginning */
245
246 switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */
247
248 case 4: { /* INFO header */
249 if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 )
250 }
251 break;
252
253 case 5: { /* NONCE header */
254 if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 )
255 }
256 break;
257
258 case 6: { /* Reason header */
259 if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 )
260 }
261 break;
262
263 case 7: { /* Version, Request, Call-id headers */
264 if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 )
265 else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 )
266 else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 )
267 }
268 break;
269
270 case 8: { /* Response header */
271 if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 )
272 }
273 break;
274
275 case 9: { /* Call-type header */
276 if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 )
277 }
278 break;
279
280 case 10: { /* User-agent, Crypto-key headers */
281 if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 )
282 else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 )
283 }
284 break;
285
286 default:
287 return -1;
288 }
289 } else return -1;
290
291 /* If it's anything else return failure as the message is invalid */
292
293 }
294
295 return 0;
296}
297
298
299#define ALLOCATE_HEADER( var, mheader_value, t_size) \
300var.header_value = calloc(sizeof *mheader_value, t_size); \
301memcpy(var.header_value, mheader_value, t_size); \
302var.size = t_size;
303
304
305/**
306 * @brief Speaks for it self.
307 *
308 * @param msg The message.
309 * @return void
310 */
311void free_message ( MSIMessage *msg )
312{
313 assert ( msg );
314
315 free ( msg->calltype.header_value );
316 free ( msg->request.header_value );
317 free ( msg->response.header_value );
318 free ( msg->useragent.header_value );
319 free ( msg->version.header_value );
320 free ( msg->info.header_value );
321 free ( msg->cryptokey.header_value );
322 free ( msg->nonce.header_value );
323 free ( msg->reason.header_value );
324 free ( msg->callid.header_value );
325
326 free ( msg );
327}
328
329
330/**
331 * @brief Create the message.
332 *
333 * @param type Request or response.
334 * @param type_id Type of request/response.
335 * @return MSIMessage* Created message.
336 * @retval NULL Error occured.
337 */
338MSIMessage *msi_new_message ( uint8_t type, const uint8_t *type_id )
339{
340 MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 );
341 assert ( _retu );
342
343 if ( type == TYPE_REQUEST ) {
344 ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char *)type_id ) )
345
346 } else if ( type == TYPE_RESPONSE ) {
347 ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char *)type_id ) )
348
349 } else {
350 free_message ( _retu );
351 return NULL;
352 }
353
354 ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char *)VERSION_STRING ) )
355
356 return _retu;
357}
358
359
360/**
361 * @brief Parse data from handle_packet.
362 *
363 * @param data The data.
364 * @return MSIMessage* Parsed message.
365 * @retval NULL Error occured.
366 */
367MSIMessage *parse_message ( const uint8_t *data, uint16_t length )
368{
369 assert ( data );
370
371 MSIMessage *_retu = calloc ( sizeof ( MSIMessage ), 1 );
372 assert ( _retu );
373
374 memset ( _retu, 0, sizeof ( MSIMessage ) );
375
376 if ( parse_raw_data ( _retu, data, length ) == -1 ) {
377
378 free_message ( _retu );
379 return NULL;
380 }
381
382 if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size ||
383 memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) {
384
385 free_message ( _retu );
386 return NULL;
387 }
388
389 return _retu;
390}
391
392
393
394/**
395 * @brief Speaks for it self.
396 *
397 * @param dest Container.
398 * @param header_field Field.
399 * @param header_value Field value.
400 * @param value_len Length of field value.
401 * @param length Pointer to container length.
402 * @return uint8_t* Iterated container.
403 */
404uint8_t *append_header_to_string (
405 uint8_t *dest,
406 const uint8_t *header_field,
407 const uint8_t *header_value,
408 uint16_t value_len,
409 uint16_t *length )
410{
411 assert ( dest );
412 assert ( header_value );
413 assert ( header_field );
414
415 const uint8_t *_hvit = header_value;
416 uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/
417
418 *dest = field_byte; /* Set the first byte */
419
420 uint8_t *_getback_byte = dest + 1; /* remeber the byte we were on */
421 dest += 3; /* swith to 4th byte where field value starts */
422
423 /* Now set the field value and calculate it's length */
424 uint16_t _i = 0;
425
426 for ( ; header_field[_i]; ++_i ) {
427 *dest = header_field[_i];
428 ++dest;
429 };
430
431 _total += _i;
432
433 /* Now set the length of the field byte */
434 *_getback_byte = ( uint8_t ) _i >> 8;
435
436 _getback_byte++;
437
438 *_getback_byte = ( uint8_t ) _i;
439
440 /* for value part do it regulary */
441 *dest = value_byte;
442
443 dest++;
444
445 *dest = ( uint8_t ) value_len >> 8;
446
447 dest++;
448
449 *dest = ( uint8_t ) value_len;
450
451 dest++;
452
453 for ( _i = value_len; _i; --_i ) {
454 *dest = *_hvit;
455 ++_hvit;
456 ++dest;
457 }
458
459 *length += _total;
460 return dest;
461}
462
463
464#define CLEAN_ASSIGN(added, var, field, header)\
465if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); }
466
467
468/**
469 * @brief Convert MSIMessage struct to _sendable_ string.
470 *
471 * @param msg The message.
472 * @param dest Destination.
473 * @return uint16_t It's final size.
474 */
475uint16_t message_to_string ( MSIMessage *msg, uint8_t *dest )
476{
477 assert ( msg );
478 assert ( dest );
479
480 uint8_t *_iterated = dest;
481 uint16_t _size = 0;
482
483 CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version );
484 CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request );
485 CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response );
486 CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype );
487 CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent );
488 CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info );
489 CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid );
490 CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason );
491 CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey );
492 CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce );
493
494 *_iterated = end_byte;
495 _size ++;
496
497 return _size;
498}
499
500
501#define GENERIC_SETTER_DEFINITION(header) \
502void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \
503{ assert(_msg); assert(header_value); \
504 free(_msg->header.header_value); \
505 ALLOCATE_HEADER( _msg->header, header_value, _size )}
506
507GENERIC_SETTER_DEFINITION ( calltype )
508GENERIC_SETTER_DEFINITION ( useragent )
509GENERIC_SETTER_DEFINITION ( reason )
510GENERIC_SETTER_DEFINITION ( info )
511GENERIC_SETTER_DEFINITION ( callid )
512GENERIC_SETTER_DEFINITION ( cryptokey )
513GENERIC_SETTER_DEFINITION ( nonce )
514
515
516/**
517 * @brief Generate _random_ alphanumerical string.
518 *
519 * @param str Destination.
520 * @param size Size of string.
521 * @return void
522 */
523void t_randomstr ( uint8_t *str, size_t size )
524{
525 assert ( str );
526
527 static const uint8_t _bytes[] =
528 "0123456789"
529 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
530 "abcdefghijklmnopqrstuvwxyz";
531
532 size_t _it = 0;
533
534 for ( ; _it < size; _it++ ) {
535 str[_it] = _bytes[ random_int() % 61 ];
536 }
537}
538
539
540typedef enum {
541 error_deadcall = 1, /* has call id but it's from old call */
542 error_id_mismatch, /* non-existing call */
543
544 error_no_callid, /* not having call id */
545 error_no_call, /* no call in session */
546 error_no_crypto_key, /* no crypto key */
547
548 error_busy
549
550} MSICallError; /* Error codes */
551
552
553/**
554 * @brief Stringify error code.
555 *
556 * @param error_code The code.
557 * @return const uint8_t* The string.
558 */
559static inline const uint8_t *stringify_error ( MSICallError error_code )
560{
561 static const uint8_t *strings[] = {
562 ( uint8_t * ) "",
563 ( uint8_t * ) "Using dead call",
564 ( uint8_t * ) "Call id not set to any call",
565 ( uint8_t * ) "Call id not available",
566 ( uint8_t * ) "No active call in session",
567 ( uint8_t * ) "No Crypto-key set",
568 ( uint8_t * ) "Callee busy"
569 };
570
571 return strings[error_code];
572}
573
574
575/**
576 * @brief Convert error_code into string.
577 *
578 * @param error_code The code.
579 * @return const uint8_t* The string.
580 */
581static inline const uint8_t *stringify_error_code ( MSICallError error_code )
582{
583 static const uint8_t *strings[] = {
584 ( uint8_t * ) "",
585 ( uint8_t * ) "1",
586 ( uint8_t * ) "2",
587 ( uint8_t * ) "3",
588 ( uint8_t * ) "4",
589 ( uint8_t * ) "5",
590 ( uint8_t * ) "6"
591 };
592
593 return strings[error_code];
594}
595
596
597/**
598 * @brief Speaks for it self.
599 *
600 * @param session Control session.
601 * @param msg The message.
602 * @param to Where to.
603 * @return int
604 * @retval -1 Error occured.
605 * @retval 0 Success.
606 */
607int send_message ( MSISession *session, MSIMessage *msg, uint32_t to )
608{
609 msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN );
610
611 uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
612 uint16_t _length = message_to_string ( msg, _msg_string_final );
613
614 return m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
615}
616
617
618/**
619 * @brief Speaks for it self.
620 *
621 * @param session Control session.
622 * @param msg The message.
623 * @param peer_id The peer.
624 * @return void
625 */
626void flush_peer_type ( MSISession *session, MSIMessage *msg, int peer_id )
627{
628 if ( msg->calltype.header_value ) {
629 if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
630 session->call->type_peer[peer_id] = type_audio;
631
632 } else if ( strcmp ( ( const char * ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
633 session->call->type_peer[peer_id] = type_video;
634 } else {} /* Error */
635 } else {} /* Error */
636}
637
638void handle_remote_connection_change(Messenger *messenger, int friend_num, uint8_t status, void *session_p)
639{
640 MSISession *session = session_p;
641
642 switch ( status ) {
643 case 0: { /* Went offline */
644 if ( session->call ) {
645 int i = 0;
646
647 for ( ; i < session->call->peer_count; i ++ )
648 if ( session->call->peers[i] == friend_num ) {
649 msi_stopcall(session); /* Stop the call for now */
650 return;
651 }
652 }
653 }
654 break;
655
656 default:
657 break;
658 }
659}
660
661/**
662 * @brief Sends error response to peer.
663 *
664 * @param session The session.
665 * @param errid The id.
666 * @param to Where to?
667 * @return int
668 * @retval 0 It's always success.
669 */
670int handle_error ( MSISession *session, MSICallError errid, uint32_t to )
671{
672 MSIMessage *_msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) );
673
674 const uint8_t *_error_code_str = stringify_error_code ( errid );
675
676 msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char * ) _error_code_str ) );
677 send_message ( session, _msg_error, to );
678 free_message ( _msg_error );
679
680 session->last_error_id = errid;
681 session->last_error_str = stringify_error ( errid );
682
683 event.rise ( callbacks[MSI_OnError], session->agent_handler );
684
685 return 0;
686}
687
688
689/**
690 * @brief Determine the error if any.
691 *
692 * @param session Control session.
693 * @param msg The message.
694 * @return int
695 * @retval -1 No error.
696 * @retval 0 Error occured and response sent.
697 */
698int has_call_error ( MSISession *session, MSIMessage *msg )
699{
700 if ( !msg->callid.header_value ) {
701 return handle_error ( session, error_no_callid, msg->friend_id );
702
703 } else if ( !session->call ) {
704 return handle_error ( session, error_no_call, msg->friend_id );
705
706 } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) {
707 return handle_error ( session, error_id_mismatch, msg->friend_id );
708
709 }
710
711 return -1;
712}
713
714
715/**
716 * @brief Function called at request timeout.
717 *
718 * @param arg Control session
719 * @return void*
720 */
721void *handle_timeout ( void *arg )
722{
723 /* Send hangup either way */
724 MSISession *_session = arg;
725
726 if ( _session && _session->call ) {
727
728 uint32_t *_peers = _session->call->peers;
729 uint16_t _peer_count = _session->call->peer_count;
730
731
732 /* Cancel all? */
733 uint16_t _it = 0;
734
735 for ( ; _it < _peer_count; _it++ )
736 msi_cancel ( arg, _peers[_it], (const uint8_t *)"Timeout" );
737
738 }
739
740 ( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler );
741 ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
742
743 return NULL;
744}
745
746
747/**
748 * @brief Add peer to peer list.
749 *
750 * @param call What call.
751 * @param peer_id Its id.
752 * @return void
753 */
754void add_peer( MSICall *call, int peer_id )
755{
756 if ( !call->peers ) {
757 call->peers = calloc(sizeof(int), 1);
758 call->peer_count = 1;
759 } else {
760 call->peer_count ++;
761 call->peers = realloc( call->peers, sizeof(int) * call->peer_count);
762 }
763
764 call->peers[call->peer_count - 1] = peer_id;
765}
766
767
768/**
769 * @brief Speaks for it self.
770 *
771 * @param session Control session.
772 * @param peers Amount of peers. (Currently it only supports 1)
773 * @param ringing_timeout Ringing timeout.
774 * @return MSICall* The created call.
775 */
776MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
777{
778 assert ( session );
779 assert ( peers );
780
781 MSICall *_call = calloc ( sizeof ( MSICall ), 1 );
782 _call->type_peer = calloc ( sizeof ( MSICallType ), peers );
783
784 assert ( _call );
785 assert ( _call->type_peer );
786
787 /*_call->_participant_count = _peers;*/
788
789 _call->request_timer_id = 0;
790 _call->ringing_timer_id = 0;
791
792 _call->key_local = NULL;
793 _call->key_peer = NULL;
794 _call->nonce_local = NULL;
795 _call->nonce_peer = NULL;
796
797 _call->ringing_tout_ms = ringing_timeout;
798
799 pthread_mutex_init ( &_call->mutex, NULL );
800
801 return _call;
802}
803
804
805/**
806 * @brief Terminate the call.
807 *
808 * @param session Control session.
809 * @return int
810 * @retval -1 Error occured.
811 * @retval 0 Success.
812 */
813int terminate_call ( MSISession *session )
814{
815 assert ( session );
816
817 if ( !session->call )
818 return -1;
819
820
821 /* Check event loop and cancel timed events if there are any
822 * NOTE: This has to be done before possibly
823 * locking the mutex the second time
824 */
825 event.timer_release ( session->call->request_timer_id );
826 event.timer_release ( session->call->ringing_timer_id );
827
828 /* Get a handle */
829 pthread_mutex_lock ( &session->call->mutex );
830
831 MSICall *_call = session->call;
832 session->call = NULL;
833
834 free ( _call->type_peer );
835 free ( _call->key_local );
836 free ( _call->key_peer );
837 free ( _call->peers);
838
839 /* Release handle */
840 pthread_mutex_unlock ( &_call->mutex );
841
842 pthread_mutex_destroy ( &_call->mutex );
843
844 free ( _call );
845
846 return 0;
847}
848
849
850/********** Request handlers **********/
851int handle_recv_invite ( MSISession *session, MSIMessage *msg )
852{
853 assert ( session );
854
855 if ( session->call ) {
856 handle_error ( session, error_busy, msg->friend_id );
857 return 0;
858 }
859
860 if ( !msg->callid.header_value ) {
861 handle_error ( session, error_no_callid, msg->friend_id );
862 return 0;
863 }
864
865 session->call = init_call ( session, 1, 0 );
866 memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN );
867 session->call->state = call_starting;
868
869 add_peer( session->call, msg->friend_id);
870
871 flush_peer_type ( session, msg, 0 );
872
873 MSIMessage *_msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) );
874 send_message ( session, _msg_ringing, msg->friend_id );
875 free_message ( _msg_ringing );
876
877 event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
878
879 return 1;
880}
881int handle_recv_start ( MSISession *session, MSIMessage *msg )
882{
883 assert ( session );
884
885 if ( has_call_error ( session, msg ) == 0 )
886 return 0;
887
888 if ( !msg->cryptokey.header_value )
889 return handle_error ( session, error_no_crypto_key, msg->friend_id );
890
891 session->call->state = call_active;
892
893 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
894 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
895
896 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
897 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
898
899 flush_peer_type ( session, msg, 0 );
900
901 event.rise ( callbacks[MSI_OnStart], session->agent_handler );
902
903 return 1;
904}
905int handle_recv_reject ( MSISession *session, MSIMessage *msg )
906{
907 assert ( session );
908
909 if ( has_call_error ( session, msg ) == 0 )
910 return 0;
911
912
913 MSIMessage *_msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
914 send_message ( session, _msg_end, msg->friend_id );
915 free_message ( _msg_end );
916
917 event.timer_release ( session->call->request_timer_id );
918 event.rise ( callbacks[MSI_OnReject], session->agent_handler );
919 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
920
921 return 1;
922}
923int handle_recv_cancel ( MSISession *session, MSIMessage *msg )
924{
925 assert ( session );
926
927 if ( has_call_error ( session, msg ) == 0 )
928 return 0;
929
930
931 terminate_call ( session );
932
933 event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
934
935 return 1;
936}
937int handle_recv_end ( MSISession *session, MSIMessage *msg )
938{
939 assert ( session );
940
941 if ( has_call_error ( session, msg ) == 0 )
942 return 0;
943
944
945 MSIMessage *_msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) );
946 send_message ( session, _msg_ending, msg->friend_id );
947 free_message ( _msg_ending );
948
949 terminate_call ( session );
950
951 event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
952
953 return 1;
954}
955
956/********** Response handlers **********/
957int handle_recv_ringing ( MSISession *session, MSIMessage *msg )
958{
959 assert ( session );
960
961 if ( has_call_error ( session, msg ) == 0 )
962 return 0;
963
964 session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
965 event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
966
967 return 1;
968}
969int handle_recv_starting ( MSISession *session, MSIMessage *msg )
970{
971 assert ( session );
972
973 if ( has_call_error ( session, msg ) == 0 )
974 return 0;
975
976 if ( !msg->cryptokey.header_value ) {
977 return handle_error ( session, error_no_crypto_key, msg->friend_id );
978 }
979
980 /* Generate local key/nonce to send */
981 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
982 new_symmetric_key ( session->call->key_local );
983
984 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
985 new_nonce ( session->call->nonce_local );
986
987 /* Save peer key/nonce */
988 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
989 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
990
991 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
992 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
993
994 session->call->state = call_active;
995
996 MSIMessage *_msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) );
997 msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES );
998 msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES );
999 send_message ( session, _msg_start, msg->friend_id );
1000 free_message ( _msg_start );
1001
1002 flush_peer_type ( session, msg, 0 );
1003
1004 event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
1005 event.timer_release ( session->call->ringing_timer_id );
1006
1007 return 1;
1008}
1009int handle_recv_ending ( MSISession *session, MSIMessage *msg )
1010{
1011 assert ( session );
1012
1013 if ( has_call_error ( session, msg ) == 0 )
1014 return 0;
1015
1016
1017 terminate_call ( session );
1018
1019 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
1020
1021 return 1;
1022}
1023int handle_recv_error ( MSISession *session, MSIMessage *msg )
1024{
1025 assert ( session );
1026 assert ( session->call );
1027
1028 /* Handle error accordingly */
1029 if ( msg->reason.header_value ) {
1030 session->last_error_id = atoi ( ( const char * ) msg->reason.header_value );
1031 session->last_error_str = stringify_error ( session->last_error_id );
1032 }
1033
1034 terminate_call ( session );
1035
1036 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
1037
1038 return 1;
1039}
1040
1041
1042/**
1043 * @brief BASIC call flow:
1044 *
1045 * ALICE BOB
1046 * | invite --> |
1047 * | |
1048 * | <-- ringing |
1049 * | |
1050 * | <-- starting |
1051 * | |
1052 * | start --> |
1053 * | |
1054 * | <-- MEDIA TRANS --> |
1055 * | |
1056 * | end --> |
1057 * | |
1058 * | <-- ending |
1059 *
1060 * Alice calls Bob by sending invite packet.
1061 * Bob recvs the packet and sends an ringing packet;
1062 * which notifies Alice that her invite is acknowledged.
1063 * Ringing screen shown on both sides.
1064 * Bob accepts the invite for a call by sending starting packet.
1065 * Alice recvs the starting packet and sends the started packet to
1066 * inform Bob that she recved the starting packet.
1067 * Now the media transmission is established ( i.e. RTP transmission ).
1068 * Alice hangs up and sends end packet.
1069 * Bob recves the end packet and sends ending packet
1070 * as the acknowledgement that the call is ending.
1071 *
1072 *
1073 */
1074void msi_handle_packet ( Messenger *messenger, int source, uint8_t *data, uint16_t length, void *object )
1075{
1076 /* Unused */
1077 (void)messenger;
1078
1079 MSISession *_session = object;
1080 MSIMessage *_msg;
1081
1082 if ( !length ) return;
1083
1084 _msg = parse_message ( data, length );
1085
1086 if ( !_msg ) return;
1087
1088 _msg->friend_id = source;
1089
1090
1091 /* Now handle message */
1092
1093 if ( _msg->request.header_value ) { /* Handle request */
1094
1095 const uint8_t *_request_value = _msg->request.header_value;
1096
1097 if ( same ( _request_value, stringify_request ( invite ) ) ) {
1098 handle_recv_invite ( _session, _msg );
1099
1100 } else if ( same ( _request_value, stringify_request ( start ) ) ) {
1101 handle_recv_start ( _session, _msg );
1102
1103 } else if ( same ( _request_value, stringify_request ( cancel ) ) ) {
1104 handle_recv_cancel ( _session, _msg );
1105
1106 } else if ( same ( _request_value, stringify_request ( reject ) ) ) {
1107 handle_recv_reject ( _session, _msg );
1108
1109 } else if ( same ( _request_value, stringify_request ( end ) ) ) {
1110 handle_recv_end ( _session, _msg );
1111 }
1112
1113 else {
1114 free_message ( _msg );
1115 return;
1116 }
1117
1118 } else if ( _msg->response.header_value ) { /* Handle response */
1119
1120 const uint8_t *_response_value = _msg->response.header_value;
1121
1122 if ( same ( _response_value, stringify_response ( ringing ) ) ) {
1123 handle_recv_ringing ( _session, _msg );
1124
1125 } else if ( same ( _response_value, stringify_response ( starting ) ) ) {
1126 handle_recv_starting ( _session, _msg );
1127
1128 } else if ( same ( _response_value, stringify_response ( ending ) ) ) {
1129 handle_recv_ending ( _session, _msg );
1130
1131 } else if ( same ( _response_value, stringify_response ( error ) ) ) {
1132 handle_recv_error ( _session, _msg );
1133 } else {
1134 free_message ( _msg );
1135 return;
1136 }
1137
1138 /* Got response so cancel timer */
1139 if ( _session->call )
1140 event.timer_release ( _session->call->request_timer_id );
1141
1142 }
1143
1144 free_message ( _msg );
1145}
1146
1147
1148/********************************************************************************************************************
1149 * *******************************************************************************************************************
1150 ********************************************************************************************************************
1151 ********************************************************************************************************************
1152 ********************************************************************************************************************
1153 *
1154 *
1155 *
1156 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
1157 *
1158 *
1159 *
1160 ********************************************************************************************************************
1161 ********************************************************************************************************************
1162 ********************************************************************************************************************
1163 ********************************************************************************************************************
1164 ********************************************************************************************************************/
1165
1166
1167
1168
1169
1170
1171
1172
1173/**
1174 * @brief Callback setter.
1175 *
1176 * @param callback The callback.
1177 * @param id The id.
1178 * @return void
1179 */
1180void msi_register_callback ( MSICallback callback, MSICallbackID id )
1181{
1182 callbacks[id] = callback;
1183}
1184
1185
1186/**
1187 * @brief Start the control session.
1188 *
1189 * @param messenger Tox* object.
1190 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
1191 * @return MSISession* The created session.
1192 * @retval NULL Error occured.
1193 */
1194MSISession *msi_init_session ( Messenger *messenger, const uint8_t *ua_name )
1195{
1196 assert ( messenger );
1197
1198 MSISession *_retu = calloc ( sizeof ( MSISession ), 1 );
1199 assert ( _retu );
1200
1201 _retu->ua_name = ua_name;
1202 _retu->messenger_handle = messenger;
1203 _retu->agent_handler = NULL;
1204
1205 _retu->call = NULL;
1206
1207 _retu->frequ = 10000; /* default value? */
1208 _retu->call_timeout = 30000; /* default value? */
1209
1210
1211 m_callback_msi_packet(messenger, msi_handle_packet, _retu );
1212
1213 /* This is called when remote terminates session */
1214 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, _retu);
1215
1216 return _retu;
1217}
1218
1219
1220/**
1221 * @brief Terminate control session.
1222 *
1223 * @param session The session
1224 * @return int
1225 */
1226int msi_terminate_session ( MSISession *session )
1227{
1228 assert ( session );
1229
1230 int _status = 0;
1231
1232 terminate_call ( session );
1233 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
1234
1235
1236 /* TODO: Clean it up more? */
1237
1238 free ( session );
1239 return _status;
1240}
1241
1242
1243/**
1244 * @brief Send invite request to friend_id.
1245 *
1246 * @param session Control session.
1247 * @param call_type Type of the call. Audio or Video(both audio and video)
1248 * @param rngsec Ringing timeout.
1249 * @param friend_id The friend.
1250 * @return int
1251 */
1252int msi_invite ( MSISession *session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id )
1253{
1254 assert ( session );
1255
1256 MSIMessage *_msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) );
1257
1258 session->call = init_call ( session, 1, rngsec ); /* Just one for now */
1259 t_randomstr ( session->call->id, CALL_ID_LEN );
1260
1261 add_peer(session->call, friend_id );
1262
1263 session->call->type_local = call_type;
1264 /* Do whatever with message */
1265
1266 if ( call_type == type_audio ) {
1267 msi_msg_set_calltype
1268 ( _msg_invite, ( const uint8_t * ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1269 } else {
1270 msi_msg_set_calltype
1271 ( _msg_invite, ( const uint8_t * ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1272 }
1273
1274 send_message ( session, _msg_invite, friend_id );
1275 free_message ( _msg_invite );
1276
1277 session->call->state = call_inviting;
1278
1279 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1280
1281 return 0;
1282}
1283
1284
1285/**
1286 * @brief Hangup active call.
1287 *
1288 * @param session Control session.
1289 * @return int
1290 * @retval -1 Error occured.
1291 * @retval 0 Success.
1292 */
1293int msi_hangup ( MSISession *session )
1294{
1295 assert ( session );
1296
1297 if ( !session->call || session->call->state != call_active )
1298 return -1;
1299
1300 MSIMessage *_msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
1301
1302 /* hangup for each peer */
1303 int _it = 0;
1304
1305 for ( ; _it < session->call->peer_count; _it ++ )
1306 send_message ( session, _msg_ending, session->call->peers[_it] );
1307
1308
1309 free_message ( _msg_ending );
1310
1311 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1312
1313 return 0;
1314}
1315
1316
1317/**
1318 * @brief Answer active call request.
1319 *
1320 * @param session Control session.
1321 * @param call_type Answer with Audio or Video(both).
1322 * @return int
1323 */
1324int msi_answer ( MSISession *session, MSICallType call_type )
1325{
1326 assert ( session );
1327
1328 MSIMessage *_msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) );
1329 session->call->type_local = call_type;
1330
1331 if ( call_type == type_audio ) {
1332 msi_msg_set_calltype
1333 ( _msg_starting, ( const uint8_t * ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1334 } else {
1335 msi_msg_set_calltype
1336 ( _msg_starting, ( const uint8_t * ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1337 }
1338
1339 /* Now set the local encryption key and pass it with STARTING message */
1340
1341 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1342 new_symmetric_key ( session->call->key_local );
1343
1344 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1345 new_nonce ( session->call->nonce_local );
1346
1347 msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES );
1348 msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES );
1349
1350 send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] );
1351 free_message ( _msg_starting );
1352
1353 session->call->state = call_active;
1354
1355 return 0;
1356}
1357
1358
1359/**
1360 * @brief Cancel request.
1361 *
1362 * @param session Control session.
1363 * @param reason Set optional reason header. Pass NULL if none.
1364 * @return int
1365 */
1366int msi_cancel ( MSISession *session, uint32_t peer, const uint8_t *reason )
1367{
1368 assert ( session );
1369
1370 MSIMessage *_msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
1371
1372 if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char *)reason));
1373
1374 send_message ( session, _msg_cancel, peer );
1375 free_message ( _msg_cancel );
1376
1377 terminate_call ( session );
1378
1379 return 0;
1380}
1381
1382
1383/**
1384 * @brief Reject request.
1385 *
1386 * @param session Control session.
1387 * @return int
1388 */
1389int msi_reject ( MSISession *session, const uint8_t *reason )
1390{
1391 assert ( session );
1392
1393 MSIMessage *_msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) );
1394
1395 if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char *)reason) + 1);
1396
1397 send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] );
1398 free_message ( _msg_reject );
1399
1400 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1401
1402 return 0;
1403}
1404
1405
1406/**
1407 * @brief Terminate the current call.
1408 *
1409 * @param session Control session.
1410 * @return int
1411 */
1412int msi_stopcall ( MSISession *session )
1413{
1414 assert ( session );
1415
1416 if ( !session->call )
1417 return -1;
1418
1419 /* just terminate it */
1420
1421 terminate_call ( session );
1422
1423 return 0;
1424} \ No newline at end of file
diff --git a/toxav/msi.h b/toxav/msi.h
new file mode 100644
index 00000000..84a30f63
--- /dev/null
+++ b/toxav/msi.h
@@ -0,0 +1,233 @@
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 at #tox-dev @ freenode.net:6667
22 */
23
24#ifndef __TOXMSI
25#define __TOXMSI
26
27#include <inttypes.h>
28#include <pthread.h>
29
30#include "../toxcore/Messenger.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 *ua_name;
107
108 void *agent_handler; /* Pointer to an object that is handling msi */
109 Messenger *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 MSI_OnInvite,
124 MSI_OnStart,
125 MSI_OnCancel,
126 MSI_OnReject,
127 MSI_OnEnd,
128
129 /* Responses */
130 MSI_OnRinging,
131 MSI_OnStarting,
132 MSI_OnEnding,
133
134 /* Protocol */
135 MSI_OnError,
136 MSI_OnRequestTimeout
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 ( Messenger *messenger, const uint8_t *ua_name );
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 peer To which peer.
209 * @param reason Set optional reason header. Pass NULL if none.
210 * @return int
211 */
212int msi_cancel ( MSISession *session, uint32_t peer, const uint8_t *reason );
213
214
215/**
216 * @brief Reject request.
217 *
218 * @param session Control session.
219 * @param reason Set optional reason header. Pass NULL if none.
220 * @return int
221 */
222int msi_reject ( MSISession *session, const uint8_t *reason );
223
224
225/**
226 * @brief Terminate the current call.
227 *
228 * @param session Control session.
229 * @return int
230 */
231int msi_stopcall ( MSISession *session );
232
233#endif /* __TOXMSI */
diff --git a/toxav/phone.c b/toxav/phone.c
new file mode 100644
index 00000000..ec7dd143
--- /dev/null
+++ b/toxav/phone.c
@@ -0,0 +1,1352 @@
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#include <stdarg.h>
43#include <unistd.h>
44#include <assert.h>
45#include <math.h>
46#include <pthread.h>
47
48//#include "media.h"
49#include "toxav.h"
50#include "event.h"
51#include "../toxcore/tox.h"
52
53#ifdef TOX_FFMPEG
54/* Video encoding/decoding */
55#include <libavcodec/avcodec.h>
56#include <libavformat/avformat.h>
57#include <libswscale/swscale.h>
58#include <libavdevice/avdevice.h>
59#include <libavutil/opt.h>
60#endif
61
62#include <AL/al.h>
63#include <AL/alc.h>
64#include <SDL/SDL.h>
65#include <SDL/SDL_thread.h>
66
67/* the quit event for SDL */
68#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
69
70#ifdef __linux__
71#define VIDEO_DRIVER "video4linux2"
72#define DEFAULT_WEBCAM "/dev/video0"
73#endif
74
75#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
76#define VIDEO_DRIVER "vfwcap"
77#define DEFAULT_WEBCAM "0"
78#endif
79
80
81/* Define client version */
82#define _USERAGENT "v.0.3.0"
83
84
85struct SDL_Surface *screen;
86
87typedef struct {
88 struct SDL_Overlay *bmp;
89 int width, height;
90} VideoPicture;
91
92
93typedef struct av_friend_s {
94 int _id;
95 int _active; /* 0=false; 1=true; */
96} av_friend_t;
97
98typedef struct av_session_s {
99 /* Encoding/decoding/capturing/playing */
100 ToxAv *av;
101
102 VideoPicture video_picture;
103 struct ALCdevice *audio_capture_device;
104
105 /* context for converting image format to something SDL can use*/
106 struct SwsContext *sws_SDL_r_ctx;
107
108 /* context for converting webcam image format to something the video encoder can use */
109 struct SwsContext *sws_ctx;
110
111 /* Thread running control */
112 int running_decaud, running_encaud,
113 running_decvid, running_encvid;
114
115 pthread_mutex_t _mutex;
116
117 Tox *_messenger;
118 av_friend_t *_friends;
119 int _friend_cout;
120 char _my_public_id[200];
121#ifdef TOX_FFMPEG
122 AVInputFormat *video_input_format;
123 AVFormatContext *video_format_ctx;
124 uint8_t video_stream;
125 AVCodecContext *webcam_decoder_ctx;
126 AVCodec *webcam_decoder;
127#endif
128} av_session_t;
129
130
131void av_allocate_friend(av_session_t *_phone, int _id, int _active)
132{
133 static int _new_id = 0;
134
135 if ( !_phone->_friends ) {
136 _phone->_friends = calloc(sizeof(av_friend_t), 1);
137 _phone->_friend_cout = 1;
138 } else {
139 _phone->_friend_cout ++;
140 _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout);
141 }
142
143 if ( _id == -1 ) {
144 _phone->_friends->_id = _new_id;
145 _new_id ++;
146 } else _phone->_friends->_id = _id;
147
148 _phone->_friends->_active = _active;
149}
150av_friend_t *av_get_friend(av_session_t *_phone, int _id)
151{
152 av_friend_t *_friends = _phone->_friends;
153
154 if ( !_friends ) return NULL;
155
156 int _it = 0;
157
158 for (; _it < _phone->_friend_cout; _it ++)
159 if ( _friends[_it]._id == _id )
160 return _friends + _it;
161
162 return NULL;
163}
164
165
166/***************** MISC *****************/
167
168void INFO (const char *_format, ...)
169{
170 printf("\r[!] ");
171 va_list _arg;
172 va_start (_arg, _format);
173 vfprintf (stdout, _format, _arg);
174 va_end (_arg);
175 printf("\n\r >> ");
176 fflush(stdout);
177}
178
179unsigned char *hex_string_to_bin(char hex_string[])
180{
181 size_t i, len = strlen(hex_string);
182 unsigned char *val = calloc(sizeof(unsigned char), len);
183 char *pos = hex_string;
184
185 for (i = 0; i < len; ++i, pos += 2)
186 sscanf(pos, "%2hhx", &val[i]);
187
188 return val;
189}
190
191int getinput( char *_buff, size_t _limit, int *_len )
192{
193 if ( fgets(_buff, _limit, stdin) == NULL )
194 return -1;
195
196 *_len = strlen(_buff) - 1;
197
198 /* Get rid of newline */
199 _buff[*_len] = '\0';
200
201 return 0;
202}
203
204char *trim_spaces ( char *buff )
205{
206
207 int _i = 0, _len = strlen(buff);
208
209 char *container = calloc(sizeof(char), _len);
210 int _ci = 0;
211
212 for ( ; _i < _len; _i++ ) {
213 while ( _i < _len && buff[_i] == ' ' )
214 _i++;
215
216 if ( _i < _len ) {
217 container[_ci] = buff[_i];
218 _ci ++;
219 }
220 }
221
222 memcpy( buff, container, _ci );
223 buff[_ci] = '\0';
224 free(container);
225 return buff;
226}
227
228#define FRADDR_TOSTR_CHUNK_LEN 8
229
230static void fraddr_to_str(uint8_t *id_bin, char *id_str)
231{
232 uint i, delta = 0, pos_extra = 0, sum_extra = 0;
233
234 for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
235 sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
236
237 if ((i + 1) == TOX_CLIENT_ID_SIZE)
238 pos_extra = 2 * (i + 1) + delta;
239
240 if (i >= TOX_CLIENT_ID_SIZE)
241 sum_extra |= id_bin[i];
242
243 if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
244 id_str[2 * (i + 1) + delta] = ' ';
245 delta++;
246 }
247 }
248
249 id_str[2 * i + delta] = 0;
250
251 if (!sum_extra)
252 id_str[pos_extra] = 0;
253}
254
255/*********************************************
256 *********************************************
257 *********************************************
258 *********************************************
259 *********************************************
260 *********************************************
261 *********************************************
262 *********************************************
263 */
264
265
266/*
267 * How av stuff _should_ look like
268 */
269/*
270int display_received_frame(av_session_t* _phone, vpx_image_t *image)
271{
272 CodecState* cs = get_cs_temp(_phone->av);
273 AVPicture pict;
274 SDL_LockYUVOverlay(_phone->video_picture.bmp);
275
276 pict.data[0] = _phone->video_picture.bmp->pixels[0];
277 pict.data[1] = _phone->video_picture.bmp->pixels[2];
278 pict.data[2] = _phone->video_picture.bmp->pixels[1];
279 pict.linesize[0] = _phone->video_picture.bmp->pitches[0];
280 pict.linesize[1] = _phone->video_picture.bmp->pitches[2];
281 pict.linesize[2] = _phone->video_picture.bmp->pitches[1];
282 */
283/* Convert the image into YUV format that SDL uses *//*
284sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
285 cs->video_decoder_ctx->height, pict.data, pict.linesize );
286
287SDL_UnlockYUVOverlay(_phone->video_picture.bmp);
288SDL_Rect rect;
289rect.x = 0;
290rect.y = 0;
291rect.w = cs->video_decoder_ctx->width;
292rect.h = cs->video_decoder_ctx->height;
293SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);
294return 1;
295}
296*/
297#ifdef TOX_FFMPEG
298void *encode_video_thread(void *arg)
299{
300 INFO("Started encode video thread!");
301
302 av_session_t *_phone = arg;
303
304 _phone->running_encvid = 1;
305 //CodecState *cs = get_cs_temp(_phone->av);
306 AVPacket pkt1, *packet = &pkt1;
307 //int p = 0;
308 //int got_packet;
309 int video_frame_finished;
310 AVFrame *s_video_frame;
311 AVFrame *webcam_frame;
312 s_video_frame = avcodec_alloc_frame();
313 webcam_frame = avcodec_alloc_frame();
314 //AVPacket enc_video_packet;
315
316 uint8_t *buffer;
317 int numBytes;
318 /* Determine required buffer size and allocate buffer */
319 numBytes = avpicture_get_size(PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height);
320 buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t), 1);
321 avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width,
322 _phone->webcam_decoder_ctx->height);
323 _phone->sws_ctx = sws_getContext(_phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height,
324 _phone->webcam_decoder_ctx->pix_fmt, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height,
325 PIX_FMT_YUV420P,
326 SWS_BILINEAR, NULL, NULL, NULL);
327
328
329 vpx_image_t *image =
330 vpx_img_alloc(NULL, VPX_IMG_FMT_I420, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, 1);
331
332 //uint32_t frame_counter = 0;
333 while (_phone->running_encvid) {
334
335 if (av_read_frame(_phone->video_format_ctx, packet) < 0) {
336 printf("error reading frame\n");
337
338 if (_phone->video_format_ctx->pb->error != 0)
339 break;
340
341 continue;
342 }
343
344 if (packet->stream_index == _phone->video_stream) {
345 if (avcodec_decode_video2(_phone->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
346 printf("couldn't decode\n");
347 continue;
348 }
349
350 av_free_packet(packet);
351 sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
352 _phone->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
353 /* create a new I-frame every 60 frames */
354 //++p;
355 /*
356 if (p == 60) {
357
358 s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
359 } else if (p == 61) {
360 s_video_frame->pict_type = AV_PICTURE_TYPE_I ;
361 p = 0;
362 } else {
363 s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
364 }*/
365
366 if (video_frame_finished) {
367 memcpy(image->planes[VPX_PLANE_Y], s_video_frame->data[0],
368 s_video_frame->linesize[0] * _phone->webcam_decoder_ctx->height);
369 memcpy(image->planes[VPX_PLANE_U], s_video_frame->data[1],
370 s_video_frame->linesize[1] * _phone->webcam_decoder_ctx->height / 2);
371 memcpy(image->planes[VPX_PLANE_V], s_video_frame->data[2],
372 s_video_frame->linesize[2] * _phone->webcam_decoder_ctx->height / 2);
373 toxav_send_video (_phone->av, image);
374 //if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) {
375 /*if (vpx_codec_encode(&cs->v_encoder, image, frame_counter, 1, 0, 0) != VPX_CODEC_OK) {
376 printf("could not encode video frame\n");
377 continue;
378 }
379 ++frame_counter;
380
381 vpx_codec_iter_t iter = NULL;
382 vpx_codec_cx_pkt_t *pkt;
383 while( (pkt = vpx_codec_get_cx_data(&cs->v_encoder, &iter)) ) {
384 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
385 toxav_send_rtp_payload(_phone->av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz);
386 }*/
387 //if (!got_packet) {
388 // continue;
389 //}
390
391 //if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
392
393 //toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
394
395 //av_free_packet(&enc_video_packet);
396 }
397 } else {
398 av_free_packet(packet);
399 }
400 }
401
402 vpx_img_free(image);
403
404 /* clean up codecs */
405 //pthread_mutex_lock(&cs->ctrl_mutex);
406 av_free(buffer);
407 av_free(webcam_frame);
408 av_free(s_video_frame);
409 sws_freeContext(_phone->sws_ctx);
410 //avcodec_close(webcam_decoder_ctx);
411 //avcodec_close(cs->video_encoder_ctx);
412 //pthread_mutex_unlock(&cs->ctrl_mutex);
413
414 _phone->running_encvid = -1;
415
416 pthread_exit ( NULL );
417}
418#endif
419
420void *encode_audio_thread(void *arg)
421{
422 INFO("Started encode audio thread!");
423 av_session_t *_phone = arg;
424 _phone->running_encaud = 1;
425
426 int ret = 0;
427 int16_t frame[4096];
428 int frame_size = AUDIO_FRAME_SIZE;
429 ALint sample = 0;
430 alcCaptureStart((ALCdevice *)_phone->audio_capture_device);
431
432 while (_phone->running_encaud) {
433 alcGetIntegerv((ALCdevice *)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
434
435 if (sample >= frame_size) {
436 alcCaptureSamples((ALCdevice *)_phone->audio_capture_device, frame, frame_size);
437
438 ret = toxav_send_audio(_phone->av, frame, frame_size);
439
440 if (ret < 0)
441 printf("Could not encode or send audio packet\n");
442
443 } else {
444 usleep(1000);
445 }
446 }
447
448 /* clean up codecs *
449 pthread_mutex_lock(&cs->ctrl_mutex);* /
450 alcCaptureStop((ALCdevice*)_phone->audio_capture_device);
451 alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device);
452 / *pthread_mutex_unlock(&cs->ctrl_mutex);*/
453 _phone->running_encaud = -1;
454 pthread_exit ( NULL );
455}
456
457void convert_to_rgb(vpx_image_t *img, unsigned char *out)
458{
459 const int w = img->d_w;
460 const int w2 = w / 2;
461 const int pstride = w * 3;
462 const int h = img->d_h;
463 const int h2 = h / 2;
464
465 const int strideY = img->stride[0];
466 const int strideU = img->stride[1];
467 const int strideV = img->stride[2];
468 int posy, posx;
469
470 for (posy = 0; posy < h2; posy++) {
471 unsigned char *dst = out + pstride * (posy * 2);
472 unsigned char *dst2 = out + pstride * (posy * 2 + 1);
473 const unsigned char *srcY = img->planes[0] + strideY * posy * 2;
474 const unsigned char *srcY2 = img->planes[0] + strideY * (posy * 2 + 1);
475 const unsigned char *srcU = img->planes[1] + strideU * posy;
476 const unsigned char *srcV = img->planes[2] + strideV * posy;
477
478 for (posx = 0; posx < w2; posx++) {
479 unsigned char Y, U, V;
480 short R, G, B;
481 short iR, iG, iB;
482
483 U = *(srcU++);
484 V = *(srcV++);
485 iR = (351 * (V - 128)) / 256;
486 iG = - (179 * (V - 128)) / 256 - (86 * (U - 128)) / 256;
487 iB = (444 * (U - 128)) / 256;
488
489 Y = *(srcY++);
490 R = Y + iR ;
491 G = Y + iG ;
492 B = Y + iB ;
493 R = (R < 0 ? 0 : (R > 255 ? 255 : R));
494 G = (G < 0 ? 0 : (G > 255 ? 255 : G));
495 B = (B < 0 ? 0 : (B > 255 ? 255 : B));
496 *(dst++) = R;
497 *(dst++) = G;
498 *(dst++) = B;
499
500 Y = *(srcY2++);
501 R = Y + iR ;
502 G = Y + iG ;
503 B = Y + iB ;
504 R = (R < 0 ? 0 : (R > 255 ? 255 : R));
505 G = (G < 0 ? 0 : (G > 255 ? 255 : G));
506 B = (B < 0 ? 0 : (B > 255 ? 255 : B));
507 *(dst2++) = R;
508 *(dst2++) = G;
509 *(dst2++) = B;
510
511 Y = *(srcY++) ;
512 R = Y + iR ;
513 G = Y + iG ;
514 B = Y + iB ;
515 R = (R < 0 ? 0 : (R > 255 ? 255 : R));
516 G = (G < 0 ? 0 : (G > 255 ? 255 : G));
517 B = (B < 0 ? 0 : (B > 255 ? 255 : B));
518 *(dst++) = R;
519 *(dst++) = G;
520 *(dst++) = B;
521
522 Y = *(srcY2++);
523 R = Y + iR ;
524 G = Y + iG ;
525 B = Y + iB ;
526 R = (R < 0 ? 0 : (R > 255 ? 255 : R));
527 G = (G < 0 ? 0 : (G > 255 ? 255 : G));
528 B = (B < 0 ? 0 : (B > 255 ? 255 : B));
529 *(dst2++) = R;
530 *(dst2++) = G;
531 *(dst2++) = B;
532 }
533 }
534}
535
536#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff })
537
538void *decode_video_thread(void *arg)
539{
540 INFO("Started decode video thread!");
541 av_session_t *_phone = arg;
542 _phone->running_decvid = 1;
543
544 //CodecState *cs = get_cs_temp(_phone->av);
545 //cs->video_stream = 0;
546
547 //int recved_size;
548 //uint8_t dest[RTP_PAYLOAD_SIZE];
549
550 //int dec_frame_finished;
551 //AVFrame *r_video_frame;
552 //r_video_frame = avcodec_alloc_frame();
553 //AVPacket dec_video_packet;
554 //av_new_packet (&dec_video_packet, 65536);
555 int width = 0;
556 int height = 0;
557
558 while (_phone->running_decvid) {
559 //recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, dest);
560 //if (recved_size) {
561 vpx_image_t *image;
562
563 if (toxav_recv_video(_phone->av, &image) == 0) {
564 //memcpy(dec_video_packet.data, dest, recved_size);
565 //dec_video_packet.size = recved_size;
566
567 //avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
568
569 //if (dec_frame_finished) {
570
571 /* Check if size has changed */
572 if (image->d_w != width || image->d_h != height) {
573
574 width = image->d_w;
575 height = image->d_h;
576
577 printf("w: %d h: %d \n", width, height);
578
579 screen = SDL_SetVideoMode(width, height, 0, 0);
580
581 //if (_phone->video_picture.bmp)
582 // SDL_FreeYUVOverlay(_phone->video_picture.bmp);
583
584 //_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
585 // _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
586 // SWS_BILINEAR, NULL, NULL, NULL);
587 }
588
589 uint8_t *rgb_image = malloc(width * height * 3);
590 convert_to_rgb(image, rgb_image);
591 SDL_Surface *img_surface = SDL_CreateRGBSurfaceFrom(rgb_image, width, height, 24, width * 3, mask32(0), mask32(1),
592 mask32(2), 0);
593
594 if (SDL_BlitSurface(img_surface, NULL, screen, NULL) == 0)
595 SDL_UpdateRect(screen, 0, 0, 0, 0);
596
597 /*
598 SDL_LockYUVOverlay(_phone->video_picture.bmp);
599 memcpy(_phone->video_picture.bmp->pixels[0], image->planes[VPX_PLANE_Y], _phone->video_picture.bmp->pitches[0] * height);
600 memcpy(_phone->video_picture.bmp->pixels[1], image->planes[VPX_PLANE_V], _phone->video_picture.bmp->pitches[1] * height / 2);
601 memcpy(_phone->video_picture.bmp->pixels[2], image->planes[VPX_PLANE_U], _phone->video_picture.bmp->pitches[2] * height / 2);
602
603 SDL_Rect rect;
604 rect.x = 0;
605 rect.y = 0;
606 rect.w = width;
607 rect.h = height;
608 SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);*/
609 free(rgb_image);
610 //display_received_frame(_phone, image);
611
612 } //else {
613
614 /* TODO: request the sender to create a new i-frame immediatly */
615 //printf("Bad video packet\n");
616 //}
617 //}
618
619 usleep(1000);
620 }
621
622 /* clean up codecs */
623 //av_free(r_video_frame);
624
625 //pthread_mutex_lock(&cs->ctrl_mutex);
626 //avcodec_close(cs->video_decoder_ctx);
627 //pthread_mutex_unlock(&cs->ctrl_mutex);
628
629 _phone->running_decvid = -1;
630
631 pthread_exit ( NULL );
632}
633
634void *decode_audio_thread(void *arg)
635{
636 INFO("Started decode audio thread!");
637 av_session_t *_phone = arg;
638 _phone->running_decaud = 1;
639
640 //int recved_size;
641 //uint8_t dest [RTP_PAYLOAD_SIZE];
642
643 int frame_size = AUDIO_FRAME_SIZE;
644 //int data_size;
645
646 ALCdevice *dev;
647 ALCcontext *ctx;
648 ALuint source, *buffers;
649 dev = alcOpenDevice(NULL);
650 ctx = alcCreateContext(dev, NULL);
651 alcMakeContextCurrent(ctx);
652 int openal_buffers = 5;
653
654 buffers = calloc(sizeof(ALuint) * openal_buffers, 1);
655 alGenBuffers(openal_buffers, buffers);
656 alGenSources((ALuint)1, &source);
657 alSourcei(source, AL_LOOPING, AL_FALSE);
658
659 ALuint buffer;
660 ALint ready;
661
662 uint16_t zeros[frame_size];
663 memset(zeros, 0, frame_size);
664 int16_t PCM[frame_size];
665
666 int i;
667
668 for (i = 0; i < openal_buffers; ++i) {
669 alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
670 }
671
672 alSourceQueueBuffers(source, openal_buffers, buffers);
673 alSourcePlay(source);
674
675 if (alGetError() != AL_NO_ERROR) {
676 fprintf(stderr, "Error starting audio\n");
677 goto ending;
678 }
679
680 int dec_frame_len = 0;
681
682 while (_phone->running_decaud) {
683
684 alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
685
686 if (ready <= 0)
687 continue;
688
689 dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM);
690
691 /* Play the packet */
692 if (dec_frame_len > 0) {
693 alSourceUnqueueBuffers(source, 1, &buffer);
694 alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
695 int error = alGetError();
696
697 if (error != AL_NO_ERROR) {
698 fprintf(stderr, "Error setting buffer %d\n", error);
699 break;
700 }
701
702 alSourceQueueBuffers(source, 1, &buffer);
703
704 if (alGetError() != AL_NO_ERROR) {
705 fprintf(stderr, "Error: could not buffer audio\n");
706 break;
707 }
708
709 alGetSourcei(source, AL_SOURCE_STATE, &ready);
710
711 if (ready != AL_PLAYING) alSourcePlay(source);
712 }
713
714 usleep(1000);
715 }
716
717
718ending:
719 /* clean up codecs */
720 //pthread_mutex_lock(&cs->ctrl_mutex);
721 /*
722 alDeleteSources(1, &source);
723 alDeleteBuffers(openal_buffers, buffers);
724 alcMakeContextCurrent(NULL);
725 alcDestroyContext(ctx);
726 alcCloseDevice(dev);
727 */
728 //pthread_mutex_unlock(&cs->ctrl_mutex);
729
730 _phone->running_decaud = -1;
731
732 pthread_exit ( NULL );
733}
734
735
736
737
738
739int phone_startmedia_loop ( ToxAv *arg )
740{
741 if ( !arg ) {
742 return -1;
743 }
744
745 toxav_prepare_transmission(arg);
746
747 /*
748 * Rise all threads
749 */
750#ifdef TOX_FFMPEG
751
752 /* Only checks for last peer */
753 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
754 0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) ) {
755 INFO("Error while starting encode_video_thread()");
756 return -1;
757 }
758
759#endif
760
761 /* Always send audio */
762 if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) ) {
763 INFO("Error while starting encode_audio_thread()");
764 return -1;
765 }
766
767 /* Only checks for last peer */
768 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
769 0 > event.rise(decode_video_thread, toxav_get_agent_handler(arg)) ) {
770 INFO("Error while starting decode_video_thread()");
771 return -1;
772 }
773
774 if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) ) {
775 INFO("Error while starting decode_audio_thread()");
776 return -1;
777 }
778
779
780 return 0;
781}
782
783
784
785
786
787
788/*********************************************
789 *********************************************
790 *********************************************
791 *********************************************
792 *********************************************
793 *********************************************
794 *********************************************
795 *********************************************
796 */
797
798
799/* Some example callbacks */
800
801void *callback_recv_invite ( void *_arg )
802{
803 assert(_arg);
804
805 switch ( toxav_get_peer_transmission_type(_arg, 0) ) {
806 case TypeAudio:
807 INFO( "Incoming audio call!");
808 break;
809
810 case TypeVideo:
811 INFO( "Incoming video call!");
812 break;
813 }
814
815 pthread_exit(NULL);
816}
817void *callback_recv_ringing ( void *_arg )
818{
819 INFO ( "Ringing!" );
820 pthread_exit(NULL);
821}
822void *callback_recv_starting ( void *_arg )
823{
824 if ( 0 != phone_startmedia_loop(_arg) ) {
825 INFO("Starting call failed!");
826 } else {
827 INFO ("Call started! ( press h to hangup )");
828 }
829
830 pthread_exit(NULL);
831}
832void *callback_recv_ending ( void *_arg )
833{
834 av_session_t *_phone = toxav_get_agent_handler(_arg);
835
836 _phone->running_encaud = 0;
837 _phone->running_decaud = 0;
838 _phone->running_encvid = 0;
839 _phone->running_decvid = 0;
840
841 /* Wait until all threads are done */
842
843 while ( _phone->running_encaud != -1 ||
844 _phone->running_decaud != -1 ||
845 _phone->running_encvid != -1 ||
846 _phone->running_decvid != -1 )
847
848 usleep(10000000);
849
850 INFO ( "Call ended!" );
851 pthread_exit(NULL);
852}
853
854void *callback_recv_error ( void *_arg )
855{
856 /*MSISession* _session = _arg;
857
858 INFO( "Error: %s", _session->last_error_str ); */
859 pthread_exit(NULL);
860}
861
862void *callback_call_started ( void *_arg )
863{
864 if ( 0 != phone_startmedia_loop(_arg) ) {
865 INFO("Starting call failed!");
866 } else {
867 INFO ("Call started! ( press h to hangup )");
868 }
869
870 pthread_exit(NULL);
871}
872void *callback_call_canceled ( void *_arg )
873{
874 INFO ( "Call canceled!" );
875 pthread_exit(NULL);
876}
877void *callback_call_rejected ( void *_arg )
878{
879 INFO ( "Call rejected!" );
880 pthread_exit(NULL);
881}
882void *callback_call_ended ( void *_arg )
883{
884 av_session_t *_phone = toxav_get_agent_handler(_arg);
885
886 _phone->running_encaud = 0;
887 _phone->running_decaud = 0;
888 _phone->running_encvid = 0;
889 _phone->running_decvid = 0;
890
891 /* Wait until all threads are done */
892
893 while ( _phone->running_encaud != -1 ||
894 _phone->running_decaud != -1 ||
895 _phone->running_encvid != -1 ||
896 _phone->running_decvid != -1 )
897
898 usleep(10000000);
899
900 toxav_kill_transmission(_phone->av);
901 INFO ( "Call ended!" );
902 pthread_exit(NULL);
903}
904
905void *callback_requ_timeout ( void *_arg )
906{
907 INFO( "No answer! " );
908 pthread_exit(NULL);
909}
910
911av_session_t *av_init_session()
912{
913 av_session_t *_retu = malloc(sizeof(av_session_t));
914
915 /* Initialize our mutex */
916 pthread_mutex_init ( &_retu->_mutex, NULL );
917
918 _retu->_messenger = tox_new(1);
919
920 if ( !_retu->_messenger ) {
921 fprintf ( stderr, "tox_new() failed!\n" );
922 return NULL;
923 }
924
925 _retu->_friends = NULL;
926
927
928 const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
929 int i = 0;
930 const ALchar *device_names[20];
931
932 if ( _device_list ) {
933 INFO("\nAvailable Capture Devices are:");
934
935 while (*_device_list ) {
936 device_names[i] = _device_list;
937 INFO("%d) %s", i, device_names[i]);
938 _device_list += strlen( _device_list ) + 1;
939 ++i;
940 }
941 }
942
943 INFO("Enter capture device number");
944
945 char dev[2];
946 char *left;
947 char *warned_ = fgets(dev, 2, stdin);
948 (void)warned_;
949 long selection = strtol(dev, &left, 10);
950
951 if ( *left ) {
952 printf("'%s' is not a number!", dev);
953 fflush(stdout);
954 exit(EXIT_FAILURE);
955 } else {
956 INFO("Selected: %d ( %s )", selection, device_names[selection]);
957 }
958
959 _retu->audio_capture_device =
960 (struct ALCdevice *)alcCaptureOpenDevice(
961 device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
962
963
964 if (alcGetError((ALCdevice *)_retu->audio_capture_device) != AL_NO_ERROR) {
965 printf("Could not start capture device! %d\n", alcGetError((ALCdevice *)_retu->audio_capture_device));
966 return 0;
967 }
968
969 uint16_t height = 0, width = 0;
970#ifdef TOX_FFMPEG
971 avdevice_register_all();
972 avcodec_register_all();
973 av_register_all();
974
975 _retu->video_input_format = av_find_input_format(VIDEO_DRIVER);
976
977 if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
978 fprintf(stderr, "Opening video_input_format failed!\n");
979 //return -1;
980 return NULL;
981 }
982
983 avformat_find_stream_info(_retu->video_format_ctx, NULL);
984 av_dump_format(_retu->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
985
986 for (i = 0; i < _retu->video_format_ctx->nb_streams; ++i) {
987 if (_retu->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
988 _retu->video_stream = i;
989 break;
990 }
991 }
992
993 _retu->webcam_decoder_ctx = _retu->video_format_ctx->streams[_retu->video_stream]->codec;
994 _retu->webcam_decoder = avcodec_find_decoder(_retu->webcam_decoder_ctx->codec_id);
995
996 if (_retu->webcam_decoder == NULL) {
997 fprintf(stderr, "Unsupported codec!\n");
998 //return -1;
999 return NULL;
1000 }
1001
1002 if (_retu->webcam_decoder_ctx == NULL) {
1003 fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
1004 //return -1;
1005 return NULL;
1006 }
1007
1008 if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
1009 fprintf(stderr, "Opening webcam decoder failed!\n");
1010 //return -1;
1011 return NULL;
1012 }
1013
1014 width = _retu->webcam_decoder_ctx->width;
1015 height = _retu->webcam_decoder_ctx->height;
1016#endif
1017 uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
1018 tox_get_address(_retu->_messenger, _byte_address );
1019 fraddr_to_str( _byte_address, _retu->_my_public_id );
1020
1021
1022 _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT, width, height);
1023
1024 /* ------------------ */
1025
1026 toxav_register_callstate_callback(callback_call_started, OnStart);
1027 toxav_register_callstate_callback(callback_call_canceled, OnCancel);
1028 toxav_register_callstate_callback(callback_call_rejected, OnReject);
1029 toxav_register_callstate_callback(callback_call_ended, OnEnd);
1030 toxav_register_callstate_callback(callback_recv_invite, OnInvite);
1031
1032 toxav_register_callstate_callback(callback_recv_ringing, OnRinging);
1033 toxav_register_callstate_callback(callback_recv_starting, OnStarting);
1034 toxav_register_callstate_callback(callback_recv_ending, OnEnding);
1035
1036 toxav_register_callstate_callback(callback_recv_error, OnError);
1037 toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout);
1038
1039 /* ------------------ */
1040
1041 return _retu;
1042}
1043
1044int av_terminate_session(av_session_t *_phone)
1045{
1046 toxav_hangup(_phone->av);
1047
1048 free(_phone->_friends);
1049 pthread_mutex_destroy ( &_phone->_mutex );
1050
1051 Tox *_p = _phone->_messenger;
1052 _phone->_messenger = NULL;
1053 usleep(100000); /* Wait for tox_poll to end */
1054
1055 tox_kill(_p);
1056 toxav_kill(_phone->av);
1057
1058 free(_phone);
1059
1060 printf("\r[i] Quit!\n");
1061 return 0;
1062}
1063
1064/****** AV HELPER FUNCTIONS ******/
1065
1066/* Auto accept friend request */
1067void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata)
1068{
1069 av_session_t *_phone = _userdata;
1070 av_allocate_friend (_phone, -1, 0);
1071
1072 INFO("Got friend request with message: %s", _data);
1073
1074 tox_add_friend_norequest(_phone->_messenger, _public_key);
1075
1076 INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id );
1077}
1078
1079void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata)
1080{
1081 av_session_t *_phone = _userdata;
1082 INFO("Friend no. %d is online", _friendnumber);
1083
1084 av_friend_t *_this_friend = av_get_friend(_phone, _friendnumber);
1085
1086 if ( !_this_friend ) {
1087 INFO("But it's not registered!");
1088 return;
1089 }
1090
1091 (*_this_friend)._active = 1;
1092}
1093
1094int av_add_friend(av_session_t *_phone, char *_friend_hash)
1095{
1096 trim_spaces(_friend_hash);
1097
1098 unsigned char *_bin_string = hex_string_to_bin(_friend_hash);
1099 int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT,
1100 sizeof("Tox phone "_USERAGENT));
1101 free(_bin_string);
1102
1103 if ( _number >= 0) {
1104 INFO("Added friend as %d", _number );
1105 av_allocate_friend(_phone, _number, 0);
1106 } else
1107 INFO("Unknown error %i", _number );
1108
1109 return _number;
1110}
1111
1112int av_connect_to_dht(av_session_t *_phone, char *_dht_key, const char *_dht_addr, unsigned short _dht_port)
1113{
1114 unsigned char *_binary_string = hex_string_to_bin(_dht_key);
1115
1116 uint16_t _port = htons(_dht_port);
1117
1118 int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string );
1119
1120 free(_binary_string);
1121
1122 return _if ? 0 : -1;
1123}
1124
1125/*********************************/
1126
1127void do_phone ( av_session_t *_phone )
1128{
1129 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
1130 "Usage: \n"
1131 "f [pubkey] (add friend)\n"
1132 "c [a/v] (type) [friend] (friend id) (calls friend if online)\n"
1133 "h (if call is active hang up)\n"
1134 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
1135 "r (reject incoming call)\n"
1136 "q (quit)\n"
1137 "================================================================================"
1138 );
1139
1140 while ( 1 ) {
1141 char _line [ 1500 ];
1142 int _len;
1143
1144 if ( -1 == getinput(_line, 1500, &_len) ) {
1145 printf(" >> ");
1146 fflush(stdout);
1147 continue;
1148 }
1149
1150 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ) {
1151 INFO("Invalid input!");
1152 continue;
1153 }
1154
1155 switch (_line[0]) {
1156
1157 case 'f': {
1158 char _id [128];
1159 strncpy(_id, _line + 2, 128);
1160
1161 av_add_friend(_phone, _id);
1162
1163 }
1164 break;
1165
1166 case 'c': {
1167 ToxAvCallType _ctype;
1168
1169 if ( _len < 5 ) {
1170 INFO("Invalid input; usage: c a/v [friend]");
1171 break;
1172 } else if ( _line[2] == 'a' || _line[2] != 'v' ) { /* default and audio */
1173 _ctype = TypeAudio;
1174 } else { /* video */
1175 _ctype = TypeVideo;
1176 }
1177
1178 char *_end;
1179 int _friend = strtol(_line + 4, &_end, 10);
1180
1181 if ( *_end ) {
1182 INFO("Friend num has to be numerical value");
1183 break;
1184 }
1185
1186 if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ) {
1187 INFO("Already in a call");
1188 break;
1189 } else INFO("Calling friend: %d!", _friend);
1190
1191 }
1192 break;
1193
1194 case 'h': {
1195 if ( toxav_hangup(_phone->av) == ErrorNoCall ) {
1196 INFO("No call!");
1197 break;
1198 } else INFO("Hung up...");
1199
1200 }
1201 break;
1202
1203 case 'a': {
1204 ToxAvError rc;
1205
1206 if ( _len > 1 && _line[2] == 'v' ) {
1207 rc = toxav_answer(_phone->av, TypeVideo);
1208 } else
1209 rc = toxav_answer(_phone->av, TypeAudio);
1210
1211 if ( rc == ErrorInvalidState ) {
1212 INFO("No call to answer!");
1213 }
1214
1215 }
1216 break;
1217
1218 case 'r': {
1219 if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState )
1220 INFO("No state to cancel!");
1221 else INFO("Call Rejected...");
1222
1223 }
1224 break;
1225
1226 case 'q': {
1227 INFO("Quitting!");
1228 return;
1229 }
1230
1231 case '\n': {
1232 }
1233
1234 default: {
1235 } break;
1236
1237 }
1238
1239 }
1240}
1241
1242void *tox_poll (void *_messenger_p)
1243{
1244 Tox **_messenger = _messenger_p;
1245
1246 while ( *_messenger ) {
1247 tox_do(*_messenger);
1248 usleep(10000);
1249 }
1250
1251 pthread_exit(NULL);
1252}
1253
1254int av_wait_dht(av_session_t *_phone, int _wait_seconds, const char *_ip, char *_key, unsigned short _port)
1255{
1256 if ( !_wait_seconds )
1257 return -1;
1258
1259 int _waited = 0;
1260
1261 while ( !tox_isconnected(_phone->_messenger) ) {
1262
1263 if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) ) {
1264 INFO("Could not connect to: %s", _ip);
1265 av_terminate_session(_phone);
1266 return -1;
1267 }
1268
1269 if ( _waited >= _wait_seconds ) return 0;
1270
1271 printf(".");
1272 fflush(stdout);
1273
1274 _waited ++;
1275 usleep(1000000);
1276 }
1277
1278 int _r = _wait_seconds - _waited;
1279 return _r ? _r : 1;
1280}
1281/* ---------------------- */
1282
1283int print_help ( const char *_name )
1284{
1285 printf ( "Usage: %s [IP] [PORT] [KEY]\n"
1286 "\t[IP] (DHT ip)\n"
1287 "\t[PORT] (DHT port)\n"
1288 "\t[KEY] (DHT public key)\n"
1289 "P.S. Friends and key are stored in ./tox_phone.conf\n"
1290 , _name );
1291 return 1;
1292}
1293
1294int main ( int argc, char *argv [] )
1295{
1296 if ( argc < 1 || argc < 4 )
1297 return print_help(argv[0]);
1298
1299 char *_convertable;
1300
1301
1302 const char *_ip = argv[1];
1303 char *_key = argv[3];
1304 unsigned short _port = strtol(argv[2], &_convertable, 10);
1305
1306 if ( *_convertable ) {
1307 printf("Invalid port: cannot convert string to long: %s", _convertable);
1308 return 1;
1309 }
1310
1311 av_session_t *_phone = av_init_session();
1312
1313 tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
1314 tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
1315
1316
1317 INFO("\r================================================================================\n"
1318 "[!] Trying dht@%s:%d"
1319 , _ip, _port);
1320
1321 /* Start tox protocol */
1322 event.rise( tox_poll, &_phone->_messenger );
1323
1324 /* Just clean one line */
1325 printf("\r \r");
1326 fflush(stdout);
1327
1328 int _r;
1329 int _wait_seconds = 5;
1330
1331 for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --;
1332
1333
1334 if ( -1 == _r ) {
1335 INFO("Error while connecting to dht: %s:%d", _ip, _port);
1336 av_terminate_session(_phone);
1337 return 1;
1338 }
1339
1340 INFO("CONNECTED!\n"
1341 "================================================================================\n"
1342 "%s\n"
1343 "================================================================================"
1344 , _phone->_my_public_id );
1345
1346
1347 do_phone (_phone);
1348
1349 av_terminate_session(_phone);
1350
1351 return 0;
1352}
diff --git a/toxav/rtp.c b/toxav/rtp.c
new file mode 100644
index 00000000..dbaecbb6
--- /dev/null
+++ b/toxav/rtp.c
@@ -0,0 +1,934 @@
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 at #tox-dev @ freenode.net:6667
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif /* HAVE_CONFIG_H */
27
28#include "rtp.h"
29#include <assert.h>
30#include <stdlib.h>
31
32
33#define PAYLOAD_ID_VALUE_OPUS 1
34#define PAYLOAD_ID_VALUE_VP8 2
35
36#define size_32 4
37
38#define inline__ inline __attribute__((always_inline))
39
40
41#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
42#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
43#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
44#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
45#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
46#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
47
48#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
49#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
50#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
51#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
52#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
53#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
54
55
56/**
57 * @brief Converts 4 bytes to uint32_t
58 *
59 * @param dest Where to convert
60 * @param bytes What bytes
61 * @return void
62 */
63inline__ void bytes_to_U32(uint32_t *dest, const uint8_t *bytes)
64{
65 *dest =
66#ifdef WORDS_BIGENDIAN
67 ( ( uint32_t ) * bytes ) |
68 ( ( uint32_t ) * ( bytes + 1 ) << 8 ) |
69 ( ( uint32_t ) * ( bytes + 2 ) << 16 ) |
70 ( ( uint32_t ) * ( bytes + 3 ) << 24 ) ;
71#else
72 ( ( uint32_t ) * bytes << 24 ) |
73 ( ( uint32_t ) * ( bytes + 1 ) << 16 ) |
74 ( ( uint32_t ) * ( bytes + 2 ) << 8 ) |
75 ( ( uint32_t ) * ( bytes + 3 ) ) ;
76#endif
77}
78
79/**
80 * @brief Converts 2 bytes to uint16_t
81 *
82 * @param dest Where to convert
83 * @param bytes What bytes
84 * @return void
85 */
86inline__ void bytes_to_U16(uint16_t *dest, const uint8_t *bytes)
87{
88 *dest =
89#ifdef WORDS_BIGENDIAN
90 ( ( uint16_t ) * bytes ) |
91 ( ( uint16_t ) * ( bytes + 1 ) << 8 );
92#else
93 ( ( uint16_t ) * bytes << 8 ) |
94 ( ( uint16_t ) * ( bytes + 1 ) );
95#endif
96}
97
98/**
99 * @brief Convert uint32_t to byte string of size 4
100 *
101 * @param dest Where to convert
102 * @param value The value
103 * @return void
104 */
105inline__ void U32_to_bytes(uint8_t *dest, uint32_t value)
106{
107#ifdef WORDS_BIGENDIAN
108 *(dest) = ( value );
109 *(dest + 1) = ( value >> 8 );
110 *(dest + 2) = ( value >> 16 );
111 *(dest + 3) = ( value >> 24 );
112#else
113 *(dest) = ( value >> 24 );
114 *(dest + 1) = ( value >> 16 );
115 *(dest + 2) = ( value >> 8 );
116 *(dest + 3) = ( value );
117#endif
118}
119
120/**
121 * @brief Convert uint16_t to byte string of size 2
122 *
123 * @param dest Where to convert
124 * @param value The value
125 * @return void
126 */
127inline__ void U16_to_bytes(uint8_t *dest, uint16_t value)
128{
129#ifdef WORDS_BIGENDIAN
130 *(dest) = ( value );
131 *(dest + 1) = ( value >> 8 );
132#else
133 *(dest) = ( value >> 8 );
134 *(dest + 1) = ( value );
135#endif
136}
137
138
139/**
140 * @brief Checks if message came in late.
141 *
142 * @param session Control session.
143 * @param msg The message.
144 * @return int
145 * @retval -1 The message came in order.
146 * @retval 0 The message came late.
147 */
148inline__ int check_late_message (RTPSession *session, RTPMessage *msg)
149{
150 /*
151 * Check Sequence number. If this new msg has lesser number then the session->rsequnum
152 * it shows that the message came in late. Also check timestamp to be 100% certain.
153 *
154 */
155 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
156}
157
158
159/**
160 * @brief Increases nonce value by 'target'
161 *
162 * @param nonce The nonce
163 * @param target The target
164 * @return void
165 */
166inline__ void increase_nonce(uint8_t *nonce, uint16_t target)
167{
168 uint16_t _nonce_counter;
169
170 uint8_t _reverse_bytes[2];
171 _reverse_bytes[0] = nonce[crypto_box_NONCEBYTES - 1];
172 _reverse_bytes[1] = nonce[crypto_box_NONCEBYTES - 2];
173
174 bytes_to_U16(&_nonce_counter, _reverse_bytes );
175
176 /* Check overflow */
177 if (_nonce_counter > UINT16_MAX - target ) { /* 2 bytes are not long enough */
178 uint8_t _it = 3;
179
180 while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1;
181
182 _nonce_counter = _nonce_counter - (UINT16_MAX - target ); /* Assign the rest of it */
183 } else { /* Increase nonce */
184
185 _nonce_counter += target;
186 }
187
188 /* Assign the last bytes */
189
190 U16_to_bytes( _reverse_bytes, _nonce_counter);
191 nonce [crypto_box_NONCEBYTES - 1] = _reverse_bytes[0];
192 nonce [crypto_box_NONCEBYTES - 2] = _reverse_bytes[1];
193
194}
195
196
197/**
198 * @brief Speaks for it self.
199 *
200 */
201static const uint32_t payload_table[] = {
202 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */
203 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */
204 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */
205 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */
208 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */
209 PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */
210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */
213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
214 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */
215};
216
217
218/**
219 * @brief Extracts header from payload.
220 *
221 * @param payload The payload.
222 * @param length The size of payload.
223 * @return RTPHeader* Extracted header.
224 * @retval NULL Error occurred while extracting header.
225 */
226RTPHeader *extract_header ( const uint8_t *payload, int length )
227{
228 if ( !payload || !length ) {
229 return NULL;
230 }
231
232 const uint8_t *_it = payload;
233
234 RTPHeader *_retu = calloc(1, sizeof (RTPHeader));
235 assert(_retu);
236
237 _retu->flags = *_it;
238 ++_it;
239
240 /* This indicates if the first 2 bits are valid.
241 * Now it may happen that this is out of order but
242 * it cuts down chances of parsing some invalid value
243 */
244
245 if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ) {
246 /* Deallocate */
247 free(_retu);
248 return NULL;
249 }
250
251 /*
252 * Added a check for the size of the header little sooner so
253 * I don't need to parse the other stuff if it's bad
254 */
255 uint8_t _cc = GET_FLAG_CSRCC ( _retu );
256 uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 );
257
258 if ( length < _length ) {
259 /* Deallocate */
260 free(_retu);
261 return NULL;
262 }
263
264 if ( _cc > 0 ) {
265 _retu->csrc = calloc (_cc, sizeof (uint32_t));
266 assert(_retu->csrc);
267
268 } else { /* But this should not happen ever */
269 /* Deallocate */
270 free(_retu);
271 return NULL;
272 }
273
274
275 _retu->marker_payloadt = *_it;
276 ++_it;
277 _retu->length = _length;
278
279
280 bytes_to_U32(&_retu->timestamp, _it);
281 _it += 4;
282 bytes_to_U32(&_retu->ssrc, _it);
283
284 uint8_t _x;
285
286 for ( _x = 0; _x < _cc; _x++ ) {
287 _it += 4;
288 bytes_to_U32(&(_retu->csrc[_x]), _it);
289 }
290
291 return _retu;
292}
293
294/**
295 * @brief Extracts external header from payload. Must be called AFTER extract_header()!
296 *
297 * @param payload The ITERATED payload.
298 * @param length The size of payload.
299 * @return RTPExtHeader* Extracted extension header.
300 * @retval NULL Error occurred while extracting extension header.
301 */
302RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
303{
304 const uint8_t *_it = payload;
305
306 RTPExtHeader *_retu = calloc(1, sizeof (RTPExtHeader));
307 assert(_retu);
308
309 uint16_t _ext_length;
310 bytes_to_U16(&_ext_length, _it);
311 _it += 2;
312
313
314 if ( length < ( _ext_length * sizeof(uint32_t) ) ) {
315 free(_retu);
316 return NULL;
317 }
318
319 _retu->length = _ext_length;
320 bytes_to_U16(&_retu->type, _it);
321 _it += 2;
322
323 _retu->table = calloc(_ext_length, sizeof (uint32_t));
324 assert(_retu->table);
325
326 uint16_t _x;
327
328 for ( _x = 0; _x < _ext_length; _x++ ) {
329 _it += 4;
330 bytes_to_U32(&(_retu->table[_x]), _it);
331 }
332
333 return _retu;
334}
335
336/**
337 * @brief Adds header to payload. Make sure _payload_ has enough space.
338 *
339 * @param header The header.
340 * @param payload The payload.
341 * @return uint8_t* Iterated position.
342 */
343uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
344{
345 uint8_t _cc = GET_FLAG_CSRCC ( header );
346
347 uint8_t *_it = payload;
348
349
350 /* Add sequence number first */
351 U16_to_bytes(_it, header->sequnum);
352 _it += 2;
353
354 *_it = header->flags;
355 ++_it;
356 *_it = header->marker_payloadt;
357 ++_it;
358
359
360 U32_to_bytes( _it, header->timestamp);
361 _it += 4;
362 U32_to_bytes( _it, header->ssrc);
363
364 if ( header->csrc ) {
365 uint8_t _x;
366
367 for ( _x = 0; _x < _cc; _x++ ) {
368 _it += 4;
369 U32_to_bytes( _it, header->csrc[_x]);
370 }
371 }
372
373 return _it + 4;
374}
375
376/**
377 * @brief Adds extension header to payload. Make sure _payload_ has enough space.
378 *
379 * @param header The header.
380 * @param payload The payload.
381 * @return uint8_t* Iterated position.
382 */
383uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
384{
385 uint8_t *_it = payload;
386
387 U16_to_bytes(_it, header->length);
388 _it += 2;
389 U16_to_bytes(_it, header->type);
390 _it -= 2; /* Return to 0 position */
391
392 if ( header->table ) {
393 uint16_t _x;
394
395 for ( _x = 0; _x < header->length; _x++ ) {
396 _it += 4;
397 U32_to_bytes(_it, header->table[_x]);
398 }
399 }
400
401 return _it + 4;
402}
403
404/**
405 * @brief Builds header from control session values.
406 *
407 * @param session Control session.
408 * @return RTPHeader* Created header.
409 */
410RTPHeader *build_header ( RTPSession *session )
411{
412 RTPHeader *_retu = calloc ( 1, sizeof (RTPHeader) );
413 assert(_retu);
414
415 ADD_FLAG_VERSION ( _retu, session->version );
416 ADD_FLAG_PADDING ( _retu, session->padding );
417 ADD_FLAG_EXTENSION ( _retu, session->extension );
418 ADD_FLAG_CSRCC ( _retu, session->cc );
419 ADD_SETTING_MARKER ( _retu, session->marker );
420 ADD_SETTING_PAYLOAD ( _retu, session->payload_type );
421
422 _retu->sequnum = session->sequnum;
423 _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */
424 _retu->ssrc = session->ssrc;
425
426 if ( session->cc > 0 ) {
427 _retu->csrc = calloc(session->cc, sizeof (uint32_t));
428 assert(_retu->csrc);
429
430 int i;
431
432 for ( i = 0; i < session->cc; i++ ) {
433 _retu->csrc[i] = session->csrc[i];
434 }
435 } else {
436 _retu->csrc = NULL;
437 }
438
439 _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
440
441 return _retu;
442}
443
444
445/**
446 * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data
447 * and so the length variable is set accordingly. _sequnum_ argument is
448 * passed by the handle_packet() since it's parsed already.
449 *
450 * @param session Control session.
451 * @param sequnum Sequence number that's parsed from payload in handle_packet()
452 * @param data Payload data.
453 * @param length Payload size.
454 * @return RTPMessage*
455 * @retval NULL Error occurred.
456 */
457RTPMessage *msg_parse ( uint16_t sequnum, const uint8_t *data, int length )
458{
459 RTPMessage *_retu = calloc(1, sizeof (RTPMessage));
460
461 _retu->header = extract_header ( data, length ); /* It allocates memory and all */
462
463 if ( !_retu->header ) {
464 free(_retu);
465 return NULL;
466 }
467
468 _retu->header->sequnum = sequnum;
469
470 _retu->length = length - _retu->header->length;
471
472 uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ;
473
474
475 if ( GET_FLAG_EXTENSION ( _retu->header ) ) {
476 _retu->ext_header = extract_ext_header ( data + _from_pos, length );
477
478 if ( _retu->ext_header ) {
479 _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
480 _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
481 } else { /* Error */
482 free (_retu->ext_header);
483 free (_retu->header);
484 free (_retu);
485 return NULL;
486 }
487 } else {
488 _retu->ext_header = NULL;
489 }
490
491 if ( length - _from_pos <= MAX_RTP_SIZE )
492 memcpy ( _retu->data, data + _from_pos, length - _from_pos );
493 else {
494 rtp_free_msg(NULL, _retu);
495 return NULL;
496 }
497
498 _retu->next = NULL;
499
500 return _retu;
501}
502
503/**
504 * @brief Callback for networking core.
505 *
506 * @param object RTPSession object.
507 * @param ip_port Where the message comes from.
508 * @param data Message data.
509 * @param length Message length.
510 * @return int
511 * @retval -1 Error occurred.
512 * @retval 0 Success.
513 */
514int rtp_handle_packet ( void *object, IP_Port ip_port, uint8_t *data, uint32_t length )
515{
516 RTPSession *_session = object;
517 RTPMessage *_msg;
518
519 if ( !_session || length < 13 ) /* 12 is the minimum length for rtp + desc. byte */
520 return -1;
521
522 uint8_t _plain[MAX_UDP_PACKET_SIZE];
523
524 uint16_t _sequnum;
525 bytes_to_U16(&_sequnum, data + 1);
526
527 /* Clculate the right nonce */
528 uint8_t _calculated[crypto_box_NONCEBYTES];
529 memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES);
530 increase_nonce ( _calculated, _sequnum );
531
532 /* Decrypt message */
533 int _decrypted_length = decrypt_data_symmetric(
534 (uint8_t *)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
535
536 /* This packet is either not encrypted properly or late
537 */
538 if ( -1 == _decrypted_length ) {
539
540 /* If this is the case, then the packet is most likely late.
541 * Try with old nonce cycle.
542 */
543 if ( _session->rsequnum < _sequnum ) {
544 _decrypted_length = decrypt_data_symmetric(
545 (uint8_t *)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain );
546
547 if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */
548
549 /* Otherwise, if decryption is ok with new cycle, set new cycle
550 */
551 } else {
552 increase_nonce ( _calculated, MAX_SEQU_NUM );
553 _decrypted_length = decrypt_data_symmetric(
554 (uint8_t *)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
555
556 if ( !_decrypted_length ) return -1; /* This is just an error */
557
558 /* A new cycle setting. */
559 memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES);
560 memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES);
561 }
562 }
563
564 _msg = msg_parse ( _sequnum, _plain, _decrypted_length );
565
566 if ( !_msg ) return -1;
567
568 /* Hopefully this goes well
569 * NOTE: Is this even used?
570 */
571 memcpy(&_msg->from, &ip_port, sizeof(IP_Port));
572
573 /* Check if message came in late */
574 if ( check_late_message(_session, _msg) < 0 ) { /* Not late */
575 _session->rsequnum = _msg->header->sequnum;
576 _session->timestamp = _msg->header->timestamp;
577 }
578
579 pthread_mutex_lock(&_session->mutex);
580
581 if ( _session->last_msg ) {
582 _session->last_msg->next = _msg;
583 _session->last_msg = _msg;
584 } else {
585 _session->last_msg = _session->oldest_msg = _msg;
586 }
587
588 pthread_mutex_unlock(&_session->mutex);
589
590 return 0;
591}
592
593
594
595/**
596 * @brief Stores headers and payload data in one container ( data )
597 * and the length is set accordingly. Returned message is used for sending _only_.
598 *
599 * @param session The control session.
600 * @param data Payload data to send ( This is what you pass ).
601 * @param length Size of the payload data.
602 * @return RTPMessage* Created message.
603 * @retval NULL Error occurred.
604 */
605RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
606{
607 if ( !session )
608 return NULL;
609
610 uint8_t *_from_pos;
611 RTPMessage *_retu = calloc(1, sizeof (RTPMessage));
612 assert(_retu);
613
614 /* Sets header values and copies the extension header in _retu */
615 _retu->header = build_header ( session ); /* It allocates memory and all */
616 _retu->ext_header = session->ext_header;
617
618
619 uint32_t _total_length = length + _retu->header->length;
620
621 if ( _retu->ext_header ) {
622 _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
623
624 _from_pos = add_header ( _retu->header, _retu->data );
625 _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 );
626 } else {
627 _from_pos = add_header ( _retu->header, _retu->data );
628 }
629
630 /*
631 * Parses the extension header into the message
632 * Of course if any
633 */
634
635 /* Appends _data on to _retu->_data */
636 memcpy ( _from_pos, data, length );
637
638 _retu->length = _total_length;
639
640 _retu->next = NULL;
641
642 return _retu;
643}
644
645
646
647
648
649
650
651/********************************************************************************************************************
652 ********************************************************************************************************************
653 ********************************************************************************************************************
654 ********************************************************************************************************************
655 ********************************************************************************************************************
656 *
657 *
658 *
659 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
660 *
661 *
662 *
663 ********************************************************************************************************************
664 ********************************************************************************************************************
665 ********************************************************************************************************************
666 ********************************************************************************************************************
667 ********************************************************************************************************************/
668
669
670
671
672
673
674
675
676
677/**
678 * @brief Release all messages held by session.
679 *
680 * @param session The session.
681 * @return int
682 * @retval -1 Error occurred.
683 * @retval 0 Success.
684 */
685int rtp_release_session_recv ( RTPSession *session )
686{
687 if ( !session ) {
688 return -1;
689 }
690
691 RTPMessage *_tmp, * _it;
692
693 pthread_mutex_lock(&session->mutex);
694
695 for ( _it = session->oldest_msg; _it; _it = _tmp ) {
696 _tmp = _it->next;
697 rtp_free_msg( session, _it);
698 }
699
700 session->last_msg = session->oldest_msg = NULL;
701
702 pthread_mutex_unlock(&session->mutex);
703
704 return 0;
705}
706
707
708/**
709 * @brief Gets oldest message in the list.
710 *
711 * @param session Where the list is.
712 * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it.
713 * @retval NULL No messages in the list, or no list.
714 */
715RTPMessage *rtp_recv_msg ( RTPSession *session )
716{
717 if ( !session )
718 return NULL;
719
720 RTPMessage *_retu = session->oldest_msg;
721
722 pthread_mutex_lock(&session->mutex);
723
724 if ( _retu )
725 session->oldest_msg = _retu->next;
726
727 if ( !session->oldest_msg )
728 session->last_msg = NULL;
729
730 pthread_mutex_unlock(&session->mutex);
731
732 return _retu;
733}
734
735
736/**
737 * @brief Sends data to _RTPSession::dest
738 *
739 * @param session The session.
740 * @param messenger Tox* object.
741 * @param data The payload.
742 * @param length Size of the payload.
743 * @return int
744 * @retval -1 On error.
745 * @retval 0 On success.
746 */
747int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length )
748{
749 RTPMessage *msg = rtp_new_message (session, data, length);
750
751 if ( !msg ) return -1;
752
753 uint8_t _send_data [ MAX_UDP_PACKET_SIZE ];
754
755 _send_data[0] = session->prefix;
756
757 /* Generate the right nonce */
758 uint8_t _calculated[crypto_box_NONCEBYTES];
759 memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES);
760 increase_nonce ( _calculated, msg->header->sequnum );
761
762 /* Need to skip 2 bytes that are for sequnum */
763 int encrypted_length = encrypt_data_symmetric( /* TODO: msg->length - 2 (fix this properly)*/
764 (uint8_t *) session->encrypt_key, _calculated, msg->data + 2, msg->length, _send_data + 3 );
765
766 int full_length = encrypted_length + 3;
767
768 _send_data[1] = msg->data[0];
769 _send_data[2] = msg->data[1];
770
771
772 /*if ( full_length != sendpacket ( messenger->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {*/
773 if ( full_length != send_custom_user_packet(messenger, session->dest, _send_data, full_length) ) {
774 printf("Rtp error: %s\n", strerror(errno));
775 return -1;
776 }
777
778
779 /* Set sequ number */
780 if ( session->sequnum >= MAX_SEQU_NUM ) {
781 session->sequnum = 0;
782 memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES);
783 } else {
784 session->sequnum++;
785 }
786
787 rtp_free_msg ( session, msg );
788 return 0;
789}
790
791
792/**
793 * @brief Speaks for it self.
794 *
795 * @param session The control session msg belongs to. You set it as NULL when freeing recved messages.
796 * Otherwise set it to session the message was created from.
797 * @param msg The message.
798 * @return void
799 */
800void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
801{
802 if ( !session ) {
803 free ( msg->header->csrc );
804
805 if ( msg->ext_header ) {
806 free ( msg->ext_header->table );
807 free ( msg->ext_header );
808 }
809 } else {
810 if ( session->csrc != msg->header->csrc )
811 free ( msg->header->csrc );
812
813 if ( msg->ext_header && session->ext_header != msg->ext_header ) {
814 free ( msg->ext_header->table );
815 free ( msg->ext_header );
816 }
817 }
818
819 free ( msg->header );
820 free ( msg );
821}
822
823
824/**
825 * @brief Must be called before calling any other rtp function. It's used
826 * to initialize RTP control session.
827 *
828 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
829 * @param messenger Tox* object.
830 * @param friend_num Friend id.
831 * @param encrypt_key Speaks for it self.
832 * @param decrypt_key Speaks for it self.
833 * @param encrypt_nonce Speaks for it self.
834 * @param decrypt_nonce Speaks for it self.
835 * @return RTPSession* Created control session.
836 * @retval NULL Error occurred.
837 */
838RTPSession *rtp_init_session ( int payload_type,
839 Messenger *messenger,
840 int friend_num,
841 const uint8_t *encrypt_key,
842 const uint8_t *decrypt_key,
843 const uint8_t *encrypt_nonce,
844 const uint8_t *decrypt_nonce )
845{
846 RTPSession *_retu = calloc(1, sizeof(RTPSession));
847 assert(_retu);
848
849 /*networking_registerhandler(messenger->net, payload_type, rtp_handle_packet, _retu);*/
850 if ( -1 == custom_user_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu) ) {
851 fprintf(stderr, "Error setting custom register handler for rtp session\n");
852 free(_retu);
853 return NULL;
854 }
855
856 _retu->version = RTP_VERSION; /* It's always 2 */
857 _retu->padding = 0; /* If some additional data is needed about the packet */
858 _retu->extension = 0; /* If extension to header is needed */
859 _retu->cc = 1; /* Amount of contributors */
860 _retu->csrc = NULL; /* Container */
861 _retu->ssrc = random_int();
862 _retu->marker = 0;
863 _retu->payload_type = payload_table[payload_type];
864
865 _retu->dest = friend_num;
866
867 _retu->rsequnum = _retu->sequnum = 1;
868
869 _retu->ext_header = NULL; /* When needed allocate */
870 _retu->framerate = -1;
871 _retu->resolution = -1;
872
873 _retu->encrypt_key = encrypt_key;
874 _retu->decrypt_key = decrypt_key;
875
876 /* Need to allocate new memory */
877 _retu->encrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) );
878 assert(_retu->encrypt_nonce);
879 _retu->decrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) );
880 assert(_retu->decrypt_nonce);
881 _retu->nonce_cycle = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) );
882 assert(_retu->nonce_cycle);
883
884 memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES);
885 memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES);
886 memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES);
887
888 _retu->csrc = calloc(1, sizeof (uint32_t));
889 assert(_retu->csrc);
890
891 _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */
892
893 /* Also set payload type as prefix */
894 _retu->prefix = payload_type;
895
896 _retu->oldest_msg = _retu->last_msg = NULL;
897
898 pthread_mutex_init(&_retu->mutex, NULL);
899 /*
900 *
901 */
902 return _retu;
903}
904
905
906/**
907 * @brief Terminate the session.
908 *
909 * @param session The session.
910 * @param messenger The messenger who owns the session
911 * @return int
912 * @retval -1 Error occurred.
913 * @retval 0 Success.
914 */
915int rtp_terminate_session ( RTPSession *session, Messenger *messenger )
916{
917 if ( !session )
918 return -1;
919
920 custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL);
921
922 free ( session->ext_header );
923 free ( session->csrc );
924 free ( session->decrypt_nonce );
925 free ( session->encrypt_nonce );
926 free ( session->nonce_cycle );
927
928 pthread_mutex_destroy(&session->mutex);
929
930 /* And finally free session */
931 free ( session );
932
933 return 0;
934} \ No newline at end of file
diff --git a/toxav/rtp.h b/toxav/rtp.h
new file mode 100644
index 00000000..c2b68b01
--- /dev/null
+++ b/toxav/rtp.h
@@ -0,0 +1,219 @@
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 at #tox-dev @ freenode.net:6667
22 */
23
24#ifndef __TOXRTP
25#define __TOXRTP
26
27#define RTP_VERSION 2
28#include <inttypes.h>
29#include <pthread.h>
30
31#include "../toxcore/util.h"
32#include "../toxcore/network.h"
33#include "../toxcore/net_crypto.h"
34#include "../toxcore/Messenger.h"
35
36#define MAX_SEQU_NUM 65535
37#define MAX_RTP_SIZE 65535
38
39/**
40 * @brief Standard rtp header
41 *
42 */
43
44typedef struct _RTPHeader {
45 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
46 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
47 uint16_t sequnum; /* Sequence Number */
48 uint32_t timestamp; /* Timestamp */
49 uint32_t ssrc; /* SSRC */
50 uint32_t *csrc; /* CSRC's table */
51 uint32_t length; /* Length of the header in payload string. */
52
53} RTPHeader;
54
55
56/**
57 * @brief Standard rtp extension header.
58 *
59 */
60typedef struct _RTPExtHeader {
61 uint16_t type; /* Extension profile */
62 uint16_t length; /* Number of extensions */
63 uint32_t *table; /* Extension's table */
64
65} RTPExtHeader;
66
67
68/**
69 * @brief Standard rtp message.
70 *
71 */
72typedef struct _RTPMessage {
73 RTPHeader *header;
74 RTPExtHeader *ext_header;
75
76 uint8_t data[MAX_RTP_SIZE];
77 uint32_t length;
78 IP_Port from;
79
80 struct _RTPMessage *next;
81} RTPMessage;
82
83
84/**
85 * @brief Our main session descriptor.
86 * It measures the session variables and controls
87 * the entire session. There are functions for manipulating
88 * the session so tend to use those instead of directly modifying
89 * session parameters.
90 *
91 */
92typedef struct _RTPSession {
93 uint8_t version;
94 uint8_t padding;
95 uint8_t extension;
96 uint8_t cc;
97 uint8_t marker;
98 uint8_t payload_type;
99 uint16_t sequnum; /* Set when sending */
100 uint16_t rsequnum; /* Check when recving msg */
101 uint32_t timestamp;
102 uint32_t ssrc;
103 uint32_t *csrc;
104
105 /* If some additional data must be sent via message
106 * apply it here. Only by allocating this member you will be
107 * automatically placing it within a message.
108 */
109 RTPExtHeader *ext_header;
110
111 /* External header identifiers */
112 int resolution;
113 int framerate;
114
115
116 /* Since these are only references of the
117 * call structure don't allocate or free
118 */
119
120 const uint8_t *encrypt_key;
121 const uint8_t *decrypt_key;
122 uint8_t *encrypt_nonce;
123 uint8_t *decrypt_nonce;
124
125 uint8_t *nonce_cycle;
126
127 RTPMessage *oldest_msg;
128 RTPMessage *last_msg; /* tail */
129
130 /* Msg prefix for core to know when recving */
131 uint8_t prefix;
132
133 pthread_mutex_t mutex;
134 int dest;
135
136} RTPSession;
137
138
139/**
140 * @brief Release all messages held by session.
141 *
142 * @param session The session.
143 * @return int
144 * @retval -1 Error occurred.
145 * @retval 0 Success.
146 */
147int rtp_release_session_recv ( RTPSession *session );
148
149
150/**
151 * @brief Get's oldest message in the list.
152 *
153 * @param session Where the list is.
154 * @return RTPMessage* The message. You need to call rtp_msg_free() to free it.
155 * @retval NULL No messages in the list, or no list.
156 */
157RTPMessage *rtp_recv_msg ( RTPSession *session );
158
159
160/**
161 * @brief Sends msg to _RTPSession::dest
162 *
163 * @param session The session.
164 * @param msg The message
165 * @param messenger Tox* object.
166 * @return int
167 * @retval -1 On error.
168 * @retval 0 On success.
169 */
170int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
171
172
173/**
174 * @brief Speaks for it self.
175 *
176 * @param session The control session msg belongs to. It can be NULL.
177 * @param msg The message.
178 * @return void
179 */
180void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
181
182
183/**
184 * @brief Must be called before calling any other rtp function. It's used
185 * to initialize RTP control session.
186 *
187 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
188 * @param messenger Tox* object.
189 * @param friend_num Friend id.
190 * @param encrypt_key Speaks for it self.
191 * @param decrypt_key Speaks for it self.
192 * @param encrypt_nonce Speaks for it self.
193 * @param decrypt_nonce Speaks for it self.
194 * @return RTPSession* Created control session.
195 * @retval NULL Error occurred.
196 */
197RTPSession *rtp_init_session ( int payload_type,
198 Messenger *messenger,
199 int friend_num,
200 const uint8_t *encrypt_key,
201 const uint8_t *decrypt_key,
202 const uint8_t *encrypt_nonce,
203 const uint8_t *decrypt_nonce );
204
205
206/**
207 * @brief Terminate the session.
208 *
209 * @param session The session.
210 * @param messenger The messenger who owns the session
211 * @return int
212 * @retval -1 Error occurred.
213 * @retval 0 Success.
214 */
215int rtp_terminate_session ( RTPSession *session, Messenger *messenger );
216
217
218
219#endif /* __TOXRTP */
diff --git a/toxav/toxav.c b/toxav/toxav.c
new file mode 100644
index 00000000..f704bc6b
--- /dev/null
+++ b/toxav/toxav.c
@@ -0,0 +1,551 @@
1/** toxav.c
2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 *
21 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif /* HAVE_CONFIG_H */
27
28#include "media.h"
29#include "rtp.h"
30#include "msi.h"
31
32#include <stdlib.h>
33#include <string.h>
34#include <assert.h>
35
36#include "toxav.h"
37
38#define inline__ inline __attribute__((always_inline))
39
40static const uint8_t audio_index = 0, video_index = 1;
41
42
43typedef enum {
44 ts_closing,
45 ts_running,
46 ts_closed
47
48} ThreadState;
49
50typedef struct _ToxAv {
51 Messenger *messenger;
52
53 MSISession *msi_session; /** Main msi session */
54
55 RTPSession *rtp_sessions[2]; /* Audio is first and video is second */
56
57 struct jitter_buffer *j_buf;
58 CodecState *cs;
59
60 void *agent_handler;
61} ToxAv;
62
63/**
64 * @brief Start new A/V session. There can only be one session at the time. If you register more
65 * it will result in undefined behaviour.
66 *
67 * @param messenger The messenger handle.
68 * @param useragent The agent handling A/V session (i.e. phone).
69 * @param ua_name Useragent name.
70 * @param video_width Width of video frame.
71 * @param video_height Height of video frame.
72 * @return ToxAv*
73 * @retval NULL On error.
74 */
75ToxAv *toxav_new( Tox *messenger, void *useragent, const char *ua_name , uint16_t video_width, uint16_t video_height)
76{
77 ToxAv *av = calloc ( sizeof(ToxAv), 1);
78
79 if (av == NULL)
80 return NULL;
81
82 av->messenger = (Messenger *)messenger;
83
84 av->msi_session = msi_init_session(av->messenger, (const unsigned char *) ua_name );
85 av->msi_session->agent_handler = av;
86
87 av->rtp_sessions[0] = av->rtp_sessions [1] = NULL;
88
89 /* NOTE: This should be user defined or? */
90 av->j_buf = create_queue(20);
91
92 av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width,
93 video_height, VIDEO_BITRATE);
94
95 av->agent_handler = useragent;
96
97 return av;
98}
99
100/**
101 * @brief Remove A/V session.
102 *
103 * @param av Handler.
104 * @return void
105 */
106void toxav_kill ( ToxAv *av )
107{
108 msi_terminate_session(av->msi_session);
109
110 if ( av->rtp_sessions[audio_index] ) {
111 rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle);
112 }
113
114 if ( av->rtp_sessions[video_index] ) {
115 rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle);
116 }
117
118 codec_terminate_session(av->cs);
119
120 free(av);
121}
122
123/**
124 * @brief Register callback for call state.
125 *
126 * @param callback The callback
127 * @param id One of the ToxAvCallbackID values
128 * @return void
129 */
130void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id )
131{
132 msi_register_callback((MSICallback)callback, (MSICallbackID) id);
133}
134
135/**
136 * @brief Call user. Use its friend_id.
137 *
138 * @param av Handler.
139 * @param user The user.
140 * @param call_type Call type.
141 * @param ringing_seconds Ringing timeout.
142 * @return int
143 * @retval 0 Success.
144 * @retval ToxAvError On error.
145 */
146int toxav_call (ToxAv *av, int user, ToxAvCallType call_type, int ringing_seconds )
147{
148 if ( av->msi_session->call ) {
149 return ErrorAlreadyInCall;
150 }
151
152 return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user);
153}
154
155/**
156 * @brief Hangup active call.
157 *
158 * @param av Handler.
159 * @return int
160 * @retval 0 Success.
161 * @retval ToxAvError On error.
162 */
163int toxav_hangup ( ToxAv *av )
164{
165 if ( !av->msi_session->call ) {
166 return ErrorNoCall;
167 }
168
169 if ( av->msi_session->call->state != call_active ) {
170 return ErrorInvalidState;
171 }
172
173 return msi_hangup(av->msi_session);
174}
175
176/**
177 * @brief Answer incomming call.
178 *
179 * @param av Handler.
180 * @param call_type Answer with...
181 * @return int
182 * @retval 0 Success.
183 * @retval ToxAvError On error.
184 */
185int toxav_answer ( ToxAv *av, ToxAvCallType call_type )
186{
187 if ( !av->msi_session->call ) {
188 return ErrorNoCall;
189 }
190
191 if ( av->msi_session->call->state != call_starting ) {
192 return ErrorInvalidState;
193 }
194
195 return msi_answer(av->msi_session, call_type);
196}
197
198/**
199 * @brief Reject incomming call.
200 *
201 * @param av Handler.
202 * @param reason Optional reason. Set NULL if none.
203 * @return int
204 * @retval 0 Success.
205 * @retval ToxAvError On error.
206 */
207int toxav_reject ( ToxAv *av, const char *reason )
208{
209 if ( !av->msi_session->call ) {
210 return ErrorNoCall;
211 }
212
213 if ( av->msi_session->call->state != call_starting ) {
214 return ErrorInvalidState;
215 }
216
217 return msi_reject(av->msi_session, (const uint8_t *) reason);
218}
219
220/**
221 * @brief Cancel outgoing request.
222 *
223 * @param av Handler.
224 * @param reason Optional reason.
225 * @return int
226 * @retval 0 Success.
227 * @retval ToxAvError On error.
228 */
229int toxav_cancel ( ToxAv *av, const char *reason )
230{
231 if ( !av->msi_session->call ) {
232 return ErrorNoCall;
233 }
234
235 return msi_cancel(av->msi_session, 0, (const uint8_t *)reason);
236}
237
238/**
239 * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
240 *
241 * @param av Handler.
242 * @return int
243 * @retval 0 Success.
244 * @retval ToxAvError On error.
245 */
246int toxav_stop_call ( ToxAv *av )
247{
248 if ( !av->msi_session->call ) {
249 return ErrorNoCall;
250 }
251
252 return msi_stopcall(av->msi_session);
253}
254
255/**
256 * @brief Must be call before any RTP transmission occurs.
257 *
258 * @param av Handler.
259 * @return int
260 * @retval 0 Success.
261 * @retval ToxAvError On error.
262 */
263int toxav_prepare_transmission ( ToxAv *av )
264{
265 assert(av->msi_session);
266
267 if ( !av->msi_session || !av->msi_session->call ) {
268 return ErrorNoCall;
269 }
270
271 av->rtp_sessions[audio_index] = rtp_init_session(
272 type_audio,
273 av->messenger,
274 av->msi_session->call->peers[0],
275 av->msi_session->call->key_peer,
276 av->msi_session->call->key_local,
277 av->msi_session->call->nonce_peer,
278 av->msi_session->call->nonce_local
279 );
280
281
282 if ( !av->rtp_sessions[audio_index] ) {
283 fprintf(stderr, "Error while starting audio RTP session!\n");
284 return ErrorStartingAudioRtp;
285 }
286
287 av->rtp_sessions[video_index] = rtp_init_session (
288 type_video,
289 av->messenger,
290 av->msi_session->call->peers[0],
291 av->msi_session->call->key_peer,
292 av->msi_session->call->key_local,
293 av->msi_session->call->nonce_peer,
294 av->msi_session->call->nonce_local
295 );
296
297
298 if ( !av->rtp_sessions[video_index] ) {
299 fprintf(stderr, "Error while starting video RTP session!\n");
300 return ErrorStartingVideoRtp;
301 }
302
303 return ErrorNone;
304}
305
306/**
307 * @brief Call this at the end of the transmission.
308 *
309 * @param av Handler.
310 * @return int
311 * @retval 0 Success.
312 * @retval ToxAvError On error.
313 */
314int toxav_kill_transmission ( ToxAv *av )
315{
316 /* Both sessions should be active at any time */
317 if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
318 return ErrorNoTransmission;
319
320
321 if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
322 fprintf(stderr, "Error while terminating audio RTP session!\n");
323 return ErrorTerminatingAudioRtp;
324 }
325
326 if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
327 fprintf(stderr, "Error while terminating video RTP session!\n");
328 return ErrorTerminatingVideoRtp;
329 }
330
331 return ErrorNone;
332}
333
334
335/**
336 * @brief Send RTP payload.
337 *
338 * @param av Handler.
339 * @param type Type of payload.
340 * @param payload The payload.
341 * @param length Size of it.
342 * @return int
343 * @retval 0 Success.
344 * @retval -1 Failure.
345 */
346inline__ int toxav_send_rtp_payload ( ToxAv *av, ToxAvCallType type, const uint8_t *payload, uint16_t length )
347{
348 if ( av->rtp_sessions[type - TypeAudio] )
349 return rtp_send_msg ( av->rtp_sessions[type - TypeAudio], av->msi_session->messenger_handle, payload, length );
350 else return -1;
351}
352
353/**
354 * @brief Receive RTP payload.
355 *
356 * @param av Handler.
357 * @param type Type of the payload.
358 * @param dest Storage.
359 * @return int
360 * @retval ToxAvError On Error.
361 * @retval >=0 Size of received payload.
362 */
363inline__ int toxav_recv_rtp_payload ( ToxAv *av, ToxAvCallType type, uint8_t *dest )
364{
365 if ( !dest ) return ErrorInternal;
366
367 if ( !av->rtp_sessions[type - TypeAudio] ) return ErrorNoRtpSession;
368
369 RTPMessage *message;
370
371 if ( type == TypeAudio ) {
372
373 do {
374 message = rtp_recv_msg(av->rtp_sessions[audio_index]);
375
376 if (message) {
377 /* push the packet into the queue */
378 queue(av->j_buf, message);
379 }
380 } while (message);
381
382 int success = 0;
383 message = dequeue(av->j_buf, &success);
384
385 if ( success == 2) return ErrorAudioPacketLost;
386 } else {
387 message = rtp_recv_msg(av->rtp_sessions[video_index]);
388 }
389
390 if ( message ) {
391 memcpy(dest, message->data, message->length);
392
393 int length = message->length;
394
395 rtp_free_msg(NULL, message);
396
397 return length;
398 }
399
400 return 0;
401}
402
403/**
404 * @brief Receive decoded video packet.
405 *
406 * @param av Handler.
407 * @param output Storage.
408 * @return int
409 * @retval 0 Success.
410 * @retval ToxAvError On Error.
411 */
412inline__ int toxav_recv_video ( ToxAv *av, vpx_image_t **output)
413{
414 if ( !output ) return ErrorInternal;
415
416 uint8_t packet [RTP_PAYLOAD_SIZE];
417 int recved_size = 0;
418
419 do {
420 recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
421
422 if (recved_size > 0) {
423 printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
424 }
425 } while (recved_size > 0);
426
427 vpx_codec_iter_t iter = NULL;
428 vpx_image_t *img;
429 img = vpx_codec_get_frame(&av->cs->v_decoder, &iter);
430
431 if (img == NULL)
432 return ErrorInternal;
433
434 *output = img;
435 return 0;
436}
437
438/**
439 * @brief Encode and send video packet.
440 *
441 * @param av Handler.
442 * @param input The packet.
443 * @return int
444 * @retval 0 Success.
445 * @retval ToxAvError On error.
446 */
447inline__ int toxav_send_video ( ToxAv *av, vpx_image_t *input)
448{
449 if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
450 printf("could not encode video frame\n");
451 return ErrorInternal;
452 }
453
454 ++av->cs->frame_counter;
455
456 vpx_codec_iter_t iter = NULL;
457 const vpx_codec_cx_pkt_t *pkt;
458 int sent = 0;
459
460 while ( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) {
461 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
462 if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1)
463 ++sent;
464 }
465 }
466
467 if (sent > 0)
468 return 0;
469
470 return ErrorInternal;
471}
472
473/**
474 * @brief Receive decoded audio frame.
475 *
476 * @param av Handler.
477 * @param frame_size ...
478 * @param dest Destination of the packet. Make sure it has enough space for
479 * RTP_PAYLOAD_SIZE bytes.
480 * @return int
481 * @retval >=0 Size of received packet.
482 * @retval ToxAvError On error.
483 */
484inline__ int toxav_recv_audio ( ToxAv *av, int frame_size, int16_t *dest )
485{
486 if ( !dest ) return ErrorInternal;
487
488 uint8_t packet [RTP_PAYLOAD_SIZE];
489
490 int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
491
492 if ( recved_size == ErrorAudioPacketLost ) {
493 printf("Lost packet\n");
494 return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
495 } else if ( recved_size ) {
496 return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
497 } else {
498 return 0; /* Nothing received */
499 }
500}
501
502/**
503 * @brief Encode and send audio frame.
504 *
505 * @param av Handler.
506 * @param frame The frame.
507 * @param frame_size It's size.
508 * @return int
509 * @retval 0 Success.
510 * @retval ToxAvError On error.
511 */
512inline__ int toxav_send_audio ( ToxAv *av, const int16_t *frame, int frame_size)
513{
514 uint8_t temp_data[RTP_PAYLOAD_SIZE];
515 int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data));
516
517 if (ret <= 0)
518 return ErrorInternal;
519
520 return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret);
521}
522
523/**
524 * @brief Get peer transmission type. It can either be audio or video.
525 *
526 * @param av Handler.
527 * @param peer The peer
528 * @return int
529 * @retval ToxAvCallType On success.
530 * @retval ToxAvError On error.
531 */
532int toxav_get_peer_transmission_type ( ToxAv *av, int peer )
533{
534 assert(av->msi_session);
535
536 if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
537 return ErrorInternal;
538
539 return av->msi_session->call->type_peer[peer];
540}
541
542/**
543 * @brief Get reference to an object that is handling av session.
544 *
545 * @param av Handler.
546 * @return void*
547 */
548void *toxav_get_agent_handler ( ToxAv *av )
549{
550 return av->agent_handler;
551}
diff --git a/toxav/toxav.h b/toxav/toxav.h
new file mode 100644
index 00000000..be7a2950
--- /dev/null
+++ b/toxav/toxav.h
@@ -0,0 +1,300 @@
1/** toxav.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 at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifndef __TOXAV
26#define __TOXAV
27#include <inttypes.h>
28
29/* vpx_image_t */
30#include <vpx/vpx_image.h>
31
32typedef void *( *ToxAVCallback ) ( void *arg );
33typedef struct _ToxAv ToxAv;
34
35#ifndef __TOX_DEFINED__
36#define __TOX_DEFINED__
37typedef struct Tox Tox;
38#endif
39
40#define RTP_PAYLOAD_SIZE 65535
41
42/* Default video bitrate in bytes/s */
43#define VIDEO_BITRATE 10*1000*100
44
45/* Default audio bitrate in bits/s */
46#define AUDIO_BITRATE 64000
47
48/* Number of audio channels. */
49#define AUDIO_CHANNELS 1
50
51/* Audio frame duration in miliseconds */
52#define AUDIO_FRAME_DURATION 20
53
54/* Audio sample rate recommended to be 48kHz for Opus */
55#define AUDIO_SAMPLE_RATE 48000
56
57/* The amount of samples in one audio frame */
58#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000
59
60/* Assume 60 fps*/
61#define MAX_ENCODE_TIME_US ((1000 / 60) * 1000)
62
63
64/**
65 * @brief Callbacks ids that handle the call states.
66 */
67typedef enum {
68 /* Requests */
69 OnInvite,
70 OnStart,
71 OnCancel,
72 OnReject,
73 OnEnd,
74
75 /* Responses */
76 OnRinging,
77 OnStarting,
78 OnEnding,
79
80 /* Protocol */
81 OnError,
82 OnRequestTimeout
83
84} ToxAvCallbackID;
85
86
87/**
88 * @brief Call type identifier.
89 */
90typedef enum {
91 TypeAudio = 70,
92 TypeVideo
93} ToxAvCallType;
94
95
96/**
97 * @brief Error indicators.
98 *
99 */
100typedef enum {
101 ErrorNone = 0,
102 ErrorInternal = -1, /* Internal error */
103 ErrorAlreadyInCall = -2, /* Already has an active call */
104 ErrorNoCall = -3, /* Trying to perform call action while not in a call */
105 ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
106 ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
107 ErrorAudioPacketLost = -6, /* Indicating packet loss */
108 ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
109 ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
110 ErrorNoTransmission = -9, /* Returned in toxav_kill_transmission() */
111 ErrorTerminatingAudioRtp = -10, /* Returned in toxav_kill_transmission() */
112 ErrorTerminatingVideoRtp = -11, /* Returned in toxav_kill_transmission() */
113
114} ToxAvError;
115
116
117/**
118 * @brief Start new A/V session. There can only be one session at the time. If you register more
119 * it will result in undefined behaviour.
120 *
121 * @param messenger The messenger handle.
122 * @param useragent The agent handling A/V session (i.e. phone).
123 * @param ua_name Useragent name.
124 * @param video_width Width of video frame.
125 * @param video_height Height of video frame.
126 * @return ToxAv*
127 * @retval NULL On error.
128 */
129ToxAv *toxav_new(Tox *messenger, void *useragent, const char *ua_name, uint16_t video_width, uint16_t video_height);
130
131/**
132 * @brief Remove A/V session.
133 *
134 * @param av Handler.
135 * @return void
136 */
137void toxav_kill(ToxAv *av);
138
139/**
140 * @brief Register callback for call state.
141 *
142 * @param callback The callback
143 * @param id One of the ToxAvCallbackID values
144 * @return void
145 */
146void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id);
147
148/**
149 * @brief Call user. Use its friend_id.
150 *
151 * @param av Handler.
152 * @param user The user.
153 * @param call_type Call type.
154 * @param ringing_seconds Ringing timeout.
155 * @return int
156 * @retval 0 Success.
157 * @retval ToxAvError On error.
158 */
159int toxav_call(ToxAv *av, int user, ToxAvCallType call_type, int ringing_seconds);
160
161/**
162 * @brief Hangup active call.
163 *
164 * @param av Handler.
165 * @return int
166 * @retval 0 Success.
167 * @retval ToxAvError On error.
168 */
169int toxav_hangup(ToxAv *av);
170
171/**
172 * @brief Answer incomming call.
173 *
174 * @param av Handler.
175 * @param call_type Answer with...
176 * @return int
177 * @retval 0 Success.
178 * @retval ToxAvError On error.
179 */
180int toxav_answer(ToxAv *av, ToxAvCallType call_type );
181
182/**
183 * @brief Reject incomming call.
184 *
185 * @param av Handler.
186 * @param reason Optional reason. Set NULL if none.
187 * @return int
188 * @retval 0 Success.
189 * @retval ToxAvError On error.
190 */
191int toxav_reject(ToxAv *av, const char *reason);
192
193/**
194 * @brief Cancel outgoing request.
195 *
196 * @param av Handler.
197 * @param reason Optional reason.
198 * @return int
199 * @retval 0 Success.
200 * @retval ToxAvError On error.
201 */
202int toxav_cancel(ToxAv *av, const char *reason);
203
204/**
205 * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
206 *
207 * @param av Handler.
208 * @return int
209 * @retval 0 Success.
210 * @retval ToxAvError On error.
211 */
212int toxav_stop_call(ToxAv *av);
213
214/**
215 * @brief Must be call before any RTP transmission occurs.
216 *
217 * @param av Handler.
218 * @return int
219 * @retval 0 Success.
220 * @retval ToxAvError On error.
221 */
222int toxav_prepare_transmission(ToxAv *av);
223
224/**
225 * @brief Call this at the end of the transmission.
226 *
227 * @param av Handler.
228 * @return int
229 * @retval 0 Success.
230 * @retval ToxAvError On error.
231 */
232int toxav_kill_transmission(ToxAv *av);
233
234/**
235 * @brief Receive decoded video packet.
236 *
237 * @param av Handler.
238 * @param output Storage.
239 * @return int
240 * @retval 0 Success.
241 * @retval ToxAvError On Error.
242 */
243int toxav_recv_video ( ToxAv *av, vpx_image_t **output);
244
245/**
246 * @brief Receive decoded audio frame.
247 *
248 * @param av Handler.
249 * @param frame_size ...
250 * @param dest Destination of the packet. Make sure it has enough space for
251 * RTP_PAYLOAD_SIZE bytes.
252 * @return int
253 * @retval >=0 Size of received packet.
254 * @retval ToxAvError On error.
255 */
256int toxav_recv_audio( ToxAv *av, int frame_size, int16_t *dest );
257
258/**
259 * @brief Encode and send video packet.
260 *
261 * @param av Handler.
262 * @param input The packet.
263 * @return int
264 * @retval 0 Success.
265 * @retval ToxAvError On error.
266 */
267int toxav_send_video ( ToxAv *av, vpx_image_t *input);
268
269/**
270 * @brief Encode and send audio frame.
271 *
272 * @param av Handler.
273 * @param frame The frame.
274 * @param frame_size It's size.
275 * @return int
276 * @retval 0 Success.
277 * @retval ToxAvError On error.
278 */
279int toxav_send_audio ( ToxAv *av, const int16_t *frame, int frame_size);
280
281/**
282 * @brief Get peer transmission type. It can either be audio or video.
283 *
284 * @param av Handler.
285 * @param peer The peer
286 * @return int
287 * @retval ToxAvCallType On success.
288 * @retval ToxAvError On error.
289 */
290int toxav_get_peer_transmission_type ( ToxAv *av, int peer );
291
292/**
293 * @brief Get reference to an object that is handling av session.
294 *
295 * @param av Handler.
296 * @return void*
297 */
298void *toxav_get_agent_handler ( ToxAv *av );
299
300#endif /* __TOXAV */ \ No newline at end of file
diff --git a/toxcore/LAN_discovery.h b/toxcore/LAN_discovery.h
index 58bd2bee..fcb094e4 100644
--- a/toxcore/LAN_discovery.h
+++ b/toxcore/LAN_discovery.h
@@ -36,7 +36,7 @@
36#endif 36#endif
37 37
38/* Interval in seconds between LAN discovery packet sending. */ 38/* Interval in seconds between LAN discovery packet sending. */
39#define LAN_DISCOVERY_INTERVAL 60 39#define LAN_DISCOVERY_INTERVAL 10
40 40
41/* Send a LAN discovery pcaket to the broadcast address with port port. */ 41/* Send a LAN discovery pcaket to the broadcast address with port port. */
42int send_LANdiscovery(uint16_t port, DHT *dht); 42int send_LANdiscovery(uint16_t port, DHT *dht);
diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c
index 6dff52ef..17914038 100644
--- a/toxcore/group_chats.c
+++ b/toxcore/group_chats.c
@@ -251,6 +251,22 @@ static int addpeer(Group_Chat *chat, uint8_t *client_id)
251} 251}
252 252
253/* 253/*
254 * Set a peer from the group chat to deleted.
255 *
256 * return 0 if success
257 * return -1 if error.
258 */
259static int del_peer_set(Group_Chat *chat, int peernum)
260{
261 if ((uint32_t)peernum >= chat->numpeers)
262 return -1;
263
264 chat->group[peernum].deleted = 1;
265 chat->group[peernum].deleted_time = unix_time();
266 return 0;
267}
268
269/*
254 * Delete a peer from the group chat. 270 * Delete a peer from the group chat.
255 * 271 *
256 * return 0 if success 272 * return 0 if success
@@ -488,6 +504,9 @@ static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
488 if (peernum == -1) 504 if (peernum == -1)
489 return 1; 505 return 1;
490 506
507 if (chat->group[peernum].deleted)
508 return 1;
509
491 /* Spam prevention (1 message per peer per second limit.) 510 /* Spam prevention (1 message per peer per second limit.)
492 511
493 if (chat->group[peernum].last_recv == temp_time) 512 if (chat->group[peernum].last_recv == temp_time)
@@ -531,7 +550,7 @@ static int handle_data(Group_Chat *chat, uint8_t *data, uint32_t len)
531 if (contents_len != 0) 550 if (contents_len != 0)
532 return 1; 551 return 1;
533 552
534 delpeer(chat, peernum); 553 del_peer_set(chat, peernum);
535 break; 554 break;
536 555
537 case GROUP_CHAT_PEER_NICK: 556 case GROUP_CHAT_PEER_NICK:
@@ -758,6 +777,7 @@ static void ping_group(Group_Chat *chat)
758 } 777 }
759} 778}
760 779
780#define DEL_PEER_DELAY 3
761static void del_dead_peers(Group_Chat *chat) 781static void del_dead_peers(Group_Chat *chat)
762{ 782{
763 uint32_t i; 783 uint32_t i;
@@ -766,6 +786,11 @@ static void del_dead_peers(Group_Chat *chat)
766 if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 4)) { 786 if (is_timeout(chat->group[i].last_recv_msgping, GROUP_PING_INTERVAL * 4)) {
767 delpeer(chat, i); 787 delpeer(chat, i);
768 } 788 }
789
790 if (chat->group[i].deleted) {
791 if (is_timeout(chat->group[i].deleted_time, DEL_PEER_DELAY))
792 delpeer(chat, i);
793 }
769 } 794 }
770} 795}
771 796
@@ -773,7 +798,7 @@ static void del_dead_peers(Group_Chat *chat)
773static void send_names_new_peer(Group_Chat *chat) 798static void send_names_new_peer(Group_Chat *chat)
774{ 799{
775 group_send_nick(chat, chat->nick, chat->nick_len); 800 group_send_nick(chat, chat->nick, chat->nick_len);
776 chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 10; 801 chat->last_sent_nick = (unix_time() - NICK_SEND_INTERVAL) + 15;
777} 802}
778static void send_names(Group_Chat *chat) 803static void send_names(Group_Chat *chat)
779{ 804{
diff --git a/toxcore/group_chats.h b/toxcore/group_chats.h
index 8d5f5e50..e31aa229 100644
--- a/toxcore/group_chats.h
+++ b/toxcore/group_chats.h
@@ -41,6 +41,9 @@ typedef struct {
41 41
42 uint8_t nick[MAX_NICK_BYTES]; 42 uint8_t nick[MAX_NICK_BYTES];
43 uint16_t nick_len; 43 uint16_t nick_len;
44
45 uint8_t deleted;
46 uint64_t deleted_time;
44} Group_Peer; 47} Group_Peer;
45 48
46typedef struct { 49typedef struct {
diff --git a/toxmsi/Makefile.inc b/toxmsi/Makefile.inc
deleted file mode 100644
index 7d620e70..00000000
--- a/toxmsi/Makefile.inc
+++ /dev/null
@@ -1,69 +0,0 @@
1if BUILD_AV
2
3lib_LTLIBRARIES += libtoxmsi.la
4
5libtoxmsi_la_include_HEADERS = \
6 ../toxmsi/toxmsi.h \
7 ../toxmsi/toxmedia.h
8
9libtoxmsi_la_includedir = $(includedir)/tox
10
11
12libtoxmsi_la_SOURCES = ../toxmsi/toxmsi.h \
13 ../toxmsi/toxmsi.c \
14 ../toxmsi/toxmsi_message.h \
15 ../toxmsi/toxmsi_message.c \
16 ../toxmsi/toxmsi_header.h \
17 ../toxmsi/toxmsi_header.c \
18 ../toxmsi/toxmsi_event.h \
19 ../toxmsi/toxmsi_event.c \
20 ../toxrtp/tests/test_helper.h \
21 ../toxrtp/tests/test_helper.c
22
23libtoxmsi_la_CFLAGS = -I../toxcore \
24 -I../toxmsi \
25 -I../toxrtp \
26 $(NACL_CFLAGS) \
27 $(PTHREAD_CFLAGS)
28
29libtoxmsi_la_LDFLAGS = $(TOXMSI_LT_LDFLAGS) \
30 $(EXTRA_LT_LDFLAGS) \
31 $(NACL_LDFLAGS) \
32 $(PTHREAD_LIBS)
33
34libtoxmsi_la_LIBS = $(NACL_LIBS)
35
36noinst_PROGRAMS += phone
37
38phone_SOURCES = ../toxmsi/phone.c \
39 ../toxmsi/toxmedia.c
40
41phone_CFLAGS = -I../toxcore \
42 -I../toxrtp \
43 $(AVFORMAT_CFLAGS) \
44 $(AVCODEC_CFLAGS) \
45 $(AVUTIL_CFLAGS) \
46 $(AVDEVICE_CFLAGS) \
47 $(SWSCALE_CFLAGS) \
48 $(SDL_CFLAGS) \
49 $(OPENAL_CFLAGS) \
50 $(NACL_CFLAGS) \
51 $(OPUS_CFLAGS) \
52 $(PTHREAD_CFLAGS)
53
54
55phone_LDADD = $(PTHREAD_LIBS) \
56 libtoxrtp.la \
57 libtoxmsi.la \
58 $(NACL_LDFLAGS) \
59 $(AVFORMAT_LIBS) \
60 $(AVCODEC_LIBS) \
61 $(AVUTIL_LIBS) \
62 $(AVDEVICE_LIBS) \
63 $(SWSCALE_LIBS) \
64 $(SDL_LIBS) \
65 $(OPENAL_LIBS) \
66 $(NACL_LIBS) \
67 $(OPUS_LIBS)
68
69endif
diff --git a/toxmsi/phone.c b/toxmsi/phone.c
deleted file mode 100644
index 432be94c..00000000
--- a/toxmsi/phone.c
+++ /dev/null
@@ -1,668 +0,0 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif /* HAVE_CONFIG_H */
4
5#define _BSD_SOURCE
6#define _GNU_SOURCE
7
8#define _CT_PHONE
9
10#ifdef _CT_PHONE
11#include "phone.h"
12#include <stdarg.h>
13#include <unistd.h>
14#include <stdio.h>
15#include <string.h>
16#include <stdlib.h>
17/* #include <termios.h> Can this be removed? */
18#include <pthread.h>
19#include "toxmedia.h"
20
21
22
23void INFO (const char *_format, ...)
24{
25 printf("\r[!] ");
26 va_list _arg;
27 va_start (_arg, _format);
28 vfprintf (stdout, _format, _arg);
29 va_end (_arg);
30 printf("\n\r >> ");
31 fflush(stdout);
32}
33
34int rtp_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length )
35{
36 phone_t *_phone = _object;
37 rtp_msg_t *_msg;
38 uint8_t _payload_id;
39
40 if ( _phone->_msi->_call && _phone->_msi->_call->_state == call_active ) {
41
42 _msg = rtp_msg_parse ( NULL, data + 1, length - 1 ); /* ignore marker byte */
43
44 if ( !_msg )
45 return 0;
46
47 _payload_id = rtp_header_get_setting_payload_type(_msg->_header);
48
49 if ( _payload_id == _PAYLOAD_OPUS && _phone->_rtp_audio )
50 rtp_store_msg(_phone->_rtp_audio, _msg);
51 else if ( _payload_id == _PAYLOAD_VP8 && _phone->_rtp_video )
52 rtp_store_msg(_phone->_rtp_video, _msg);
53 else rtp_free_msg( NULL, _msg);
54 }
55
56 return SUCCESS;
57}
58int msi_handlepacket ( void *_object, tox_IP_Port ip_port, uint8_t *data, uint32_t length )
59{
60 msi_session_t *_session = _object;
61 msi_msg_t *_msg;
62
63 _msg = msi_parse_msg ( data + 1 ); /* ignore marker byte */
64
65 if ( _msg ) {
66 /* my current solution for "hole punching" */
67 _session->_friend_id = ip_port;
68 } else {
69 return FAILURE;
70 }
71
72 /* place message in a session */
73 msi_store_msg(_session, _msg);
74
75 return SUCCESS;
76}
77
78void *phone_receivepacket ( void *_phone_p )
79{
80 phone_t *_phone = _phone_p;
81
82
83 networking_registerhandler(_phone->_networking, MSI_PACKET, msi_handlepacket, _phone->_msi);
84 networking_registerhandler(_phone->_networking, RTP_PACKET, rtp_handlepacket, _phone);
85
86 /* Now start main networking loop */
87 while ( _phone->_networking ) { /* so not thread safe */
88 networking_poll(_phone->_networking);
89 usleep(10000);
90 }
91
92 pthread_exit ( NULL );
93}
94
95/* Media transport callback */
96typedef struct hmtc_args_s {
97 rtp_session_t **_rtp_audio;
98 rtp_session_t **_rtp_video;
99 call_type *_local_type_call;
100 call_state *_this_call;
101 void *_core_handler;
102} hmtc_args_t;
103
104void *phone_handle_media_transport_poll ( void *_hmtc_args_p )
105{
106 rtp_msg_t *_audio_msg, * _video_msg;
107
108 hmtc_args_t *_hmtc_args = _hmtc_args_p;
109
110 rtp_session_t *_rtp_audio = *_hmtc_args->_rtp_audio;
111 rtp_session_t *_rtp_video = *_hmtc_args->_rtp_video;
112
113 call_type *_type = _hmtc_args->_local_type_call;
114 void *_core_handler = _hmtc_args->_core_handler;
115
116
117 call_state *_this_call = _hmtc_args->_this_call;
118
119 while ( *_this_call == call_active ) {
120
121 // THREADLOCK()
122
123 _audio_msg = rtp_recv_msg ( _rtp_audio );
124 _video_msg = rtp_recv_msg ( _rtp_video );
125
126 if ( _audio_msg ) {
127 /* Do whatever with msg */
128 puts("audio");
129 /* Do whatever with msg
130 puts(_audio_msg->_data);*/
131 rtp_free_msg ( _rtp_audio, _audio_msg );
132 }
133
134 if ( _video_msg ) {
135 /* Do whatever with msg */
136 puts("video");
137 /* Do whatever with msg
138 puts(_video_msg->_data); */
139 rtp_free_msg ( _rtp_video, _video_msg );
140 _video_msg = NULL;
141 }
142
143 /* -------------------- */
144
145 _audio_msg = rtp_msg_new ( _rtp_audio, (const uint8_t *)"audio\0", 6 ) ;
146 rtp_send_msg ( _rtp_audio, _audio_msg, _core_handler );
147 _audio_msg = NULL;
148
149 if ( *_type == type_video ) { /* if local call send video */
150 _video_msg = rtp_msg_new ( _rtp_video, (const uint8_t *)"video\0", 6 ) ;
151 rtp_send_msg ( _rtp_video, _video_msg, _core_handler );
152 _video_msg = NULL;
153 }
154
155 //THREADUNLOCK()
156
157 usleep ( 10000 );
158 /* -------------------- */
159 }
160
161 //THREADLOCK()
162
163 if ( _audio_msg ) {
164 rtp_free_msg(_rtp_audio, _audio_msg);
165 }
166
167 if ( _video_msg ) {
168 rtp_free_msg(_rtp_video, _video_msg);
169 }
170
171 rtp_release_session_recv(_rtp_video);
172 rtp_release_session_recv(_rtp_audio);
173
174 rtp_terminate_session(_rtp_audio);
175 rtp_terminate_session(_rtp_video);
176
177 *_hmtc_args->_rtp_audio = NULL;
178 *_hmtc_args->_rtp_video = NULL;
179
180 free(_hmtc_args_p);
181
182 //THREADUNLOCK()
183
184 INFO("Media thread finished!");
185
186 pthread_exit ( NULL );
187}
188
189pthread_t phone_startmedia_loop ( phone_t *_phone )
190{
191 if ( !_phone ) {
192 return 0;
193 }
194
195 int _status;
196
197 uint8_t _prefix = RTP_PACKET;
198
199 pthread_t _rtp_tid;
200 int _rtp_thread_running = 1;
201
202 _phone->_rtp_audio = rtp_init_session ( -1, 1 );
203 rtp_set_prefix ( _phone->_rtp_audio, &_prefix, 1 );
204 rtp_add_receiver ( _phone->_rtp_audio, &_phone->_msi->_friend_id );
205 rtp_set_payload_type(_phone->_rtp_audio, _PAYLOAD_OPUS);
206
207 _phone->_rtp_video = rtp_init_session ( -1, 1 );
208 rtp_set_prefix ( _phone->_rtp_video, &_prefix, 1 );
209 rtp_add_receiver ( _phone->_rtp_video, &_phone->_msi->_friend_id );
210 rtp_set_payload_type(_phone->_rtp_video, _PAYLOAD_VP8);
211
212
213
214 hmtc_args_t *rtp_targs = calloc(sizeof(hmtc_args_t), 1);
215
216
217 rtp_targs->_rtp_audio = &_phone->_rtp_audio;
218 rtp_targs->_rtp_video = &_phone->_rtp_video;
219 rtp_targs->_local_type_call = &_phone->_msi->_call->_type_local;
220 rtp_targs->_this_call = &_phone->_msi->_call->_state;
221 rtp_targs->_core_handler = _phone->_networking;
222
223 codec_state *cs;
224 cs = _phone->cs;
225 //_status = pthread_create ( &_rtp_tid, NULL, phone_handle_media_transport_poll, rtp_targs );
226 cs->_rtp_audio = _phone->_rtp_audio;
227 cs->_rtp_video = _phone->_rtp_video;
228 cs->_networking = _phone->_networking;
229 cs->socket = _phone->_tox_sock;
230 cs->quit = 0;
231
232 printf("support: %d %d\n", cs->support_send_audio, cs->support_send_video);
233
234 if (cs->support_send_audio && cs->support_send_video) /* quick fix */
235 pthread_create(&_phone->cs->encode_audio_thread, NULL, encode_audio_thread, _phone->cs);
236
237 if (cs->support_receive_audio)
238 pthread_create(&_phone->cs->decode_audio_thread, NULL, decode_audio_thread, _phone->cs);
239
240 if (cs->support_send_video)
241 pthread_create(&_phone->cs->encode_video_thread, NULL, encode_video_thread, _phone->cs);
242
243 if (cs->support_receive_video)
244 pthread_create(&_phone->cs->decode_video_thread, NULL, decode_video_thread, _phone->cs);
245
246//
247 return 1;
248
249
250
251
252}
253
254
255/* Some example callbacks */
256
257MCBTYPE callback_recv_invite ( MCBARGS )
258{
259 const char *_call_type;
260
261 msi_session_t *_msi = _arg;
262
263 /* Get the last one */
264 call_type _type = _msi->_call->_type_peer[_msi->_call->_participants - 1];
265
266 switch ( _type ) {
267 case type_audio:
268 _call_type = "audio";
269 break;
270
271 case type_video:
272 _call_type = "video";
273 break;
274 }
275
276 INFO( "Incoming %s call!", _call_type );
277
278}
279MCBTYPE callback_recv_trying ( MCBARGS )
280{
281 INFO ( "Trying...");
282}
283MCBTYPE callback_recv_ringing ( MCBARGS )
284{
285 INFO ( "Ringing!" );
286}
287MCBTYPE callback_recv_starting ( MCBARGS )
288{
289 msi_session_t *_session = _arg;
290
291 if ( !phone_startmedia_loop(_session->_agent_handler) ) {
292 INFO("Starting call failed!");
293 } else {
294 INFO ("Call started! ( press h to hangup )");
295 }
296}
297MCBTYPE callback_recv_ending ( MCBARGS )
298{
299 msi_session_t *_session = _arg;
300 phone_t *_phone = _session->_agent_handler;
301 _phone->cs->quit = 1;
302
303 if (_phone->cs->encode_video_thread)
304 pthread_join(_phone->cs->encode_video_thread, NULL);
305
306 if (_phone->cs->encode_audio_thread)
307 pthread_join(_phone->cs->encode_audio_thread, NULL);
308
309 if (_phone->cs->decode_audio_thread)
310 pthread_join(_phone->cs->decode_audio_thread, NULL);
311
312 if (_phone->cs->decode_video_thread)
313 pthread_join(_phone->cs->decode_video_thread, NULL);
314
315 SDL_Quit();
316 printf("all A/V threads successfully shut down\n");
317
318 INFO ( "Call ended!" );
319}
320
321MCBTYPE callback_recv_error ( MCBARGS )
322{
323 msi_session_t *_session = _arg;
324
325 INFO( "Error: %s", _session->_last_error_str );
326}
327
328MCBTYPE callback_call_started ( MCBARGS )
329{
330 msi_session_t *_session = _arg;
331
332 if ( !phone_startmedia_loop(_session->_agent_handler) ) {
333 INFO("Starting call failed!");
334 } else {
335 INFO ("Call started! ( press h to hangup )");
336 }
337
338}
339MCBTYPE callback_call_canceled ( MCBARGS )
340{
341 INFO ( "Call canceled!" );
342}
343MCBTYPE callback_call_rejected ( MCBARGS )
344{
345 INFO ( "Call rejected!\n" );
346}
347MCBTYPE callback_call_ended ( MCBARGS )
348{
349
350 msi_session_t *_session = _arg;
351 phone_t *_phone = _session->_agent_handler;
352 _phone->cs->quit = 1;
353
354 if (_phone->cs->encode_video_thread)
355 pthread_join(_phone->cs->encode_video_thread, NULL);
356
357 if (_phone->cs->encode_audio_thread)
358 pthread_join(_phone->cs->encode_audio_thread, NULL);
359
360 if (_phone->cs->decode_audio_thread)
361 pthread_join(_phone->cs->decode_audio_thread, NULL);
362
363 if (_phone->cs->decode_video_thread)
364 pthread_join(_phone->cs->decode_video_thread, NULL);
365
366 SDL_Quit();
367 printf("all A/V threads successfully shut down\n");
368
369 INFO ( "Call ended!" );
370}
371
372MCBTYPE callback_requ_timeout ( MCBARGS )
373{
374 INFO( "No answer! " );
375}
376
377
378phone_t *initPhone(uint16_t _listen_port, uint16_t _send_port)
379{
380 phone_t *_retu = calloc(sizeof(phone_t), 1);
381 _retu->cs = av_calloc(sizeof(codec_state), 1);
382
383 /* Initialize our mutex */
384 pthread_mutex_init ( &_mutex, NULL );
385
386 IP_Port _local;
387 ip_init(&_local.ip, 0);
388 _local.ip.ip4.uint32 = htonl ( INADDR_ANY );
389
390 /* Bind local receive port to any address */
391 _retu->_networking = new_networking ( _local.ip, _listen_port );
392
393 if ( !_retu->_networking ) {
394 fprintf ( stderr, "new_networking() failed!\n" );
395 return NULL;
396 }
397
398 _retu->_send_port = _send_port;
399 _retu->_recv_port = _listen_port;
400
401 _retu->_tox_sock = _retu->_networking->sock;
402
403 _retu->_rtp_audio = NULL;
404 _retu->_rtp_video = NULL;
405
406
407 /* Initialize msi */
408 _retu->_msi = msi_init_session ( _retu->_networking, (const uint8_t *)_USERAGENT );
409
410 if ( !_retu->_msi ) {
411 fprintf ( stderr, "msi_init_session() failed\n" );
412 return NULL;
413 }
414
415 /* Initiate codecs */
416 init_encoder(_retu->cs);
417 init_decoder(_retu->cs);
418
419 _retu->_msi->_agent_handler = _retu;
420 /* Initiate callbacks */
421 msi_register_callback_send ( sendpacket ); /* Using core's send */
422
423 msi_register_callback_call_started ( callback_call_started );
424 msi_register_callback_call_canceled ( callback_call_canceled );
425 msi_register_callback_call_rejected ( callback_call_rejected );
426 msi_register_callback_call_ended ( callback_call_ended );
427
428 msi_register_callback_recv_invite ( callback_recv_invite );
429 msi_register_callback_recv_ringing ( callback_recv_ringing );
430 msi_register_callback_recv_starting ( callback_recv_starting );
431 msi_register_callback_recv_ending ( callback_recv_ending );
432 msi_register_callback_recv_error(callback_recv_error);
433
434 msi_register_callback_requ_timeout ( callback_requ_timeout );
435 /* ------------------ */
436
437 /* Now start msi main loop. It's a must!
438 * Define a frequency in ms; 10 ms is just fine
439 */
440 msi_start_main_loop ( _retu->_msi, 10 );
441
442 return _retu;
443}
444
445pthread_t phone_startmain_loop(phone_t *_phone)
446{
447 int _status;
448 /* Start receive thread */
449 pthread_t _recv_thread, _phone_loop_thread;
450 _status = pthread_create ( &_recv_thread, NULL, phone_receivepacket, _phone );
451
452 if ( _status < 0 ) {
453 printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
454 return 0;
455 }
456
457 _status = pthread_detach ( _recv_thread );
458
459 if ( _status < 0 ) {
460 printf ( "Error while starting handle call: %d, %s\n", errno, strerror ( errno ) );
461 return 0;
462 }
463
464 _status = pthread_create ( &_phone_loop_thread, NULL, phone_poll, _phone );
465
466 if ( _status < 0 ) {
467 printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
468 return 0;
469 }
470
471 _status = pthread_join ( _phone_loop_thread, NULL );
472
473 if ( _status < 0 ) {
474 printf ( "Error while starting main phone loop: %d, %s\n", errno, strerror ( errno ) );
475 return 0;
476 }
477
478 return _phone_loop_thread;
479}
480
481void *phone_poll ( void *_p_phone )
482{
483 phone_t *_phone = _p_phone;
484
485 int _status = SUCCESS;
486
487 char _line[100];
488 size_t _len;
489
490
491 char _dest[17]; /* For parsing destination ip */
492 memset(_dest, '\0', 17);
493
494 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
495 "Usage: \n"
496 "c [a/v] (type) [0.0.0.0] (dest ip) (calls dest ip)\n"
497 "h (if call is active hang up)\n"
498 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
499 "r (reject incoming call)\n"
500 "q (quit)\n"
501 "================================================================================"
502 );
503
504 while ( 1 ) {
505 fgets(_line, sizeof(_line), stdin);
506 int i;
507
508 for (i = 0; i < 100; i++) {
509 if (_line[i] == '\n') {
510 _line[i] = '\0';
511 }
512 }
513
514 _len = strlen(_line);
515
516 if ( !_len ) {
517 printf(" >> ");
518 fflush(stdout);
519 continue;
520 }
521
522 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ) {
523 INFO("Invalid input!");
524 continue;
525 }
526
527 switch (_line[0]) {
528
529 case 'c': {
530 if ( _phone->_msi->_call ) {
531 INFO("Already in a call");
532 break;
533 }
534
535 call_type _ctype;
536
537 if ( _len < 11 ) {
538 INFO("Invalid input; usage: c a/v 0.0.0.0");
539 break;
540 } else if ( _line[2] == 'a' || _line[2] != 'v' ) { /* default and audio */
541 _ctype = type_audio;
542 } else { /* video */
543 _ctype = type_video;
544 }
545
546 strcpy(_dest, _line + 4 );
547 _status = t_setipport(_dest, _phone->_send_port, &(_phone->_msi->_friend_id));
548
549 if ( _status < 0 ) {
550 INFO("Could not resolve address!");
551 } else {
552 /* Set timeout */
553 msi_invite ( _phone->_msi, _ctype, 30 * 1000 );
554 INFO("Calling!");
555 }
556
557 t_memset((uint8_t *)_dest, '\0', 17);
558
559 }
560 break;
561
562 case 'h': {
563 if ( !_phone->_msi->_call ) {
564 break;
565 }
566
567 msi_hangup(_phone->_msi);
568
569 INFO("Hung up...");
570
571 }
572 break;
573
574 case 'a': {
575 if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) {
576 break;
577 }
578
579 if ( _len > 1 && _line[2] == 'v' )
580 msi_answer(_phone->_msi, type_video);
581 else
582 msi_answer(_phone->_msi, type_audio);
583
584 }
585 break;
586
587 case 'r': {
588 if ( _phone->_msi->_call && _phone->_msi->_call->_state != call_starting ) {
589 break;
590 }
591
592 msi_reject(_phone->_msi);
593
594 INFO("Call Rejected...");
595
596 }
597 break;
598
599 case 'q': {
600 INFO("Quitting!");
601 pthread_exit(NULL);
602 }
603
604 default: {
605 INFO("Invalid command!");
606 }
607 break;
608
609 }
610
611 usleep(1000);
612 }
613
614 pthread_exit(NULL);
615}
616
617int quitPhone(phone_t *_phone)
618{
619 if ( _phone->_msi->_call ) {
620 msi_hangup(_phone->_msi); /* Hangup the phone first */
621 }
622
623 msi_terminate_session(_phone->_msi);
624 pthread_mutex_destroy ( &_mutex );
625
626 printf("\r[i] Quit!\n");
627 return SUCCESS;
628}
629
630/* ---------------------- */
631
632int print_help ( const char *_name )
633{
634 printf ( "Usage: %s -m (mode) -r/s ( for setting the ports on test version )\n", _name );
635 return FAILURE;
636}
637
638int main ( int argc, char *argv [] )
639{
640 arg_t *_args = parse_args ( argc, argv );
641
642 const char *_mode = find_arg_duble ( _args, "-m" );
643 uint16_t _listen_port;
644 uint16_t _send_port;
645
646 if ( !_mode )
647 return print_help ( argv[0] );
648
649 if ( _mode[0] == 'r' ) {
650 _send_port = 31000;
651 _listen_port = 31001;
652 } else if ( _mode[0] == 's' ) {
653 _send_port = 31001;
654 _listen_port = 31000;
655 } else return print_help ( argv[0] );
656
657 phone_t *_phone = initPhone(_listen_port, _send_port);
658
659 if ( _phone ) {
660 phone_startmain_loop(_phone);
661
662 quitPhone(_phone);
663 }
664
665 return SUCCESS;
666}
667
668#endif /* _CT_PHONE */
diff --git a/toxmsi/phone.h b/toxmsi/phone.h
deleted file mode 100644
index f96aac73..00000000
--- a/toxmsi/phone.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#ifndef _PHONE_H_
2#define _PHONE_H_
3
4#include "toxmsi.h"
5#include "../toxrtp/toxrtp.h"
6#include "toxmsi_message.h"
7#include "../toxrtp/toxrtp_message.h"
8#include "../toxrtp/tests/test_helper.h"
9#include <assert.h>
10#include <pthread.h>
11#include "toxmedia.h"
12
13/* Define client version */
14#define _USERAGENT "tox_phone-v.0.2.1"
15
16static pthread_mutex_t _mutex;
17
18#define THREADLOCK() \
19pthread_mutex_lock ( &_mutex );
20
21#define THREADUNLOCK() \
22pthread_mutex_unlock ( &_mutex );
23
24typedef struct phone_s {
25 msi_session_t* _msi;
26
27 rtp_session_t* _rtp_audio;
28 rtp_session_t* _rtp_video;
29
30 uint32_t _frame_rate;
31
32 uint16_t _send_port, _recv_port;
33
34 int _tox_sock;
35
36 pthread_t _medialoop_id;
37 codec_state *cs;
38
39 Networking_Core* _networking;
40} phone_t;
41
42phone_t* initPhone(uint16_t _listen_port, uint16_t _send_port);
43int quitPhone(phone_t* _phone);
44
45/* My recv functions */
46int rtp_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length );
47int msi_handlepacket ( void* _object, tox_IP_Port ip_port, uint8_t* data, uint32_t length );
48
49/* This is basically representation of networking_poll of toxcore */
50void* phone_receivepacket ( void* _phone );
51
52/* Phones main loop */
53void* phone_poll ( void* _phone );
54
55pthread_t phone_startmain_loop(phone_t* _phone);
56pthread_t phone_startmedia_loop ( phone_t* _phone );
57
58/* Thread handlers */
59void* phone_handle_receive_callback ( void* _p );
60void* phone_handle_media_transport_poll ( void* _hmtc_args_p );
61
62#endif /* _PHONE_H_ */
diff --git a/toxmsi/toxmedia.c b/toxmsi/toxmedia.c
deleted file mode 100644
index 4c9f5261..00000000
--- a/toxmsi/toxmedia.c
+++ /dev/null
@@ -1,825 +0,0 @@
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 "toxmsi_message.h"
46#include "../toxrtp/toxrtp_message.h"
47#include "../toxrtp/tests/test_helper.h"
48#include "phone.h"
49#include "toxmedia.h"
50
51SDL_Surface *screen;
52
53int display_received_frame(codec_state *cs, AVFrame *r_video_frame)
54{
55 AVPicture pict;
56 SDL_LockYUVOverlay(cs->video_picture.bmp);
57
58 pict.data[0] = cs->video_picture.bmp->pixels[0];
59 pict.data[1] = cs->video_picture.bmp->pixels[2];
60 pict.data[2] = cs->video_picture.bmp->pixels[1];
61 pict.linesize[0] = cs->video_picture.bmp->pitches[0];
62 pict.linesize[1] = cs->video_picture.bmp->pitches[2];
63 pict.linesize[2] = cs->video_picture.bmp->pitches[1];
64
65 /* Convert the image into YUV format that SDL uses */
66 sws_scale(cs->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
67 cs->video_decoder_ctx->height, pict.data, pict.linesize );
68
69 SDL_UnlockYUVOverlay(cs->video_picture.bmp);
70 SDL_Rect rect;
71 rect.x = 0;
72 rect.y = 0;
73 rect.w = cs->video_decoder_ctx->width;
74 rect.h = cs->video_decoder_ctx->height;
75 SDL_DisplayYUVOverlay(cs->video_picture.bmp, &rect);
76 return 1;
77}
78
79struct jitter_buffer {
80 rtp_msg_t **queue;
81 uint16_t capacity;
82 uint16_t size;
83 uint16_t front;
84 uint16_t rear;
85 uint8_t queue_ready;
86 uint16_t current_id;
87 uint32_t current_ts;
88 uint8_t id_set;
89};
90
91struct jitter_buffer *create_queue(int capacity)
92{
93 struct jitter_buffer *q;
94 q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1);
95 q->queue = (rtp_msg_t **)calloc((sizeof(rtp_msg_t) * capacity),1);
96 int i = 0;
97
98 for (i = 0; i < capacity; ++i) {
99 q->queue[i] = NULL;
100 }
101
102 q->size = 0;
103 q->capacity = capacity;
104 q->front = 0;
105 q->rear = -1;
106 q->queue_ready = 0;
107 q->current_id = 0;
108 q->current_ts = 0;
109 q->id_set = 0;
110 return q;
111}
112
113/* returns 1 if 'a' has a higher sequence number than 'b' */
114uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b)
115{
116 /* should be stable enough */
117 return (sn_a > sn_b || ts_a > ts_b);
118}
119
120/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
121rtp_msg_t *dequeue(struct jitter_buffer *q, int *success)
122{
123 if (q->size == 0 || q->queue_ready == 0) {
124 q->queue_ready = 0;
125 *success = 0;
126 return NULL;
127 }
128
129 int front = q->front;
130
131 if (q->id_set == 0) {
132 q->current_id = q->queue[front]->_header->_sequence_number;
133 q->current_ts = q->queue[front]->_header->_timestamp;
134 q->id_set = 1;
135 } else {
136 int next_id = q->queue[front]->_header->_sequence_number;
137 int next_ts = q->queue[front]->_header->_timestamp;
138
139 /* if this packet is indeed the expected packet */
140 if (next_id == (q->current_id + 1) % _MAX_SEQU_NUM) {
141 q->current_id = next_id;
142 q->current_ts = next_ts;
143 } else {
144 if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
145 printf("nextid: %d current: %d\n", next_id, q->current_id);
146 q->current_id = (q->current_id + 1) % _MAX_SEQU_NUM;
147 *success = 2; /* tell the decoder the packet is lost */
148 return NULL;
149 } else {
150 /* packet too old */
151 printf("packet too old\n");
152 *success = 0;
153 return NULL;
154 }
155 }
156 }
157
158 q->size--;
159 q->front++;
160
161 if (q->front == q->capacity)
162 q->front = 0;
163
164 *success = 1;
165 q->current_id = q->queue[front]->_header->_sequence_number;
166 q->current_ts = q->queue[front]->_header->_timestamp;
167 return q->queue[front];
168}
169
170int empty_queue(struct jitter_buffer *q)
171{
172 while (q->size > 0) {
173 q->size--;
174 /* FIXME: */
175 /* rtp_free_msg(cs->_rtp_video, q->queue[q->front]); */
176 q->front++;
177
178 if (q->front == q->capacity)
179 q->front = 0;
180 }
181
182 q->id_set = 0;
183 q->queue_ready = 0;
184 return 0;
185}
186
187int queue(struct jitter_buffer *q, rtp_msg_t *pk)
188{
189 if (q->size == q->capacity) {
190 printf("buffer full, emptying buffer...\n");
191 empty_queue(q);
192 return 0;
193 }
194
195 if (q->size > 8)
196 q->queue_ready = 1;
197
198 ++q->size;
199 ++q->rear;
200
201 if (q->rear == q->capacity)
202 q->rear = 0;
203
204 q->queue[q->rear] = pk;
205
206 int a;
207 int b;
208 int j;
209 a = q->rear;
210
211 for (j = 0; j < q->size - 1; ++j) {
212 b = a - 1;
213
214 if (b < 0)
215 b += q->capacity;
216
217 if (sequence_number_older(q->queue[b]->_header->_sequence_number, q->queue[a]->_header->_sequence_number,
218 q->queue[b]->_header->_timestamp, q->queue[a]->_header->_timestamp)) {
219 rtp_msg_t *temp;
220 temp = q->queue[a];
221 q->queue[a] = q->queue[b];
222 q->queue[b] = temp;
223 printf("had to swap\n");
224 } else {
225 break;
226 }
227
228 a -= 1;
229
230 if (a < 0)
231 a += q->capacity;
232 }
233
234 if (pk)
235 return 1;
236
237 return 0;
238}
239
240int init_receive_audio(codec_state *cs)
241{
242 int err = OPUS_OK;
243 cs->audio_decoder = opus_decoder_create(48000, 1, &err);
244 opus_decoder_init(cs->audio_decoder, 48000, 1);
245 printf("init audio decoder successful\n");
246 return 1;
247}
248
249int init_receive_video(codec_state *cs)
250{
251 cs->video_decoder = avcodec_find_decoder(VIDEO_CODEC);
252
253 if (!cs->video_decoder) {
254 printf("init video_decoder failed\n");
255 return 0;
256 }
257
258 cs->video_decoder_ctx = avcodec_alloc_context3(cs->video_decoder);
259
260 if (!cs->video_decoder_ctx) {
261 printf("init video_decoder_ctx failed\n");
262 return 0;
263 }
264
265 if (avcodec_open2(cs->video_decoder_ctx, cs->video_decoder, NULL) < 0) {
266 printf("opening video decoder failed\n");
267 return 0;
268 }
269
270 printf("init video decoder successful\n");
271 return 1;
272}
273
274int init_send_video(codec_state *cs)
275{
276 cs->video_input_format = av_find_input_format(VIDEO_DRIVER);
277
278 if (avformat_open_input(&cs->video_format_ctx, DEFAULT_WEBCAM, cs->video_input_format, NULL) != 0) {
279 printf("opening video_input_format failed\n");
280 return 0;
281 }
282
283 avformat_find_stream_info(cs->video_format_ctx, NULL);
284 av_dump_format(cs->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
285
286 int i;
287
288 for (i = 0; i < cs->video_format_ctx->nb_streams; ++i) {
289 if (cs->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
290 cs->video_stream = i;
291 break;
292 }
293 }
294
295 cs->webcam_decoder_ctx = cs->video_format_ctx->streams[cs->video_stream]->codec;
296 cs->webcam_decoder = avcodec_find_decoder(cs->webcam_decoder_ctx->codec_id);
297
298 if (cs->webcam_decoder == NULL) {
299 printf("Unsupported codec\n");
300 return 0;
301 }
302
303 if (cs->webcam_decoder_ctx == NULL) {
304 printf("init webcam_decoder_ctx failed\n");
305 return 0;
306 }
307
308 if (avcodec_open2(cs->webcam_decoder_ctx, cs->webcam_decoder, NULL) < 0) {
309 printf("opening webcam decoder failed\n");
310 return 0;
311 }
312
313 cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
314
315 if (!cs->video_encoder) {
316 printf("init video_encoder failed\n");
317 return 0;
318 }
319
320 cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
321
322 if (!cs->video_encoder_ctx) {
323 printf("init video_encoder_ctx failed\n");
324 return 0;
325 }
326
327 cs->video_encoder_ctx->bit_rate = VIDEO_BITRATE;
328 cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
329 av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
330 av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
331
332 cs->video_encoder_ctx->thread_count = 4;
333 cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
334 cs->video_encoder_ctx->rc_buffer_size = VIDEO_BITRATE * 6;
335 cs->video_encoder_ctx->profile = 3;
336 cs->video_encoder_ctx->qmax = 54;
337 cs->video_encoder_ctx->qmin = 4;
338 AVRational myrational = {1, 25};
339 cs->video_encoder_ctx->time_base = myrational;
340 cs->video_encoder_ctx->gop_size = 99999;
341 cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P;
342 cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width;
343 cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
344
345 if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
346 printf("opening video encoder failed\n");
347 return 0;
348 }
349
350 printf("init video encoder successful\n");
351 return 1;
352}
353
354int init_send_audio(codec_state *cs)
355{
356 cs->support_send_audio = 0;
357
358 const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
359 int i = 0;
360 const ALchar *device_names[20];
361
362 if (pDeviceList) {
363 printf("\nAvailable Capture Devices are:\n");
364
365 while (*pDeviceList) {
366 device_names[i] = pDeviceList;
367 printf("%d) %s\n", i, device_names[i]);
368 pDeviceList += strlen(pDeviceList) + 1;
369 ++i;
370 }
371 }
372
373 printf("enter capture device number: \n");
374 char dev[2];
375 fgets(dev, sizeof(dev), stdin);
376 cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16,
377 AUDIO_FRAME_SIZE * 4);
378
379 if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) {
380 printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device));
381 return 0;
382 }
383
384 int err = OPUS_OK;
385 cs->audio_bitrate = AUDIO_BITRATE;
386 cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err);
387 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
388 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
389 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
390
391 opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP);
392
393 int nfo;
394 err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo));
395 /* printf("Encoder lookahead delay : %d\n", nfo); */
396 printf("init audio encoder successful\n");
397
398 return 1;
399}
400
401int init_encoder(codec_state *cs)
402{
403 avdevice_register_all();
404 avcodec_register_all();
405 avdevice_register_all();
406 av_register_all();
407
408 pthread_mutex_init(&cs->rtp_msg_mutex_lock, NULL);
409 pthread_mutex_init(&cs->avcodec_mutex_lock, NULL);
410
411 cs->support_send_video = init_send_video(cs);
412 cs->support_send_audio = init_send_audio(cs);
413
414 cs->send_audio = 1;
415 cs->send_video = 1;
416
417 return 1;
418}
419
420int init_decoder(codec_state *cs)
421{
422 avdevice_register_all();
423 avcodec_register_all();
424 avdevice_register_all();
425 av_register_all();
426
427 cs->receive_video = 0;
428 cs->receive_audio = 0;
429
430 cs->support_receive_video = init_receive_video(cs);
431 cs->support_receive_audio = init_receive_audio(cs);
432
433 cs->receive_audio = 1;
434 cs->receive_video = 1;
435
436 return 1;
437}
438
439int video_encoder_refresh(codec_state *cs, int bps)
440{
441 if (cs->video_encoder_ctx)
442 avcodec_close(cs->video_encoder_ctx);
443
444 cs->video_encoder = avcodec_find_encoder(VIDEO_CODEC);
445
446 if (!cs->video_encoder) {
447 printf("init video_encoder failed\n");
448 return -1;
449 }
450
451 cs->video_encoder_ctx = avcodec_alloc_context3(cs->video_encoder);
452
453 if (!cs->video_encoder_ctx) {
454 printf("init video_encoder_ctx failed\n");
455 return -1;
456 }
457
458 cs->video_encoder_ctx->bit_rate = bps;
459 cs->video_encoder_ctx->rc_min_rate = cs->video_encoder_ctx->rc_max_rate = cs->video_encoder_ctx->bit_rate;
460 av_opt_set_double(cs->video_encoder_ctx->priv_data, "max-intra-rate", 90, 0);
461 av_opt_set(cs->video_encoder_ctx->priv_data, "quality", "realtime", 0);
462
463 cs->video_encoder_ctx->thread_count = 4;
464 cs->video_encoder_ctx->rc_buffer_aggressivity = 0.95;
465 cs->video_encoder_ctx->rc_buffer_size = bps * 6;
466 cs->video_encoder_ctx->profile = 0;
467 cs->video_encoder_ctx->qmax = 54;
468 cs->video_encoder_ctx->qmin = 4;
469 AVRational myrational = {1, 25};
470 cs->video_encoder_ctx->time_base = myrational;
471 cs->video_encoder_ctx->gop_size = 99999;
472 cs->video_encoder_ctx->pix_fmt = PIX_FMT_YUV420P;
473 cs->video_encoder_ctx->width = cs->webcam_decoder_ctx->width;
474 cs->video_encoder_ctx->height = cs->webcam_decoder_ctx->height;
475
476 if (avcodec_open2(cs->video_encoder_ctx, cs->video_encoder, NULL) < 0) {
477 printf("opening video encoder failed\n");
478 return -1;
479 }
480 return 0;
481}
482
483void *encode_video_thread(void *arg)
484{
485 codec_state *cs = (codec_state *)arg;
486 AVPacket pkt1, *packet = &pkt1;
487 int p = 0;
488 int err;
489 int got_packet;
490 rtp_msg_t *s_video_msg;
491 int video_frame_finished;
492 AVFrame *s_video_frame;
493 AVFrame *webcam_frame;
494 s_video_frame = avcodec_alloc_frame();
495 webcam_frame = avcodec_alloc_frame();
496 AVPacket enc_video_packet;
497
498 uint8_t *buffer;
499 int numBytes;
500 /* Determine required buffer size and allocate buffer */
501 numBytes = avpicture_get_size(PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height);
502 buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1);
503 avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, cs->webcam_decoder_ctx->width,
504 cs->webcam_decoder_ctx->height);
505 cs->sws_ctx = sws_getContext(cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height,
506 cs->webcam_decoder_ctx->pix_fmt, cs->webcam_decoder_ctx->width, cs->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
507 SWS_BILINEAR, NULL, NULL, NULL);
508
509 while (!cs->quit && cs->send_video) {
510
511 if (av_read_frame(cs->video_format_ctx, packet) < 0) {
512 printf("error reading frame\n");
513
514 if (cs->video_format_ctx->pb->error != 0)
515 break;
516
517 continue;
518 }
519
520 if (packet->stream_index == cs->video_stream) {
521 if (avcodec_decode_video2(cs->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
522 printf("couldn't decode\n");
523 continue;
524 }
525
526 av_free_packet(packet);
527 sws_scale(cs->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
528 cs->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
529 /* create a new I-frame every 60 frames */
530 ++p;
531
532 if (p == 60) {
533
534 s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
535 } else if (p == 61) {
536 s_video_frame->pict_type = AV_PICTURE_TYPE_I ;
537 p = 0;
538 } else {
539 s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
540 }
541
542 if (video_frame_finished) {
543 err = avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet);
544
545 if (err < 0) {
546 printf("could not encode video frame\n");
547 continue;
548 }
549
550 if (!got_packet) {
551 continue;
552 }
553
554 pthread_mutex_lock(&cs->rtp_msg_mutex_lock);
555 THREADLOCK()
556
557 if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
558
559 s_video_msg = rtp_msg_new ( cs->_rtp_video, enc_video_packet.data, enc_video_packet.size ) ;
560
561 if (!s_video_msg) {
562 printf("invalid message\n");
563 }
564
565 rtp_send_msg ( cs->_rtp_video, s_video_msg, cs->_networking );
566 THREADUNLOCK()
567 pthread_mutex_unlock(&cs->rtp_msg_mutex_lock);
568 av_free_packet(&enc_video_packet);
569 }
570 } else {
571 av_free_packet(packet);
572 }
573 }
574
575 /* clean up codecs */
576 pthread_mutex_lock(&cs->avcodec_mutex_lock);
577 av_free(buffer);
578 av_free(webcam_frame);
579 av_free(s_video_frame);
580 sws_freeContext(cs->sws_ctx);
581 avcodec_close(cs->webcam_decoder_ctx);
582 avcodec_close(cs->video_encoder_ctx);
583 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
584 pthread_exit ( NULL );
585}
586
587void *encode_audio_thread(void *arg)
588{
589 codec_state *cs = (codec_state *)arg;
590 rtp_msg_t *s_audio_msg;
591 unsigned char encoded_data[4096];
592 int encoded_size = 0;
593 int16_t frame[4096];
594 int frame_size = AUDIO_FRAME_SIZE;
595 ALint sample = 0;
596 alcCaptureStart(cs->audio_capture_device);
597
598 while (!cs->quit && cs->send_audio) {
599 alcGetIntegerv(cs->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
600
601 if (sample >= frame_size) {
602 alcCaptureSamples(cs->audio_capture_device, frame, frame_size);
603 encoded_size = opus_encode(cs->audio_encoder, frame, frame_size, encoded_data, 480);
604
605 if (encoded_size <= 0) {
606 printf("Could not encode audio packet\n");
607 } else {
608 pthread_mutex_lock(&cs->rtp_msg_mutex_lock);
609 THREADLOCK()
610 rtp_set_payload_type(cs->_rtp_audio, 96);
611 s_audio_msg = rtp_msg_new (cs->_rtp_audio, encoded_data, encoded_size) ;
612 rtp_send_msg ( cs->_rtp_audio, s_audio_msg, cs->_networking );
613 pthread_mutex_unlock(&cs->rtp_msg_mutex_lock);
614 THREADUNLOCK()
615 }
616 } else {
617 usleep(1000);
618 }
619 }
620
621 /* clean up codecs */
622 pthread_mutex_lock(&cs->avcodec_mutex_lock);
623 alcCaptureStop(cs->audio_capture_device);
624 alcCaptureCloseDevice(cs->audio_capture_device);
625
626 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
627 pthread_exit ( NULL );
628}
629
630
631int video_decoder_refresh(codec_state *cs, int width, int height)
632{
633 printf("need to refresh\n");
634 screen = SDL_SetVideoMode(width, height, 0, 0);
635
636 if (cs->video_picture.bmp)
637 SDL_FreeYUVOverlay(cs->video_picture.bmp);
638
639 cs->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
640 cs->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
641 SWS_BILINEAR, NULL, NULL, NULL);
642 return 1;
643}
644
645void *decode_video_thread(void *arg)
646{
647 codec_state *cs = (codec_state *)arg;
648 cs->video_stream = 0;
649 rtp_msg_t *r_msg;
650 int dec_frame_finished;
651 AVFrame *r_video_frame;
652 r_video_frame = avcodec_alloc_frame();
653 AVPacket dec_video_packet;
654 av_new_packet (&dec_video_packet, 65536);
655 int width = 0;
656 int height = 0;
657
658 while (!cs->quit && cs->receive_video) {
659 r_msg = rtp_recv_msg ( cs->_rtp_video );
660
661 if (r_msg) {
662 memcpy(dec_video_packet.data, r_msg->_data, r_msg->_length);
663 dec_video_packet.size = r_msg->_length;
664 avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
665
666 if (dec_frame_finished) {
667 if (cs->video_decoder_ctx->width != width || cs->video_decoder_ctx->height != height) {
668 width = cs->video_decoder_ctx->width;
669 height = cs->video_decoder_ctx->height;
670 printf("w: %d h%d \n", width, height);
671 video_decoder_refresh(cs, width, height);
672 }
673
674 display_received_frame(cs, r_video_frame);
675 } else {
676 /* TODO: request the sender to create a new i-frame immediatly */
677 printf("bad video packet\n");
678 }
679
680 rtp_free_msg(cs->_rtp_video, r_msg);
681 }
682
683 usleep(1000);
684 }
685
686 printf("vend\n");
687 /* clean up codecs */
688 pthread_mutex_lock(&cs->avcodec_mutex_lock);
689 av_free(r_video_frame);
690 avcodec_close(cs->video_decoder_ctx);
691 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
692 pthread_exit ( NULL );
693}
694
695void *decode_audio_thread(void *arg)
696{
697 codec_state *cs = (codec_state *)arg;
698 rtp_msg_t *r_msg;
699
700 int frame_size = AUDIO_FRAME_SIZE;
701 int data_size;
702
703 ALCdevice *dev;
704 ALCcontext *ctx;
705 ALuint source, *buffers;
706 dev = alcOpenDevice(NULL);
707 ctx = alcCreateContext(dev, NULL);
708 alcMakeContextCurrent(ctx);
709 int openal_buffers = 5;
710
711 buffers = calloc(sizeof(ALuint) * openal_buffers,1);
712 alGenBuffers(openal_buffers, buffers);
713 alGenSources((ALuint)1, &source);
714 alSourcei(source, AL_LOOPING, AL_FALSE);
715
716 ALuint buffer;
717 ALint val;
718
719 ALenum error;
720 uint16_t zeros[frame_size];
721 int i;
722
723 for (i = 0; i < frame_size; i++) {
724 zeros[i] = 0;
725 }
726
727 for (i = 0; i < openal_buffers; ++i) {
728 alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
729 }
730
731 alSourceQueueBuffers(source, openal_buffers, buffers);
732 alSourcePlay(source);
733
734 if (alGetError() != AL_NO_ERROR) {
735 fprintf(stderr, "Error starting audio\n");
736 cs->quit = 1;
737 }
738
739 struct jitter_buffer *j_buf = NULL;
740
741 j_buf = create_queue(20);
742
743 int success = 0;
744
745 int dec_frame_len;
746
747 opus_int16 PCM[frame_size];
748
749 while (!cs->quit && cs->receive_audio) {
750 THREADLOCK()
751 r_msg = rtp_recv_msg ( cs->_rtp_audio );
752
753 if (r_msg) {
754 /* push the packet into the queue */
755 queue(j_buf, r_msg);
756 }
757
758 /* grab a packet from the queue */
759 success = 0;
760 alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
761
762 if (val > 0)
763 r_msg = dequeue(j_buf, &success);
764
765 if (success > 0) {
766 /* good packet */
767 if (success == 1) {
768 dec_frame_len = opus_decode(cs->audio_decoder, r_msg->_data, r_msg->_length, PCM, frame_size, 0);
769 rtp_free_msg(cs->_rtp_audio, r_msg);
770 }
771
772 /* lost packet */
773 if (success == 2) {
774 printf("lost packet\n");
775 dec_frame_len = opus_decode(cs->audio_decoder, NULL, 0, PCM, frame_size, 1);
776 }
777
778 if (dec_frame_len > 0) {
779 alGetSourcei(source, AL_BUFFERS_PROCESSED, &val);
780
781 if (val <= 0)
782 continue;
783
784 alSourceUnqueueBuffers(source, 1, &buffer);
785 data_size = av_samples_get_buffer_size(NULL, 1, dec_frame_len, AV_SAMPLE_FMT_S16, 1);
786 alBufferData(buffer, AL_FORMAT_MONO16, PCM, data_size, 48000);
787 int error = alGetError();
788
789 if (error != AL_NO_ERROR) {
790 fprintf(stderr, "Error setting buffer %d\n", error);
791 break;
792 }
793
794 alSourceQueueBuffers(source, 1, &buffer);
795
796 if (alGetError() != AL_NO_ERROR) {
797 fprintf(stderr, "error: could not buffer audio\n");
798 break;
799 }
800
801 alGetSourcei(source, AL_SOURCE_STATE, &val);
802
803 if (val != AL_PLAYING)
804 alSourcePlay(source);
805
806
807 }
808 }
809
810 THREADUNLOCK()
811 usleep(1000);
812 }
813
814 /* clean up codecs */
815 pthread_mutex_lock(&cs->avcodec_mutex_lock);
816
817 /* clean up openal */
818 alDeleteSources(1, &source);
819 alDeleteBuffers(openal_buffers, buffers);
820 alcMakeContextCurrent(NULL);
821 alcDestroyContext(ctx);
822 alcCloseDevice(dev);
823 pthread_mutex_unlock(&cs->avcodec_mutex_lock);
824 pthread_exit ( NULL );
825}
diff --git a/toxmsi/toxmedia.h b/toxmsi/toxmedia.h
deleted file mode 100644
index 8734b173..00000000
--- a/toxmsi/toxmedia.h
+++ /dev/null
@@ -1,168 +0,0 @@
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 rtp_session_t *_rtp_video;
130 rtp_session_t *_rtp_audio;
131 int socket;
132 Networking_Core *_networking;
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 msi_session_t *_msi;
147 uint32_t _frame_rate;
148 uint16_t _send_port, _recv_port;
149 int _tox_sock;
150 //pthread_id _medialoop_id;
151
152} codec_state;
153
154int display_received_frame(codec_state *cs, AVFrame *r_video_frame);
155int init_receive_audio(codec_state *cs);
156int init_decoder(codec_state *cs);
157int init_send_video(codec_state *cs);
158int init_send_audio(codec_state *cs);
159int init_encoder(codec_state *cs);
160int video_encoder_refresh(codec_state *cs, int bps);
161void *encode_video_thread(void *arg);
162void *encode_audio_thread(void *arg);
163int video_decoder_refresh(codec_state *cs, int width, int height);
164int handle_rtp_video_packet(codec_state *cs, rtp_msg_t *r_msg);
165void *decode_video_thread(void *arg);
166void *decode_audio_thread(void *arg);
167
168#endif
diff --git a/toxmsi/toxmsi.c b/toxmsi/toxmsi.c
deleted file mode 100644
index 38af28fb..00000000
--- a/toxmsi/toxmsi.c
+++ /dev/null
@@ -1,835 +0,0 @@
1
2#ifdef HAVE_CONFIG_H
3#include "config.h"
4#endif /* HAVE_CONFIG_H */
5
6#define _BSD_SOURCE
7
8#include "toxmsi.h"
9#include "toxmsi_event.h"
10#include "toxmsi_message.h"
11#include "../toxrtp/toxrtp_helper.h"
12#include "../toxcore/network.h"
13
14#include <assert.h>
15#include <unistd.h>
16#include <string.h>
17
18#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
19
20typedef enum {
21 error_deadcall = 1, /* has call id but it's from old call */
22 error_id_mismatch, /* non-existing call */
23
24 error_no_callid, /* not having call id */
25 error_no_call, /* no call in session */
26
27 error_busy
28} msi_error_t; /* Error codes */
29
30static inline const uint8_t *stringify_error(msi_error_t _error_code)
31{
32 static const uint8_t* strings[] =
33 {
34 (uint8_t*)"",
35 (uint8_t*)"Using dead call",
36 (uint8_t*)"Call id not set to any call",
37 (uint8_t*)"Call id not available",
38 (uint8_t*)"No active call in session",
39 (uint8_t*)"Callee busy"
40 };
41
42 return strings[_error_code];
43}
44
45static inline const uint8_t *stringify_error_code(msi_error_t _error_code)
46{
47 static const uint8_t* strings[] =
48 {
49 (uint8_t*)"",
50 (uint8_t*)"1",
51 (uint8_t*)"2",
52 (uint8_t*)"3",
53 (uint8_t*)"4",
54 (uint8_t*)"5"
55 };
56
57 return strings[_error_code];
58}
59
60/* ******************* */
61/* --------- GLOBAL FUNCTIONS USED BY THIS FILE --------- */
62
63/* CALLBACKS */
64/*int (*msi_send_message_callback) ( int, uint8_t*, uint32_t ) = NULL;*/
65int ( *msi_send_message_callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) = NULL;
66int ( *msi_recv_message_callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) = NULL;
67
68MCBTYPE ( *msi_recv_invite_callback ) ( MCBARGS ) = NULL;
69MCBTYPE ( *msi_start_call_callback ) ( MCBARGS ) = NULL;
70MCBTYPE ( *msi_reject_call_callback ) ( MCBARGS ) = NULL;
71MCBTYPE ( *msi_cancel_call_callback ) ( MCBARGS ) = NULL;
72MCBTYPE ( *msi_end_call_callback ) ( MCBARGS ) = NULL;
73
74MCBTYPE ( *msi_ringing_callback ) ( MCBARGS ) = NULL;
75MCBTYPE ( *msi_starting_callback ) ( MCBARGS ) = NULL;
76MCBTYPE ( *msi_ending_callback ) ( MCBARGS ) = NULL;
77MCBTYPE ( *msi_error_callback ) ( MCBARGS ) = NULL;
78
79MCBTYPE ( *msi_timeout_callback ) ( MCBARGS ) = NULL;
80/* End of CALLBACKS */
81
82/*------------------------*/
83/*------------------------*/
84/*------------------------*/
85/*------------------------*/
86/*------------------------*/
87/*------------------------*/
88/*------------------------*/
89/*------------------------*/
90/*------------------------*/
91/*------------------------*/
92/*------------------------*/
93/*------------------------*/
94
95/* REGISTER CALLBACKS */
96/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) )*/
97void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) )
98{
99 msi_send_message_callback = callback;
100}
101
102void msi_register_callback_recv ( int ( *callback ) ( tox_IP_Port*, uint8_t*, uint32_t* ) )
103{
104 msi_recv_message_callback = callback;
105}
106
107/* Function to be called when received invite.
108 * This callback is all about what you do with it.
109 * Everything else is done internally.
110 */
111void msi_register_callback_recv_invite ( MCALLBACK )
112{
113 msi_recv_invite_callback = callback;
114}
115
116/* Function to be called when the call is started
117 * This callback is all about what you do with it.
118 * Everything else is done internally.
119 */
120void msi_register_callback_call_started ( MCALLBACK )
121{
122 msi_start_call_callback = callback;
123}
124
125/* Function to be called when call is rejected
126 * This callback is all about what you do with it.
127 * Everything else is done internally.
128 */
129void msi_register_callback_call_rejected ( MCALLBACK )
130{
131 msi_reject_call_callback = callback;
132}
133
134/* Function to be called when call is canceled
135 * This callback is all about what you do with it.
136 * Everything else is done internally.
137 */
138void msi_register_callback_call_canceled ( MCALLBACK )
139{
140 msi_cancel_call_callback = callback;
141}
142
143void msi_register_callback_call_ended ( MCALLBACK )
144{
145 msi_end_call_callback = callback;
146}
147
148
149/* Functions to be called when gotten x response */
150
151void msi_register_callback_recv_ringing ( MCALLBACK )
152{
153 msi_ringing_callback = callback;
154}
155void msi_register_callback_recv_starting ( MCALLBACK )
156{
157 msi_starting_callback = callback;
158}
159void msi_register_callback_recv_ending ( MCALLBACK )
160{
161 msi_ending_callback = callback;
162}
163
164void msi_register_callback_recv_error ( MCALLBACK )
165{
166 msi_error_callback = callback;
167}
168
169/* Timeout */
170void msi_register_callback_requ_timeout ( MCALLBACK )
171{
172 msi_timeout_callback = callback;
173}
174/* END REGISTERING */
175
176/*------------------------*/
177/*------------------------*/
178/*------------------------*/
179/*------------------------*/
180/*------------------------*/
181/*------------------------*/
182/*------------------------*/
183/*------------------------*/
184/*------------------------*/
185/*------------------------*/
186/*------------------------*/
187/*------------------------*/
188
189/* Function for receiving and parsing a message that will be used internally */
190
191msi_msg_t* receive_message ( msi_session_t* _session )
192{
193 assert(_session);
194
195
196 msi_msg_t* _retu = _session->_oldest_msg;
197
198 pthread_mutex_lock ( &_session->_mutex );
199
200 if ( _retu )
201 _session->_oldest_msg = _retu->_next;
202
203 if ( !_session->_oldest_msg )
204 _session->_last_msg = NULL;
205
206 pthread_mutex_unlock ( &_session->_mutex );
207
208 return _retu;
209}
210
211void msi_store_msg ( msi_session_t* _session, msi_msg_t* _msg )
212{
213 assert(_session);
214 assert(_msg);
215
216 pthread_mutex_lock ( &_session->_mutex );
217
218 if ( _session->_last_msg ) {
219 _session->_last_msg->_next = _msg;
220 _session->_last_msg = _msg;
221 } else {
222 _session->_last_msg = _session->_oldest_msg = _msg;
223 }
224
225 pthread_mutex_unlock ( &_session->_mutex );
226}
227
228int msi_send_msg ( msi_session_t* _session, msi_msg_t* _msg )
229{
230 int _status;
231
232 if ( !_session->_call ) /* Which should never happen */
233 return FAILURE;
234
235 msi_msg_set_call_id ( _msg, _session->_call->_id );
236
237 uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
238 t_memset ( _msg_string_final, '\0', MSI_MAXMSG_SIZE );
239
240 _msg_string_final[0] = 69;
241
242 uint8_t* _msg_string = msi_msg_to_string ( _msg );
243
244 size_t _lenght = t_memlen ( _msg_string );
245
246 memcpy ( _msg_string_final + 1, _msg_string, _lenght );
247
248 _lenght += 1;
249
250 _status = ( *msi_send_message_callback ) ( _session->_core_handler, _session->_friend_id, _msg_string_final, _lenght );
251
252 free ( _msg_string );
253
254 return _status;
255}
256
257/* Random stuff */
258void flush_peer_type ( msi_session_t* _session, msi_msg_t* _msg, int _peer_id )
259{
260 if ( _msg->_call_type ) {
261 if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
262 _session->_call->_type_peer[_peer_id] = type_audio;
263
264 } else if ( strcmp ( ( const char* ) _msg->_call_type->_header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
265 _session->_call->_type_peer[_peer_id] = type_video;
266 } else {} /* Error */
267 } else {} /* Error */
268}
269
270int has_call_error ( msi_session_t* _session, msi_msg_t* _msg )
271{
272 msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
273
274 if ( !_msg->_call_id ) {
275 msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid) );
276
277 } else if ( !_session->_call ) {
278 msi_msg_set_reason(_msg_error, stringify_error_code(error_no_call) );
279
280 } else if ( strcmp((const char*)_session->_call->_id, (const char*)_msg->_call_id->_header_value ) != 0 ) {
281 msi_msg_set_reason(_msg_error, stringify_error_code(error_id_mismatch) );
282 }
283
284 if ( _msg_error->_reason ) {
285 msi_send_msg ( _session, _msg_error );
286 msi_free_msg ( _msg_error );
287 return SUCCESS;
288 }
289
290 msi_free_msg ( _msg_error );
291 return FAILURE;
292}
293
294/* --------- END OF GLOBAL FUNCTIONS USED BY THIS FILE --------- */
295
296/*------------------------*/
297/*------------------------*/
298/*------------------------*/
299/*------------------------*/
300/*------------------------*/
301/*------------------------*/
302/*------------------------*/
303/*------------------------*/
304/*------------------------*/
305/*------------------------*/
306/*------------------------*/
307/*------------------------*/
308
309msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent )
310{
311 assert(_core_handler);
312 assert(_user_agent);
313
314 msi_session_t* _session = calloc ( sizeof ( msi_session_t ), 1 );
315 assert(_session);
316
317 _session->_oldest_msg = _session->_last_msg = NULL;
318 _session->_core_handler = _core_handler;
319
320 _session->_user_agent = t_strallcpy ( _user_agent );
321 _session->_agent_handler = NULL;
322
323 _session->_key = 0;
324 _session->_call = NULL;
325
326 _session->_frequ = 10000; /* default value? */
327 _session->_call_timeout = 30000; /* default value? */
328
329 /* Use the same frequency */
330 _session->_event_handler = init_event_poll ( _session->_frequ );
331
332 pthread_mutex_init ( &_session->_mutex, NULL );
333
334 return _session;
335}
336
337int msi_terminate_session ( msi_session_t* _session )
338{
339 assert(_session);
340
341 int _status = 0;
342
343 terminate_event_poll ( _session->_event_handler );
344 free ( _session );
345 /* TODO: terminate the rest of the session */
346
347 pthread_mutex_destroy ( &_session->_mutex );
348
349 return _status;
350}
351
352msi_call_t* msi_init_call ( msi_session_t* _session, int _peers, uint32_t _timeoutms )
353{
354 assert(_session);
355 assert(_peers);
356
357 msi_call_t* _call = calloc ( sizeof ( msi_call_t ), 1 );
358 _call->_type_peer = calloc ( sizeof ( call_type ), _peers );
359
360 assert(_call);
361 assert(_call->_type_peer);
362
363 _call->_participants = _peers;
364 _call->_key = _session->_key;
365 _call->_timeoutst = _timeoutms;
366 _call->_outgoing_timer_id = 0;
367
368 return _call;
369}
370
371int msi_terminate_call ( msi_session_t* _session )
372{
373 assert(_session);
374
375 if ( _session->_call->_type_peer )
376 free ( _session->_call->_type_peer );
377
378 cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id);
379
380 free ( _session->_call );
381
382 _session->_call = NULL;
383
384 return SUCCESS;
385}
386/*------------------------*/
387/*------------------------*/
388/*------------------------*/
389/*------------------------*/
390/*------------------------*/
391/*------------------------*/
392/*------------------------*/
393/*------------------------*/
394/*------------------------*/
395/*------------------------*/
396/*------------------------*/
397/*------------------------*/
398
399/* STATE HANDLERS */
400
401/* REQUESTS */
402int msi_handle_recv_invite ( msi_session_t* _session, msi_msg_t* _msg )
403{
404 assert(_session);
405
406 if ( _session->_call ) {
407 msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
408 msi_msg_set_reason(_msg_error, stringify_error_code(error_busy));
409 msi_send_msg(_session, _msg_error);
410 msi_free_msg(_msg_error);
411
412 return 0;
413 }
414 if ( !_msg->_call_id ) {
415 msi_msg_t* _msg_error = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _error ) );
416 msi_msg_set_reason(_msg_error, stringify_error_code(error_no_callid));
417 msi_send_msg(_session, _msg_error);
418 msi_free_msg(_msg_error);
419 return 0;
420 }
421
422 _session->_call = msi_init_call ( _session, 1, _session->_call_timeout );
423 t_memcpy(_session->_call->_id, _msg->_call_id->_header_value, _CALL_ID_LEN);
424 _session->_call->_state = call_starting;
425
426 flush_peer_type ( _session, _msg, 0 );
427
428 msi_msg_t* _msg_ringing = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ringing ) );
429 msi_send_msg ( _session, _msg_ringing );
430 msi_free_msg ( _msg_ringing );
431
432 throw_event ( _session->_event_handler, msi_recv_invite_callback, _session );
433 return 1;
434}
435int msi_handle_recv_start ( msi_session_t* _session, msi_msg_t* _msg )
436{
437 assert(_session);
438
439 if ( has_call_error(_session, _msg) == 0 )
440 return 0;
441
442 _session->_call->_state = call_active;
443
444 flush_peer_type ( _session, _msg, 0 );
445
446 throw_event ( _session->_event_handler, msi_start_call_callback, _session );
447 return 1;
448}
449int msi_handle_recv_reject ( msi_session_t* _session, msi_msg_t* _msg )
450{
451 assert(_session);
452
453 if ( has_call_error(_session, _msg) == 0 )
454 return 0;
455
456 msi_msg_t* _msg_end = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) );
457 msi_send_msg ( _session, _msg_end );
458 msi_free_msg ( _msg_end );
459
460 throw_event ( _session->_event_handler, msi_reject_call_callback, _session );
461
462 return 1;
463}
464int msi_handle_recv_cancel ( msi_session_t* _session, msi_msg_t* _msg )
465{
466 assert(_session);
467
468 if ( has_call_error(_session, _msg) == 0 )
469 return 0;
470
471 msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) );
472 msi_send_msg ( _session, _msg_ending );
473 msi_free_msg ( _msg_ending );
474
475 msi_terminate_call ( _session );
476
477 throw_event ( _session->_event_handler, msi_cancel_call_callback, _session );
478
479 return 1;
480}
481int msi_handle_recv_end ( msi_session_t* _session, msi_msg_t* _msg )
482{
483 assert(_session);
484
485 if ( has_call_error(_session, _msg) == 0 )
486 return 0;
487
488 msi_msg_t* _msg_ending = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _ending ) );
489 msi_send_msg ( _session, _msg_ending );
490 msi_free_msg ( _msg_ending );
491
492 msi_terminate_call ( _session );
493
494 throw_event ( _session->_event_handler, msi_end_call_callback, _session );
495
496 return 1;
497}
498/*--------*/
499
500/* RESPONSES */
501int msi_handle_recv_ringing ( msi_session_t* _session, msi_msg_t* _msg )
502{
503 assert(_session);
504
505 if ( has_call_error(_session, _msg) == 0 )
506 return 0;
507
508 throw_event ( _session->_event_handler, msi_ringing_callback, _session );
509
510 return 1;
511}
512int msi_handle_recv_starting ( msi_session_t* _session, msi_msg_t* _msg )
513{
514 assert(_session);
515
516 if ( has_call_error(_session, _msg) == 0 )
517 return 0;
518
519 _session->_call->_state = call_active;
520
521 msi_msg_t* _msg_start = msi_msg_new ( TYPE_REQUEST, stringify_request ( _start ) );
522 msi_send_msg ( _session, _msg_start );
523 msi_free_msg ( _msg_start );
524
525 flush_peer_type ( _session, _msg, 0 );
526
527 throw_event ( _session->_event_handler, msi_starting_callback, _session );
528 cancel_timer_event(_session->_event_handler, _session->_call->_outgoing_timer_id);
529 _session->_call->_outgoing_timer_id = 0;
530
531 return 1;
532}
533int msi_handle_recv_ending ( msi_session_t* _session, msi_msg_t* _msg )
534{
535 assert(_session);
536
537 if ( has_call_error(_session, _msg) == 0 )
538 return 0;
539
540 msi_terminate_call ( _session );
541 throw_event ( _session->_event_handler, msi_ending_callback, _session );
542
543 return 1;
544}
545int msi_handle_recv_error ( msi_session_t* _session, msi_msg_t* _msg )
546{
547 assert(_session);
548 assert(_session->_call);
549
550 /* Handle error accordingly */
551 if ( _msg->_reason ) {
552 _session->_last_error_id = atoi((const char*)_msg->_reason->_header_value);
553 _session->_last_error_str = stringify_error(_session->_last_error_id);
554 }
555
556 msi_terminate_call(_session);
557
558 throw_event ( _session->_event_handler, msi_error_callback, _session );
559
560 return 1;
561}
562/* ------------------ */
563
564MCBTYPE msi_handle_timeout (void* _arg)
565{
566 msi_session_t* _session = _arg;
567 msi_terminate_call(_session);
568
569 (*msi_timeout_callback) (_arg);
570 (*msi_ending_callback) (_arg);
571
572}
573
574/*------------------------*/
575/*------------------------*/
576/*------------------------*/
577/*------------------------*/
578/*------------------------*/
579/*------------------------*/
580/*------------------------*/
581/*------------------------*/
582/*------------------------*/
583/*------------------------*/
584/*------------------------*/
585/*------------------------*/
586
587int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms )
588{
589 assert(_session);
590
591 if ( !msi_send_message_callback )
592 return 0;
593
594 msi_msg_t* _msg_invite = msi_msg_new ( TYPE_REQUEST, stringify_request ( _invite ) );
595
596 _session->_call = msi_init_call ( _session, 1, _timeoutms ); /* Just one for now */
597 msi_genterate_call_id(_session->_call->_id, _CALL_ID_LEN);
598 _session->_call->_type_local = _call_type;
599 /* Do whatever with message */
600
601 if ( _call_type == type_audio ) {
602 msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE );
603 } else {
604 msi_msg_set_call_type ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE );
605 }
606
607 msi_send_msg ( _session, _msg_invite );
608 msi_free_msg ( _msg_invite );
609
610 _session->_call->_state = call_inviting;
611
612 _session->_call->_outgoing_timer_id = throw_timer_event(_session->_event_handler, msi_handle_timeout, _session, _timeoutms );
613
614 return 1;
615}
616int msi_hangup ( msi_session_t* _session )
617{
618 assert(_session);
619
620 if ( !_session->_call || ( !msi_send_message_callback && _session->_call->_state != call_active ) )
621 return 0;
622
623 msi_msg_t* _msg_ending = msi_msg_new ( TYPE_REQUEST, stringify_request ( _end ) );
624 msi_send_msg ( _session, _msg_ending );
625 msi_free_msg ( _msg_ending );
626
627 return 1;
628}
629
630
631int msi_answer ( msi_session_t* _session, call_type _call_type )
632{
633 assert(_session);
634
635 if ( !msi_send_message_callback || !_session->_call )
636 return 0;
637
638 msi_msg_t* _msg_starting = msi_msg_new ( TYPE_RESPONSE, stringify_response ( _starting ) );
639 _session->_call->_type_local = _call_type;
640
641 if ( _call_type == type_audio ) {
642 msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE );
643 } else {
644 msi_msg_set_call_type ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE );
645 }
646
647 msi_send_msg ( _session, _msg_starting );
648 msi_free_msg ( _msg_starting );
649
650 _session->_call->_state = call_active;
651 return 1;
652}
653int msi_cancel ( msi_session_t* _session )
654{
655 assert(_session);
656
657 if ( !_session->_call || !msi_send_message_callback )
658 return 0;
659
660 msi_msg_t* _msg_cancel = msi_msg_new ( TYPE_REQUEST, stringify_request ( _cancel ) );
661 msi_send_msg ( _session, _msg_cancel );
662 msi_free_msg ( _msg_cancel );
663
664
665
666 return 1;
667}
668int msi_reject ( msi_session_t* _session )
669{
670 assert(_session);
671
672 if ( !_session->_call || !msi_send_message_callback )
673 return 0;
674
675 msi_msg_t* _msg_reject = msi_msg_new ( TYPE_REQUEST, stringify_request ( _reject ) );
676 msi_send_msg ( _session, _msg_reject );
677 msi_free_msg ( _msg_reject );
678
679 return 1;
680}
681
682/*------------------------*/
683/*------------------------*/
684/*------------------------*/
685/*------------------------*/
686/*------------------------*/
687/*------------------------*/
688/*------------------------*/
689/*------------------------*/
690/*------------------------*/
691/*------------------------*/
692/*------------------------*/
693/*------------------------*/
694
695/* OUR MAIN POOL FUNCTION */
696/*
697 * Forks it self to other thread and then handles the session initiation.
698 *
699 * BASIC call flow:
700 *
701 * ALICE BOB
702 * | invite --> |
703 * | |
704 * | <-- ringing |
705 * | |
706 * | <-- starting |
707 * | |
708 * | start --> |
709 * | |
710 * | <-- MEDIA TRANS --> |
711 * | |
712 * | end --> |
713 * | |
714 * | <-- ending |
715 *
716 * Alice calls Bob by sending invite packet.
717 * Bob recvs the packet and sends an ringing packet;
718 * which notifies Alice that her invite is acknowledged.
719 * Ringing screen shown on both sides.
720 * Bob accepts the invite for a call by sending starting packet.
721 * Alice recvs the starting packet and sends the started packet to
722 * inform Bob that she recved the starting packet.
723 * Now the media transmission is established ( i.e. RTP transmission ).
724 * Alice hangs up and sends end packet.
725 * Bob recves the end packet and sends ending packet
726 * as the acknowledgement that the call is ending.
727 *
728 *
729 */
730
731
732/*
733 * Needs a bit more work on the protocol
734 */
735void* msi_poll_stack ( void* _session_p )
736{
737 msi_session_t* _session = ( msi_session_t* ) _session_p;
738 msi_msg_t* _msg = NULL;
739
740 uint32_t* _frequ = &_session->_frequ;
741 while ( _session ) {
742
743 /* At this point it's already parsed */
744 _msg = receive_message ( _session );
745
746 if ( _msg ) {
747
748 if ( _msg->_request ) { /* Handle request */
749
750 const uint8_t* _request_value = _msg->_request->_header_value;
751
752 if ( same ( _request_value, stringify_request ( _invite ) ) ) {
753 msi_handle_recv_invite ( _session, _msg );
754
755 } else if ( same ( _request_value, stringify_request ( _start ) ) ) {
756 msi_handle_recv_start ( _session, _msg );
757
758 } else if ( same ( _request_value, stringify_request ( _cancel ) ) ) {
759 msi_handle_recv_cancel ( _session, _msg );
760
761 } else if ( same ( _request_value, stringify_request ( _reject ) ) ) {
762 msi_handle_recv_reject ( _session, _msg );
763
764 } else if ( same ( _request_value, stringify_request ( _end ) ) ) {
765 msi_handle_recv_end ( _session, _msg );
766 }
767
768 } else if ( _msg->_response ) { /* Handle response */
769
770 const uint8_t* _response_value = _msg->_response->_header_value;
771
772 if ( same ( _response_value, stringify_response ( _ringing ) ) ) {
773 msi_handle_recv_ringing ( _session, _msg );
774
775 } else if ( same ( _response_value, stringify_response ( _starting ) ) ) {
776 msi_handle_recv_starting ( _session, _msg );
777
778 } else if ( same ( _response_value, stringify_response ( _ending ) ) ) {
779 msi_handle_recv_ending ( _session, _msg );
780
781 } else if ( same ( _response_value, stringify_response ( _error ) ) ) {
782 msi_handle_recv_error ( _session, _msg );
783 }
784
785 }
786
787 msi_free_msg ( _msg );
788
789 }
790 usleep ( *_frequ );
791 }
792
793 return NULL;
794}
795
796/*------------------------*/
797/*------------------------*/
798/*------------------------*/
799/*------------------------*/
800/*------------------------*/
801/*------------------------*/
802/*------------------------*/
803/*------------------------*/
804/*------------------------*/
805/*------------------------*/
806/*------------------------*/
807/*------------------------*/
808
809/* Easy way to start the poll */
810
811pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms )
812{
813 assert(_session);
814
815 int _status;
816 pthread_t _thread_id;
817
818
819 _session->_frequ = _frequms * 1000;
820
821 _status = pthread_create ( &_thread_id, NULL, msi_poll_stack, _session );
822
823 if ( _status < 0 ) {
824 printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) );
825 return _status;
826 }
827
828 _status = pthread_detach ( _thread_id );
829
830 if ( _status < 0 ) {
831 printf ( "Error while starting main loop: %d, %s\n", errno, strerror ( errno ) );
832 }
833
834 return _thread_id;
835}
diff --git a/toxmsi/toxmsi.h b/toxmsi/toxmsi.h
deleted file mode 100644
index d8985c64..00000000
--- a/toxmsi/toxmsi.h
+++ /dev/null
@@ -1,145 +0,0 @@
1/* msi_initiation.h
2*
3* Has function for session initiation along with session description.
4* It follows the Tox API ( http://wiki.tox.im/index.php/Messaging_Protocol ). !Red!
5*
6*
7* Copyright (C) 2013 Tox project All Rights Reserved.
8*
9* This file is part of Tox.
10*
11* Tox is free software: you can redistribute it and/or modify
12* it under the terms of the GNU General Public License as published by
13* the Free Software Foundation, either version 3 of the License, or
14* (at your option) any later version.
15*
16* Tox is distributed in the hope that it will be useful,
17* but WITHOUT ANY WARRANTY; without even the implied warranty of
18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19* GNU General Public License for more details.
20*
21* You should have received a copy of the GNU General Public License
22* along with Tox. If not, see <http://www.gnu.org/licenses/>.
23*
24*/
25
26
27#ifndef _MSI_IMPL_H_
28#define _MSI_IMPL_H_
29
30#include <inttypes.h>
31#include "tox.h"
32#include <pthread.h>
33
34#define MCBTYPE void
35#define MCBARGS void* _arg
36#define MCALLBACK MCBTYPE(*callback)(void* _arg)
37
38#define MSI_PACKET 69
39
40#define CT_AUDIO_HEADER_VALUE "AUDIO"
41#define CT_VIDEO_HEADER_VALUE "VIDEO"
42
43/* define size for call_id */
44#define _CALL_ID_LEN 12
45
46typedef enum {
47 type_audio = 1,
48 type_video,
49} call_type;
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} call_state;
58
59typedef int crypto_key;
60
61typedef struct msi_call_s { /* Call info structure */
62 call_state _state;
63 call_type _type_local;
64 call_type* _type_peer; /* Support for conference starts with this */
65 uint8_t _id[_CALL_ID_LEN]; /* Random value identifying the call */
66 crypto_key _key; /* What is the type again? */
67 uint16_t _participants; /* Number of participants */
68 uint32_t _timeoutst; /* Time of the timeout for some action to end; 0 if infinite */
69 int _outgoing_timer_id; /* Timer id */
70
71} msi_call_t;
72
73typedef struct msi_session_s {
74 pthread_mutex_t _mutex;
75
76 crypto_key _key; /* The key */
77
78 /* Call information/handler. ( Maybe only information? ) */
79 msi_call_t* _call;
80
81 /* Storage for message receiving */
82 struct msi_msg_s* _oldest_msg;
83 struct msi_msg_s* _last_msg; /* tail */
84
85 /*int _friend_id;*/
86 tox_IP_Port _friend_id;
87
88 int _last_error_id; /* Determine the last error */
89 const uint8_t* _last_error_str;
90
91 const uint8_t* _user_agent;
92
93 void* _agent_handler; /* Pointer to an object that is handling msi */
94 void* _core_handler; /* Pointer to networking core or to anything that
95 * should handle interaction with core/networking
96 */
97 void* _event_handler; /* Pointer to an object which handles the events */
98
99 uint32_t _frequ;
100 uint32_t _call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
101} msi_session_t;
102
103
104
105msi_session_t* msi_init_session ( void* _core_handler, const uint8_t* _user_agent );
106int msi_terminate_session ( msi_session_t* _session );
107
108pthread_t msi_start_main_loop ( msi_session_t* _session, uint32_t _frequms );
109
110/* Registering callbacks */
111
112/*void msi_register_callback_send(int (*callback) ( int, uint8_t*, uint32_t ) );*/
113void msi_register_callback_send ( int ( *callback ) ( void* _core_handler, tox_IP_Port, uint8_t*, uint32_t ) );
114
115/* Callbacks that handle the states */
116void msi_register_callback_call_started ( MCALLBACK );
117void msi_register_callback_call_canceled ( MCALLBACK );
118void msi_register_callback_call_rejected ( MCALLBACK );
119void msi_register_callback_call_ended ( MCALLBACK );
120
121void msi_register_callback_recv_invite ( MCALLBACK );
122void msi_register_callback_recv_ringing ( MCALLBACK );
123void msi_register_callback_recv_starting ( MCALLBACK );
124void msi_register_callback_recv_ending ( MCALLBACK );
125void msi_register_callback_recv_error ( MCALLBACK );
126
127void msi_register_callback_requ_timeout ( MCALLBACK );
128/* -------- */
129
130
131/* Function handling receiving from core */
132/*static int msi_handlepacket ( tox_IP_Port ip_port, uint8_t* _data, uint16_t _lenght ); */
133
134/* functions describing the usage of msi */
135int msi_invite ( msi_session_t* _session, call_type _call_type, uint32_t _timeoutms );
136int msi_hangup ( msi_session_t* _session );
137
138int msi_answer ( msi_session_t* _session, call_type _call_type );
139int msi_cancel ( msi_session_t* _session );
140int msi_reject ( msi_session_t* _session );
141
142int msi_send_msg ( msi_session_t* _session, struct msi_msg_s* _msg );
143void msi_store_msg ( msi_session_t* _session, struct msi_msg_s* _msg );
144
145#endif /* _MSI_IMPL_H_ */
diff --git a/toxmsi/toxmsi_event.c b/toxmsi/toxmsi_event.c
deleted file mode 100644
index d8c0d269..00000000
--- a/toxmsi/toxmsi_event.c
+++ /dev/null
@@ -1,214 +0,0 @@
1
2#ifdef HAVE_CONFIG_H
3#include "config.h"
4#endif /* HAVE_CONFIG_H */
5
6#include <stdlib.h>
7
8#include "toxmsi_event.h"
9
10#include "../toxrtp/toxrtp_helper.h"
11#include <assert.h>
12#include <stdlib.h>
13#include <unistd.h>
14
15static int _unique_id = 1;
16
17/* clear events */
18void clear_events (event_container_t** _event_container, size_t* _counter)
19{
20 assert( *_event_container );
21
22 free(*_event_container);
23 *_event_container = NULL;
24
25 *_counter = 0;
26}
27
28int pop_id ( event_container_t** _event_container, size_t* _counter, int _id )
29{
30 if ( !*_event_container || !*_counter || !_id )
31 return FAILURE;
32
33 event_container_t* _it = *_event_container;
34 int i;
35
36 for ( i = *_counter; i > 0 ; -- i ){
37 if ( _it->_id == _id ) { /* Hit! */
38 break;
39 }
40 ++_it;
41 }
42
43 if ( i ) {
44 for ( ; i > 0; -- i ){ *_it = *(_it + 1); ++_it; }
45 -- (*_counter);
46 *_event_container = realloc(*_event_container, sizeof(event_container_t) * (*_counter)); /* resize */
47
48 if ( *_counter )
49 assert(*_event_container);
50
51 return SUCCESS;
52
53 } else {
54 assert(i);
55 return FAILURE;
56 }
57}
58
59/* main poll for event execution */
60void* event_poll( void* _event_handler_p )
61{
62 event_handler_t* _event_handler = _event_handler_p;
63 uint32_t* _frequms = &_event_handler->_frequms;
64
65
66 while ( _event_handler->_running )
67 {
68
69 if ( _event_handler->_events_count ){
70 pthread_mutex_lock(&_event_handler->_mutex);
71
72 int i;
73 for ( i = 0; i < _event_handler->_events_count; i ++ ){
74 _event_handler->_events[i]._event(_event_handler->_events[i]._event_args);
75
76 }
77 clear_events(&_event_handler->_events, &_event_handler->_events_count);
78
79 pthread_mutex_unlock(&_event_handler->_mutex);
80 }
81
82 if ( _event_handler->_timed_events_count ){
83 pthread_mutex_lock(&_event_handler->_mutex);
84
85 uint32_t _time = t_time();
86
87 if ( _event_handler->_timed_events[0]._timeout < _time ) {
88 _event_handler->_timed_events[0]._event(_event_handler->_timed_events[0]._event_args);
89
90 pop_id(&_event_handler->_timed_events,
91 &_event_handler->_timed_events_count,
92 _event_handler->_timed_events[0]._id);
93 }
94 pthread_mutex_unlock(&_event_handler->_mutex);
95 }
96
97
98 usleep(*_frequms);
99 }
100
101 _event_handler->_running = -1;
102 pthread_exit(NULL);
103}
104
105void push_event ( event_container_t** _container, size_t* _counter, event_t _func, event_arg_t _arg )
106{
107 (*_counter)++;
108 (*_container) = realloc((*_container), sizeof(event_container_t) * (*_counter));
109 assert((*_container) != NULL);
110
111 (*_container[*_counter - 1])._event = _func;
112 (*_container[*_counter - 1])._event_args = _arg;
113 (*_container[*_counter - 1])._timeout = 0;
114 (*_container[*_counter - 1])._id = 0;
115}
116
117void throw_event( void* _event_handler_p, event_t _func, event_arg_t _arg )
118{
119 if ( !_func )
120 return;
121
122 event_handler_t* _event_handler = _event_handler_p;
123
124 pthread_mutex_lock(&_event_handler->_mutex);
125
126 push_event(&_event_handler->_events, &_event_handler->_events_count, _func, _arg);
127
128 pthread_mutex_unlock(&_event_handler->_mutex);
129}
130
131int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout)
132{
133 if ( !_func )
134 return FAILURE;
135
136 event_handler_t* _event_handler = _event_handler_p;
137
138 pthread_mutex_lock(&_event_handler->_mutex);
139
140 push_event(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _func, _arg);
141 size_t _counter = _event_handler->_timed_events_count;
142 _event_handler->_timed_events[_counter - 1]._timeout = _timeout + t_time();
143 _event_handler->_timed_events[_counter - 1]._id = _unique_id; ++_unique_id;
144
145
146 /* reorder */
147 if ( _counter > 1 ) {
148
149 int i = _counter - 1;
150 /* start from behind excluding last added member */
151 event_container_t* _it = &_event_handler->_timed_events[i - 1];
152
153 event_container_t _last_added = _event_handler->_timed_events[i];
154
155 for ( ; i > 0; --i ) {
156 if ( _it->_timeout > _timeout ){
157 *(_it + 1) = *_it;
158 *_it = _last_added; -- _it;
159 }
160 }
161
162 }
163
164 pthread_mutex_unlock(&_event_handler->_mutex);
165
166 return _event_handler->_timed_events[_counter - 1]._id;
167}
168int cancel_timer_event ( void* _event_handler_p, int _id )
169{
170 event_handler_t* _event_handler = _event_handler_p;
171 return pop_id(&_event_handler->_timed_events, &_event_handler->_timed_events_count, _id);
172}
173
174event_handler_t* init_event_poll (uint32_t _frequms)
175{
176 event_handler_t* _retu = calloc(sizeof(event_handler_t), 1);
177 assert(_retu);
178 /* Initialize basic events */
179 _retu->_events = NULL ;
180
181 /* Initialize timed events */
182 _retu->_timed_events = NULL;
183
184 _retu->_frequms = _frequms;
185 _retu->_running = 1;
186 pthread_mutex_init(&_retu->_mutex, NULL);
187
188 pthread_create(&_retu->_thread_id, NULL, event_poll, _retu);
189 int _rc = pthread_detach(_retu->_thread_id);
190 assert(_rc == 0);
191
192 return _retu;
193}
194
195int terminate_event_poll(event_handler_t* _handler)
196{
197 if ( !_handler )
198 return FAILURE;
199
200 _handler->_running = 0;
201 while (_handler->_running != -1); /* Wait for execution */
202
203 if (_handler->_events)
204 clear_events(&_handler->_events, &_handler->_events_count);
205 if (_handler->_events)
206 clear_events(&_handler->_timed_events, &_handler->_timed_events_count);
207
208 pthread_mutex_destroy( &_handler->_mutex );
209
210 free(_handler);
211
212 return SUCCESS;
213}
214
diff --git a/toxmsi/toxmsi_event.h b/toxmsi/toxmsi_event.h
deleted file mode 100644
index 032e4df5..00000000
--- a/toxmsi/toxmsi_event.h
+++ /dev/null
@@ -1,46 +0,0 @@
1#ifndef _MSI__EVENT_H_
2#define _MSI__EVENT_H_
3
4#include <stddef.h>
5#include <inttypes.h>
6#include <pthread.h>
7
8typedef void* event_arg_t;
9
10typedef void ( *event_t ) ( event_arg_t );
11typedef void ( *timed_event_t ) ( event_arg_t );
12
13typedef struct event_container_s {
14 event_t _event;
15 event_arg_t _event_args;
16 uint32_t _timeout;
17 long long _id;
18
19} event_container_t;
20
21typedef struct event_handler_s {
22 event_container_t* _events;
23 size_t _events_count;
24
25 event_container_t* _timed_events;
26 size_t _timed_events_count;
27
28 uint32_t _frequms;
29 int _running;
30
31 pthread_mutex_t _mutex;
32 pthread_t _thread_id;
33
34} event_handler_t;
35
36event_handler_t* init_event_poll ( uint32_t _frequms );
37int terminate_event_poll ( event_handler_t* _event_handler );
38
39void throw_event ( void* _event_handler_p, event_t _func, event_arg_t _arg );
40
41/* Not yet ready for use */
42int throw_timer_event ( void* _event_handler_p, event_t _func, event_arg_t _arg, uint32_t _timeout);
43int cancel_timer_event ( void* _event_handler_p, int _id );
44
45
46#endif /* _MSI__EVENT_H_ */
diff --git a/toxmsi/toxmsi_header.c b/toxmsi/toxmsi_header.c
deleted file mode 100644
index 448ebc7f..00000000
--- a/toxmsi/toxmsi_header.c
+++ /dev/null
@@ -1,181 +0,0 @@
1
2#ifdef HAVE_CONFIG_H
3#include "config.h"
4#endif /* HAVE_CONFIG_H */
5
6#include "toxmsi_message.h"
7#include <string.h>
8#include "../toxrtp/toxrtp_helper.h"
9#include <assert.h>
10#include "../toxcore/Lossless_UDP.h"
11
12#define ALLOC_ADD_DATA(_tempval, _hdrlist, _fielddef, _msgvar, _alloctype) \
13_tempval = msi_search_field(_hdrlist, (const uint8_t*)_fielddef); \
14if ( _tempval ){ \
15 _msgvar = calloc(sizeof(_alloctype), 1); \
16 assert(_msgvar); \
17 _msgvar->_header_value = _tempval; \
18}
19
20uint8_t* msi_search_field ( msi_header_t* _list, const uint8_t* _field )
21{
22 assert(_list);
23 assert(_field);
24
25 msi_header_t* _iterator;
26
27 for ( _iterator = _list;
28 _iterator && strcmp((const char*)_iterator->_header_field, (const char*)_field) != 0;
29 _iterator = _iterator->next );
30
31 if ( _iterator ){
32 return t_strallcpy(_iterator->_header_value);
33 } else return NULL;
34}
35
36int msi_parse_headers ( msi_msg_t* _msg )
37{
38 assert(_msg);
39
40 if ( !_msg->_headers )
41 return FAILURE;
42
43 msi_header_t* _list = _msg->_headers;
44 uint8_t* _field_current;
45
46 ALLOC_ADD_DATA(_field_current, _list, _CALL_ID_FIELD, _msg->_call_id, msi_header_call_id_t)
47 ALLOC_ADD_DATA(_field_current, _list, _VERSION_FIELD, _msg->_version, msi_header_version_t)
48 ALLOC_ADD_DATA(_field_current, _list, _REQUEST_FIELD, _msg->_request, msi_header_request_t)
49 ALLOC_ADD_DATA(_field_current, _list, _RESPONSE_FIELD, _msg->_response, msi_header_response_t)
50 ALLOC_ADD_DATA(_field_current, _list, _FRIENDID_FIELD, _msg->_friend_id, msi_header_friendid_t)
51 ALLOC_ADD_DATA(_field_current, _list, _CALLTYPE_FIELD, _msg->_call_type, msi_header_call_type_t)
52 ALLOC_ADD_DATA(_field_current, _list, _USERAGENT_FIELD, _msg->_user_agent, msi_header_user_agent_t)
53 ALLOC_ADD_DATA(_field_current, _list, _INFO_FIELD, _msg->_info, msi_header_info_t)
54 ALLOC_ADD_DATA(_field_current, _list, _REASON_FIELD, _msg->_reason, msi_header_reason_t)
55
56 /* Since we don't need the raw header anymore remove it */
57 msi_header_t* _temp;
58 while ( _list ){
59 _temp = _list->next;
60 free(_list->_header_field);
61 free(_list->_header_value);
62 free(_list);
63 _list = _temp;
64 }
65
66 _msg->_headers = NULL;
67
68 return SUCCESS;
69}
70
71/*
72 * If you find better way of parsing values let me know
73 */
74msi_header_t* msi_add_new_header ( uint8_t* _value )
75{
76 if ( !_value )
77 return NULL;
78
79 size_t _length = t_memlen(_value);
80
81 if ( !_length ) {
82 return NULL;
83 }
84
85 size_t _first_len = t_strfind(_value, (const uint8_t*)" ");
86 if ( !_first_len ){
87 return NULL;
88 }
89
90 size_t _second_len = (_length - _first_len);
91 if ( !_second_len ){
92 return NULL;
93 }
94
95
96 uint8_t* _identifier = calloc(sizeof (uint8_t), (_first_len + 1) );
97 uint8_t* _data = calloc(sizeof (uint8_t), (_second_len + 1) );
98
99 assert(_identifier);
100 assert(_data);
101
102
103 uint8_t* _p_it = _value;
104 size_t _num_it;
105
106 for ( _num_it = 0; *_p_it != ' '; _num_it++ ){
107 _identifier[_num_it] = *_p_it;
108 ++_p_it;
109 }
110 _identifier[_num_it] = '\0';
111 ++_p_it;
112
113
114 for ( _num_it = 0; *_p_it != '\r'; _num_it++ ){
115 _data[_num_it] = *_p_it;
116 ++_p_it;
117 }
118 _data[_num_it] = '\r';
119 _data[_num_it + 1] = '\0';
120
121 msi_header_t* _retu = calloc(sizeof(msi_header_t), 1);
122 assert(_retu);
123
124 _retu->_header_field = _identifier;
125 _retu->_header_value = _data;
126
127 _retu->next = NULL;
128
129 return _retu;
130}
131
132msi_header_t* msi_parse_raw_data ( const uint8_t* _data )
133{
134 assert(_data);
135
136 uint8_t* _header_string;
137
138 _header_string = (uint8_t*) strtok ((char*)_data, _RAW_TERMINATOR);
139
140 msi_header_t* _head = msi_add_new_header(_header_string);
141 msi_header_t* _it = _head;
142
143 while ( _header_string && _it ){
144
145 _header_string = (uint8_t*) strtok (NULL, _RAW_TERMINATOR);
146 _it->next = msi_add_new_header(_header_string);
147 if ( _it->next ){
148 _it = _it->next;
149 }
150 }
151
152 /* Iterate through list and remove all fault headers if any */
153
154 msi_header_t* _tmp = _it;
155
156 for ( _it = _head; _it; _it = _it->next ){
157
158 if ( !_it->_header_value || !_it->_header_field ) {
159 _tmp ->next = _it->next;
160
161 if ( _it->_header_field )
162 free(_it->_header_field);
163 if ( _it->_header_value )
164 free(_it->_header_value);
165
166 if ( _it == _head ){
167 _head = _head->next;
168 }
169
170 free(_it);
171 _it = _tmp;
172 } else
173 _tmp = _it;
174
175 }
176
177 return _head;
178}
179
180
181
diff --git a/toxmsi/toxmsi_header.h b/toxmsi/toxmsi_header.h
deleted file mode 100644
index 599fafa3..00000000
--- a/toxmsi/toxmsi_header.h
+++ /dev/null
@@ -1,99 +0,0 @@
1#ifndef _MSI_HEADER_
2#define _MSI_HEADER_
3
4/* Basic format of the unparsed header string
5 * ( No spaces in field allowed )
6 * Version 0.1.1\n\r
7 * Request INVITE\n\r
8 * Response\n\r
9 * Friend-id ( from ip )\n\r
10 * Call-type AUDIO\n\r
11 * User-agent phone-v.1.0.0\n\r
12 */
13
14
15/* define raw header terminator */
16static const char* _RAW_TERMINATOR = "\n\r";
17
18/* define string formats for the identifiers */
19#define _VERSION_FIELD "Version"
20#define _REQUEST_FIELD "Request"
21#define _RESPONSE_FIELD "Response"
22#define _FRIENDID_FIELD "Friend-id"
23#define _CALLTYPE_FIELD "Call-type"
24#define _USERAGENT_FIELD "User-agent"
25#define _INFO_FIELD "INFO"
26#define _REASON_FIELD "Reason"
27#define _CALL_ID_FIELD "Call-id"
28
29#define HEADER_VALUES \
30/*uint8_t* _header_field */ \
31uint8_t* _header_value;
32
33typedef struct msi_header_s { /* raw header list */
34 uint8_t* _header_field;
35 uint8_t* _header_value;
36
37 struct msi_header_s* next;
38
39} msi_header_t;
40
41
42
43typedef struct msi_header_version_s { /* Defines our version */
44 HEADER_VALUES
45
46} msi_header_version_t;
47
48typedef struct msi_header_request_s { /* Defines our request */
49 HEADER_VALUES
50
51} msi_header_request_t;
52
53typedef struct msi_header_response_s { /* Defines our response */
54 HEADER_VALUES
55
56} msi_header_response_t;
57
58typedef struct msi_header_friendid_s { /* Defines source that sent the message */
59 HEADER_VALUES
60
61} msi_header_friendid_t;
62
63typedef struct msi_header_call_type_s { /* Defines the type of the call */
64 HEADER_VALUES
65
66} msi_header_call_type_t;
67
68typedef struct msi_header_user_agent_s { /* Defines the device of the participant */
69 HEADER_VALUES
70
71} msi_header_user_agent_t;
72
73
74typedef struct msi_header_call_id_s { /* Call id that is used to identify the call */
75 HEADER_VALUES
76
77} msi_header_call_id_t;
78
79typedef struct msi_header_info_s { /* Defines informational message header */
80 HEADER_VALUES
81
82} msi_header_info_t;
83
84typedef struct msi_header_reason_s { /* Defines reason mostly for error messages */
85 HEADER_VALUES
86
87} msi_header_reason_t;
88
89struct msi_msg_s;
90
91/*
92 * Parses the header list to header types
93 */
94int msi_parse_headers ( struct msi_msg_s* _msg );
95
96/* Make sure it's null terminated */
97msi_header_t* msi_parse_raw_data ( const uint8_t* _data );
98
99#endif /* _MSI_HEADER_ */
diff --git a/toxmsi/toxmsi_message.c b/toxmsi/toxmsi_message.c
deleted file mode 100644
index 72d7ee01..00000000
--- a/toxmsi/toxmsi_message.c
+++ /dev/null
@@ -1,267 +0,0 @@
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif /* HAVE_CONFIG_H */
4
5#include "toxmsi_message.h"
6#include <stdlib.h>
7#include <string.h>
8#include "../toxrtp/toxrtp_helper.h"
9#include <assert.h>
10#include <stdlib.h>
11
12#define ALLOCATE_HEADER(_header_type, _var, _m_header_value) \
13_var = calloc( sizeof(_header_type), 1 ); \
14assert(_var); \
15_var->_header_value = t_strallcpy((const uint8_t*)_m_header_value);
16
17#define DEALLOCATE_HEADER(_header) \
18if ( _header && _header->_header_value ) { \
19free( _header->_header_value ); \
20free( _header ); }
21
22#define SET_HEADER(_header_type, _var, _m_header_value) \
23if ( _var ){ \
24free(_var->_header_value); \
25free(_var);} \
26ALLOCATE_HEADER( _header_type, _var, _m_header_value )
27
28
29/* Sets empty message
30 */
31void set_msg ( msi_msg_t* _msg )
32{
33 _msg->_call_type = NULL;
34 _msg->_version = NULL;
35 _msg->_request = NULL;
36 _msg->_response = NULL;
37 _msg->_friend_id = NULL;
38 _msg->_user_agent = NULL;
39 _msg->_call_id = NULL;
40 _msg->_reason = NULL;
41 _msg->_info = NULL;
42 _msg->_next = NULL;
43 _msg->_headers = NULL;
44}
45
46void msi_free_msg ( msi_msg_t* _msg )
47{
48 assert(_msg);
49
50 DEALLOCATE_HEADER(_msg->_call_type);
51 DEALLOCATE_HEADER(_msg->_friend_id);
52 DEALLOCATE_HEADER(_msg->_request);
53 DEALLOCATE_HEADER(_msg->_response);
54 DEALLOCATE_HEADER(_msg->_user_agent);
55 DEALLOCATE_HEADER(_msg->_version);
56 DEALLOCATE_HEADER(_msg->_info);
57 DEALLOCATE_HEADER(_msg->_reason);
58 DEALLOCATE_HEADER(_msg->_call_id);
59
60 free(_msg);
61}
62
63void append_header_to_string ( uint8_t* _dest, const uint8_t* _header_field, const uint8_t* _header_value )
64{
65 assert(_dest);
66 assert(_header_value);
67 assert(_header_field);
68
69 size_t _dest_len = t_memlen(_dest);
70
71 uint8_t* _storage_iterator = _dest + _dest_len;
72 const uint8_t* _header_fit = _header_field;
73 const uint8_t* _header_val = _header_value;
74 const uint8_t* _term_it = (const uint8_t*) _RAW_TERMINATOR;
75
76 while ( *_header_fit ){
77 *_storage_iterator = *_header_fit;
78 ++_header_fit;
79 ++_storage_iterator;
80 }
81
82 *_storage_iterator = ' '; /* place space */
83 ++_storage_iterator;
84
85 while ( *_header_val ){
86 *_storage_iterator = *_header_val;
87 ++_header_val;
88 ++_storage_iterator;
89 }
90
91 while ( *_term_it ){
92 *_storage_iterator = *_term_it;
93 ++_term_it;
94 ++_storage_iterator;
95 }
96}
97
98msi_msg_t* msi_parse_msg ( const uint8_t* _data )
99{
100 assert(_data);
101
102 msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 );
103 assert(_retu);
104
105 set_msg(_retu);
106
107 _retu->_headers = msi_parse_raw_data ( _data );
108
109 if ( msi_parse_headers (_retu) == FAILURE ) {
110 msi_free_msg(_retu);
111 return NULL;
112 }
113
114 if ( !_retu->_version || strcmp((const char*)_retu->_version->_header_value, VERSION_STRING) != 0 ){
115 msi_free_msg(_retu);
116 return NULL;
117 }
118
119 return _retu;
120}
121
122
123uint8_t* msi_msg_to_string ( msi_msg_t* _msg )
124{
125 assert(_msg);
126
127 uint8_t* _retu = calloc(sizeof(uint8_t), MSI_MAXMSG_SIZE );
128 assert(_retu);
129
130 t_memset(_retu, '\0', MSI_MAXMSG_SIZE);
131
132 if ( _msg->_version ){
133 append_header_to_string(_retu, (const uint8_t*)_VERSION_FIELD, _msg->_version->_header_value);
134 }
135
136 if ( _msg->_request ){
137 append_header_to_string(_retu, (const uint8_t*)_REQUEST_FIELD, _msg->_request->_header_value);
138 }
139
140 if ( _msg->_response ){
141 append_header_to_string(_retu, (const uint8_t*)_RESPONSE_FIELD, _msg->_response->_header_value);
142 }
143
144 if ( _msg->_friend_id ){
145 append_header_to_string(_retu, (const uint8_t*)_FRIENDID_FIELD, _msg->_friend_id->_header_value);
146 }
147
148 if ( _msg->_call_type ){
149 append_header_to_string(_retu, (const uint8_t*)_CALLTYPE_FIELD, _msg->_call_type->_header_value);
150 }
151
152 if ( _msg->_user_agent ){
153 append_header_to_string(_retu, (const uint8_t*)_USERAGENT_FIELD, _msg->_user_agent->_header_value);
154 }
155
156 if ( _msg->_info ){
157 append_header_to_string(_retu, (const uint8_t*)_INFO_FIELD, _msg->_info->_header_value);
158 }
159
160 if ( _msg->_call_id ){
161 append_header_to_string(_retu, (const uint8_t*)_CALL_ID_FIELD, _msg->_call_id->_header_value);
162 }
163
164 if ( _msg->_reason ){
165 append_header_to_string(_retu, (const uint8_t*)_REASON_FIELD, _msg->_reason->_header_value);
166 }
167
168 return _retu;
169}
170
171msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _typeid )
172{
173 msi_msg_t* _retu = calloc ( sizeof ( msi_msg_t ), 1 );
174 assert(_retu);
175
176 set_msg(_retu);
177
178 if ( _type == TYPE_REQUEST ){
179 ALLOCATE_HEADER( msi_header_request_t, _retu->_request, _typeid )
180 _retu->_response = NULL;
181
182 } else if ( _type == TYPE_RESPONSE ) {
183 ALLOCATE_HEADER( msi_header_response_t, _retu->_response, _typeid )
184 _retu->_request = NULL;
185
186 } else {
187 msi_free_msg(_retu);
188 return NULL;
189 }
190
191
192 ALLOCATE_HEADER( msi_header_version_t, _retu->_version, VERSION_STRING)
193
194 _retu->_friend_id = NULL;
195 _retu->_call_type = NULL;
196 _retu->_user_agent = NULL;
197 _retu->_info = NULL;
198
199 _retu->_next = NULL;
200
201 return _retu;
202}
203
204uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len )
205{
206 assert(_storage);
207
208 static const char _alphanum[] =
209 "0123456789"
210 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
211 "abcdefghijklmnopqrstuvwxyz"; /* avoids warning */
212
213 uint8_t _val = 0;
214 size_t _it;
215
216 /* Generate random values 1-255 */
217 for ( _it = 0; _it < _len; _it ++ ) {
218 while ( !_val ) _val = (uint8_t) _alphanum[ t_random(61) ];
219
220 _storage[_it] = _val;
221 _val = 0;
222 }
223
224 return _storage;
225}
226
227/* HEADER SETTING
228 */
229
230void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field )
231{
232 assert(_msg);
233 assert(_header_field);
234
235 SET_HEADER(msi_header_call_type_t, _msg->_call_type, _header_field)
236}
237void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field )
238{
239 assert(_msg);
240 assert(_header_field);
241
242 SET_HEADER(msi_header_user_agent_t, _msg->_user_agent, _header_field)
243}
244void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field )
245{
246 assert(_msg);
247 assert(_header_field);
248
249 SET_HEADER(msi_header_friendid_t, _msg->_friend_id, _header_field)
250}
251void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field )
252{
253}
254void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field )
255{
256 assert(_msg);
257 assert(_header_field);
258
259 SET_HEADER(msi_header_reason_t, _msg->_reason, _header_field)
260}
261void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field )
262{
263 assert(_msg);
264 assert(_header_field);
265
266 SET_HEADER(msi_header_call_id_t, _msg->_call_id, _header_field)
267}
diff --git a/toxmsi/toxmsi_message.h b/toxmsi/toxmsi_message.h
deleted file mode 100644
index 87dfccc2..00000000
--- a/toxmsi/toxmsi_message.h
+++ /dev/null
@@ -1,120 +0,0 @@
1#ifndef _MSI_MESSAGE_H_
2#define _MSI_MESSAGE_H_
3
4#ifdef HAVE_CONFIG_H
5#include "config.h"
6#endif /* HAVE_CONFIG_H */
7
8#include <inttypes.h>
9#include "../toxcore/network.h"
10#include "../toxcore/tox.h"
11
12#include "toxmsi_header.h"
13
14#define TYPE_REQUEST 1
15#define TYPE_RESPONSE 2
16
17#define VERSION_STRING "0.2.2"
18
19#define MSI_MAXMSG_SIZE 65535
20
21typedef enum {
22 _invite,
23 _start,
24 _cancel,
25 _reject,
26 _end,
27
28} msi_request_t;
29
30static inline const uint8_t *stringify_request(msi_request_t _request)
31{
32 static const uint8_t* strings[] =
33 {
34 (uint8_t*)"INVITE",
35 (uint8_t*)"START",
36 (uint8_t*)"CANCEL",
37 (uint8_t*)"REJECT",
38 (uint8_t*)"END"
39 };
40
41 return strings[_request];
42}
43
44typedef enum {
45 _trying,
46 _ringing,
47 _starting,
48 _ending,
49 _error
50
51} msi_response_t;
52
53static inline const uint8_t *stringify_response(msi_response_t _response)
54{
55 static const uint8_t* strings[] =
56 {
57 (uint8_t*)"trying",
58 (uint8_t*)"ringing",
59 (uint8_t*)"starting",
60 (uint8_t*)"ending",
61 (uint8_t*)"error"
62 };
63
64 return strings[_response];
65}
66
67
68typedef struct msi_msg_s {
69 /* This is the header list which contains unparsed headers */
70 msi_header_t* _headers;
71
72 /* Headers parsed */
73 msi_header_version_t* _version;
74 msi_header_request_t* _request;
75 msi_header_response_t* _response;
76 msi_header_friendid_t* _friend_id;
77 msi_header_call_type_t* _call_type;
78 msi_header_user_agent_t* _user_agent;
79 msi_header_info_t* _info;
80 msi_header_reason_t* _reason;
81 msi_header_call_id_t* _call_id;
82
83 /* Pointer to next member since it's list duuh */
84 struct msi_msg_s* _next;
85
86} msi_msg_t;
87
88
89/*
90 * Parse data received from socket
91 */
92msi_msg_t* msi_parse_msg ( const uint8_t* _data );
93
94/*
95 * Make new message. Arguments: _type: (request, response); _type_field ( value )
96 */
97msi_msg_t* msi_msg_new ( uint8_t _type, const uint8_t* _type_field );
98
99/* Header setting */
100void msi_msg_set_call_type ( msi_msg_t* _msg, const uint8_t* _header_field );
101void msi_msg_set_user_agent ( msi_msg_t* _msg, const uint8_t* _header_field );
102void msi_msg_set_friend_id ( msi_msg_t* _msg, const uint8_t* _header_field );
103void msi_msg_set_info ( msi_msg_t* _msg, const uint8_t* _header_field );
104void msi_msg_set_reason ( msi_msg_t* _msg, const uint8_t* _header_field );
105void msi_msg_set_call_id ( msi_msg_t* _msg, const uint8_t* _header_field );
106
107
108uint8_t* msi_genterate_call_id ( uint8_t* _storage, size_t _len );
109/*
110 * Parses message struct to string.
111 * Allocates memory so don't forget to free it
112 */
113uint8_t* msi_msg_to_string ( msi_msg_t* _msg );
114
115/*
116 * msi_msg_s struct deallocator
117 */
118void msi_free_msg ( msi_msg_t* _msg );
119
120#endif /* _MSI_MESSAGE_H_ */
diff --git a/toxrtp/Makefile.inc b/toxrtp/Makefile.inc
deleted file mode 100644
index 801a7fd3..00000000
--- a/toxrtp/Makefile.inc
+++ /dev/null
@@ -1,34 +0,0 @@
1if BUILD_AV
2
3lib_LTLIBRARIES += libtoxrtp.la
4
5libtoxrtp_la_include_HEADERS = \
6 ../toxrtp/toxrtp.h
7
8libtoxrtp_la_includedir = $(includedir)/tox
9
10libtoxrtp_la_SOURCES = ../toxrtp/toxrtp_error.h \
11 ../toxrtp/toxrtp_error.c \
12 ../toxrtp/toxrtp_error_id.h \
13 ../toxrtp/toxrtp_helper.h \
14 ../toxrtp/toxrtp_helper.c \
15 ../toxrtp/toxrtp.h \
16 ../toxrtp/toxrtp.c \
17 ../toxrtp/toxrtp_message.h \
18 ../toxrtp/toxrtp_message.c \
19 ../toxcore/network.h \
20 ../toxcore/network.c \
21 ../toxcore/util.h \
22 ../toxcore/util.c
23
24libtoxrtp_la_CFLAGS = -I../toxcore \
25 -I../toxrtp \
26 $(NACL_CFLAGS)
27
28libtoxrtp_la_LDFLAGS = $(TOXRTP_LT_LDFLAGS) \
29 $(NACL_LDFLAGS) \
30 $(EXTRA_LT_LDFLAGS)
31
32libtoxrtp_la_LIBS = $(NACL_LIBS)
33
34endif
diff --git a/toxrtp/tests/Makefile.inc b/toxrtp/tests/Makefile.inc
deleted file mode 100644
index 8d1c8b69..00000000
--- a/toxrtp/tests/Makefile.inc
+++ /dev/null
@@ -1 +0,0 @@
1
diff --git a/toxrtp/tests/test_bidirect.c b/toxrtp/tests/test_bidirect.c
deleted file mode 100644
index b5a0899e..00000000
--- a/toxrtp/tests/test_bidirect.c
+++ /dev/null
@@ -1,109 +0,0 @@
1#define _BSD_SOURCE
2
3#include "../toxrtp.h"
4#include "../toxrtp_message.h"
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <utime.h>
9#include <assert.h>
10
11#include "test_helper.h"
12#include "../../toxcore/tox.h"
13
14#ifdef _CT_BIDIRECT
15
16int _print_help( const char* name )
17{
18 char* _help = malloc( 300 );
19 memset(_help, '\0', 300);
20
21 strcat(_help, " Usage: ");
22 strcat(_help, name);
23 strcat(_help, "\n -d IP ( destination )\n"
24 " -p PORT ( dest Port )\n"
25 " -l PORT ( listen Port ) \n");
26
27 puts ( _help );
28
29 free(_help);
30 return FAILURE;
31}
32
33int main( int argc, char* argv[] )
34{
35 int status;
36 tox_IP_Port Ip_port;
37 const char* ip, *psend, *plisten;
38 uint16_t port_send, port_listen;
39 const uint8_t* test_bytes = "0123456789012345678901234567890123456789012345678901234567890123456789"
40 "0123456789012345678901234567890123456789012345678901234567890123456789"
41 "0123456789012345678901234567890123456789012345678901234567890123456789"
42 "0123456789012345678901234567890123456789012345678901234567890123456789";
43
44
45 rtp_session_t* _m_session;
46 rtp_msg_t *_m_msg_R, *_m_msg_S;
47 arg_t* _list = parse_args ( argc, argv );
48
49 ip = find_arg_duble(_list, "-d");
50 psend = find_arg_duble(_list, "-p");
51 plisten = find_arg_duble(_list, "-l");
52
53 if ( !ip || !plisten || !psend )
54 return _print_help(argv[0]);
55
56 port_send = atoi(psend);
57 port_listen = atoi(plisten);
58
59 IP_Port local, remote;
60
61 /*
62 * This is the Local ip. We initiate networking on
63 * this value for it's the local one. To make stuff simpler we receive over this value
64 * and send on the other one ( see remote )
65 */
66 local.ip.i = htonl(INADDR_ANY);
67 local.port = port_listen;
68 Networking_Core* _networking = new_networking(local.ip, port_listen);
69
70 if ( !_networking )
71 return FAILURE;
72
73 int _socket = _networking->sock;
74 /*
75 * Now this is the remote. It's used by rtp_session_t to determine the receivers ip etc.
76 */
77 t_setipport ( ip, port_send, &remote );
78 _m_session = rtp_init_session(-1, -1);
79 rtp_add_receiver( _m_session, &remote );
80
81 /* Now let's start our main loop in both recv and send mode */
82
83 for ( ;; )
84 {
85 /*
86 * This part checks for received messages and if gotten one
87 * display 'Received msg!' indicator and free message
88 */
89 _m_msg_R = rtp_recv_msg ( _m_session );
90
91 if ( _m_msg_R ) {
92 puts ( "Received msg!" );
93 rtp_free_msg(_m_session, _m_msg_R);
94 }
95 /* -------------------- */
96
97 /*
98 * This one makes a test msg and sends that message to the 'remote'
99 */
100 _m_msg_S = rtp_msg_new ( _m_session, test_bytes, 280 ) ;
101 rtp_send_msg ( _m_session, _m_msg_S, _socket );
102 usleep ( 10000 );
103 /* -------------------- */
104 }
105
106 return SUCCESS;
107}
108
109#endif /* _CT_BIDIRECT */
diff --git a/toxrtp/tests/test_headers.c b/toxrtp/tests/test_headers.c
deleted file mode 100644
index be3f1375..00000000
--- a/toxrtp/tests/test_headers.c
+++ /dev/null
@@ -1,316 +0,0 @@
1/* test_headers.c
2 *
3 * Tests header parsing. You probably won't need this. !Red!
4 *
5 *
6 * Copyright (C) 2013 Tox project All Rights Reserved.
7 *
8 * This file is part of Tox.
9 *
10 * Tox is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tox is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include "test_helper.h"
26#include "../toxrtp.h"
27#include "../toxrtp_message.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <utime.h>
33#include <assert.h>
34#include "../toxrtp_error_id.h"
35
36#define _CT_HEADERS_
37
38#ifdef _CT_HEADERS
39
40int _socket;
41pthread_mutex_t _mutex;
42
43int print_help()
44{
45 puts (
46 " Usage: Tuxrtp [-s (send mode) -d IP ( destination ) -p PORT ( dest Port )] \n"
47 " [-r ( recv mode ) ]"
48 );
49 return FAILURE;
50}
51
52void print_session_stats ( rtp_session_t* _m_session )
53{
54 printf
55 (
56 "Session test:\n"
57 "\tPackets sent:%d\n"
58 "\tPackets recv:%d\n\n"
59 "\tBytes sent:%d\n"
60 "\tBytes recv:%d\n\n"
61 "\tHeader CCSRs:%d\n"
62 ,
63 _m_session->_packets_sent,
64 _m_session->_packets_recv,
65 _m_session->_bytes_sent,
66 _m_session->_bytes_recv,
67 _m_session->_cc
68 );
69
70 uint8_t i;
71 for ( i = 0; i < _m_session->_cc; i++ ) {
72 printf (
73 "\t%d > :%d\n", i, _m_session->_csrc[i]
74 );
75 }
76}
77
78void print_header_info ( rtp_header_t* _header )
79{
80 printf
81 (
82 "Header info:\n"
83 "\tVersion :%d\n"
84 "\tPadding :%d\n"
85 "\tExtension :%d\n"
86 "\tCSRC count :%d\n"
87 "\tPayload type :%d\n"
88 "\tMarker :%d\n\n"
89
90 "\tSSrc :%d\n"
91 "\tSequence num :%d\n"
92 "\tLenght: :%d\n"
93 "\tCSRC's:\n"
94 ,
95 rtp_header_get_flag_version ( _header ),
96 rtp_header_get_flag_padding ( _header ),
97 rtp_header_get_flag_extension ( _header ),
98 rtp_header_get_flag_CSRC_count ( _header ),
99 rtp_header_get_setting_payload_type ( _header ),
100 rtp_header_get_setting_marker ( _header ),
101
102 _header->_ssrc,
103 _header->_sequence_number,
104 _header->_length
105 );
106
107
108 uint8_t i;
109 for ( i = 0; i < rtp_header_get_flag_CSRC_count ( _header ); i++ ) {
110 printf (
111 "\t%d > :%d\n", i, _header->_csrc[i]
112 );
113 }
114
115 puts ( "\n" );
116}
117
118void print_ext_header_info(rtp_ext_header_t* _ext_header)
119{
120 printf
121 (
122 "External Header info: \n"
123 "\tLenght :%d\n"
124 "\tID :%d\n"
125 "\tValue H :%d\n"
126 "\tValue W :%d\n\n",
127 _ext_header->_ext_len,
128 _ext_header->_ext_type,
129 rtp_get_resolution_marking_height(_ext_header, 0),
130 rtp_get_resolution_marking_width(_ext_header, 0)
131 );
132}
133
134int rtp_handlepacket ( rtp_session_t* _session, rtp_msg_t* _msg )
135{
136 if ( !_msg )
137 return FAILURE;
138
139 if ( rtp_check_late_message(_session, _msg) < 0 ) {
140 rtp_register_msg(_session, _msg);
141 }
142
143 if ( _session->_last_msg ) {
144 _session->_last_msg->_next = _msg;
145 _session->_last_msg = _msg;
146 } else {
147 _session->_last_msg = _session->_oldest_msg = _msg;
148 }
149
150
151 return SUCCESS;
152}
153
154void* receivepacket_callback(void* _p_session)
155{
156 rtp_msg_t* _msg;
157 rtp_session_t* _session = _p_session;
158
159 uint32_t _bytes;
160 tox_IP_Port _from;
161 uint8_t _socket_data[MAX_UDP_PACKET_SIZE];
162
163 int _m_socket = _socket;
164
165 while ( 1 )
166 {
167 int _status = receivepacket ( _m_socket, &_from, _socket_data, &_bytes );
168
169 if ( _status == FAILURE ) { /* nothing recved */
170 usleep(1000);
171 continue;
172 }
173
174 pthread_mutex_lock ( &_mutex );
175
176 _msg = rtp_msg_parse ( NULL, _socket_data, _bytes );
177 rtp_handlepacket(_session, _msg);
178
179 pthread_mutex_unlock ( &_mutex );
180 }
181
182 pthread_exit(NULL);
183}
184
185int main ( int argc, char* argv[] )
186{
187 arg_t* _list = parse_args ( argc, argv );
188
189 if ( _list == NULL ) { /* failed */
190 return print_help();
191 }
192
193 pthread_mutex_init ( &_mutex, NULL );
194
195 int status;
196 IP_Port Ip_port;
197 const char* ip;
198 uint16_t port;
199
200
201 const uint8_t* test_bytes [300];
202 memset(test_bytes, 'a', 300);
203
204 rtp_session_t* _m_session;
205 rtp_msg_t* _m_msg;
206
207 if ( find_arg_simple ( _list, "-r" ) != FAILURE ) { /* Server mode */
208
209 IP_Port LOCAL_IP; /* since you need at least 1 recv-er */
210 LOCAL_IP.ip.i = htonl(INADDR_ANY);
211 LOCAL_IP.port = RTP_PORT;
212 LOCAL_IP.padding = -1;
213
214 _m_session = rtp_init_session ( -1, -1 );
215 Networking_Core* _networking = new_networking(LOCAL_IP.ip, RTP_PORT_LISTEN);
216 _socket = _networking->sock;
217
218
219 if ( !_networking ){
220 pthread_mutex_destroy ( &_mutex );
221 return FAILURE;
222 }
223
224 int _socket = _networking->sock;
225
226 if ( status < 0 ) {
227 pthread_mutex_destroy ( &_mutex );
228 return FAILURE;
229 }
230 /* -- start in recv mode, get 1 message and then analyze it -- */
231 pthread_t _tid;
232 RUN_IN_THREAD(receivepacket_callback, _tid, _m_session)
233
234 for ( ; ; ) { /* Recv for x seconds */
235 _m_msg = rtp_recv_msg ( _m_session );
236
237 /* _m_msg = rtp_session_get_message_queded ( _m_session ); DEPRECATED */
238 if ( _m_msg ) {
239 /*rtp_free_msg(_m_session, _m_msg);
240 _m_msg = NULL;*/
241 printf("Timestamp: %d\n", _m_msg->_header->_timestamp);
242 }
243
244 usleep ( 10000 );
245 }
246
247 if ( _m_msg->_header ) {
248 rtp_header_print ( _m_msg->_header );
249 }
250 if ( _m_msg->_ext_header ){
251 print_ext_header_info(_m_msg->_ext_header);
252 }
253
254 //print_session_stats ( _m_session );
255
256
257 //printf ( "Payload: ( %d ) \n%s\n", _m_msg->_length, _m_msg->_data );
258
259
260 } else if ( find_arg_simple ( _list, "-s" ) != FAILURE ) {
261 ip = find_arg_duble ( _list, "-d" );
262
263 if ( ip == NULL ) {
264 pthread_mutex_destroy ( &_mutex );
265 return FAILURE;
266 }
267
268 const char* _port = find_arg_duble ( _list, "-p" );
269
270 if ( _port != NULL ) {
271 port = atoi ( _port );
272 }
273
274 t_setipport ( ip, port, &Ip_port );
275 printf ( "Remote: %s:%d\n", ip, port );
276
277 Networking_Core* _networking = new_networking(Ip_port.ip, RTP_PORT);
278
279 if ( !_networking ){
280 pthread_mutex_destroy ( &_mutex );
281 return FAILURE;
282 }
283
284 int _socket = _networking->sock;
285
286 _m_session = rtp_init_session ( -1, -1 );
287 rtp_add_receiver( _m_session, &Ip_port );
288 //rtp_add_resolution_marking(_m_session, 1920, 1080);
289 //rtp_add_framerate_marking(_m_session, 1000);
290
291 puts ( "Now sending payload!\n" );
292 uint16_t _first_sequ = _m_session->_sequence_number;
293
294 /* use already defined buffer lenght */
295 while ( 1 ){
296 _m_msg = rtp_msg_new ( _m_session, test_bytes, 300 );
297 rtp_send_msg ( _m_session, _m_msg, _socket );
298 usleep(10000);
299 }
300
301 if ( _m_session->_last_error ) {
302 puts ( _m_session->_last_error );
303 }
304
305 return rtp_terminate_session(_m_session);
306
307 } else {
308 pthread_mutex_destroy ( &_mutex );
309 return FAILURE;
310 }
311 pthread_mutex_destroy ( &_mutex );
312
313 return SUCCESS;
314}
315
316#endif /* _CT_HEADERS */
diff --git a/toxrtp/tests/test_helper.c b/toxrtp/tests/test_helper.c
deleted file mode 100644
index 526b6b38..00000000
--- a/toxrtp/tests/test_helper.c
+++ /dev/null
@@ -1,83 +0,0 @@
1/* test_helper.c
2 *
3 * Tests support. !Red!
4 *
5 *
6 * Copyright (C) 2013 Tox project All Rights Reserved.
7 *
8 * This file is part of Tox.
9 *
10 * Tox is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tox is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include "test_helper.h"
26
27#include <string.h>
28#include <stdlib.h>
29
30arg_t* parse_args ( int argc, char* argv[] )
31{
32 arg_t* _list = calloc(sizeof(arg_t), 1);
33 _list->next = _list->prev = NULL;
34
35 arg_t* it = _list;
36
37 size_t val;
38 for ( val = 0; val < argc; val ++ ) {
39 it->value = argv[val];
40
41 if ( val < argc - 1 ) { /* just about to end */
42 it->next = calloc(sizeof(arg_t), 1);
43 it->next->prev = it;
44 it = it->next;
45 it->next = NULL;
46 }
47 }
48
49 return _list;
50}
51
52int find_arg_simple ( arg_t* _head, const char* _id )
53{
54 arg_t* it = _head;
55
56 int i;
57 for ( i = 1; it != NULL; it = it->next ) {
58 if ( strcmp ( _id, it->value ) == 0 ) {
59 return i;
60 }
61
62 i++;
63 }
64
65 return FAILURE;
66}
67
68const char* find_arg_duble ( arg_t* _head, const char* _id )
69{
70 arg_t* _it;
71 for ( _it = _head; _it != NULL; _it = _it->next ) {
72 if ( strcmp ( _id, _it->value ) == 0 ) {
73 if ( _it->next && _it->next->value[0] != '-' ) { /* exclude option */
74 return _it->next->value;
75 } else {
76 return NULL;
77 }
78 }
79 }
80
81 return NULL;
82}
83
diff --git a/toxrtp/tests/test_helper.h b/toxrtp/tests/test_helper.h
deleted file mode 100644
index de654743..00000000
--- a/toxrtp/tests/test_helper.h
+++ /dev/null
@@ -1,61 +0,0 @@
1/* test_helper.h
2 *
3 * Tests support. !Red!
4 *
5 *
6 * Copyright (C) 2013 Tox project All Rights Reserved.
7 *
8 * This file is part of Tox.
9 *
10 * Tox is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * Tox is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#ifndef _TEST__HELPER_
26#define _TEST__HELPER_
27
28#include "../toxrtp_helper.h"
29
30#define RTP_PORT 31003
31#define RTP_PORT_LISTEN 31001
32
33#define _SLEEP_INTERVAL 1000
34
35typedef struct arg_s {
36 const char* value;
37 struct arg_s* next;
38 struct arg_s* prev;
39
40} arg_t;
41
42
43
44/* Parses arguments into d-list arg_t */
45arg_t* parse_args ( int argc, char* argv[] );
46
47/* Get a single argument ( i.e. ./test -s |find if has 's' >> | find_arg_simple(_t, "-s") )
48 * A little error checking, of course, returns FAILURE if not found and if found returns position
49 * where it's found.
50 */
51int find_arg_simple ( arg_t* _head, const char* _id );
52
53/* Get a single argument ( i.e. ./test -d 127.0.0.1 |get 'd' value >> | find_arg_duble(_t, "-d") )
54 * A little error checking, of course, returns NULL if not found and if found returns value
55 * of that argument ( i.e. '127.0.0.1').
56 */
57const char* find_arg_duble ( arg_t* _head, const char* _id );
58
59#endif /* _TEST__HELPER_ */
60
61
diff --git a/toxrtp/toxrtp.c b/toxrtp/toxrtp.c
deleted file mode 100644
index 6844b0b1..00000000
--- a/toxrtp/toxrtp.c
+++ /dev/null
@@ -1,693 +0,0 @@
1/* rtp_impl.c
2 *
3 * Rtp implementation includes rtp_session_s struct which is a session identifier.
4 * It contains session information and it's a must for every session.
5 * It's best if you don't touch any variable directly but use callbacks to do so. !Red!
6 *
7 *
8 * Copyright (C) 2013 Tox project All Rights Reserved.
9 *
10 * This file is part of Tox.
11 *
12 * Tox is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * Tox is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
24 *
25 */
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif /* HAVE_CONFIG_H */
30
31#include "toxrtp.h"
32#include "toxrtp_message.h"
33#include "toxrtp_helper.h"
34#include <assert.h>
35#include <pthread.h>
36#include "../toxcore/util.h"
37#include "../toxcore/network.h"
38
39/* Some defines */
40#define PAYLOAD_ID_VALUE_OPUS 1
41#define PAYLOAD_ID_VALUE_VP8 2
42
43#define size_32 4
44/* End of defines */
45
46#ifdef _USE_ERRORS
47#include "toxrtp_error_id.h"
48#endif /* _USE_ERRORS */
49
50static const uint32_t _payload_table[] = /* PAYLOAD TABLE */
51{
52 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */
53 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */
54 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */
55 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70-79 */
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */
61 0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_OPUS, 0, 0, 0, /* 90-99 */
62 0, 0, 0, 0, 0, 0, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, /* 100-109 */
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
64 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */
65};
66
67/* Current compatibility solution */
68int m_sendpacket(Networking_Core* _core_handler, void *ip_port, uint8_t *data, uint32_t length)
69{
70 return sendpacket(_core_handler, *((IP_Port*) ip_port), data, length);
71}
72
73rtp_session_t* rtp_init_session ( int max_users, int _multi_session )
74{
75#ifdef _USE_ERRORS
76 REGISTER_RTP_ERRORS
77#endif /* _USE_ERRORS */
78
79 rtp_session_t* _retu = calloc(sizeof(rtp_session_t), 1);
80 assert(_retu);
81
82 _retu->_dest_list = _retu->_last_user = NULL;
83
84 _retu->_max_users = max_users;
85 _retu->_packets_recv = 0;
86 _retu->_packets_sent = 0;
87 _retu->_bytes_sent = 0;
88 _retu->_bytes_recv = 0;
89 _retu->_last_error = NULL;
90 _retu->_packet_loss = 0;
91
92 /*
93 * SET HEADER FIELDS
94 */
95
96 _retu->_version = RTP_VERSION; /* It's always 2 */
97 _retu->_padding = 0; /* If some additional data is needed about
98 * the packet */
99 _retu->_extension = 0; /* If extension to header is needed */
100 _retu->_cc = 1; /* It basically represents amount of contributors */
101 _retu->_csrc = NULL; /* Container */
102 _retu->_ssrc = t_random ( -1 );
103 _retu->_marker = 0;
104 _retu->_payload_type = 0; /* You should specify payload type */
105
106 /* Sequence starts at random number and goes to _MAX_SEQU_NUM */
107 _retu->_sequence_number = t_random ( _MAX_SEQU_NUM );
108 _retu->_last_sequence_number = _retu->_sequence_number; /* Do not touch this variable */
109
110 _retu->_initial_time = t_time(); /* In seconds */
111 assert(_retu->_initial_time);
112 _retu->_time_elapsed = 0; /* In seconds */
113
114 _retu->_ext_header = NULL; /* When needed allocate */
115 _retu->_exthdr_framerate = -1;
116 _retu->_exthdr_resolution = -1;
117
118 _retu->_csrc = calloc(sizeof(uint32_t), 1);
119 assert(_retu->_csrc);
120
121 _retu->_csrc[0] = _retu->_ssrc; /* Set my ssrc to the list receive */
122
123 _retu->_prefix_length = 0;
124 _retu->_prefix = NULL;
125
126 _retu->_multi_session = _multi_session;
127
128 /* Initial */
129 _retu->_current_framerate = 0;
130
131
132 _retu->_oldest_msg = _retu->_last_msg = NULL;
133
134 pthread_mutex_init(&_retu->_mutex, NULL);
135 /*
136 *
137 */
138 return _retu;
139}
140
141int rtp_terminate_session ( rtp_session_t* _session )
142{
143 if ( !_session )
144 return FAILURE;
145
146 if ( _session->_dest_list ){
147 rtp_dest_list_t* _fordel = NULL;
148 rtp_dest_list_t* _tmp = _session->_dest_list;
149
150 while( _tmp ){
151 _fordel = _tmp;
152 _tmp = _tmp->next;
153 free(_fordel);
154 }
155 }
156
157 if ( _session->_ext_header )
158 free ( _session->_ext_header );
159
160 if ( _session->_csrc )
161 free ( _session->_csrc );
162
163 if ( _session->_prefix )
164 free ( _session->_prefix );
165
166 pthread_mutex_destroy(&_session->_mutex);
167
168 /* And finally free session */
169 free ( _session );
170
171 return SUCCESS;
172}
173
174uint16_t rtp_get_resolution_marking_height ( rtp_ext_header_t* _header, uint32_t _position )
175{
176 if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION )
177 return _header->_hd_ext[_position];
178 else
179 return 0;
180}
181
182uint16_t rtp_get_resolution_marking_width ( rtp_ext_header_t* _header, uint32_t _position )
183{
184 if ( _header->_ext_type & RTP_EXT_TYPE_RESOLUTION )
185 return ( _header->_hd_ext[_position] >> 16 );
186 else
187 return 0;
188}
189
190void rtp_free_msg ( rtp_session_t* _session, rtp_msg_t* _message )
191{
192 free ( _message->_data );
193
194 if ( !_session ){
195 free ( _message->_header->_csrc );
196 if ( _message->_ext_header ){
197 free ( _message->_ext_header->_hd_ext );
198 free ( _message->_ext_header );
199 }
200 } else {
201 if ( _session->_csrc != _message->_header->_csrc )
202 free ( _message->_header->_csrc );
203 if ( _message->_ext_header && _session->_ext_header != _message->_ext_header ) {
204 free ( _message->_ext_header->_hd_ext );
205 free ( _message->_ext_header );
206 }
207 }
208
209 free ( _message->_header );
210 free ( _message );
211}
212
213rtp_header_t* rtp_build_header ( rtp_session_t* _session )
214{
215 rtp_header_t* _retu;
216 _retu = calloc ( sizeof * _retu, 1 );
217 assert(_retu);
218
219 rtp_header_add_flag_version ( _retu, _session->_version );
220 rtp_header_add_flag_padding ( _retu, _session->_padding );
221 rtp_header_add_flag_extension ( _retu, _session->_extension );
222 rtp_header_add_flag_CSRC_count ( _retu, _session->_cc );
223 rtp_header_add_setting_marker ( _retu, _session->_marker );
224 rtp_header_add_setting_payload ( _retu, _session->_payload_type );
225
226 _retu->_sequence_number = _session->_sequence_number;
227 _session->_time_elapsed = t_time() - _session->_initial_time;
228 _retu->_timestamp = t_time();
229 _retu->_ssrc = _session->_ssrc;
230
231 if ( _session->_cc > 0 ) {
232 _retu->_csrc = calloc(sizeof(uint32_t), _session->_cc);
233 assert(_retu->_csrc);
234
235 int i;
236
237 for ( i = 0; i < _session->_cc; i++ ) {
238 _retu->_csrc[i] = _session->_csrc[i];
239 }
240 } else {
241 _retu->_csrc = NULL;
242 }
243
244 _retu->_length = _MIN_HEADER_LENGTH + ( _session->_cc * size_32 );
245
246 return _retu;
247}
248
249void rtp_set_payload_type ( rtp_session_t* _session, uint8_t _payload_value )
250{
251 _session->_payload_type = _payload_value;
252}
253uint32_t rtp_get_payload_type ( rtp_session_t* _session )
254{
255 return _payload_table[_session->_payload_type];
256}
257
258int rtp_add_receiver ( rtp_session_t* _session, tox_IP_Port* _dest )
259{
260 if ( !_session )
261 return FAILURE;
262
263 rtp_dest_list_t* _new_user = calloc(sizeof(rtp_dest_list_t), 1);
264 assert(_new_user);
265
266 _new_user->next = NULL;
267 _new_user->_dest = *_dest;
268
269 if ( _session->_last_user == NULL ) { /* New member */
270 _session->_dest_list = _session->_last_user = _new_user;
271
272 } else { /* Append */
273 _session->_last_user->next = _new_user;
274 _session->_last_user = _new_user;
275 }
276
277 return SUCCESS;
278}
279
280int rtp_send_msg ( rtp_session_t* _session, rtp_msg_t* _msg, void* _core_handler )
281{
282 if ( !_msg || _msg->_data == NULL || _msg->_length <= 0 ) {
283 t_perror ( RTP_ERROR_EMPTY_MESSAGE );
284 return FAILURE;
285 }
286
287 int _last;
288 unsigned long long _total = 0;
289
290 size_t _length = _msg->_length;
291 uint8_t _send_data [ MAX_UDP_PACKET_SIZE ];
292
293 uint16_t _prefix_length = _session->_prefix_length;
294
295 _send_data[0] = 70;
296
297 if ( _session->_prefix && _length + _prefix_length < MAX_UDP_PACKET_SIZE ) {
298 /*t_memcpy(_send_data, _session->_prefix, _prefix_length);*/
299 t_memcpy ( _send_data + 1, _msg->_data, _length );
300 } else {
301 t_memcpy ( _send_data + 1, _msg->_data, _length );
302 }
303
304 /* Set sequ number */
305 if ( _session->_sequence_number >= _MAX_SEQU_NUM ) {
306 _session->_sequence_number = 0;
307 } else {
308 _session->_sequence_number++;
309 }
310
311 /* Start sending loop */
312 rtp_dest_list_t* _it;
313 for ( _it = _session->_dest_list; _it != NULL; _it = _it->next ) {
314
315 _last = m_sendpacket ( _core_handler, &_it->_dest, _send_data, _length + 1);
316
317 if ( _last < 0 ) {
318 t_perror ( RTP_ERROR_STD_SEND_FAILURE );
319 printf("Stderror: %s", strerror(errno));
320 } else {
321 _session->_packets_sent ++;
322 _total += _last;
323 }
324
325 }
326
327 rtp_free_msg ( _session, _msg );
328 _session->_bytes_sent += _total;
329 return SUCCESS;
330}
331
332rtp_msg_t* rtp_recv_msg ( rtp_session_t* _session )
333{
334 if ( !_session )
335 return NULL;
336
337 rtp_msg_t* _retu = _session->_oldest_msg;
338
339 pthread_mutex_lock(&_session->_mutex);
340
341 if ( _retu )
342 _session->_oldest_msg = _retu->_next;
343
344 if ( !_session->_oldest_msg )
345 _session->_last_msg = NULL;
346
347 pthread_mutex_unlock(&_session->_mutex);
348
349 return _retu;
350}
351
352void rtp_store_msg ( rtp_session_t* _session, rtp_msg_t* _msg )
353{
354 if ( rtp_check_late_message(_session, _msg) < 0 ) {
355 rtp_register_msg(_session, _msg);
356 }
357
358 pthread_mutex_lock(&_session->_mutex);
359
360 if ( _session->_last_msg ) {
361 _session->_last_msg->_next = _msg;
362 _session->_last_msg = _msg;
363 } else {
364 _session->_last_msg = _session->_oldest_msg = _msg;
365 }
366
367 pthread_mutex_unlock(&_session->_mutex);
368 return;
369}
370
371int rtp_release_session_recv ( rtp_session_t* _session )
372{
373 if ( !_session ){
374 return FAILURE;
375 }
376
377 rtp_msg_t* _tmp,* _it;
378
379 pthread_mutex_lock(&_session->_mutex);
380
381 for ( _it = _session->_oldest_msg; _it; _it = _tmp ){
382 _tmp = _it->_next;
383 rtp_free_msg(_session, _it);
384 }
385
386 _session->_last_msg = _session->_oldest_msg = NULL;
387
388 pthread_mutex_unlock(&_session->_mutex);
389
390 return SUCCESS;
391}
392
393rtp_msg_t* rtp_msg_new ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length )
394{
395 if ( !_session )
396 return NULL;
397
398 uint8_t* _from_pos;
399 rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1);
400 assert(_retu);
401
402 /* Sets header values and copies the extension header in _retu */
403 _retu->_header = rtp_build_header ( _session ); /* It allocates memory and all */
404 _retu->_ext_header = _session->_ext_header;
405
406 uint32_t _total_lenght = _length + _retu->_header->_length;
407
408 if ( _retu->_ext_header ) {
409
410 _total_lenght += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
411 /* Allocate Memory for _retu->_data */
412 _retu->_data = calloc ( sizeof _retu->_data, _total_lenght );
413 assert(_retu->_data);
414
415 _from_pos = rtp_add_header ( _retu->_header, _retu->_data );
416 _from_pos = rtp_add_extention_header ( _retu->_ext_header, _from_pos + 1 );
417 } else {
418 /* Allocate Memory for _retu->_data */
419 _retu->_data = calloc ( sizeof _retu->_data, _total_lenght );
420 assert(_retu->_data);
421
422 _from_pos = rtp_add_header ( _retu->_header, _retu->_data );
423 }
424
425 /*
426 * Parses the extension header into the message
427 * Of course if any
428 */
429
430 /* Appends _data on to _retu->_data */
431 t_memcpy ( _from_pos + 1, _data, _length );
432
433 _retu->_length = _total_lenght;
434
435 _retu->_next = NULL;
436
437 return _retu;
438}
439
440rtp_msg_t* rtp_msg_parse ( rtp_session_t* _session, const uint8_t* _data, uint32_t _length )
441{
442 rtp_msg_t* _retu = calloc(sizeof(rtp_msg_t), 1);
443 assert(_retu);
444
445 _retu->_header = rtp_extract_header ( _data, _length ); /* It allocates memory and all */
446 if ( !_retu->_header ){
447 free(_retu);
448 return NULL;
449 }
450
451 _retu->_length = _length - _retu->_header->_length;
452
453 uint16_t _from_pos = _retu->_header->_length;
454
455
456 if ( rtp_header_get_flag_extension ( _retu->_header ) ) {
457 _retu->_ext_header = rtp_extract_ext_header ( _data + _from_pos, _length );
458 if ( _retu->_ext_header ){
459 _retu->_length -= ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
460 _from_pos += ( _MIN_EXT_HEADER_LENGTH + _retu->_ext_header->_ext_len * size_32 );
461 } else {
462 free (_retu->_ext_header);
463 free (_retu->_header);
464 free (_retu);
465 return NULL;
466 }
467 } else {
468 _retu->_ext_header = NULL;
469 }
470
471 /* Get the payload */
472 _retu->_data = calloc ( sizeof ( uint8_t ), _retu->_length );
473 assert(_retu->_data);
474
475 t_memcpy ( _retu->_data, _data + _from_pos, _length - _from_pos );
476
477 _retu->_next = NULL;
478
479
480 if ( _session && !_session->_multi_session && rtp_check_late_message(_session, _retu) < 0 ){
481 rtp_register_msg(_session, _retu);
482 }
483
484 return _retu;
485}
486
487int rtp_check_late_message (rtp_session_t* _session, rtp_msg_t* _msg)
488{
489 /*
490 * Check Sequence number. If this new msg has lesser number then the _session->_last_sequence_number
491 * it shows that the message came in late
492 */
493 if ( _msg->_header->_sequence_number < _session->_last_sequence_number &&
494 _msg->_header->_timestamp < _session->_current_timestamp
495 ) {
496 return SUCCESS; /* Drop the packet. You can check if the packet dropped by checking _packet_loss increment. */
497 }
498 return FAILURE;
499}
500
501void rtp_register_msg ( rtp_session_t* _session, rtp_msg_t* _msg )
502{
503 _session->_last_sequence_number = _msg->_header->_sequence_number;
504 _session->_current_timestamp = _msg->_header->_timestamp;
505}
506
507
508int rtp_add_resolution_marking ( rtp_session_t* _session, uint16_t _width, uint16_t _height )
509{
510 if ( !_session )
511 return FAILURE;
512
513 rtp_ext_header_t* _ext_header = _session->_ext_header;
514 _session->_exthdr_resolution = 0;
515
516 if ( ! ( _ext_header ) ) {
517 _session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1);
518 assert(_session->_ext_header);
519
520 _session->_extension = 1;
521 _session->_ext_header->_ext_len = 1;
522 _ext_header = _session->_ext_header;
523 _session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1);
524 assert(_session->_ext_header->_hd_ext);
525
526 } else { /* If there is need for more headers this will be needed to change */
527 if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION) ){
528 uint32_t _exthdr_framerate = _ext_header->_hd_ext[_session->_exthdr_framerate];
529 /* it's position is at 2nd place by default */
530 _session->_exthdr_framerate ++;
531
532 /* Update length */
533 _ext_header->_ext_len++;
534
535 /* Allocate the value */
536 _ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len);
537 assert(_ext_header->_hd_ext);
538
539 /* Reset other values */
540 _ext_header->_hd_ext[_session->_exthdr_framerate] = _exthdr_framerate;
541 }
542 }
543
544 /* Add flag */
545 _ext_header->_ext_type |= RTP_EXT_TYPE_RESOLUTION;
546
547 _ext_header->_hd_ext[_session->_exthdr_resolution] = _width << 16 | ( uint32_t ) _height;
548
549 return SUCCESS;
550}
551
552int rtp_remove_resolution_marking ( rtp_session_t* _session )
553{
554 if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) {
555 t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
556 return FAILURE;
557 }
558
559 if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_RESOLUTION ) ) {
560 t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
561 return FAILURE;
562 }
563
564 _session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_RESOLUTION; /* Remove the flag */
565 _session->_exthdr_resolution = -1; /* Remove identifier */
566
567 /* Check if extension is empty */
568 if ( _session->_ext_header->_ext_type == 0 ){
569
570 free ( _session->_ext_header->_hd_ext );
571 free ( _session->_ext_header );
572
573 _session->_ext_header = NULL; /* It's very important */
574 _session->_extension = 0;
575
576 } else {
577 _session->_ext_header->_ext_len --;
578
579 /* this will also be needed to change if there are more than 2 headers */
580 if ( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ){
581 memcpy(_session->_ext_header->_hd_ext + 1, _session->_ext_header->_hd_ext, _session->_ext_header->_ext_len);
582 _session->_exthdr_framerate = 0;
583 _session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len );
584 assert(_session->_ext_header->_hd_ext);
585 }
586 }
587
588 return SUCCESS;
589}
590
591int rtp_add_framerate_marking ( rtp_session_t* _session, uint32_t _value )
592{
593 if ( !_session )
594 return FAILURE;
595
596 rtp_ext_header_t* _ext_header = _session->_ext_header;
597 _session->_exthdr_framerate = 0;
598
599 if ( ! ( _ext_header ) ) {
600 _session->_ext_header = calloc (sizeof(rtp_ext_header_t), 1);
601 assert(_session->_ext_header);
602
603 _session->_extension = 1;
604 _session->_ext_header->_ext_len = 1;
605 _ext_header = _session->_ext_header;
606 _session->_ext_header->_hd_ext = calloc(sizeof(uint32_t), 1);
607 assert(_session->_ext_header->_hd_ext);
608 } else { /* If there is need for more headers this will be needed to change */
609 if ( !(_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE) ){
610 /* it's position is at 2nd place by default */
611 _session->_exthdr_framerate ++;
612
613 /* Update length */
614 _ext_header->_ext_len++;
615
616 /* Allocate the value */
617 _ext_header->_hd_ext = realloc(_ext_header->_hd_ext, sizeof(rtp_ext_header_t) * _ext_header->_ext_len);
618 assert(_ext_header->_hd_ext);
619
620 }
621 }
622
623 /* Add flag */
624 _ext_header->_ext_type |= RTP_EXT_TYPE_FRAMERATE;
625
626 _ext_header->_hd_ext[_session->_exthdr_framerate] = _value;
627
628 return SUCCESS;
629}
630
631
632int rtp_remove_framerate_marking ( rtp_session_t* _session )
633{
634 if ( _session->_extension == 0 || ! ( _session->_ext_header ) ) {
635 t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
636 return FAILURE;
637 }
638
639 if ( !( _session->_ext_header->_ext_type & RTP_EXT_TYPE_FRAMERATE ) ) {
640 t_perror ( RTP_ERROR_INVALID_EXTERNAL_HEADER );
641 return FAILURE;
642 }
643
644 _session->_ext_header->_ext_type &= ~RTP_EXT_TYPE_FRAMERATE; /* Remove the flag */
645 _session->_exthdr_framerate = -1; /* Remove identifier */
646 _session->_ext_header->_ext_len --;
647
648 /* Check if extension is empty */
649 if ( _session->_ext_header->_ext_type == 0 ){
650
651 free ( _session->_ext_header->_hd_ext );
652 free ( _session->_ext_header );
653
654 _session->_ext_header = NULL; /* It's very important */
655 _session->_extension = 0;
656
657 } else if ( !_session->_ext_header->_ext_len ) {
658
659 /* this will also be needed to change if there are more than 2 headers */
660 _session->_ext_header->_hd_ext = realloc( _session->_ext_header->_hd_ext, sizeof( rtp_ext_header_t ) * _session->_ext_header->_ext_len );
661 assert(_session->_ext_header->_hd_ext);
662
663 }
664
665 return SUCCESS;
666}
667
668uint32_t rtp_get_framerate_marking ( rtp_ext_header_t* _header )
669{
670 if ( _header->_ext_len == 1 ){
671 return _header->_hd_ext[0];
672 } else {
673 return _header->_hd_ext[1];
674 }
675}
676
677int rtp_set_prefix ( rtp_session_t* _session, uint8_t* _prefix, uint16_t _prefix_length )
678{
679 if ( !_session )
680 return FAILURE;
681
682 if ( _session->_prefix ) {
683 free ( _session->_prefix );
684 }
685
686 _session->_prefix = calloc ( ( sizeof * _session->_prefix ), _prefix_length );
687 assert(_session->_prefix);
688
689 t_memcpy ( _session->_prefix, _prefix, _prefix_length );
690 _session->_prefix_length = _prefix_length;
691
692 return SUCCESS;
693}
diff --git a/toxrtp/toxrtp.h b/toxrtp/toxrtp.h
deleted file mode 100644
index 0aa89993..00000000
--- a/toxrtp/toxrtp.h
+++ /dev/null
@@ -1,188 +0,0 @@
1/* rtp_impl.h
2 *
3 * Rtp implementation includes rtp_session_s struct which is a session identifier.
4 * It contains session information and it's a must for every session.
5 * It's best if you don't touch any variable directly but use callbacks to do so. !Red!
6 *
7 *
8 * Copyright (C) 2013 Tox project All Rights Reserved.
9 *
10 * This file is part of Tox.
11 *
12 * Tox is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * Tox is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
24 *
25 */
26
27
28#ifndef _RTP__IMPL_H_
29#define _RTP__IMPL_H_
30
31#define RTP_VERSION 2
32#include <inttypes.h>
33#include "tox.h"
34#include <pthread.h>
35/* Extension header flags */
36#define RTP_EXT_TYPE_RESOLUTION 0x01
37#define RTP_EXT_TYPE_FRAMERATE 0x02
38
39/* Some defines */
40
41#define RTP_PACKET 70
42
43/* Payload identifiers */
44
45/* Audio */
46#define _PAYLOAD_OPUS 96
47
48/* Video */
49#define _PAYLOAD_VP8 106
50
51/* End of Payload identifiers */
52
53/* End of defines */
54
55
56/* Our main session descriptor.
57 * It measures the session variables and controls
58 * the entire session. There are functions for manipulating
59 * the session so tend to use those instead of directly accessing
60 * session parameters.
61 */
62typedef struct rtp_session_s {
63 uint8_t _version;
64 uint8_t _padding;
65 uint8_t _extension;
66 uint8_t _cc;
67 uint8_t _marker;
68 uint8_t _payload_type;
69 uint16_t _sequence_number; /* Set when sending */
70 uint16_t _last_sequence_number; /* Check when recving msg */
71 uint32_t _initial_time;
72 uint32_t _time_elapsed;
73 uint32_t _current_timestamp;
74 uint32_t _ssrc;
75 uint32_t *_csrc;
76
77
78 /* If some additional data must be sent via message
79 * apply it here. Only by allocating this member you will be
80 * automatically placing it within a message.
81 */
82
83 struct rtp_ext_header_s *_ext_header;
84 /* External header identifiers */
85 int _exthdr_resolution;
86 int _exthdr_framerate;
87
88 int _max_users; /* -1 undefined */
89
90 uint64_t _packets_sent; /* measure packets */
91 uint64_t _packets_recv;
92
93 uint64_t _bytes_sent;
94 uint64_t _bytes_recv;
95
96 uint64_t _packet_loss;
97
98 const char *_last_error;
99
100 struct rtp_dest_list_s *_dest_list;
101 struct rtp_dest_list_s *_last_user; /* a tail for faster appending */
102
103 struct rtp_msg_s *_oldest_msg;
104 struct rtp_msg_s *_last_msg; /* tail */
105
106 uint16_t _prefix_length;
107 uint8_t *_prefix;
108
109 /* Specifies multiple session use.
110 * When using one session it uses default value ( -1 )
111 * Otherwise it's set to 1 and rtp_register_msg () is required
112 */
113 int _multi_session;
114
115 uint32_t _current_framerate;
116
117 pthread_mutex_t _mutex;
118
119} rtp_session_t;
120
121
122/*
123 * Now i don't believe we need to store this _from thing every time
124 * since we have csrc table but will leave it like this for a while
125 */
126
127
128void rtp_free_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg );
129int rtp_release_session_recv ( rtp_session_t *_session );
130
131/* Functions handling receiving */
132struct rtp_msg_s *rtp_recv_msg ( rtp_session_t *_session );
133void rtp_store_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg );
134
135/*
136 * rtp_msg_parse() stores headers separately from the payload data
137 * and so the _length variable is set accordingly
138 */
139struct rtp_msg_s *rtp_msg_parse ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length );
140
141int rtp_check_late_message (rtp_session_t *_session, struct rtp_msg_s *_msg);
142void rtp_register_msg ( rtp_session_t *_session, struct rtp_msg_s * );
143
144/* Functions handling sending */
145int rtp_send_msg ( rtp_session_t *_session, struct rtp_msg_s *_msg, void *_core_handler );
146
147/*
148 * rtp_msg_new() stores headers and payload data in one container ( _data )
149 * and the _length is set accordingly. Returned message is used for sending only
150 * so there is not much use of the headers there
151 */
152struct rtp_msg_s *rtp_msg_new ( rtp_session_t *_session, const uint8_t *_data, uint32_t _length );
153
154
155/* Convenient functions for creating a header */
156struct rtp_header_s *rtp_build_header ( rtp_session_t *_session );
157
158/* Functions handling session control */
159
160/* Handling an rtp packet */
161/* int rtp_handlepacket(uint8_t * packet, uint32_t length, IP_Port source); */
162
163/* Session initiation and termination.
164 * Set _multi_session to -1 if not using multiple sessions
165 */
166rtp_session_t *rtp_init_session ( int _max_users, int _multi_session );
167int rtp_terminate_session ( rtp_session_t *_session );
168
169/* Adding receiver */
170int rtp_add_receiver ( rtp_session_t *_session, tox_IP_Port *_dest );
171
172/* Convenient functions for marking the resolution */
173int rtp_add_resolution_marking ( rtp_session_t *_session, uint16_t _width, uint16_t _height );
174int rtp_remove_resolution_marking ( rtp_session_t *_session );
175uint16_t rtp_get_resolution_marking_height ( struct rtp_ext_header_s *_header, uint32_t _position );
176uint16_t rtp_get_resolution_marking_width ( struct rtp_ext_header_s *_header, uint32_t _position );
177
178int rtp_add_framerate_marking ( rtp_session_t *_session, uint32_t _value );
179int rtp_remove_framerate_marking ( rtp_session_t *_session );
180uint32_t rtp_get_framerate_marking ( struct rtp_ext_header_s *_header );
181/* Convenient functions for marking the payload */
182void rtp_set_payload_type ( rtp_session_t *_session, uint8_t _payload_value );
183uint32_t rtp_get_payload_type ( rtp_session_t *_session );
184
185/* When using RTP in core be sure to set prefix when sending via rtp_send_msg */
186int rtp_set_prefix ( rtp_session_t *_session, uint8_t *_prefix, uint16_t _prefix_length );
187
188#endif /* _RTP__IMPL_H_ */
diff --git a/toxrtp/toxrtp_error.c b/toxrtp/toxrtp_error.c
deleted file mode 100644
index 3a7ff9a5..00000000
--- a/toxrtp/toxrtp_error.c
+++ /dev/null
@@ -1,68 +0,0 @@
1
2#ifdef HAVE_CONFIG_H
3#include "config.h"
4#endif /* HAVE_CONFIG_H */
5
6#include "toxrtp_error.h"
7#include "toxrtp_helper.h"
8
9#include <stdio.h>
10#include <stdarg.h>
11#include <string.h>
12#include <stdlib.h>
13#include <assert.h>
14
15typedef struct rtp_error_s {
16 char* _message;
17 int _id;
18
19} rtp_error_t;
20
21static rtp_error_t* _register = NULL;
22static size_t _it = 0;
23
24void t_rtperr_register ( int _id, const char* _info )
25{
26 size_t _info_size = strlen ( _info );
27
28 if ( !_register ) {
29 _register = calloc ( sizeof ( rtp_error_t ), 1 );
30 } else {
31 _register = realloc ( _register, sizeof ( rtp_error_t ) * ( _it + 1 ) );
32 }
33 assert(_register);
34
35
36 rtp_error_t* _current = & _register[_it];
37
38 _current->_id = _id;
39 _current->_message = calloc ( sizeof(char), _info_size );
40 assert(_current->_message);
41
42 t_memcpy ( (uint8_t*)_current->_message, (const uint8_t*)_info, _info_size );
43 _it ++;
44}
45
46const char* t_rtperr ( int _errno )
47{
48 if ( !_register )
49 return "Unregistered";
50
51 uint32_t _i;
52
53 for ( _i = _it; _i--; ) {
54 if ( _register[_i]._id == _errno ) {
55 return _register[_i]._message;
56 }
57 }
58
59 return "Invalid error id!";
60}
61
62void t_rtperr_print ( const char* _val, ... )
63{
64 va_list _args;
65 va_start ( _args, _val );
66 vfprintf ( stderr, _val, _args );
67 va_end ( _args );
68}
diff --git a/toxrtp/toxrtp_error.h b/toxrtp/toxrtp_error.h
deleted file mode 100644
index 0e017246..00000000
--- a/toxrtp/toxrtp_error.h
+++ /dev/null
@@ -1,25 +0,0 @@
1#ifndef _RTP_ERROR_
2#define _RTP_ERROR_
3
4#define PRINT_FORMAT "Error %d: %s at %s:%d\n"
5#define PRINT_ARGS( _errno ) _errno, t_rtperr(_errno), __FILE__, __LINE__
6
7
8const char* t_rtperr ( int _errno );
9void t_rtperr_register ( int _id, const char* _info );
10
11void t_invoke_error ( int _id );
12void t_rtperr_print ( const char* _val, ... );
13
14
15#ifdef _USE_ERRORS
16#define t_perror( _errno ) t_rtperr_print ( PRINT_FORMAT, PRINT_ARGS ( _errno ) )
17#else
18#define t_perror( _errno )do { } while(0)
19#endif /* _USE_ERRORS */
20
21#ifdef _STDIO_H
22#define t_errexit( _errno ) exit(-_errno)
23#endif /* _STDIO_H */
24
25#endif /* _RTP_ERROR_ */
diff --git a/toxrtp/toxrtp_error_id.h b/toxrtp/toxrtp_error_id.h
deleted file mode 100644
index 201aa936..00000000
--- a/toxrtp/toxrtp_error_id.h
+++ /dev/null
@@ -1,32 +0,0 @@
1#ifndef _RTP_ERROR_ID_
2#define _RTP_ERROR_ID_
3
4#include "toxrtp_error.h"
5
6typedef enum error_s {
7 RTP_ERROR_PACKET_DROPED = 1,
8 RTP_ERROR_EMPTY_MESSAGE,
9 RTP_ERROR_STD_SEND_FAILURE,
10 RTP_ERROR_NO_EXTERNAL_HEADER,
11 RTP_ERROR_INVALID_EXTERNAL_HEADER,
12 RTP_ERROR_HEADER_PARSING,
13 RTP_ERROR_PAYLOAD_NULL,
14 RTP_ERROR_PAYLOAD_INVALID,
15
16} error_t;
17
18
19/* Only needed to be called once */
20#ifndef REGISTER_RTP_ERRORS
21#define REGISTER_RTP_ERRORS \
22 t_rtperr_register( RTP_ERROR_PACKET_DROPED, "Ivalid sequence number, packet is late" ); \
23 t_rtperr_register( RTP_ERROR_EMPTY_MESSAGE, "Tried to send an empty message" ); \
24 t_rtperr_register( RTP_ERROR_STD_SEND_FAILURE, "Failed call function: sendto" ); \
25 t_rtperr_register( RTP_ERROR_NO_EXTERNAL_HEADER, "While parsing external header" ); \
26 t_rtperr_register( RTP_ERROR_INVALID_EXTERNAL_HEADER, "While parsing external header" ); \
27 t_rtperr_register( RTP_ERROR_HEADER_PARSING, "While parsing header" ); \
28 t_rtperr_register( RTP_ERROR_PAYLOAD_NULL, "Payload is NULL" ); \
29 t_rtperr_register( RTP_ERROR_PAYLOAD_INVALID, "Invalid payload size" );
30#endif /* REGISTER_RTP_ERRORS */
31
32#endif /* _RTP_ERROR_ID_ */
diff --git a/toxrtp/toxrtp_helper.c b/toxrtp/toxrtp_helper.c
deleted file mode 100644
index 6f952359..00000000
--- a/toxrtp/toxrtp_helper.c
+++ /dev/null
@@ -1,208 +0,0 @@
1/* rtp_helper.c
2*
3* Has some standard functions. !Red!
4*
5*
6* Copyright (C) 2013 Tox project All Rights Reserved.
7*
8* This file is part of Tox.
9*
10* Tox is free software: you can redistribute it and/or modify
11* it under the terms of the GNU General Public License as published by
12* the Free Software Foundation, either version 3 of the License, or
13* (at your option) any later version.
14*
15* Tox is distributed in the hope that it will be useful,
16* but WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18* GNU General Public License for more details.
19*
20* You should have received a copy of the GNU General Public License
21* along with Tox. If not, see <http://www.gnu.org/licenses/>.
22*
23*/
24
25
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif /* HAVE_CONFIG_H */
30
31#include "toxrtp_helper.h"
32#include "../toxcore/network.h"
33
34#include <assert.h>
35
36#ifdef WIN
37#include <windows.h>
38#endif /* WIN */
39
40
41static int _seed = 0; /* Not initiated */
42
43int t_setipport ( const char *_ip, unsigned short _port, void *_dest )
44{
45 assert(_dest);
46
47 IP_Port *_dest_c = ( IP_Port * ) _dest;
48 ip_init(&_dest_c->ip, 0);
49
50 IP_Port _ipv6_garbage;
51
52 if ( !addr_resolve(_ip, &_dest_c->ip, &_ipv6_garbage.ip) )
53 return FAILURE;
54
55 _dest_c->port = htons ( _port );
56
57 return SUCCESS;
58}
59
60uint32_t t_random ( uint32_t _max )
61{
62 if ( !_seed ) {
63 srand ( t_time() );
64 _seed++;
65 }
66
67 if ( _max <= 0 ) {
68 return ( unsigned ) rand();
69 } else {
70 return ( unsigned ) rand() % _max;
71 }
72}
73
74void t_memcpy ( uint8_t *_dest, const uint8_t *_source, size_t _size )
75{
76 /*
77 * Using countdown to zero method
78 * It's faster than for(_it = 0; _it < _size; _it++);
79 */
80 size_t _it = _size;
81
82 do {
83 _it--;
84 _dest[_it] = _source[_it];
85 } while ( _it );
86
87}
88
89uint8_t *t_memset ( uint8_t *_dest, uint8_t _valu, size_t _size )
90{
91 /*
92 * Again using countdown to zero method
93 */
94 size_t _it = _size;
95
96 do {
97 _it--;
98 _dest[_it] = _valu;
99 } while ( _it );
100
101 return _dest;
102}
103
104size_t t_memlen ( const uint8_t *_valu)
105{
106 const uint8_t *_it;
107 size_t _retu = 0;
108
109 for ( _it = _valu; *_it; ++_it ) ++_retu;
110
111 return _retu;
112}
113
114uint8_t *t_strallcpy ( const uint8_t *_source ) /* string alloc and copy */
115{
116 assert(_source);
117
118 size_t _length = t_memlen(_source) + 1; /* make space for null character */
119
120 uint8_t *_dest = calloc( sizeof ( uint8_t ), _length );
121 assert(_dest);
122
123 t_memcpy(_dest, _source, _length);
124
125 return _dest;
126}
127
128size_t t_strfind ( const uint8_t *_str, const uint8_t *_substr )
129{
130 size_t _pos = 0;
131 size_t _it, _delit = 0;
132
133 for ( _it = 0; _str[_it] != '\0'; _it++ ) {
134 if ( _str[_it] == _substr[_delit] ) {
135 _pos = _it;
136
137 while ( _str[_it] == _substr[_delit] && _str[_it] != '\0' ) {
138 _it ++;
139 _delit++;
140
141 if ( _substr[_delit] == '\0' ) {
142 return _pos;
143 }
144 }
145
146 _delit = 0;
147 _pos = 0;
148 }
149 }
150
151 return _pos;
152}
153
154#ifdef WIN
155#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
156#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
157#else
158#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
159#endif
160
161struct timezone {
162 int tz_minuteswest; /* minutes W of Greenwich */
163 int tz_dsttime; /* type of dst correction */
164};
165
166int gettimeofday(struct timeval *tv, struct timezone *tz)
167{
168 FILETIME ft;
169 unsigned __int64 tmpres = 0;
170 static int tzflag;
171
172 if (NULL != tv) {
173 GetSystemTimeAsFileTime(&ft);
174
175 tmpres |= ft.dwHighDateTime;
176 tmpres <<= 32;
177 tmpres |= ft.dwLowDateTime;
178
179 /*converting file time to unix epoch*/
180 tmpres -= DELTA_EPOCH_IN_MICROSECS;
181 tmpres /= 10; /*convert into microseconds*/
182 tv->tv_sec = (long)(tmpres / 1000000UL);
183 tv->tv_usec = (long)(tmpres % 1000000UL);
184 }
185
186 if (NULL != tz) {
187 if (!tzflag) {
188 _tzset();
189 tzflag++;
190 }
191
192 tz->tz_minuteswest = _timezone / 60;
193 tz->tz_dsttime = _daylight;
194 }
195
196 return 0;
197}
198#endif /* WIN */
199
200
201uint64_t t_time()
202{
203 struct timeval _tv;
204 gettimeofday(&_tv, NULL);
205 uint64_t _retu_usec = _tv.tv_sec % 1000000; /* get 6 digits an leave space for 3 more */
206 _retu_usec = _retu_usec * 1000 + (_tv.tv_usec / 1000 );
207 return _retu_usec;
208}
diff --git a/toxrtp/toxrtp_helper.h b/toxrtp/toxrtp_helper.h
deleted file mode 100644
index c9bcfcca..00000000
--- a/toxrtp/toxrtp_helper.h
+++ /dev/null
@@ -1,77 +0,0 @@
1/* rtp_helper.h
2*
3* Has some standard functions. !Red!
4*
5*
6* Copyright (C) 2013 Tox project All Rights Reserved.
7*
8* This file is part of Tox.
9*
10* Tox is free software: you can redistribute it and/or modify
11* it under the terms of the GNU General Public License as published by
12* the Free Software Foundation, either version 3 of the License, or
13* (at your option) any later version.
14*
15* Tox is distributed in the hope that it will be useful,
16* but WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18* GNU General Public License for more details.
19*
20* You should have received a copy of the GNU General Public License
21* along with Tox. If not, see <http://www.gnu.org/licenses/>.
22*
23*/
24
25#ifndef _RTP__HELPER_H_
26#define _RTP__HELPER_H_
27
28#include <time.h>
29#include <inttypes.h>
30
31/* Current time, unix format */
32/*#define _time ((uint32_t)time(NULL))*/
33
34#define SUCCESS 0
35#define FAILURE -1
36
37#define _USE_ERRORS
38
39#define RUN_IN_THREAD(_func, _thread_id, _arg_ptr) \
40if ( pthread_create ( &_thread_id, NULL, _func, _arg_ptr ) ) { \
41 pthread_detach ( _thread_id ); \
42}
43
44/* Core adaptation helper */
45int t_setipport ( const char* _ip, unsigned short _port, void* _cont );
46uint32_t t_random ( uint32_t _max );
47
48/* It's a bit faster than the memcpy it self and more optimized for using
49 * a uint8_t since memcpy has optimizations when copying "words" i.e. long type.
50 * Otherwise it just copies char's while we need only uint8_t
51 */
52void t_memcpy ( uint8_t* _dest, const uint8_t* _source, size_t _size );
53
54
55/* This is our memset. It's also a bit faster than the memset for it
56 * does not cast _dest to char* and uses faster loop algorithm.
57 */
58uint8_t* t_memset ( uint8_t* _dest, uint8_t _valu, size_t _size );
59
60/* Get null terminated len */
61size_t t_memlen ( const uint8_t* _valu );
62
63/* finds location of substring */
64size_t t_strfind ( const uint8_t* _str, const uint8_t* _substr );
65
66/* string alloc and copy ( ! must be null terminated ) */
67uint8_t* t_strallcpy ( const uint8_t* _source );
68
69/* Get current time in milliseconds */
70uint64_t t_time();
71
72
73#endif /* _RTP__HELPER_H_ */
74
75
76
77
diff --git a/toxrtp/toxrtp_message.c b/toxrtp/toxrtp_message.c
deleted file mode 100644
index e7f1f2c0..00000000
--- a/toxrtp/toxrtp_message.c
+++ /dev/null
@@ -1,351 +0,0 @@
1/* rtp_message.c
2 *
3 * Rtp Message handler. It handles message/header parsing.
4 * Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red!
5 *
6 *
7 * Copyright (C) 2013 Tox project All Rights Reserved.
8 *
9 * This file is part of Tox.
10 *
11 * Tox is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * Tox is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif /* HAVE_CONFIG_H */
29
30#include "toxrtp_message.h"
31#include "toxrtp.h"
32#include <stdio.h>
33
34#ifdef _USE_ERRORS
35#include "toxrtp_error_id.h"
36#endif /* _USE_ERRORS */
37
38#include <assert.h>
39
40/* Some defines */
41
42/* End of defines */
43
44void rtp_header_print (const rtp_header_t* _header)
45{
46 printf("Header: \n"
47 "Version: %d\n"
48 "Padding: %d\n"
49 "Ext: %d\n"
50 "CC: %d\n"
51 "marker: %d\n"
52 "payload typ:%d\n\n"
53 "sequ num: %d\n"
54 "Timestamp: %d\n"
55 "SSrc: %d\n"
56 "CSrc: %d\n"
57 "Lenght: %d\n"
58 ,rtp_header_get_flag_version(_header)
59 ,rtp_header_get_flag_padding(_header)
60 ,rtp_header_get_flag_extension(_header)
61 ,rtp_header_get_flag_CSRC_count(_header)
62 ,rtp_header_get_setting_marker(_header)
63 ,rtp_header_get_setting_payload_type(_header)
64 ,_header->_sequence_number
65 ,_header->_timestamp
66 ,_header->_ssrc
67 ,_header->_csrc[0]
68 ,_header->_length
69 );
70}
71
72rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes )
73{
74 if ( !_payload ) {
75 t_perror ( RTP_ERROR_PAYLOAD_NULL );
76 return NULL;
77 }
78 const uint8_t* _it = _payload;
79
80 rtp_header_t* _retu = calloc(sizeof(rtp_header_t), 1);
81 assert(_retu);
82
83 _retu->_flags = *_it; ++_it;
84
85 /* This indicates if the first 2 bytes are valid.
86 * Now it my happen that this is out of order but
87 * it cuts down chances of parsing some invalid value
88 */
89 if ( rtp_header_get_flag_version(_retu) != RTP_VERSION ){
90 printf("Invalid version: %d\n", rtp_header_get_flag_version(_retu));
91 //assert(rtp_header_get_flag_version(_retu) == RTP_VERSION);
92 /* Deallocate */
93 //DEALLOCATOR(_retu);
94 //return NULL;
95 }
96
97 /*
98 * Added a check for the size of the header little sooner so
99 * I don't need to parse the other stuff if it's bad
100 */
101 uint8_t cc = rtp_header_get_flag_CSRC_count ( _retu );
102 uint32_t _lenght = _MIN_HEADER_LENGTH + ( cc * 4 );
103
104 if ( _bytes < _lenght ) {
105 t_perror ( RTP_ERROR_PAYLOAD_INVALID );
106 return NULL;
107 }
108
109 if ( cc > 0 ) {
110 _retu->_csrc = calloc ( sizeof ( uint32_t ), cc );
111 assert(_retu->_csrc);
112
113 } else { /* But this should not happen ever */
114 t_perror ( RTP_ERROR_HEADER_PARSING );
115 return NULL;
116 }
117
118
119 _retu->_marker_payload_t = *_it; ++_it;
120 _retu->_length = _lenght;
121 _retu->_sequence_number = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 );
122
123 _it += 2;
124
125 _retu->_timestamp = ( ( uint32_t ) * _it << 24 ) |
126 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
127 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
128 ( * ( _it + 3 ) ) ;
129
130 _it += 4;
131
132 _retu->_ssrc = ( ( uint32_t ) * _it << 24 ) |
133 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
134 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
135 ( ( uint32_t ) * ( _it + 3 ) ) ;
136
137
138 size_t x;
139 for ( x = 0; x < cc; x++ ) {
140 _it += 4;
141 _retu->_csrc[x] = ( ( uint32_t ) * _it << 24 ) |
142 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
143 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
144 ( ( uint32_t ) * ( _it + 3 ) ) ;
145 }
146
147 return _retu;
148}
149
150rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes )
151{
152 if ( !_payload ) {
153 t_perror ( RTP_ERROR_PAYLOAD_NULL );
154 return NULL;
155 }
156
157
158
159 const uint8_t* _it = _payload;
160
161 rtp_ext_header_t* _retu = calloc(sizeof(rtp_ext_header_t), 1);
162 assert(_retu);
163
164 uint16_t _ext_len = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it += 2;
165
166 if ( _bytes < ( _ext_len * sizeof(uint32_t) ) ) {
167 t_perror ( RTP_ERROR_PAYLOAD_INVALID );
168 return NULL;
169 }
170
171 _retu->_ext_len = _ext_len;
172 _retu->_ext_type = ( ( uint16_t ) * _it << 8 ) | * ( _it + 1 ); _it -= 2;
173
174 _retu->_hd_ext = calloc(sizeof(uint32_t), _ext_len);
175 assert(_retu->_hd_ext);
176
177 uint32_t* _hd_ext = _retu->_hd_ext;
178 size_t i;
179 for ( i = 0; i < _ext_len; i++ ) {
180 _it += 4;
181 _hd_ext[i] = ( ( uint32_t ) * _it << 24 ) |
182 ( ( uint32_t ) * ( _it + 1 ) << 16 ) |
183 ( ( uint32_t ) * ( _it + 2 ) << 8 ) |
184 ( ( uint32_t ) * ( _it + 3 ) ) ;
185 }
186
187 return _retu;
188}
189
190uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload )
191{
192 uint8_t cc = rtp_header_get_flag_CSRC_count ( _header );
193
194 uint8_t* _it = _payload;
195
196 *_it = _header->_flags; ++_it;
197 *_it = _header->_marker_payload_t; ++_it;
198
199 *_it = ( _header->_sequence_number >> 8 ); ++_it;
200 *_it = ( _header->_sequence_number ); ++_it;
201
202 uint32_t _timestamp = _header->_timestamp;
203 *_it = ( _timestamp >> 24 ); ++_it;
204 *_it = ( _timestamp >> 16 ); ++_it;
205 *_it = ( _timestamp >> 8 ); ++_it;
206 *_it = ( _timestamp ); ++_it;
207
208 uint32_t _ssrc = _header->_ssrc;
209 *_it = ( _ssrc >> 24 ); ++_it;
210 *_it = ( _ssrc >> 16 ); ++_it;
211 *_it = ( _ssrc >> 8 ); ++_it;
212 *_it = ( _ssrc );
213
214 uint32_t *_csrc = _header->_csrc;
215 size_t x;
216 for ( x = 0; x < cc; x++ ) {
217 ++_it;
218 *_it = ( _csrc[x] >> 24 ); ++_it;
219 *_it = ( _csrc[x] >> 16 ); ++_it;
220 *_it = ( _csrc[x] >> 8 ); ++_it;
221 *_it = ( _csrc[x] );
222 }
223
224 return _it;
225}
226
227uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload )
228{
229 uint8_t* _it = _payload;
230
231 *_it = ( _header->_ext_len >> 8 ); _it++;
232 *_it = ( _header->_ext_len ); _it++;
233
234 *_it = ( _header->_ext_type >> 8 ); ++_it;
235 *_it = ( _header->_ext_type );
236
237 size_t x;
238
239 uint32_t* _hd_ext = _header->_hd_ext;
240 for ( x = 0; x < _header->_ext_len; x++ ) {
241 ++_it;
242 *_it = ( _hd_ext[x] >> 24 ); ++_it;
243 *_it = ( _hd_ext[x] >> 16 ); ++_it;
244 *_it = ( _hd_ext[x] >> 8 ); ++_it;
245 *_it = ( _hd_ext[x] );
246 }
247
248 return _it;
249}
250
251size_t rtp_header_get_size ( const rtp_header_t* _header )
252{
253 return ( 8 + ( rtp_header_get_flag_CSRC_count ( _header ) * 4 ) );
254}
255/* Setting flags */
256
257void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value )
258{
259 ( _header->_flags ) &= 0x3F;
260 ( _header->_flags ) |= ( ( ( value ) << 6 ) & 0xC0 );
261}
262
263void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value )
264{
265 if ( value > 0 ) {
266 value = 1; /* It can only be 1 */
267 }
268
269 ( _header->_flags ) &= 0xDF;
270 ( _header->_flags ) |= ( ( ( value ) << 5 ) & 0x20 );
271}
272
273void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value )
274{
275 if ( value > 0 ) {
276 value = 1; /* It can only be 1 */
277 }
278
279 ( _header->_flags ) &= 0xEF;
280 ( _header->_flags ) |= ( ( ( value ) << 4 ) & 0x10 );
281}
282
283void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value )
284{
285 ( _header->_flags ) &= 0xF0;
286 ( _header->_flags ) |= ( ( value ) & 0x0F );
287}
288
289void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value )
290{
291 if ( value > 1 )
292 value = 1;
293
294 ( _header->_marker_payload_t ) &= 0x7F;
295 ( _header->_marker_payload_t ) |= ( ( ( value ) << 7 ) /*& 0x80 */ );
296}
297
298void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value )
299{
300 if ( value > 127 )
301 value = 127; /* Well set to maximum */
302
303 ( _header->_marker_payload_t ) &= 0x80;
304 ( _header->_marker_payload_t ) |= ( ( value ) /* & 0x7F */ );
305}
306
307/* Getting values from flags */
308uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header )
309{
310 return ( _header->_flags & 0xd0 ) >> 6;
311}
312
313uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header )
314{
315 return ( _header->_flags & 0x20 ) >> 5;
316}
317
318uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header )
319{
320 return ( _header->_flags & 0x10 ) >> 4;
321}
322
323uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header )
324{
325 return ( _header->_flags & 0x0f );
326}
327uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header )
328{
329 return ( _header->_marker_payload_t ) >> 7;
330}
331uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header )
332{
333 /*
334 uint8_t _retu;
335
336 if ( _header->_marker_payload_t >> 7 == 1 ) {
337 _header->_marker_payload_t ^= 0x80;
338 _retu = _header->_marker_payload_t;
339 _header->_marker_payload_t ^= 0x80;
340 } else {
341 _retu = _header->_marker_payload_t;
342 }
343 */
344 /* return to start value
345 return _retu; */
346 return _header->_marker_payload_t & 0x7f;
347}
348
349/* */
350
351
diff --git a/toxrtp/toxrtp_message.h b/toxrtp/toxrtp_message.h
deleted file mode 100644
index 8feea5d9..00000000
--- a/toxrtp/toxrtp_message.h
+++ /dev/null
@@ -1,111 +0,0 @@
1/* rtp_message.h
2 *
3 * Rtp Message handler. It handles message/header parsing.
4 * Refer to RTP: A Transport Protocol for Real-Time Applications ( RFC 3550 ) for more info. !Red!
5 *
6 *
7 * Copyright (C) 2013 Tox project All Rights Reserved.
8 *
9 * This file is part of Tox.
10 *
11 * Tox is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * Tox is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#ifndef _RTP__MESSAGE_H_
27#define _RTP__MESSAGE_H_
28
29#include "../toxcore/network.h"
30#include "toxrtp_helper.h"
31#include "../toxcore/tox.h"
32/* Defines */
33
34#define _MAX_SEQU_NUM 65535
35
36/* Minimum size */
37#define _MIN_HEADER_LENGTH 12
38#define _MIN_EXT_HEADER_LENGTH 4
39
40/* End of defines */
41
42
43typedef struct rtp_dest_list_s {
44 tox_IP_Port _dest;
45 struct rtp_dest_list_s* next;
46
47} rtp_dest_list_t;
48
49typedef struct rtp_header_s {
50 uint8_t _flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
51 uint8_t _marker_payload_t; /* Marker(1), PlayLoad Type(7) */
52 uint16_t _sequence_number; /* Sequence Number */
53 uint32_t _timestamp; /* Timestamp */
54 uint32_t _ssrc; /* SSRC */
55 uint32_t* _csrc; /* CSRC's table */
56
57 uint32_t _length; /* A little something for allocation */
58
59} rtp_header_t;
60
61typedef struct rtp_ext_header_s {
62 uint16_t _ext_type; /* Extension profile */
63 uint16_t _ext_len; /* Number of extensions */
64 uint32_t* _hd_ext; /* Extension's table */
65
66
67} rtp_ext_header_t;
68
69typedef struct rtp_msg_s {
70 struct rtp_header_s* _header;
71 struct rtp_ext_header_s* _ext_header;
72 uint32_t _header_lenght;
73
74 uint8_t* _data;
75 uint32_t _length;
76 tox_IP_Port _from;
77
78 struct rtp_msg_s* _next;
79} rtp_msg_t;
80
81/* Extracts the header from the payload starting at _from */
82rtp_header_t* rtp_extract_header ( const uint8_t* _payload, size_t _bytes );
83rtp_ext_header_t* rtp_extract_ext_header ( const uint8_t* _payload, size_t _bytes );
84
85
86uint8_t* rtp_add_header ( rtp_header_t* _header, uint8_t* _payload );
87uint8_t* rtp_add_extention_header ( rtp_ext_header_t* _header, uint8_t* _payload );
88
89/* Gets the size of the header _header in bytes */
90size_t rtp_header_get_size ( const rtp_header_t* _header );
91
92void rtp_header_print (const rtp_header_t* _header);
93
94/* Adding flags and settings */
95void rtp_header_add_flag_version ( rtp_header_t* _header, uint32_t value );
96void rtp_header_add_flag_padding ( rtp_header_t* _header, uint32_t value );
97void rtp_header_add_flag_extension ( rtp_header_t* _header, uint32_t value );
98void rtp_header_add_flag_CSRC_count ( rtp_header_t* _header, uint32_t value );
99void rtp_header_add_setting_marker ( rtp_header_t* _header, uint32_t value );
100void rtp_header_add_setting_payload ( rtp_header_t* _header, uint32_t value );
101
102
103/* Getting values from flags and settings */
104uint8_t rtp_header_get_flag_version ( const rtp_header_t* _header );
105uint8_t rtp_header_get_flag_padding ( const rtp_header_t* _header );
106uint8_t rtp_header_get_flag_extension ( const rtp_header_t* _header );
107uint8_t rtp_header_get_flag_CSRC_count ( const rtp_header_t* _header );
108uint8_t rtp_header_get_setting_marker ( const rtp_header_t* _header );
109uint8_t rtp_header_get_setting_payload_type ( const rtp_header_t* _header );
110
111#endif /* _RTP__MESSAGE_H_ */