summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Mullen (grayhatter) <greg@grayhatter.com>2015-11-07 20:36:57 -0800
committerGregory Mullen (grayhatter) <greg@grayhatter.com>2015-11-07 20:36:57 -0800
commite1ad6cc8f9a5613439937096b55b476f65a00730 (patch)
tree9dcf444c993681cc654f0e2874ab66264ec289c6
parent3631b460a6b763acda718bb71b7f6a1ee31a3299 (diff)
parent6a494e2cbdd146bb13185d8220061322661a5f5a (diff)
Merge remote-tracking branch 'upstream/master' into rm-files
-rw-r--r--INSTALL.md8
-rw-r--r--README.md7
-rw-r--r--auto_tests/dht_test.c6
-rw-r--r--auto_tests/messenger_test.c4
-rw-r--r--auto_tests/toxav_basic_test.c883
-rw-r--r--auto_tests/toxav_many_test.c539
-rw-r--r--configure.ac14
-rw-r--r--other/apidsl/toxav.in.h615
-rw-r--r--testing/av_test.c766
-rw-r--r--toxav/Makefile.inc56
-rw-r--r--toxav/audio.c439
-rw-r--r--toxav/audio.h64
-rw-r--r--toxav/bwcontroler.c205
-rw-r--r--toxav/bwcontroler.h37
-rw-r--r--toxav/codec.c705
-rw-r--r--toxav/codec.h176
-rw-r--r--toxav/group.c12
-rw-r--r--toxav/msi.c1808
-rw-r--r--toxav/msi.h191
-rw-r--r--toxav/rtp.c677
-rw-r--r--toxav/rtp.h166
-rw-r--r--toxav/toxav.c1507
-rw-r--r--toxav/toxav.h762
-rw-r--r--toxav/toxav_old.c81
-rw-r--r--toxav/video.c264
-rw-r--r--toxav/video.h67
-rw-r--r--toxcore/Messenger.c17
-rw-r--r--toxcore/assoc.c8
-rw-r--r--toxcore/assoc.h4
-rw-r--r--toxcore/logger.c30
-rw-r--r--toxcore/logger.h19
-rw-r--r--toxcore/network.c12
-rw-r--r--toxcore/onion_client.h2
-rw-r--r--toxcore/util.c84
-rw-r--r--toxcore/util.h13
-rw-r--r--toxdns/toxdns.c2
36 files changed, 5971 insertions, 4279 deletions
diff --git a/INSTALL.md b/INSTALL.md
index adb0810a..b5a925ad 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -78,7 +78,7 @@ Note, if you install from ports select NaCl for performance, and sodium if you w
78 78
79You should get and install [libsodium](https://github.com/jedisct1/libsodium). If you have installed `libsodium` from repo, ommit this step, and jump directly to [compiling toxcore](#compile-toxcore): 79You should get and install [libsodium](https://github.com/jedisct1/libsodium). If you have installed `libsodium` from repo, ommit this step, and jump directly to [compiling toxcore](#compile-toxcore):
80```bash 80```bash
81git clone git://github.com/jedisct1/libsodium.git 81git clone https://github.com/jedisct1/libsodium.git
82cd libsodium 82cd libsodium
83git checkout tags/1.0.3 83git checkout tags/1.0.3
84./autogen.sh 84./autogen.sh
@@ -93,7 +93,7 @@ Or if checkinstall is not easily available for your distribution (e.g., Fedora),
93this will install the libs to /usr/local/lib and the headers to /usr/local/include: 93this will install the libs to /usr/local/lib and the headers to /usr/local/include:
94 94
95```bash 95```bash
96git clone git://github.com/jedisct1/libsodium.git 96git clone https://github.com/jedisct1/libsodium.git
97cd libsodium 97cd libsodium
98git checkout tags/1.0.3 98git checkout tags/1.0.3
99./autogen.sh 99./autogen.sh
@@ -121,7 +121,7 @@ sudo ldconfig
121 121
122Then clone this repo, generate makefile, and install `toxcore` system-wide: 122Then clone this repo, generate makefile, and install `toxcore` system-wide:
123```bash 123```bash
124git clone git://github.com/irungentoo/toxcore.git 124git clone https://github.com/irungentoo/toxcore.git
125cd toxcore 125cd toxcore
126autoreconf -i 126autoreconf -i
127./configure 127./configure
@@ -153,7 +153,7 @@ brew install libtool automake autoconf libsodium check
153``` 153```
154Then clone this repo and generate makefile: 154Then clone this repo and generate makefile:
155```bash 155```bash
156git clone git://github.com/irungentoo/toxcore.git 156git clone https://github.com/irungentoo/toxcore.git
157cd toxcore 157cd toxcore
158autoreconf -i 158autoreconf -i
159./configure 159./configure
diff --git a/README.md b/README.md
index 4d08dcda..ed62a9b2 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
1![Project Tox](https://raw.github.com/irungentoo/toxcore/master/other/tox.png "Project Tox") 1![Project Tox](https://raw.github.com/irungentoo/toxcore/master/other/tox.png "Project Tox")
2*** 2***
3 3
4With the rise of governmental monitoring programs, Tox, a FOSS initiative, aims to be an easy to use, all-in-one communication platform that ensures their users full privacy and secure message delivery.<br /> <br /> 4With the rise of government surveillance programs, Tox, a FOSS initiative, aims to be an easy to use, all-in-one communication platform that ensures full privacy and secure message delivery.<br /> <br />
5 5
6[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://wiki.tox.chat/Binaries) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md) **|** **IRC Channel:** [#tox@freenode](https://webchat.freenode.net/?channels=tox) 6[**Website**](https://tox.chat) **|** [**Wiki**](https://wiki.tox.chat/) **|** [**Blog**](https://blog.tox.chat/) **|** [**FAQ**](https://wiki.tox.chat/doku.php?id=users:faq) **|** [**Binaries/Downloads**](https://wiki.tox.chat/Binaries) **|** [**Clients**](https://wiki.tox.chat/doku.php?id=clients) **|** [**Compiling**](/INSTALL.md)
7
8**IRC Channels:** [#tox@freenode](https://webchat.freenode.net/?channels=tox), [#tox-dev@freenode](https://webchat.freenode.net/?channels=tox-dev)
7 9
8 10
9## The Complex Stuff: 11## The Complex Stuff:
@@ -36,4 +38,3 @@ The goal of this project is to create a configuration-free P2P Skype replacement
36- [Compiling](/INSTALL.md) 38- [Compiling](/INSTALL.md)
37- [DHT Protocol](/docs/updates/DHT.md)<br /> 39- [DHT Protocol](/docs/updates/DHT.md)<br />
38- [Crypto](/docs/updates/Crypto.md)<br /> 40- [Crypto](/docs/updates/Crypto.md)<br />
39
diff --git a/auto_tests/dht_test.c b/auto_tests/dht_test.c
index 32985bca..8304a046 100644
--- a/auto_tests/dht_test.c
+++ b/auto_tests/dht_test.c
@@ -153,7 +153,8 @@ void test_addto_lists_bad(DHT *dht,
153{ 153{
154 // check "bad" clients replacement 154 // check "bad" clients replacement
155 int used, test1, test2, test3; 155 int used, test1, test2, test3;
156 uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES]; 156 uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES],
157 test_id3[crypto_box_PUBLICKEYBYTES];
157 uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; 158 uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0;
158 159
159 randombytes(public_key, sizeof(public_key)); 160 randombytes(public_key, sizeof(public_key));
@@ -196,7 +197,8 @@ void test_addto_lists_possible_bad(DHT *dht,
196{ 197{
197 // check "possibly bad" clients replacement 198 // check "possibly bad" clients replacement
198 int used, test1, test2, test3; 199 int used, test1, test2, test3;
199 uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES], test_id3[crypto_box_PUBLICKEYBYTES]; 200 uint8_t public_key[crypto_box_PUBLICKEYBYTES], test_id1[crypto_box_PUBLICKEYBYTES], test_id2[crypto_box_PUBLICKEYBYTES],
201 test_id3[crypto_box_PUBLICKEYBYTES];
200 uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0; 202 uint8_t ipv6 = ip_port->ip.family == AF_INET6 ? 1 : 0;
201 203
202 randombytes(public_key, sizeof(public_key)); 204 randombytes(public_key, sizeof(public_key));
diff --git a/auto_tests/messenger_test.c b/auto_tests/messenger_test.c
index 2a813c9b..2b0b2519 100644
--- a/auto_tests/messenger_test.c
+++ b/auto_tests/messenger_test.c
@@ -135,7 +135,7 @@ START_TEST(test_m_addfriend)
135 if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, really_bad_len) != FAERR_TOOLONG) 135 if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, really_bad_len) != FAERR_TOOLONG)
136 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", really_bad_len); 136 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", really_bad_len);
137*/ 137*/
138/* this will error if the original m_addfriend_norequest() failed */ 138/* this will return an error if the original m_addfriend_norequest() failed */
139/* if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, good_len) != FAERR_ALREADYSENT) 139/* if(m_addfriend(m, (uint8_t *)friend_id, (uint8_t *)good_data, good_len) != FAERR_ALREADYSENT)
140 ck_abort_msg("m_addfriend did NOT catch adding a friend we already have.\n" 140 ck_abort_msg("m_addfriend did NOT catch adding a friend we already have.\n"
141 "(this can be caused by the error of m_addfriend_norequest in" 141 "(this can be caused by the error of m_addfriend_norequest in"
@@ -144,7 +144,7 @@ START_TEST(test_m_addfriend)
144 if(m_addfriend(m, (uint8_t *)good_id_b, (uint8_t *)bad_data, bad_len) != FAERR_NOMESSAGE) 144 if(m_addfriend(m, (uint8_t *)good_id_b, (uint8_t *)bad_data, bad_len) != FAERR_NOMESSAGE)
145 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", bad_len); 145 ck_abort_msg("m_addfriend did NOT catch the following length: %d\n", bad_len);
146*/ 146*/
147/* this should REALLY error */ 147/* this should REALLY return an error */
148/* 148/*
149 * TODO: validate client_id in m_addfriend? 149 * TODO: validate client_id in m_addfriend?
150if(m_addfriend((uint8_t *)bad_id, (uint8_t *)good_data, good_len) >= 0) 150if(m_addfriend((uint8_t *)bad_id, (uint8_t *)good_data, good_len) >= 0)
diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c
index af8d91e9..20432dca 100644
--- a/auto_tests/toxav_basic_test.c
+++ b/auto_tests/toxav_basic_test.c
@@ -2,23 +2,31 @@
2#include "config.h" 2#include "config.h"
3#endif 3#endif
4 4
5#ifndef HAVE_LIBCHECK
6# include <assert.h>
7
8# define ck_assert(X) assert(X);
9# define START_TEST(NAME) void NAME ()
10# define END_TEST
11#else
12# include "helpers.h"
13#endif
14
5#include <sys/types.h> 15#include <sys/types.h>
6#include <stdint.h> 16#include <stdint.h>
7#include <string.h> 17#include <string.h>
8#include <stdio.h> 18#include <stdio.h>
9#include <check.h>
10#include <stdlib.h> 19#include <stdlib.h>
11#include <time.h> 20#include <time.h>
12#include <assert.h>
13 21
14#include <vpx/vpx_image.h> 22#include <vpx/vpx_image.h>
15 23
16#include "../toxcore/tox.h" 24#include "../toxcore/tox.h"
25#include "../toxcore/util.h"
17#include "../toxcore/logger.h" 26#include "../toxcore/logger.h"
18#include "../toxcore/crypto_core.h" 27#include "../toxcore/crypto_core.h"
19#include "../toxav/toxav.h" 28#include "../toxav/toxav.h"
20 29
21#include "helpers.h"
22 30
23#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32) 31#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
24#define c_sleep(x) Sleep(1*x) 32#define c_sleep(x) Sleep(1*x)
@@ -28,604 +36,564 @@
28#endif 36#endif
29 37
30 38
39#define TEST_REGULAR_AV 1
40#define TEST_REGULAR_A 1
41#define TEST_REGULAR_V 1
42#define TEST_REJECT 1
43#define TEST_CANCEL 1
44#define TEST_MUTE_UNMUTE 1
45#define TEST_STOP_RESUME_PAYLOAD 1
46#define TEST_PAUSE_RESUME_SEND 1
31 47
32typedef enum _CallStatus {
33 none,
34 InCall,
35 Ringing,
36 Ended,
37 Rejected,
38 Canceled,
39 TimedOut
40 48
41} CallStatus; 49typedef struct {
50 bool incoming;
51 uint32_t state;
42 52
43typedef struct _Party { 53} CallControl;
44 CallStatus status;
45 ToxAv *av;
46 time_t *CallStarted;
47 int call_index;
48} Party;
49 54
50typedef struct _Status {
51 Party Alice;
52 Party Bob;
53} Status;
54 55
55/* My default settings */ 56/**
56static ToxAvCSettings muhcaps; 57 * Callbacks
57 58 */
58void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) 59void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
59{ 60{
60 if (length == 7 && memcmp("gentoo", data, 7) == 0) { 61 (void) av;
61 tox_friend_add_norequest(m, public_key, 0); 62 (void) friend_number;
62 } 63 (void) audio_enabled;
63} 64 (void) video_enabled;
64 65
65 66 printf("Handling CALL callback\n");
66/******************************************************************************/ 67 ((CallControl *)user_data)->incoming = true;
67void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
68{
69 Status *cast = _arg;
70
71 if (cast->Alice.av == av) {
72 // ...
73 } else if (cast->Bob.av == av) {
74 /* Bob always receives invite */
75 cast->Bob.status = Ringing;
76 cast->Bob.call_index = call_index;
77 }
78} 68}
79void callback_recv_ringing ( void *av, int32_t call_index, void *_arg ) 69void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
80{ 70{
81 Status *cast = _arg; 71 (void) av;
72 (void) friend_number;
82 73
83 if (cast->Alice.av == av) { 74 printf("Handling CALL STATE callback: %d\n", state);
84 /* Alice always sends invite */ 75 ((CallControl *)user_data)->state = state;
85 cast->Alice.status = Ringing;
86 } else if (cast->Bob.av == av) {
87 // ...
88 }
89} 76}
90 77void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
91 78 uint16_t width, uint16_t height,
92void callback_call_started ( void *av, int32_t call_index, void *_arg ) 79 uint8_t const *y, uint8_t const *u, uint8_t const *v,
80 int32_t ystride, int32_t ustride, int32_t vstride,
81 void *user_data)
93{ 82{
94 Status *cast = _arg; 83 (void) av;
95 84 (void) friend_number;
96 if (cast->Alice.av == av) { 85 (void) width;
97 printf("Call started on Alices side...\n"); 86 (void) height;
98 cast->Alice.status = InCall; 87 (void) y;
99 toxav_prepare_transmission(av, call_index, 1); 88 (void) u;
100 } else if (cast->Bob.av == av) { 89 (void) v;
101 printf("Call started on Bob side...\n"); 90 (void) ystride;
102 cast->Bob.status = InCall; 91 (void) ustride;
103 toxav_prepare_transmission(av, call_index, 1); 92 (void) vstride;
104 } 93 (void) user_data;
94 printf("Received video payload\n");
105} 95}
106void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) 96void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
97 int16_t const *pcm,
98 size_t sample_count,
99 uint8_t channels,
100 uint32_t sampling_rate,
101 void *user_data)
107{ 102{
108 Status *cast = _arg; 103 (void) av;
109 104 (void) friend_number;
110 if (cast->Alice.av == av) { 105 (void) pcm;
111 // ... 106 (void) sample_count;
112 } else if (cast->Bob.av == av) { 107 (void) channels;
113 printf ( "Call Canceled for Bob!\n" ); 108 (void) sampling_rate;
114 cast->Bob.status = Canceled; 109 (void) user_data;
115 } 110 printf("Received audio payload\n");
116} 111}
117void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) 112void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
118{ 113{
119 Status *cast = _arg; 114 (void) userdata;
120 115
121 printf ( "Call rejected by Bob!\n" 116 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
122 "Call ended for Alice!\n" ); 117 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
123
124 /* If Bob rejects, call is ended for alice and she sends ending */
125 if (cast->Alice.av == av) {
126 cast->Alice.status = Rejected;
127 } else if (cast->Bob.av == av) {
128 //... ignor
129 }
130}
131void callback_call_ended ( void *av, int32_t call_index, void *_arg )
132{
133 Status *cast = _arg;
134
135 if (cast->Alice.av == av) {
136 printf ( "Call ended for Alice!\n" );
137 cast->Alice.status = Ended;
138 } else if (cast->Bob.av == av) {
139 printf ( "Call ended for Bob!\n" );
140 cast->Bob.status = Ended;
141 } 118 }
142} 119}
143 120
144void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg )
145{
146 ToxAvCSettings csettings;
147 toxav_get_peer_csettings(av, call_index, 0, &csettings);
148
149 printf("Peer changing settings to: \n"
150 "Type: %u \n"
151 "Video bitrate: %u \n"
152 "Video height: %u \n"
153 "Video width: %u \n"
154 "Audio bitrate: %u \n"
155 "Audio framedur: %u \n"
156 "Audio sample rate: %u \n"
157 "Audio channels: %u \n",
158 csettings.call_type,
159 csettings.video_bitrate,
160 csettings.max_video_height,
161 csettings.max_video_width,
162 csettings.audio_bitrate,
163 csettings.audio_frame_duration,
164 csettings.audio_sample_rate,
165 csettings.audio_channels
166 );
167}
168
169void callback_self_cs_change ( void *av, int32_t call_index, void *_arg )
170{
171 ToxAvCSettings csettings;
172 toxav_get_peer_csettings(av, call_index, 0, &csettings);
173
174 printf("Changed settings to: \n"
175 "Type: %u \n"
176 "Video bitrate: %u \n"
177 "Video height: %u \n"
178 "Video width: %u \n"
179 "Audio bitrate: %u \n"
180 "Audio framedur: %u \n"
181 "Audio sample rate: %u \n"
182 "Audio channels: %u \n",
183 csettings.call_type,
184 csettings.video_bitrate,
185 csettings.max_video_height,
186 csettings.max_video_width,
187 csettings.audio_bitrate,
188 csettings.audio_frame_duration,
189 csettings.audio_sample_rate,
190 csettings.audio_channels
191 );
192}
193 121
194void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) 122/**
123 * Iterate helper
124 */
125int iterate_tox(Tox *bootstrap, Tox *Alice, Tox *Bob)
195{ 126{
196 Status *cast = _arg; 127 tox_iterate(bootstrap);
197 printf("Call timed-out!\n"); 128 tox_iterate(Alice);
129 tox_iterate(Bob);
198 130
199 if (cast->Alice.av == av) { 131 return MIN(tox_iteration_interval(Alice), tox_iteration_interval(Bob));
200 cast->Alice.status = TimedOut;
201 } else if (cast->Bob.av == av) {
202 cast->Bob.status = TimedOut;
203 }
204} 132}
205 133
206void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
207{}
208 134
209void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
210{}
211 135
212void register_callbacks(ToxAv *av, void *data) 136START_TEST(test_AV_flows)
213{ 137{
214 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 138 Tox *Alice, *Bob, *bootstrap;
215 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); 139 ToxAV *AliceAV, *BobAV;
216 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
217 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data);
218 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data);
219 toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data);
220 toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
221 toxav_register_callstate_callback(av, callback_peer_cs_change, av_OnPeerCSChange, data);
222 toxav_register_callstate_callback(av, callback_self_cs_change, av_OnSelfCSChange, data);
223 toxav_register_audio_callback(av, callback_audio, NULL);
224 toxav_register_video_callback(av, callback_video, NULL);
225}
226 140
141 CallControl AliceCC, BobCC;
227 142
228/*************************************************************************************************/ 143 {
144 TOX_ERR_NEW error;
229 145
230/* Alice calls bob and the call starts. 146 bootstrap = tox_new(NULL, &error);
231 * What happens during the call is defined after. To quit the loop use: step++; 147 ck_assert(error == TOX_ERR_NEW_OK);
232 */
233#define CALL_AND_START_LOOP(AliceCallType, BobCallType) \
234{ int step = 0, running = 1; while (running) {\
235 tox_iterate(bootstrap_node); tox_iterate(Alice); tox_iterate(Bob); \
236 toxav_do(status_control.Bob.av); toxav_do(status_control.Alice.av); \
237 switch ( step ) {\
238 case 0: /* Alice */ printf("Alice is calling...\n");\
239 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); step++; break;\
240 case 1: /* Bob */ if (status_control.Bob.status == Ringing) { printf("Bob answers...\n");\
241 cur_time = time(NULL); toxav_answer(status_control.Bob.av, status_control.Bob.call_index, &muhcaps); step++; } break; \
242 case 2: /* Rtp transmission */ \
243 if (status_control.Bob.status == InCall && status_control.Alice.status == InCall)
244
245
246#define TERMINATE_SCOPE() break;\
247case 3: /* Wait for Both to have status ended */\
248if (status_control.Alice.status == Ended && status_control.Bob.status == Ended) running = 0; break; } c_sleep(20); } } printf("\n");
249 148
250START_TEST(test_AV_flows) 149 Alice = tox_new(NULL, &error);
251{ 150 ck_assert(error == TOX_ERR_NEW_OK);
252 long long unsigned int cur_time = time(NULL);
253 Tox *bootstrap_node = tox_new(0, 0);
254 Tox *Alice = tox_new(0, 0);
255 Tox *Bob = tox_new(0, 0);
256 151
257 ck_assert_msg(bootstrap_node || Alice || Bob, "Failed to create 3 tox instances"); 152 Bob = tox_new(NULL, &error);
153 ck_assert(error == TOX_ERR_NEW_OK);
154 }
155
156 printf("Created 3 instances of Tox\n");
157 printf("Preparing network...\n");
158 long long unsigned int cur_time = time(NULL);
258 159
259 uint32_t to_compare = 974536; 160 uint32_t to_compare = 974536;
260 tox_callback_friend_request(Alice, accept_friend_request, &to_compare);
261 uint8_t address[TOX_ADDRESS_SIZE]; 161 uint8_t address[TOX_ADDRESS_SIZE];
162
163 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
262 tox_self_get_address(Alice, address); 164 tox_self_get_address(Alice, address);
263 uint32_t test = tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, 0);
264 165
265 ck_assert_msg(test == 0, "Failed to add friend error code: %i", test); 166
167 ck_assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
266 168
267 uint8_t off = 1; 169 uint8_t off = 1;
268 170
269 while (1) { 171 while (1) {
270 tox_iterate(bootstrap_node); 172 iterate_tox(bootstrap, Alice, Bob);
271 tox_iterate(Alice);
272 tox_iterate(Bob);
273 173
274 if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice) 174 if (tox_self_get_connection_status(bootstrap) &&
275 && tox_self_get_connection_status(Bob) 175 tox_self_get_connection_status(Alice) &&
276 && off) { 176 tox_self_get_connection_status(Bob) && off) {
277 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); 177 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
278 off = 0; 178 off = 0;
279 } 179 }
280 180
281 if (tox_friend_get_connection_status(Alice, 0, 0) && tox_friend_get_connection_status(Bob, 0, 0)) 181 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
182 tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
282 break; 183 break;
283 184
284 c_sleep(20); 185 c_sleep(20);
285 } 186 }
286 187
287 printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time);
288 188
289 muhcaps = av_DefaultSettings; 189 {
290 muhcaps.max_video_height = muhcaps.max_video_width = 128; 190 TOXAV_ERR_NEW error;
291 191 AliceAV = toxav_new(Alice, &error);
292 Status status_control = { 192 ck_assert(error == TOXAV_ERR_NEW_OK);
293 {none, toxav_new(Alice, 1), NULL, -1},
294 {none, toxav_new(Bob, 1), NULL, -1},
295 };
296
297 ck_assert_msg(status_control.Alice.av || status_control.Bob.av, "Failed to create 2 toxav instances");
298
299
300 register_callbacks(status_control.Alice.av, &status_control);
301 register_callbacks(status_control.Bob.av, &status_control);
302
303 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000);
304 int16_t sample_payload[frame_size];
305 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size);
306 193
307 uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; 194 BobAV = toxav_new(Bob, &error);
308 int payload_size; 195 ck_assert(error == TOXAV_ERR_NEW_OK);
196 }
309 197
310 vpx_image_t *sample_image = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 128, 128, 1); 198 toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC);
199 toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC);
200 toxav_callback_video_receive_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC);
201 toxav_callback_audio_receive_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC);
202
203 toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC);
204 toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC);
205 toxav_callback_video_receive_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC);
206 toxav_callback_audio_receive_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC);
207
208 printf("Created 2 instances of ToxAV\n");
209 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
210
211
212#define REGULAR_CALL_FLOW(A_BR, V_BR) \
213 do { \
214 memset(&AliceCC, 0, sizeof(CallControl)); \
215 memset(&BobCC, 0, sizeof(CallControl)); \
216 \
217 TOXAV_ERR_CALL rc; \
218 toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \
219 \
220 if (rc != TOXAV_ERR_CALL_OK) { \
221 printf("toxav_call failed: %d\n", rc); \
222 ck_assert(0); \
223 } \
224 \
225 \
226 long long unsigned int start_time = time(NULL); \
227 \
228 \
229 while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED) { \
230 \
231 if (BobCC.incoming) { \
232 TOXAV_ERR_ANSWER rc; \
233 toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \
234 \
235 if (rc != TOXAV_ERR_ANSWER_OK) { \
236 printf("toxav_answer failed: %d\n", rc); \
237 ck_assert(0); \
238 } \
239 BobCC.incoming = false; \
240 } else { \
241 /* TODO rtp */ \
242 \
243 if (time(NULL) - start_time >= 1) { \
244 \
245 TOXAV_ERR_CALL_CONTROL rc; \
246 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \
247 \
248 if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \
249 printf("toxav_call_control failed: %d\n", rc); \
250 ck_assert(0); \
251 } \
252 } \
253 } \
254 \
255 iterate_tox(bootstrap, Alice, Bob); \
256 } \
257 printf("Success!\n");\
258 } while(0)
259
260 if (TEST_REGULAR_AV) {
261 printf("\nTrying regular call (Audio and Video)...\n");
262 REGULAR_CALL_FLOW(48, 4000);
263 }
311 264
312 memcpy(sample_image->planes[VPX_PLANE_Y], sample_payload, 10); 265 if (TEST_REGULAR_A) {
313 memcpy(sample_image->planes[VPX_PLANE_U], sample_payload, 10); 266 printf("\nTrying regular call (Audio only)...\n");
314 memcpy(sample_image->planes[VPX_PLANE_V], sample_payload, 10); 267 REGULAR_CALL_FLOW(48, 0);
268 }
315 269
270 if (TEST_REGULAR_V) {
271 printf("\nTrying regular call (Video only)...\n");
272 REGULAR_CALL_FLOW(0, 4000);
273 }
316 274
317 /************************************************************************************************* 275#undef REGULAR_CALL_FLOW
318 * Successful flows (when call starts)
319 */
320 276
321 /* 277 if (TEST_REJECT) { /* Alice calls; Bob rejects */
322 * Call with audio only on both sides. Alice calls Bob. 278 printf("\nTrying reject flow...\n");
323 */
324 279
280 memset(&AliceCC, 0, sizeof(CallControl));
281 memset(&BobCC, 0, sizeof(CallControl));
325 282
326 CALL_AND_START_LOOP(TypeAudio, TypeAudio) { 283 {
327 /* Both send */ 284 TOXAV_ERR_CALL rc;
328 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 285 toxav_call(AliceAV, 0, 48, 0, &rc);
329 1000, sample_payload, frame_size);
330 286
331 if ( payload_size < 0 ) { 287 if (rc != TOXAV_ERR_CALL_OK) {
332 ck_assert_msg ( 0, "Failed to encode payload" ); 288 printf("toxav_call failed: %d\n", rc);
289 ck_assert(0);
290 }
333 } 291 }
334 292
335 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 293 while (!BobCC.incoming)
294 iterate_tox(bootstrap, Alice, Bob);
336 295
337 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 296 /* Reject */
338 sample_payload, frame_size); 297 {
298 TOXAV_ERR_CALL_CONTROL rc;
299 toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
339 300
340 if ( payload_size < 0 ) { 301 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
341 ck_assert_msg ( 0, "Failed to encode payload" ); 302 printf("toxav_call_control failed: %d\n", rc);
303 ck_assert(0);
304 }
342 } 305 }
343 306
344 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 307 while (AliceCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED)
345 308 iterate_tox(bootstrap, Alice, Bob);
346 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */
347 step++; /* This terminates the loop */
348 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index);
349 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index);
350 309
351 /* Call over Alice hangs up */ 310 printf("Success!\n");
352 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index);
353 }
354 } 311 }
355 TERMINATE_SCOPE()
356
357 312
358 /* 313 if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */
359 * Call with audio on both sides and video on one side. Alice calls Bob. 314 printf("\nTrying cancel (while ringing) flow...\n");
360 */
361 CALL_AND_START_LOOP(TypeAudio, TypeVideo) {
362 /* Both send */
363 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload,
364 1000, sample_payload, frame_size);
365
366 if ( payload_size < 0 ) {
367 ck_assert_msg ( 0, "Failed to encode payload" );
368 }
369 315
370 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 316 memset(&AliceCC, 0, sizeof(CallControl));
317 memset(&BobCC, 0, sizeof(CallControl));
371 318
372 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 319 {
373 sample_payload, frame_size); 320 TOXAV_ERR_CALL rc;
321 toxav_call(AliceAV, 0, 48, 0, &rc);
374 322
375 if ( payload_size < 0 ) { 323 if (rc != TOXAV_ERR_CALL_OK) {
376 ck_assert_msg ( 0, "Failed to encode payload" ); 324 printf("toxav_call failed: %d\n", rc);
325 ck_assert(0);
326 }
377 } 327 }
378 328
379 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 329 while (!BobCC.incoming)
380 330 iterate_tox(bootstrap, Alice, Bob);
381// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image);
382 331
383 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 332 /* Cancel */
384 step++; /* This terminates the loop */ 333 {
385 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 334 TOXAV_ERR_CALL_CONTROL rc;
386 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 335 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
387 336
388 /* Call over Alice hangs up */ 337 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
389 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 338 printf("toxav_call_control failed: %d\n", rc);
339 ck_assert(0);
340 }
390 } 341 }
391 }
392 TERMINATE_SCOPE()
393
394 342
395 /* 343 /* Alice will not receive end state */
396 * Call with audio and video on both sides. Alice calls Bob. 344 while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED)
397 */ 345 iterate_tox(bootstrap, Alice, Bob);
398 CALL_AND_START_LOOP(TypeVideo, TypeVideo) {
399 /* Both send */
400 346
401 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 347 printf("Success!\n");
402 1000, sample_payload, frame_size); 348 }
403 349
404 if ( payload_size < 0 ) { 350 if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */
405 ck_assert_msg ( 0, "Failed to encode payload" ); 351 printf("\nTrying mute functionality...\n");
406 }
407 352
408 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 353 memset(&AliceCC, 0, sizeof(CallControl));
354 memset(&BobCC, 0, sizeof(CallControl));
409 355
410 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 356 /* Assume sending audio and video */
411 sample_payload, frame_size); 357 {
358 TOXAV_ERR_CALL rc;
359 toxav_call(AliceAV, 0, 48, 1000, &rc);
412 360
413 if ( payload_size < 0 ) { 361 if (rc != TOXAV_ERR_CALL_OK) {
414 ck_assert_msg ( 0, "Failed to encode payload" ); 362 printf("toxav_call failed: %d\n", rc);
363 ck_assert(0);
364 }
415 } 365 }
416 366
417 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 367 while (!BobCC.incoming)
418 368 iterate_tox(bootstrap, Alice, Bob);
419// toxav_send_video(status_control.Alice.av, status_control.Alice.call_index, sample_image);
420// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image);
421 369
370 /* At first try all stuff while in invalid state */
371 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
372 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
373 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
374 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
375 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, NULL));
376 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL));
422 377
423 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 378 {
424 step++; /* This terminates the loop */ 379 TOXAV_ERR_ANSWER rc;
425 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 380 toxav_answer(BobAV, 0, 48, 4000, &rc);
426 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index);
427 381
428 /* Call over Alice hangs up */ 382 if (rc != TOXAV_ERR_ANSWER_OK) {
429 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 383 printf("toxav_answer failed: %d\n", rc);
384 ck_assert(0);
385 }
430 } 386 }
431 }
432 TERMINATE_SCOPE()
433
434 387
435 uint64_t times_they_are_a_changin = time(NULL); 388 iterate_tox(bootstrap, Alice, Bob);
436 /* Media change */ 389
437 CALL_AND_START_LOOP(TypeAudio, TypeAudio) { 390 /* Pause and Resume */
438 /* Both send */ 391 printf("Pause and Resume\n");
439 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 392 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
440 1000, sample_payload, frame_size); 393 iterate_tox(bootstrap, Alice, Bob);
441 394 ck_assert(BobCC.state == 0);
442 if ( payload_size < 0 ) { 395 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
443 ck_assert_msg ( 0, "Failed to encode payload" ); 396 iterate_tox(bootstrap, Alice, Bob);
397 ck_assert(BobCC.state & (TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V));
398
399 /* Mute/Unmute single */
400 printf("Mute/Unmute single\n");
401 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
402 iterate_tox(bootstrap, Alice, Bob);
403 ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
404 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
405 iterate_tox(bootstrap, Alice, Bob);
406 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
407
408 /* Mute/Unmute both */
409 printf("Mute/Unmute both\n");
410 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
411 iterate_tox(bootstrap, Alice, Bob);
412 ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
413 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, NULL));
414 iterate_tox(bootstrap, Alice, Bob);
415 ck_assert(BobCC.state ^ TOXAV_FRIEND_CALL_STATE_ACCEPTING_V);
416 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
417 iterate_tox(bootstrap, Alice, Bob);
418 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A);
419 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL));
420 iterate_tox(bootstrap, Alice, Bob);
421 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V);
422
423 {
424 TOXAV_ERR_CALL_CONTROL rc;
425 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
426
427 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
428 printf("toxav_call_control failed: %d\n", rc);
429 ck_assert(0);
430 }
444 } 431 }
445 432
446 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 433 iterate_tox(bootstrap, Alice, Bob);
434 ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
447 435
448 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 436 printf("Success!\n");
449 sample_payload, frame_size); 437 }
450 438
451 if ( payload_size < 0 ) { 439 if (TEST_STOP_RESUME_PAYLOAD) { /* Stop and resume audio/video payload */
452 ck_assert_msg ( 0, "Failed to encode payload" ); 440 printf("\nTrying stop/resume functionality...\n");
453 }
454 441
455 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 442 memset(&AliceCC, 0, sizeof(CallControl));
443 memset(&BobCC, 0, sizeof(CallControl));
456 444
457 /* Wait 2 seconds and change transmission type */ 445 /* Assume sending audio and video */
458 if (time(NULL) - times_they_are_a_changin > 2) { 446 {
459 times_they_are_a_changin = time(NULL); 447 TOXAV_ERR_CALL rc;
460 muhcaps.audio_bitrate ++; 448 toxav_call(AliceAV, 0, 48, 0, &rc);
461 toxav_change_settings(status_control.Alice.av, status_control.Alice.call_index, &muhcaps);
462 }
463 449
464 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 450 if (rc != TOXAV_ERR_CALL_OK) {
465 step++; /* This terminates the loop */ 451 printf("toxav_call failed: %d\n", rc);
466 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 452 ck_assert(0);
467 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 453 }
468
469 /* Call over Alice hangs up */
470 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index);
471 } 454 }
472 }
473 TERMINATE_SCOPE()
474 455
456 while (!BobCC.incoming)
457 iterate_tox(bootstrap, Alice, Bob);
475 458
476 /************************************************************************************************* 459 {
477 * Other flows 460 TOXAV_ERR_ANSWER rc;
478 */ 461 toxav_answer(BobAV, 0, 48, 0, &rc);
479 462
480 /* 463 if (rc != TOXAV_ERR_ANSWER_OK) {
481 * Call and reject 464 printf("toxav_answer failed: %d\n", rc);
482 */ 465 ck_assert(0);
483 {
484 int step = 0;
485 int running = 1;
486
487 while (running) {
488 tox_iterate(bootstrap_node);
489 tox_iterate(Alice);
490 tox_iterate(Bob);
491
492 switch ( step ) {
493 case 0: /* Alice */
494 printf("Alice is calling...\n");
495 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10);
496 step++;
497 break;
498
499 case 1: /* Bob */
500 if (status_control.Bob.status == Ringing) {
501 printf("Bob rejects...\n");
502 toxav_reject(status_control.Bob.av, status_control.Bob.call_index, "Who likes D's anyway?");
503 step++;
504 }
505
506 break;
507
508 case 2: /* Wait for Both to have status ended */
509 if (status_control.Alice.status == Rejected && status_control.Bob.status == Ended) running = 0;
510
511 break;
512 } 466 }
513
514 c_sleep(20);
515 } 467 }
516 468
517 printf("\n"); 469 iterate_tox(bootstrap, Alice, Bob);
518 }
519
520
521 /*
522 * Call and cancel
523 */
524 {
525 int step = 0;
526 int running = 1;
527
528 while (running) {
529 tox_iterate(bootstrap_node);
530 tox_iterate(Alice);
531 tox_iterate(Bob);
532 470
533 toxav_do(status_control.Alice.av); 471 printf("Call started as audio only\n");
534 toxav_do(status_control.Bob.av); 472 printf("Turning on video for Alice...\n");
473 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL));
535 474
475 iterate_tox(bootstrap, Alice, Bob);
476 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V);
536 477
537 switch ( step ) { 478 printf("Turning off video for Alice...\n");
538 case 0: /* Alice */ 479 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL));
539 printf("Alice is calling...\n");
540 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10);
541 step++;
542 break;
543 480
481 iterate_tox(bootstrap, Alice, Bob);
482 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V));
544 483
545 case 1: /* Alice again */ 484 printf("Turning off audio for Alice...\n");
546 if (status_control.Bob.status == Ringing) { 485 ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL));
547 printf("Alice cancels...\n");
548 toxav_cancel(status_control.Alice.av, status_control.Alice.call_index, 0, "Who likes D's anyway?");
549 step++;
550 }
551 486
552 break; 487 iterate_tox(bootstrap, Alice, Bob);
488 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A));
553 489
554 case 2: /* Wait for Both to have status ended */ 490 {
555 if (status_control.Bob.status == Canceled) running = 0; 491 TOXAV_ERR_CALL_CONTROL rc;
492 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
556 493
557 break; 494 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
495 printf("toxav_call_control failed: %d\n", rc);
496 ck_assert(0);
558 } 497 }
559
560 c_sleep(20);
561 } 498 }
562 499
563 printf("\n"); 500 iterate_tox(bootstrap, Alice, Bob);
501 ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
502
503 printf("Success!\n");
564 } 504 }
565 505
566 /* 506 if (TEST_PAUSE_RESUME_SEND) { /* Stop and resume audio/video payload and test send options */
567 * Timeout 507 printf("\nTrying stop/resume functionality...\n");
568 */
569 {
570 int step = 0;
571 int running = 1;
572 508
573 while (running) { 509 memset(&AliceCC, 0, sizeof(CallControl));
574 tox_iterate(bootstrap_node); 510 memset(&BobCC, 0, sizeof(CallControl));
575 tox_iterate(Alice);
576 tox_iterate(Bob);
577 511
578 toxav_do(status_control.Alice.av); 512 /* Assume sending audio and video */
579 toxav_do(status_control.Bob.av); 513 {
514 TOXAV_ERR_CALL rc;
515 toxav_call(AliceAV, 0, 48, 0, &rc);
580 516
581 switch ( step ) { 517 if (rc != TOXAV_ERR_CALL_OK) {
582 case 0: 518 printf("toxav_call failed: %d\n", rc);
583 printf("Alice is calling...\n"); 519 ck_assert(0);
584 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); 520 }
585 step++; 521 }
586 break;
587 522
588 case 1: 523 while (!BobCC.incoming)
589 if (status_control.Alice.status == TimedOut) running = 0; 524 iterate_tox(bootstrap, Alice, Bob);
590 525
591 break; 526 {
527 TOXAV_ERR_ANSWER rc;
528 toxav_answer(BobAV, 0, 48, 0, &rc);
529
530 if (rc != TOXAV_ERR_ANSWER_OK) {
531 printf("toxav_answer failed: %d\n", rc);
532 ck_assert(0);
592 } 533 }
534 }
593 535
594 c_sleep(20); 536 int16_t PCM[5670];
537
538 iterate_tox(bootstrap, Alice, Bob);
539 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
540 iterate_tox(bootstrap, Alice, Bob);
541 ck_assert(!toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, NULL));
542 ck_assert(!toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL));
543 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
544 iterate_tox(bootstrap, Alice, Bob);
545 ck_assert(toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, NULL));
546 ck_assert(toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL));
547 iterate_tox(bootstrap, Alice, Bob);
548
549 {
550 TOXAV_ERR_CALL_CONTROL rc;
551 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
552
553 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
554 printf("toxav_call_control failed: %d\n", rc);
555 ck_assert(0);
556 }
595 } 557 }
596 558
597 printf("\n"); 559 iterate_tox(bootstrap, Alice, Bob);
560 ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
561
562 printf("Success!\n");
598 } 563 }
599 564
600 vpx_img_free(sample_image); 565 toxav_kill(BobAV);
601 toxav_kill(status_control.Alice.av); 566 toxav_kill(AliceAV);
602 toxav_kill(status_control.Bob.av);
603 tox_kill(bootstrap_node);
604 tox_kill(Alice);
605 tox_kill(Bob); 567 tox_kill(Bob);
568 tox_kill(Alice);
569 tox_kill(bootstrap);
606 570
607 printf("Calls ended!\n"); 571 printf("\nTest successful!\n");
608} 572}
609END_TEST 573END_TEST
610 574
611/*************************************************************************************************/ 575#ifndef HAVE_LIBCHECK
612 576int main(int argc, char *argv[])
613 577{
614/*************************************************************************************************/ 578 (void) argc;
615 579 (void) argv;
616/*************************************************************************************************/
617
618 580
581 test_AV_flows();
582 return 0;
583}
584#else
619Suite *tox_suite(void) 585Suite *tox_suite(void)
620{ 586{
621 Suite *s = suite_create("ToxAV"); 587 Suite *s = suite_create("ToxAV");
622 588
623 DEFTESTCASE_SLOW(AV_flows, 200); 589 DEFTESTCASE_SLOW(AV_flows, 200);
624
625 return s; 590 return s;
626} 591}
627int main(int argc, char *argv[]) 592int main(int argc, char *argv[])
628{ 593{
594 (void) argc;
595 (void) argv;
596
629 Suite *tox = tox_suite(); 597 Suite *tox = tox_suite();
630 SRunner *test_runner = srunner_create(tox); 598 SRunner *test_runner = srunner_create(tox);
631 599
@@ -637,6 +605,5 @@ int main(int argc, char *argv[])
637 srunner_free(test_runner); 605 srunner_free(test_runner);
638 606
639 return number_failed; 607 return number_failed;
640
641// return test_AV_flows();
642} 608}
609#endif
diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c
index 6017e526..ab496024 100644
--- a/auto_tests/toxav_many_test.c
+++ b/auto_tests/toxav_many_test.c
@@ -2,18 +2,27 @@
2#include "config.h" 2#include "config.h"
3#endif 3#endif
4 4
5#ifndef HAVE_LIBCHECK
6# include <assert.h>
7
8# define ck_assert(X) assert(X);
9# define START_TEST(NAME) void NAME ()
10# define END_TEST
11#else
12# include "helpers.h"
13#endif
14
5#include <sys/types.h> 15#include <sys/types.h>
6#include <stdint.h> 16#include <stdint.h>
7#include <string.h> 17#include <string.h>
8#include <stdio.h> 18#include <stdio.h>
9#include <check.h>
10#include <stdlib.h> 19#include <stdlib.h>
11#include <time.h> 20#include <time.h>
12#include <assert.h>
13 21
14#include <vpx/vpx_image.h> 22#include <vpx/vpx_image.h>
15 23
16#include "../toxcore/tox.h" 24#include "../toxcore/tox.h"
25#include "../toxcore/util.h"
17#include "../toxcore/logger.h" 26#include "../toxcore/logger.h"
18#include "../toxcore/crypto_core.h" 27#include "../toxcore/crypto_core.h"
19#include "../toxav/toxav.h" 28#include "../toxav/toxav.h"
@@ -26,365 +35,321 @@
26#define c_sleep(x) usleep(1000*x) 35#define c_sleep(x) usleep(1000*x)
27#endif 36#endif
28 37
29pthread_mutex_t muhmutex;
30
31typedef enum _CallStatus {
32 none,
33 InCall,
34 Ringing,
35 Ended,
36 Rejected,
37 Canceled
38
39} CallStatus;
40 38
41typedef struct _Party { 39typedef struct {
42 CallStatus status; 40 bool incoming;
43 ToxAv *av; 41 uint32_t state;
44 int id; 42} CallControl;
45} Party;
46 43
47typedef struct _ACall { 44typedef struct {
48 pthread_t tid; 45 ToxAV *AliceAV;
49 int idx; 46 ToxAV *BobAV;
47 CallControl *AliceCC;
48 CallControl *BobCC;
49 uint32_t friend_number;
50} thread_data;
50 51
51 Party Caller; 52/**
52 Party Callee; 53 * Callbacks
53} ACall; 54 */
54 55void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
55typedef struct _Status {
56 ACall calls[3]; /* Make 3 calls for this test */
57} Status;
58
59Status status_control;
60
61void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
62{ 56{
63 if (length == 7 && memcmp("gentoo", data, 7) == 0) { 57 (void) av;
64 tox_friend_add_norequest(m, public_key, 0); 58 (void) audio_enabled;
65 } 59 (void) video_enabled;
66}
67 60
68 61 printf("Handling CALL callback\n");
69/******************************************************************************/ 62 ((CallControl *)user_data)[friend_number].incoming = true;
70void callback_recv_invite ( void *av, int32_t call_index, void *_arg )
71{
72 Status *cast = _arg;
73 cast->calls[call_index].Callee.status = Ringing;
74}
75void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
76{
77 Status *cast = _arg;
78 cast->calls[call_index].Caller.status = Ringing;
79} 63}
80void callback_call_ended ( void *av, int32_t call_index, void *_arg ) 64void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
81{ 65{
82 Status *cast = _arg; 66 printf("Handling CALL STATE callback: %d %p\n", state, av);
83 67 ((CallControl *)user_data)[friend_number].state = state;
84 if (av == cast->calls[call_index].Caller.av)
85 cast->calls[call_index].Caller.status = Ended;
86 else
87 cast->calls[call_index].Callee.status = Ended;
88} 68}
89void callback_call_started ( void *av, int32_t call_index, void *_arg ) 69void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
70 uint16_t width, uint16_t height,
71 uint8_t const *y, uint8_t const *u, uint8_t const *v,
72 int32_t ystride, int32_t ustride, int32_t vstride,
73 void *user_data)
90{ 74{
91 Status *cast = _arg; 75 (void) av;
92 76 (void) friend_number;
93 if (av == cast->calls[call_index].Caller.av) 77 (void) width;
94 cast->calls[call_index].Caller.status = InCall; 78 (void) height;
95 else 79 (void) y;
96 cast->calls[call_index].Callee.status = InCall; 80 (void) u;
81 (void) v;
82 (void) ystride;
83 (void) ustride;
84 (void) vstride;
85 (void) user_data;
97} 86}
98void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) 87void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
88 int16_t const *pcm,
89 size_t sample_count,
90 uint8_t channels,
91 uint32_t sampling_rate,
92 void *user_data)
99{ 93{
94 (void) av;
95 (void) friend_number;
96 (void) pcm;
97 (void) sample_count;
98 (void) channels;
99 (void) sampling_rate;
100 (void) user_data;
100} 101}
101void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) 102void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
102{ 103{
103 Status *cast = _arg; 104 (void) userdata;
104 cast->calls[call_index].Caller.status = Rejected;
105}
106 105
107void callback_requ_timeout ( void *av, int32_t call_index, void *_arg ) 106 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
108{ 107 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
109 ck_assert_msg(0, "No answer!"); 108 }
110} 109}
111 110
112void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
113{}
114
115void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
116{}
117 111
118void register_callbacks(ToxAv *av, void *data) 112/**
113 * Iterate helper
114 */
115ToxAV *setup_av_instance(Tox *tox, CallControl *CC)
119{ 116{
120 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 117 TOXAV_ERR_NEW error;
121 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data);
122 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data);
123 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data);
124 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data);
125 toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data);
126 toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data);
127 118
119 ToxAV *av = toxav_new(tox, &error);
120 ck_assert(error == TOXAV_ERR_NEW_OK);
128 121
129 toxav_register_audio_callback(av, callback_audio, NULL); 122 toxav_callback_call(av, t_toxav_call_cb, CC);
130 toxav_register_video_callback(av, callback_video, NULL); 123 toxav_callback_call_state(av, t_toxav_call_state_cb, CC);
131} 124 toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC);
132/*************************************************************************************************/ 125 toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC);
133
134int call_running[3];
135 126
136void *in_thread_call (void *arg) 127 return av;
128}
129void *call_thread(void *pd)
137{ 130{
138#define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args) 131 ToxAV *AliceAV = ((thread_data *) pd)->AliceAV;
139 132 ToxAV *BobAV = ((thread_data *) pd)->BobAV;
140 ACall *this_call = arg; 133 CallControl *AliceCC = ((thread_data *) pd)->AliceCC;
141 uint64_t start = 0; 134 CallControl *BobCC = ((thread_data *) pd)->BobCC;
142 int step = 0; 135 uint32_t friend_number = ((thread_data *) pd)->friend_number;
143 int call_idx;
144
145 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000);
146 int16_t sample_payload[frame_size];
147 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size);
148
149 uint8_t prepared_payload[RTP_PAYLOAD_SIZE];
150
151 register_callbacks(this_call->Caller.av, &status_control);
152 register_callbacks(this_call->Callee.av, arg);
153
154 /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */
155 pthread_mutex_lock(&muhmutex);
156
157 while (call_running[this_call->idx]) {
158
159 pthread_mutex_unlock(&muhmutex);
160
161 switch ( step ) {
162 case 0: /* CALLER */
163 toxav_call(this_call->Caller.av, &call_idx, this_call->Callee.id, &av_DefaultSettings, 10);
164 call_print(call_idx, "Calling ...");
165 step++;
166 break;
167 136
168 case 1: /* CALLEE */
169 pthread_mutex_lock(&muhmutex);
170 137
171 if (this_call->Caller.status == Ringing) { 138 memset(AliceCC, 0, sizeof(CallControl));
172 call_print(call_idx, "Callee answers ..."); 139 memset(BobCC, 0, sizeof(CallControl));
173 pthread_mutex_unlock(&muhmutex);
174 toxav_answer(this_call->Callee.av, 0, &av_DefaultSettings);
175 step++;
176 start = time(NULL);
177 pthread_mutex_lock(&muhmutex);
178 }
179 140
180 pthread_mutex_unlock(&muhmutex); 141 { /* Call */
181 break; 142 TOXAV_ERR_CALL rc;
143 toxav_call(AliceAV, friend_number, 48, 3000, &rc);
182 144
183 case 2: /* Rtp transmission */ 145 if (rc != TOXAV_ERR_CALL_OK) {
184 pthread_mutex_lock(&muhmutex); 146 printf("toxav_call failed: %d\n", rc);
185 147 ck_assert(0);
186 if (this_call->Caller.status == InCall) { /* I think this is okay */ 148 }
187 call_print(call_idx, "Sending rtp ..."); 149 }
188 pthread_mutex_unlock(&muhmutex);
189
190 c_sleep(1000); /* We have race condition here */
191 toxav_prepare_transmission(this_call->Callee.av, 0, 1);
192 toxav_prepare_transmission(this_call->Caller.av, call_idx, 1);
193
194 int payload_size = toxav_prepare_audio_frame(this_call->Caller.av, call_idx, prepared_payload, RTP_PAYLOAD_SIZE,
195 sample_payload, frame_size);
196
197 if ( payload_size < 0 ) {
198 ck_assert_msg ( 0, "Failed to encode payload" );
199 }
200
201
202 while (time(NULL) - start < 10) { /* 10 seconds */
203 /* Both send */
204 toxav_send_audio(this_call->Caller.av, call_idx, prepared_payload, payload_size);
205 150
206 toxav_send_audio(this_call->Callee.av, 0, prepared_payload, payload_size); 151 while (!BobCC->incoming)
152 c_sleep(10);
207 153
208 /* Both receive */ 154 { /* Answer */
209 int16_t storage[RTP_PAYLOAD_SIZE]; 155 TOXAV_ERR_ANSWER rc;
210 int recved; 156 toxav_answer(BobAV, 0, 8, 500, &rc);
211 157
212 c_sleep(20); 158 if (rc != TOXAV_ERR_ANSWER_OK) {
213 } 159 printf("toxav_answer failed: %d\n", rc);
160 ck_assert(0);
161 }
162 }
214 163
215 step++; /* This terminates the loop */ 164 c_sleep(30);
216 165
217 pthread_mutex_lock(&muhmutex); 166 int16_t PCM[960];
218 toxav_kill_transmission(this_call->Callee.av, 0); 167 uint8_t video_y[800 * 600];
219 toxav_kill_transmission(this_call->Caller.av, call_idx); 168 uint8_t video_u[800 * 600 / 2];
220 pthread_mutex_unlock(&muhmutex); 169 uint8_t video_v[800 * 600 / 2];
221 170
222 /* Call over CALLER hangs up */ 171 memset(PCM, 0, sizeof(PCM));
223 toxav_hangup(this_call->Caller.av, call_idx); 172 memset(video_y, 0, sizeof(video_y));
224 call_print(call_idx, "Hanging up ..."); 173 memset(video_u, 0, sizeof(video_u));
174 memset(video_v, 0, sizeof(video_v));
225 175
226 pthread_mutex_lock(&muhmutex); 176 time_t start_time = time(NULL);
227 }
228 177
229 pthread_mutex_unlock(&muhmutex); 178 while (time(NULL) - start_time < 4) {
230 break; 179 toxav_iterate(AliceAV);
180 toxav_iterate(BobAV);
231 181
232 case 3: /* Wait for Both to have status ended */ 182 toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL);
233 pthread_mutex_lock(&muhmutex); 183 toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL);
234 184
235 if (this_call->Caller.status == Ended) { 185 toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL);
236 pthread_mutex_unlock(&muhmutex); 186 toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL);
237 c_sleep(1000); /* race condition */
238 pthread_mutex_lock(&muhmutex);
239 this_call->Callee.status = Ended;
240 call_running[this_call->idx] = 0;
241 }
242 187
243 pthread_mutex_unlock(&muhmutex); 188 c_sleep(10);
189 }
244 190
245 break; 191 { /* Hangup */
192 TOXAV_ERR_CALL_CONTROL rc;
193 toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc);
246 194
195 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
196 printf("toxav_call_control failed: %d %p %p\n", rc, AliceAV, BobAV);
247 } 197 }
248
249 c_sleep(20);
250
251 pthread_mutex_lock(&muhmutex);
252 } 198 }
253 199
254 pthread_mutex_unlock(&muhmutex); 200 c_sleep(30);
255 call_print(call_idx, "Call ended successfully!"); 201
202 printf ("Closing thread\n");
256 pthread_exit(NULL); 203 pthread_exit(NULL);
257} 204}
258 205
259 206
207START_TEST(test_AV_three_calls)
208{
209 Tox *Alice, *bootstrap, *Bobs[3];
210 ToxAV *AliceAV, *BobsAV[3];
260 211
212 CallControl AliceCC[3], BobsCC[3];
261 213
214 {
215 TOX_ERR_NEW error;
262 216
263START_TEST(test_AV_three_calls) 217 bootstrap = tox_new(NULL, &error);
264// void test_AV_three_calls() 218 ck_assert(error == TOX_ERR_NEW_OK);
265{
266 long long unsigned int cur_time = time(NULL);
267 Tox *bootstrap_node = tox_new(0, 0);
268 Tox *caller = tox_new(0, 0);
269 Tox *callees[3] = {
270 tox_new(0, 0),
271 tox_new(0, 0),
272 tox_new(0, 0),
273 };
274 219
220 Alice = tox_new(NULL, &error);
221 ck_assert(error == TOX_ERR_NEW_OK);
275 222
276 ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node"); 223 Bobs[0] = tox_new(NULL, &error);
224 ck_assert(error == TOX_ERR_NEW_OK);
277 225
278 int i = 0; 226 Bobs[1] = tox_new(NULL, &error);
227 ck_assert(error == TOX_ERR_NEW_OK);
279 228
280 for (; i < 3; i ++) { 229 Bobs[2] = tox_new(NULL, &error);
281 ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances"); 230 ck_assert(error == TOX_ERR_NEW_OK);
282 } 231 }
283 232
284 for ( i = 0; i < 3; i ++ ) { 233 printf("Created 5 instances of Tox\n");
285 uint32_t to_compare = 974536; 234 printf("Preparing network...\n");
286 tox_callback_friend_request(callees[i], accept_friend_request, &to_compare); 235 long long unsigned int cur_time = time(NULL);
287 uint8_t address[TOX_ADDRESS_SIZE];
288 tox_self_get_address(callees[i], address);
289 236
290 uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0); 237 uint32_t to_compare = 974536;
291 ck_assert_msg( test == i, "Failed to add friend error code: %i", test); 238 uint8_t address[TOX_ADDRESS_SIZE];
292 }
293 239
294 uint8_t off = 1; 240 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
241 tox_self_get_address(Alice, address);
295 242
296 while (1) {
297 tox_iterate(bootstrap_node);
298 tox_iterate(caller);
299 243
300 for (i = 0; i < 3; i ++) { 244 ck_assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
301 tox_iterate(callees[i]); 245 ck_assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
302 } 246 ck_assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
303 247
248 uint8_t off = 1;
304 249
305 if (tox_self_get_connection_status(bootstrap_node) && 250 while (1) {
306 tox_self_get_connection_status(caller) && 251 tox_iterate(bootstrap);
307 tox_self_get_connection_status(callees[0]) && 252 tox_iterate(Alice);
308 tox_self_get_connection_status(callees[1]) && 253 tox_iterate(Bobs[0]);
309 tox_self_get_connection_status(callees[2]) && off) { 254 tox_iterate(Bobs[1]);
255 tox_iterate(Bobs[2]);
256
257 if (tox_self_get_connection_status(bootstrap) &&
258 tox_self_get_connection_status(Alice) &&
259 tox_self_get_connection_status(Bobs[0]) &&
260 tox_self_get_connection_status(Bobs[1]) &&
261 tox_self_get_connection_status(Bobs[2]) && off) {
310 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); 262 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
311 off = 0; 263 off = 0;
312 } 264 }
313 265
314 266 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
315 if (tox_friend_get_connection_status(caller, 0, 0) && 267 tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP &&
316 tox_friend_get_connection_status(caller, 1, 0) && 268 tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP &&
317 tox_friend_get_connection_status(caller, 2, 0) ) 269 tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP &&
270 tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP &&
271 tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP)
318 break; 272 break;
319 273
320 c_sleep(20); 274 c_sleep(20);
321 } 275 }
322 276
323 printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); 277 AliceAV = setup_av_instance(Alice, AliceCC);
324 278 BobsAV[0] = setup_av_instance(Bobs[0], BobsCC + 0);
325 ToxAv *uniqcallerav = toxav_new(caller, 3); 279 BobsAV[1] = setup_av_instance(Bobs[1], BobsCC + 1);
326 280 BobsAV[2] = setup_av_instance(Bobs[2], BobsCC + 2);
327 for (i = 0; i < 3; i ++) { 281
328 status_control.calls[i].idx = i; 282 printf("Created 4 instances of ToxAV\n");
329 283 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
330 status_control.calls[i].Caller.av = uniqcallerav; 284
331 status_control.calls[i].Caller.id = 0; 285 thread_data tds[3];
332 status_control.calls[i].Caller.status = none; 286 tds[0].AliceAV = AliceAV;
333 287 tds[0].BobAV = BobsAV[0];
334 status_control.calls[i].Callee.av = toxav_new(callees[i], 1); 288 tds[0].AliceCC = AliceCC + 0;
335 status_control.calls[i].Callee.id = i; 289 tds[0].BobCC = BobsCC + 0;
336 status_control.calls[i].Callee.status = none; 290 tds[0].friend_number = 0;
337 } 291
338 292 tds[1].AliceAV = AliceAV;
339 pthread_mutex_init(&muhmutex, NULL); 293 tds[1].BobAV = BobsAV[1];
340 294 tds[1].AliceCC = AliceCC + 1;
341 for ( i = 0; i < 3; i++ ) { 295 tds[1].BobCC = BobsCC + 1;
342 call_running[i] = 1; 296 tds[1].friend_number = 1;
343 pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]); 297
344 } 298 tds[2].AliceAV = AliceAV;
345 299 tds[2].BobAV = BobsAV[2];
346 /* Now start 3 calls and they'll run for 10 s */ 300 tds[2].AliceCC = AliceCC + 2;
347 301 tds[2].BobCC = BobsCC + 2;
348 for ( i = 0; i < 3; i++ ) 302 tds[2].friend_number = 2;
349 pthread_detach(status_control.calls[i].tid); 303
350 304 pthread_t tids[3];
351 while (call_running[0] || call_running[1] || call_running[2]) { 305 (void) pthread_create(tids + 0, NULL, call_thread, tds + 0);
352 pthread_mutex_lock(&muhmutex); 306 (void) pthread_create(tids + 1, NULL, call_thread, tds + 1);
353 307 (void) pthread_create(tids + 2, NULL, call_thread, tds + 2);
354 tox_iterate(bootstrap_node); 308
355 tox_iterate(caller); 309 (void) pthread_detach(tids[0]);
356 tox_iterate(callees[0]); 310 (void) pthread_detach(tids[1]);
357 tox_iterate(callees[1]); 311 (void) pthread_detach(tids[2]);
358 tox_iterate(callees[2]); 312
359 313 time_t start_time = time(NULL);
360 for ( i = 0; i < 3; i++ ) 314
361 toxav_do(status_control.calls[0].Caller.av); 315 while (time(NULL) - start_time < 5) {
362 316 tox_iterate(Alice);
363 toxav_do(status_control.calls[0].Callee.av); 317 tox_iterate(Bobs[0]);
364 toxav_do(status_control.calls[1].Callee.av); 318 tox_iterate(Bobs[1]);
365 toxav_do(status_control.calls[2].Callee.av); 319 tox_iterate(Bobs[2]);
366
367 pthread_mutex_unlock(&muhmutex);
368 c_sleep(20); 320 c_sleep(20);
369 } 321 }
370 322
371 toxav_kill(status_control.calls[0].Caller.av); 323 (void) pthread_join(tids[0], NULL);
372 toxav_kill(status_control.calls[0].Callee.av); 324 (void) pthread_join(tids[1], NULL);
373 toxav_kill(status_control.calls[1].Callee.av); 325 (void) pthread_join(tids[2], NULL);
374 toxav_kill(status_control.calls[2].Callee.av); 326
375 327 printf ("Killing all instances\n");
376 tox_kill(bootstrap_node); 328 toxav_kill(BobsAV[0]);
377 tox_kill(caller); 329 toxav_kill(BobsAV[1]);
378 330 toxav_kill(BobsAV[2]);
379 for ( i = 0; i < 3; i ++) 331 toxav_kill(AliceAV);
380 tox_kill(callees[i]); 332 tox_kill(Bobs[0]);
381 333 tox_kill(Bobs[1]);
334 tox_kill(Bobs[2]);
335 tox_kill(Alice);
336 tox_kill(bootstrap);
337
338 printf("\nTest successful!\n");
382} 339}
383END_TEST 340END_TEST
384 341
385 342
343#ifndef HAVE_LIBCHECK
344int main(int argc, char *argv[])
345{
346 (void) argc;
347 (void) argv;
386 348
387 349 test_AV_three_calls();
350 return 0;
351}
352#else
388Suite *tox_suite(void) 353Suite *tox_suite(void)
389{ 354{
390 Suite *s = suite_create("ToxAV"); 355 Suite *s = suite_create("ToxAV");
@@ -399,6 +364,9 @@ Suite *tox_suite(void)
399 364
400int main(int argc, char *argv[]) 365int main(int argc, char *argv[])
401{ 366{
367 (void) argc;
368 (void) argv;
369
402 Suite *tox = tox_suite(); 370 Suite *tox = tox_suite();
403 SRunner *test_runner = srunner_create(tox); 371 SRunner *test_runner = srunner_create(tox);
404 372
@@ -410,8 +378,5 @@ int main(int argc, char *argv[])
410 srunner_free(test_runner); 378 srunner_free(test_runner);
411 379
412 return number_failed; 380 return number_failed;
413
414// test_AV_three_calls();
415
416// return 0;
417} 381}
382#endif
diff --git a/configure.ac b/configure.ac
index 30f71968..ecbb5e46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -33,7 +33,7 @@ BUILD_TESTS="yes"
33BUILD_AV="yes" 33BUILD_AV="yes"
34BUILD_TESTING="yes" 34BUILD_TESTING="yes"
35 35
36LOGGING="no" 36TOX_LOGGER="no"
37LOGGING_OUTNAM="libtoxcore.log" 37LOGGING_OUTNAM="libtoxcore.log"
38 38
39NCURSES_FOUND="no" 39NCURSES_FOUND="no"
@@ -82,13 +82,13 @@ AC_ARG_ENABLE([randombytes-stir],
82 ] 82 ]
83) 83)
84 84
85AC_ARG_ENABLE([log], 85AC_ARG_ENABLE([logging],
86 [AC_HELP_STRING([--enable-log], [enable logging (default: auto)]) ], 86 [AC_HELP_STRING([--enable-logging], [enable logging (default: auto)]) ],
87 [ 87 [
88 if test "x$enableval" = "xyes"; then 88 if test "x$enableval" = "xyes"; then
89 LOGGING="yes" 89 TOX_LOGGER="yes"
90 90
91 AC_DEFINE([LOGGING], [], [If logging enabled]) 91 AC_DEFINE([TOX_LOGGER], [], [If logging enabled])
92 AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value]) 92 AC_DEFINE([LOGGER_LEVEL], [LOG_DEBUG], [LOG_LEVEL value])
93 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger]) 93 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$LOGGING_OUTNAM"], [Output of logger])
94 fi 94 fi
@@ -99,7 +99,7 @@ AC_ARG_WITH(log-level,
99 AC_HELP_STRING([--with-log-level=LEVEL], 99 AC_HELP_STRING([--with-log-level=LEVEL],
100 [Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]), 100 [Logger levels: TRACE; DEBUG; INFO; WARNING; ERROR ]),
101 [ 101 [
102 if test "x$LOGGING" = "xno"; then 102 if test "x$TOX_LOGGER" = "xno"; then
103 AC_MSG_WARN([Logging disabled!]) 103 AC_MSG_WARN([Logging disabled!])
104 else 104 else
105 if test "x$withval" = "xTRACE"; then 105 if test "x$withval" = "xTRACE"; then
@@ -127,7 +127,7 @@ AC_ARG_WITH(log-path,
127 AC_HELP_STRING([--with-log-path=DIR], 127 AC_HELP_STRING([--with-log-path=DIR],
128 [Path of logger output]), 128 [Path of logger output]),
129 [ 129 [
130 if test "x$LOGGING" = "xno"; then 130 if test "x$TOX_LOGGER" = "xno"; then
131 AC_MSG_WARN([Logging disabled!]) 131 AC_MSG_WARN([Logging disabled!])
132 else 132 else
133 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger]) 133 AC_DEFINE_UNQUOTED([LOGGER_OUTPUT_FILE], ["$withval""/""$LOGGING_OUTNAM"], [Output of logger])
diff --git a/other/apidsl/toxav.in.h b/other/apidsl/toxav.in.h
new file mode 100644
index 00000000..ab89b0ea
--- /dev/null
+++ b/other/apidsl/toxav.in.h
@@ -0,0 +1,615 @@
1%{
2/* toxav.h
3 *
4 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
5 *
6 * This file is part of Tox.
7 *
8 * Tox is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Tox is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#ifndef TOXAV_H
24#define TOXAV_H
25
26#include <stdbool.h>
27#include <stddef.h>
28#include <stdint.h>
29
30#ifdef __cplusplus
31extern "C" {
32#endif
33%}
34
35/** \page av Public audio/video API for Tox clients.
36 *
37 * This API can handle multiple calls. Each call has its state, in very rare
38 * occasions the library can change the state of the call without apps knowledge.
39 *
40 */
41
42/** \subsection events Events and callbacks
43 *
44 * As in Core API, events are handled by callbacks. One callback can be
45 * registered per event. All events have a callback function type named
46 * `toxav_{event}_cb` and a function to register it named `tox_callback_{event}`.
47 * Passing a NULL callback will result in no callback being registered for that
48 * event. Only one callback per event can be registered, so if a client needs
49 * multiple event listeners, it needs to implement the dispatch functionality
50 * itself. Unlike Core API, lack of some event handlers will cause the the
51 * library to drop calls before they are started. Hanging up call from a
52 * callback causes undefined behaviour.
53 *
54 */
55
56/** \subsection threading Threading implications
57 *
58 * Unlike the Core API, this API is fully thread-safe. The library will ensure
59 * the proper synchronization of parallel calls.
60 *
61 * A common way to run ToxAV (multiple or single instance) is to have a thread,
62 * separate from tox instance thread, running a simple ${toxAV.iterate} loop,
63 * sleeping for ${toxAV.iteration_interval} * milliseconds on each iteration.
64 *
65 * An important thing to note is that events are triggered from both tox and
66 * toxav thread (see above). audio and video receive frame events are triggered
67 * from toxav thread while all the other events are triggered from tox thread.
68 *
69 * Tox thread has priority with mutex mechanisms. Any api function can
70 * fail if mutexes are held by tox thread in which case they will set SYNC
71 * error code.
72 */
73
74/**
75 * External Tox type.
76 */
77class tox {
78 struct this;
79}
80
81/**
82 * ToxAV.
83 */
84class toxAV {
85
86/**
87 * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
88 * instance, and Tox instance can have only one ToxAV instance. One must make
89 * sure to close ToxAV instance prior closing Tox instance otherwise undefined
90 * behaviour occurs. Upon closing of ToxAV instance, all active calls will be
91 * forcibly terminated without notifying peers.
92 *
93 */
94struct this;
95/*******************************************************************************
96 *
97 * :: API version
98 *
99 ******************************************************************************/
100/**
101 * The major version number. Incremented when the API or ABI changes in an
102 * incompatible way.
103 */
104#define TOXAV_VERSION_MAJOR 0u
105/**
106 * The minor version number. Incremented when functionality is added without
107 * breaking the API or ABI. Set to 0 when the major version number is
108 * incremented.
109 */
110#define TOXAV_VERSION_MINOR 0u
111/**
112 * The patch or revision number. Incremented when bugfixes are applied without
113 * changing any functionality or API or ABI.
114 */
115#define TOXAV_VERSION_PATCH 0u
116
117/**
118 * A macro to check at preprocessing time whether the client code is compatible
119 * with the installed version of ToxAV.
120 */
121#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
122 (TOXAV_VERSION_MAJOR == MAJOR && \
123 (TOXAV_VERSION_MINOR > MINOR || \
124 (TOXAV_VERSION_MINOR == MINOR && \
125 TOXAV_VERSION_PATCH >= PATCH)))
126
127/**
128 * A macro to make compilation fail if the client code is not compatible with
129 * the installed version of ToxAV.
130 */
131#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
132 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
133
134/**
135 * A convenience macro to call ${version.is_compatible} with the currently
136 * compiling API version.
137 */
138#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
139 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
140
141
142static namespace version {
143
144 /**
145 * Return the major version number of the library. Can be used to display the
146 * ToxAV library version or to check whether the client is compatible with the
147 * dynamically linked version of ToxAV.
148 */
149 uint32_t major();
150
151 /**
152 * Return the minor version number of the library.
153 */
154 uint32_t minor();
155
156 /**
157 * Return the patch number of the library.
158 */
159 uint32_t patch();
160
161 /**
162 * Return whether the compiled library version is compatible with the passed
163 * version numbers.
164 */
165 bool is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
166
167}
168/*******************************************************************************
169 *
170 * :: Creation and destruction
171 *
172 ******************************************************************************/
173/**
174 * Start new A/V session. There can only be only one session per Tox instance.
175 */
176static this new (tox::this *tox) {
177 NULL,
178 /**
179 * Memory allocation failure while trying to allocate structures required for
180 * the A/V session.
181 */
182 MALLOC,
183 /**
184 * Attempted to create a second session for the same Tox instance.
185 */
186 MULTIPLE,
187}
188/**
189 * Releases all resources associated with the A/V session.
190 *
191 * If any calls were ongoing, these will be forcibly terminated without
192 * notifying peers. After calling this function, no other functions may be
193 * called and the av pointer becomes invalid.
194 */
195void kill();
196/**
197 * Returns the Tox instance the A/V object was created for.
198 */
199tox::this *tox { get(); }
200/*******************************************************************************
201 *
202 * :: A/V event loop
203 *
204 ******************************************************************************/
205/**
206 * Returns the interval in milliseconds when the next toxav_iterate call should
207 * be. If no call is active at the moment, this function returns 200.
208 */
209const uint32_t iteration_interval();
210/**
211 * Main loop for the session. This function needs to be called in intervals of
212 * toxav_iteration_interval() milliseconds. It is best called in the separate
213 * thread from tox_iterate.
214 */
215void iterate();
216/*******************************************************************************
217 *
218 * :: Call setup
219 *
220 ******************************************************************************/
221/**
222 * Call a friend. This will start ringing the friend.
223 *
224 * It is the client's responsibility to stop ringing after a certain timeout,
225 * if such behaviour is desired. If the client does not stop ringing, the
226 * library will not stop until the friend is disconnected. Audio and video
227 * receiving are both enabled by default.
228 *
229 * @param friend_number The friend number of the friend that should be called.
230 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
231 * audio sending.
232 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
233 * video sending.
234 */
235bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
236 /**
237 * A resource allocation error occurred while trying to create the structures
238 * required for the call.
239 */
240 MALLOC,
241 /**
242 * Synchronization error occurred.
243 */
244 SYNC,
245 /**
246 * The friend number did not designate a valid friend.
247 */
248 FRIEND_NOT_FOUND,
249 /**
250 * The friend was valid, but not currently connected.
251 */
252 FRIEND_NOT_CONNECTED,
253 /**
254 * Attempted to call a friend while already in an audio or video call with
255 * them.
256 */
257 FRIEND_ALREADY_IN_CALL,
258 /**
259 * Audio or video bit rate is invalid.
260 */
261 INVALID_BIT_RATE,
262}
263event call {
264 /**
265 * The function type for the ${event call} callback.
266 *
267 * @param friend_number The friend number from which the call is incoming.
268 * @param audio_enabled True if friend is sending audio.
269 * @param video_enabled True if friend is sending video.
270 */
271 typedef void(uint32_t friend_number, bool audio_enabled, bool video_enabled);
272}
273/**
274 * Accept an incoming call.
275 *
276 * If answering fails for any reason, the call will still be pending and it is
277 * possible to try and answer it later. Audio and video receiving are both
278 * enabled by default.
279 *
280 * @param friend_number The friend number of the friend that is calling.
281 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
282 * audio sending.
283 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
284 * video sending.
285 */
286bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
287 /**
288 * Synchronization error occurred.
289 */
290 SYNC,
291 /**
292 * Failed to initialize codecs for call session. Note that codec initiation
293 * will fail if there is no receive callback registered for either audio or
294 * video.
295 */
296 CODEC_INITIALIZATION,
297 /**
298 * The friend number did not designate a valid friend.
299 */
300 FRIEND_NOT_FOUND,
301 /**
302 * The friend was valid, but they are not currently trying to initiate a call.
303 * This is also returned if this client is already in a call with the friend.
304 */
305 FRIEND_NOT_CALLING,
306 /**
307 * Audio or video bit rate is invalid.
308 */
309 INVALID_BIT_RATE,
310}
311/*******************************************************************************
312 *
313 * :: Call state graph
314 *
315 ******************************************************************************/
316bitmask FRIEND_CALL_STATE {
317 /**
318 * Set by the AV core if an error occurred on the remote end or if friend
319 * timed out. This is the final state after which no more state
320 * transitions can occur for the call. This call state will never be triggered
321 * in combination with other call states.
322 */
323 ERROR,
324 /**
325 * The call has finished. This is the final state after which no more state
326 * transitions can occur for the call. This call state will never be
327 * triggered in combination with other call states.
328 */
329 FINISHED,
330 /**
331 * The flag that marks that friend is sending audio.
332 */
333 SENDING_A,
334 /**
335 * The flag that marks that friend is sending video.
336 */
337 SENDING_V,
338 /**
339 * The flag that marks that friend is receiving audio.
340 */
341 ACCEPTING_A,
342 /**
343 * The flag that marks that friend is receiving video.
344 */
345 ACCEPTING_V,
346}
347event call_state {
348 /**
349 * The function type for the ${event call_state} callback.
350 *
351 * @param friend_number The friend number for which the call state changed.
352 * @param state The bitmask of the new call state which is guaranteed to be
353 * different than the previous state. The state is set to 0 when the call is
354 * paused. The bitmask represents all the activities currently performed by the
355 * friend.
356 */
357 typedef void(uint32_t friend_number, uint32_t state);
358}
359/*******************************************************************************
360 *
361 * :: Call control
362 *
363 ******************************************************************************/
364enum class CALL_CONTROL {
365 /**
366 * Resume a previously paused call. Only valid if the pause was caused by this
367 * client, if not, this control is ignored. Not valid before the call is accepted.
368 */
369 RESUME,
370 /**
371 * Put a call on hold. Not valid before the call is accepted.
372 */
373 PAUSE,
374 /**
375 * Reject a call if it was not answered, yet. Cancel a call after it was
376 * answered.
377 */
378 CANCEL,
379 /**
380 * Request that the friend stops sending audio. Regardless of the friend's
381 * compliance, this will cause the ${event audio.receive_frame} event to stop being
382 * triggered on receiving an audio frame from the friend.
383 */
384 MUTE_AUDIO,
385 /**
386 * Calling this control will notify client to start sending audio again.
387 */
388 UNMUTE_AUDIO,
389 /**
390 * Request that the friend stops sending video. Regardless of the friend's
391 * compliance, this will cause the ${event video.receive_frame} event to stop being
392 * triggered on receiving a video frame from the friend.
393 */
394 HIDE_VIDEO,
395 /**
396 * Calling this control will notify client to start sending video again.
397 */
398 SHOW_VIDEO,
399}
400/**
401 * Sends a call control command to a friend.
402 *
403 * @param friend_number The friend number of the friend this client is in a call
404 * with.
405 * @param control The control command to send.
406 *
407 * @return true on success.
408 */
409bool call_control (uint32_t friend_number, CALL_CONTROL control) {
410 /**
411 * Synchronization error occurred.
412 */
413 SYNC,
414 /**
415 * The friend_number passed did not designate a valid friend.
416 */
417 FRIEND_NOT_FOUND,
418 /**
419 * This client is currently not in a call with the friend. Before the call is
420 * answered, only CANCEL is a valid control.
421 */
422 FRIEND_NOT_IN_CALL,
423 /**
424 * Happens if user tried to pause an already paused call or if trying to
425 * resume a call that is not paused.
426 */
427 INVALID_TRANSITION,
428}
429/*******************************************************************************
430 *
431 * :: Controlling bit rates
432 *
433 ******************************************************************************/
434namespace bit_rate {
435 /**
436 * Set the audio bit rate to be used in subsequent audio/video frames.
437 *
438 * @param friend_number The friend number.
439 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
440 * audio sending. Set to -1 to leave unchanged.
441 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
442 * video sending. Set to -1 to leave unchanged.
443 *
444 */
445 bool set(uint32_t friend_number, int32_t audio_bit_rate, int32_t video_bit_rate) {
446 /**
447 * Synchronization error occurred.
448 */
449 SYNC,
450 /**
451 * The bit rate passed was not one of the supported values.
452 */
453 INVALID,
454 /**
455 * The friend_number passed did not designate a valid friend.
456 */
457 FRIEND_NOT_FOUND,
458 /**
459 * This client is currently not in a call with the friend.
460 */
461 FRIEND_NOT_IN_CALL,
462 }
463 event status {
464 /**
465 * The function type for the ${event status} callback. The event is triggered
466 * when the network becomes too saturated for current bit rates at which
467 * point core suggests new bit rates.
468 *
469 * @param friend_number The friend number.
470 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
471 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
472 */
473 typedef void(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate);
474 }
475}
476/*******************************************************************************
477 *
478 * :: A/V sending
479 *
480 ******************************************************************************/
481error for send_frame {
482 /**
483 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
484 * data pointer was NULL.
485 */
486 NULL,
487 /**
488 * The friend_number passed did not designate a valid friend.
489 */
490 FRIEND_NOT_FOUND,
491 /**
492 * This client is currently not in a call with the friend.
493 */
494 FRIEND_NOT_IN_CALL,
495 /**
496 * Synchronization error occurred.
497 */
498 SYNC,
499 /**
500 * One of the frame parameters was invalid. E.g. the resolution may be too
501 * small or too large, or the audio sampling rate may be unsupported.
502 */
503 INVALID,
504 /**
505 * Either friend turned off audio or video receiving or we turned off sending
506 * for the said payload.
507 */
508 PAYLOAD_TYPE_DISABLED,
509 /**
510 * Failed to push frame through rtp interface.
511 */
512 RTP_FAILED,
513}
514namespace audio {
515 /**
516 * Send an audio frame to a friend.
517 *
518 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
519 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
520 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
521 * this means the expected format is LRLRLR... with samples for left and right
522 * alternating.
523 *
524 * @param friend_number The friend number of the friend to which to send an
525 * audio frame.
526 * @param pcm An array of audio samples. The size of this array must be
527 * sample_count * channels.
528 * @param sample_count Number of samples in this frame. Valid numbers here are
529 * ((sample rate) * (audio length) / 1000), where audio length can be
530 * 2.5, 5, 10, 20, 40 or 60 millseconds.
531 * @param channels Number of audio channels. Supported values are 1 and 2.
532 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
533 * rates are 8000, 12000, 16000, 24000, or 48000.
534 */
535 bool send_frame(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
536 uint8_t channels, uint32_t sampling_rate) with error for send_frame;
537}
538namespace video {
539 /**
540 * Send a video frame to a friend.
541 *
542 * Y - plane should be of size: height * width
543 * U - plane should be of size: (height/2) * (width/2)
544 * V - plane should be of size: (height/2) * (width/2)
545 *
546 * @param friend_number The friend number of the friend to which to send a video
547 * frame.
548 * @param width Width of the frame in pixels.
549 * @param height Height of the frame in pixels.
550 * @param y Y (Luminance) plane data.
551 * @param u U (Chroma) plane data.
552 * @param v V (Chroma) plane data.
553 */
554 bool send_frame(uint32_t friend_number, uint16_t width, uint16_t height,
555 const uint8_t *y, const uint8_t *u, const uint8_t *v) with error for send_frame;
556}
557/*******************************************************************************
558 *
559 * :: A/V receiving
560 *
561 ******************************************************************************/
562namespace audio {
563 event receive_frame {
564 /**
565 * The function type for the ${event receive_frame} callback. The callback can be
566 * called multiple times per single iteration depending on the amount of queued
567 * frames in the buffer. The received format is the same as in send function.
568 *
569 * @param friend_number The friend number of the friend who sent an audio frame.
570 * @param pcm An array of audio samples (sample_count * channels elements).
571 * @param sample_count The number of audio samples per channel in the PCM array.
572 * @param channels Number of audio channels.
573 * @param sampling_rate Sampling rate used in this frame.
574 *
575 */
576 typedef void(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
577 uint8_t channels, uint32_t sampling_rate);
578 }
579}
580namespace video {
581 event receive_frame {
582 /**
583 * The function type for the ${event receive_frame} callback.
584 *
585 * @param friend_number The friend number of the friend who sent a video frame.
586 * @param width Width of the frame in pixels.
587 * @param height Height of the frame in pixels.
588 * @param y
589 * @param u
590 * @param v Plane data.
591 * The size of plane data is derived from width and height where
592 * Y = MAX(width, abs(ystride)) * height,
593 * U = MAX(width/2, abs(ustride)) * (height/2) and
594 * V = MAX(width/2, abs(vstride)) * (height/2).
595 * @param ystride
596 * @param ustride
597 * @param vstride Strides data. Strides represent padding for each plane
598 * that may or may not be present. You must handle strides in
599 * your image processing code. Strides are negative if the
600 * image is bottom-up hence why you MUST abs() it when
601 * calculating plane buffer size.
602 */
603 typedef void(uint32_t friend_number, uint16_t width, uint16_t height,
604 const uint8_t *y, const uint8_t *u, const uint8_t *v,
605 int32_t ystride, int32_t ustride, int32_t vstride);
606 }
607}
608
609}
610%{
611#ifdef __cplusplus
612}
613#endif
614#endif /* TOXAV_H */
615%}
diff --git a/testing/av_test.c b/testing/av_test.c
new file mode 100644
index 00000000..4b2fe61d
--- /dev/null
+++ b/testing/av_test.c
@@ -0,0 +1,766 @@
1/** av_test.c
2 *
3 * Copyright (C) 2013-2015 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 * Compile with (Linux only; in newly created directory toxcore/dir_name):
21 * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \
22 * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio
23 */
24
25
26#include "../toxav/toxav.h"
27#include "../toxcore/tox.h"
28#include "../toxcore/util.h"
29#include "../toxcore/network.h" /* current_time_monotonic() */
30
31/* Playing audio data */
32#include <portaudio.h>
33/* Reading audio */
34#include <sndfile.h>
35
36/* Reading and Displaying video data */
37#include <opencv/cv.h>
38#include <opencv/highgui.h>
39#include <opencv/cvwimage.h>
40
41#include <sys/stat.h>
42#include <assert.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <time.h>
46#include <string.h>
47#include <errno.h>
48#include <unistd.h>
49
50#define c_sleep(x) usleep(1000*x)
51
52
53#define CLIP(X) ((X) > 255 ? 255 : (X) < 0 ? 0 : X)
54
55// RGB -> YUV
56#define RGB2Y(R, G, B) CLIP((( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
57#define RGB2U(R, G, B) CLIP(((-38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
58#define RGB2V(R, G, B) CLIP(((112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
59
60// YUV -> RGB
61#define C(Y) ((Y) - 16 )
62#define D(U) ((U) - 128 )
63#define E(V) ((V) - 128 )
64
65#define YUV2R(Y, U, V) CLIP((298 * C(Y) + 409 * E(V) + 128) >> 8)
66#define YUV2G(Y, U, V) CLIP((298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
67#define YUV2B(Y, U, V) CLIP((298 * C(Y) + 516 * D(U) + 128) >> 8)
68
69
70#define TEST_TRANSFER_A 0
71#define TEST_TRANSFER_V 1
72
73
74typedef struct {
75 bool incoming;
76 uint32_t state;
77 pthread_mutex_t arb_mutex[1];
78 RingBuffer *arb; /* Audio ring buffer */
79
80} CallControl;
81
82struct toxav_thread_data {
83 ToxAV *AliceAV;
84 ToxAV *BobAV;
85 int32_t sig;
86};
87
88const char *vdout = "AV Test"; /* Video output */
89PaStream *adout = NULL; /* Audio output */
90
91typedef struct {
92 uint16_t size;
93 int16_t data[];
94} frame;
95
96void *pa_write_thread (void *d)
97{
98 /* The purpose of this thread is to make sure Pa_WriteStream will not block
99 * toxav_iterate thread
100 */
101 CallControl *cc = d;
102
103 while (Pa_IsStreamActive(adout)) {
104 frame *f;
105 pthread_mutex_lock(cc->arb_mutex);
106
107 if (rb_read(cc->arb, (void **)&f)) {
108 pthread_mutex_unlock(cc->arb_mutex);
109 Pa_WriteStream(adout, f->data, f->size);
110 free(f);
111 } else {
112 pthread_mutex_unlock(cc->arb_mutex);
113 c_sleep(10);
114 }
115 }
116}
117
118/**
119 * Callbacks
120 */
121void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
122{
123 printf("Handling CALL callback\n");
124 ((CallControl *)user_data)->incoming = true;
125}
126void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
127{
128 printf("Handling CALL STATE callback: %d\n", state);
129 ((CallControl *)user_data)->state = state;
130}
131void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
132 uint16_t width, uint16_t height,
133 uint8_t const *y, uint8_t const *u, uint8_t const *v,
134 int32_t ystride, int32_t ustride, int32_t vstride,
135 void *user_data)
136{
137 ystride = abs(ystride);
138 ustride = abs(ustride);
139 vstride = abs(vstride);
140
141 uint16_t *img_data = malloc(height * width * 6);
142
143 unsigned long int i, j;
144
145 for (i = 0; i < height; ++i) {
146 for (j = 0; j < width; ++j) {
147 uint8_t *point = (uint8_t *) img_data + 3 * ((i * width) + j);
148 int yx = y[(i * ystride) + j];
149 int ux = u[((i / 2) * ustride) + (j / 2)];
150 int vx = v[((i / 2) * vstride) + (j / 2)];
151
152 point[0] = YUV2R(yx, ux, vx);
153 point[1] = YUV2G(yx, ux, vx);
154 point[2] = YUV2B(yx, ux, vx);
155 }
156 }
157
158
159 CvMat mat = cvMat(height, width, CV_8UC3, img_data);
160
161 CvSize sz = {.height = height, .width = width};
162
163 IplImage *header = cvCreateImageHeader(sz, 1, 3);
164 IplImage *img = cvGetImage(&mat, header);
165 cvShowImage(vdout, img);
166 free(img_data);
167}
168void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
169 int16_t const *pcm,
170 size_t sample_count,
171 uint8_t channels,
172 uint32_t sampling_rate,
173 void *user_data)
174{
175 CallControl *cc = user_data;
176 frame *f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels);
177 memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels);
178 f->size = sample_count;
179
180 pthread_mutex_lock(cc->arb_mutex);
181 free(rb_write(cc->arb, f));
182 pthread_mutex_unlock(cc->arb_mutex);
183}
184void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
185 uint32_t audio_bit_rate, uint32_t video_bit_rate,
186 void *user_data)
187{
188 printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate);
189}
190void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
191{
192 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
193 assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
194 }
195}
196
197/**
198 */
199void initialize_tox(Tox **bootstrap, ToxAV **AliceAV, CallControl *AliceCC, ToxAV **BobAV, CallControl *BobCC)
200{
201 Tox *Alice;
202 Tox *Bob;
203
204 struct Tox_Options opts;
205 tox_options_default(&opts);
206
207 opts.end_port = 0;
208 opts.ipv6_enabled = false;
209
210 {
211 TOX_ERR_NEW error;
212
213 opts.start_port = 33445;
214 *bootstrap = tox_new(&opts, &error);
215 assert(error == TOX_ERR_NEW_OK);
216
217 opts.start_port = 33455;
218 Alice = tox_new(&opts, &error);
219 assert(error == TOX_ERR_NEW_OK);
220
221 opts.start_port = 33465;
222 Bob = tox_new(&opts, &error);
223 assert(error == TOX_ERR_NEW_OK);
224 }
225
226 printf("Created 3 instances of Tox\n");
227 printf("Preparing network...\n");
228 long long unsigned int cur_time = time(NULL);
229
230 uint32_t to_compare = 974536;
231 uint8_t address[TOX_ADDRESS_SIZE];
232
233 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
234 tox_self_get_address(Alice, address);
235
236
237 assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
238
239 uint8_t off = 1;
240
241 while (1) {
242 tox_iterate(*bootstrap);
243 tox_iterate(Alice);
244 tox_iterate(Bob);
245
246 if (tox_self_get_connection_status(*bootstrap) &&
247 tox_self_get_connection_status(Alice) &&
248 tox_self_get_connection_status(Bob) && off) {
249 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
250 off = 0;
251 }
252
253 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
254 tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
255 break;
256
257 c_sleep(20);
258 }
259
260
261 TOXAV_ERR_NEW rc;
262 *AliceAV = toxav_new(Alice, &rc);
263 assert(rc == TOXAV_ERR_NEW_OK);
264
265 *BobAV = toxav_new(Bob, &rc);
266 assert(rc == TOXAV_ERR_NEW_OK);
267
268
269 /* Alice */
270 toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
271 toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
272 toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC);
273 toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
274 toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
275
276 /* Bob */
277 toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
278 toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
279 toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC);
280 toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
281 toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
282
283
284 printf("Created 2 instances of ToxAV\n");
285 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
286}
287int iterate_tox(Tox *bootstrap, ToxAV *AliceAV, ToxAV *BobAV)
288{
289 tox_iterate(bootstrap);
290 tox_iterate(toxav_get_tox(AliceAV));
291 tox_iterate(toxav_get_tox(BobAV));
292
293 return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
294}
295void *iterate_toxav (void *data)
296{
297 struct toxav_thread_data *data_cast = data;
298#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
299 cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE);
300#endif
301
302 while (data_cast->sig == 0) {
303 toxav_iterate(data_cast->AliceAV);
304 toxav_iterate(data_cast->BobAV);
305 int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV));
306
307 printf("\rIteration interval: %d ", rc);
308 fflush(stdout);
309
310#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
311
312 if (!rc)
313 rc = 1;
314
315 cvWaitKey(rc);
316#else
317 c_sleep(rc);
318#endif
319 }
320
321 data_cast->sig = 1;
322
323#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
324 cvDestroyWindow(vdout);
325#endif
326
327 pthread_exit(NULL);
328}
329
330int send_opencv_img(ToxAV *av, uint32_t friend_number, const IplImage *img)
331{
332 int32_t strides[3] = { 1280, 640, 640 };
333 uint8_t *planes[3] = {
334 malloc(img->height * img->width),
335 malloc(img->height * img->width / 4),
336 malloc(img->height * img->width / 4),
337 };
338
339 int x_chroma_shift = 1;
340 int y_chroma_shift = 1;
341
342 int x, y;
343
344 for (y = 0; y < img->height; ++y) {
345 for (x = 0; x < img->width; ++x) {
346 uint8_t r = img->imageData[(x + y * img->width) * 3 + 0];
347 uint8_t g = img->imageData[(x + y * img->width) * 3 + 1];
348 uint8_t b = img->imageData[(x + y * img->width) * 3 + 2];
349
350 planes[0][x + y * strides[0]] = RGB2Y(r, g, b);
351
352 if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) {
353 const int i = x / (1 << x_chroma_shift);
354 const int j = y / (1 << y_chroma_shift);
355 planes[1][i + j * strides[1]] = RGB2U(r, g, b);
356 planes[2][i + j * strides[2]] = RGB2V(r, g, b);
357 }
358 }
359 }
360
361 int rc = toxav_video_send_frame(av, friend_number, img->width, img->height,
362 planes[0], planes[1], planes[2], NULL);
363 free(planes[0]);
364 free(planes[1]);
365 free(planes[2]);
366 return rc;
367}
368int print_audio_devices()
369{
370 int i = 0;
371
372 for (i = 0; i < Pa_GetDeviceCount(); ++i) {
373 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
374
375 if (info)
376 printf("%d) %s\n", i, info->name);
377 }
378
379 return 0;
380}
381int print_help (const char *name)
382{
383 printf("Usage: %s -[a:v:o:dh]\n"
384 "-a <path> audio input file\n"
385 "-b <ms> audio frame duration\n"
386 "-v <path> video input file\n"
387 "-x <ms> video frame duration\n"
388 "-o <idx> output audio device index\n"
389 "-d print output audio devices\n"
390 "-h print this help\n", name);
391
392 return 0;
393}
394
395int main (int argc, char **argv)
396{
397 freopen("/dev/zero", "w", stderr);
398 Pa_Initialize();
399
400 struct stat st;
401
402 /* AV files for testing */
403 const char *af_name = NULL;
404 const char *vf_name = NULL;
405 long audio_out_dev_idx = -1;
406
407 int32_t audio_frame_duration = 20;
408 int32_t video_frame_duration = 10;
409
410 /* Parse settings */
411CHECK_ARG:
412
413 switch (getopt(argc, argv, "a:b:v:x:o:dh")) {
414 case 'a':
415 af_name = optarg;
416 goto CHECK_ARG;
417
418 case 'b': {
419 char *d;
420 audio_frame_duration = strtol(optarg, &d, 10);
421
422 if (*d) {
423 printf("Invalid value for argument: 'b'");
424 exit(1);
425 }
426
427 goto CHECK_ARG;
428 }
429
430 case 'v':
431 vf_name = optarg;
432 goto CHECK_ARG;
433
434 case 'x': {
435 char *d;
436 video_frame_duration = strtol(optarg, &d, 10);
437
438 if (*d) {
439 printf("Invalid value for argument: 'x'");
440 exit(1);
441 }
442
443 goto CHECK_ARG;
444 }
445
446 case 'o': {
447 char *d;
448 audio_out_dev_idx = strtol(optarg, &d, 10);
449
450 if (*d) {
451 printf("Invalid value for argument: 'o'");
452 exit(1);
453 }
454
455 goto CHECK_ARG;
456 }
457
458 case 'd':
459 return print_audio_devices();
460
461 case 'h':
462 return print_help(argv[0]);
463
464 case '?':
465 exit(1);
466
467 case -1:
468 ;
469 }
470
471 { /* Check files */
472 if (!af_name) {
473 printf("Required audio input file!\n");
474 exit(1);
475 }
476
477 if (!vf_name) {
478 printf("Required video input file!\n");
479 exit(1);
480 }
481
482 /* Check for files */
483 if (stat(af_name, &st) != 0 || !S_ISREG(st.st_mode)) {
484 printf("%s doesn't seem to be a regular file!\n", af_name);
485 exit(1);
486 }
487
488 if (stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode)) {
489 printf("%s doesn't seem to be a regular file!\n", vf_name);
490 exit(1);
491 }
492 }
493
494 if (audio_out_dev_idx < 0)
495 audio_out_dev_idx = Pa_GetDefaultOutputDevice();
496
497 const PaDeviceInfo *audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx);
498
499 if (!audio_dev) {
500 fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx);
501 return 1;
502 }
503
504 printf("Using audio device: %s\n", audio_dev->name);
505 printf("Using audio file: %s\n", af_name);
506 printf("Using video file: %s\n", vf_name);
507
508 /* START TOX NETWORK */
509
510 Tox *bootstrap;
511 ToxAV *AliceAV;
512 ToxAV *BobAV;
513
514 CallControl AliceCC;
515 CallControl BobCC;
516
517 initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC);
518
519 if (TEST_TRANSFER_A) {
520 SNDFILE *af_handle;
521 SF_INFO af_info;
522
523 printf("\nTrying audio enc/dec...\n");
524
525 memset(&AliceCC, 0, sizeof(CallControl));
526 memset(&BobCC, 0, sizeof(CallControl));
527
528 pthread_mutex_init(AliceCC.arb_mutex, NULL);
529 pthread_mutex_init(BobCC.arb_mutex, NULL);
530
531 AliceCC.arb = rb_new(16);
532 BobCC.arb = rb_new(16);
533
534 { /* Call */
535 TOXAV_ERR_CALL rc;
536 toxav_call(AliceAV, 0, 48, 0, &rc);
537
538 if (rc != TOXAV_ERR_CALL_OK) {
539 printf("toxav_call failed: %d\n", rc);
540 exit(1);
541 }
542 }
543
544 while (!BobCC.incoming)
545 iterate_tox(bootstrap, AliceAV, BobAV);
546
547 { /* Answer */
548 TOXAV_ERR_ANSWER rc;
549 toxav_answer(BobAV, 0, 48, 0, &rc);
550
551 if (rc != TOXAV_ERR_ANSWER_OK) {
552 printf("toxav_answer failed: %d\n", rc);
553 exit(1);
554 }
555 }
556
557 while (AliceCC.state == 0)
558 iterate_tox(bootstrap, AliceAV, BobAV);
559
560 /* Open audio file */
561 af_handle = sf_open(af_name, SFM_READ, &af_info);
562
563 if (af_handle == NULL) {
564 printf("Failed to open the file.\n");
565 exit(1);
566 }
567
568 int16_t PCM[5760];
569
570 time_t start_time = time(NULL);
571 time_t expected_time = af_info.frames / af_info.samplerate + 2;
572
573
574 /* Start decode thread */
575 struct toxav_thread_data data = {
576 .AliceAV = AliceAV,
577 .BobAV = BobAV,
578 .sig = 0
579 };
580
581 pthread_t dect;
582 pthread_create(&dect, NULL, iterate_toxav, &data);
583 pthread_detach(dect);
584
585 int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels;
586
587 struct PaStreamParameters output;
588 output.device = audio_out_dev_idx;
589 output.channelCount = af_info.channels;
590 output.sampleFormat = paInt16;
591 output.suggestedLatency = audio_dev->defaultHighOutputLatency;
592 output.hostApiSpecificStreamInfo = NULL;
593
594 PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL);
595 assert(err == paNoError);
596
597 err = Pa_StartStream(adout);
598 assert(err == paNoError);
599
600// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
601
602 /* Start write thread */
603 pthread_t t;
604 pthread_create(&t, NULL, pa_write_thread, &BobCC);
605 pthread_detach(t);
606
607 printf("Sample rate %d\n", af_info.samplerate);
608
609 while (start_time + expected_time > time(NULL) ) {
610 uint64_t enc_start_time = current_time_monotonic();
611 int64_t count = sf_read_short(af_handle, PCM, frame_size);
612
613 if (count > 0) {
614 TOXAV_ERR_SEND_FRAME rc;
615
616 if (toxav_audio_send_frame(AliceAV, 0, PCM, count / af_info.channels, af_info.channels, af_info.samplerate,
617 &rc) == false) {
618 printf("Error sending frame of size %ld: %d\n", count, rc);
619 }
620 }
621
622 iterate_tox(bootstrap, AliceAV, BobAV);
623 c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1));
624 }
625
626 printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time);
627
628 Pa_StopStream(adout);
629 sf_close(af_handle);
630
631 { /* Hangup */
632 TOXAV_ERR_CALL_CONTROL rc;
633 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
634
635 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
636 printf("toxav_call_control failed: %d\n", rc);
637 exit(1);
638 }
639 }
640
641 iterate_tox(bootstrap, AliceAV, BobAV);
642 assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
643
644 /* Stop decode thread */
645 data.sig = -1;
646
647 while (data.sig != 1)
648 pthread_yield();
649
650 pthread_mutex_destroy(AliceCC.arb_mutex);
651 pthread_mutex_destroy(BobCC.arb_mutex);
652
653 void *f = NULL;
654
655 while (rb_read(AliceCC.arb, &f))
656 free(f);
657
658 while (rb_read(BobCC.arb, &f))
659 free(f);
660
661 printf("Success!");
662 }
663
664 if (TEST_TRANSFER_V) {
665 printf("\nTrying video enc/dec...\n");
666
667 memset(&AliceCC, 0, sizeof(CallControl));
668 memset(&BobCC, 0, sizeof(CallControl));
669
670 { /* Call */
671 TOXAV_ERR_CALL rc;
672 toxav_call(AliceAV, 0, 0, 2000, &rc);
673
674 if (rc != TOXAV_ERR_CALL_OK) {
675 printf("toxav_call failed: %d\n", rc);
676 exit(1);
677 }
678 }
679
680 while (!BobCC.incoming)
681 iterate_tox(bootstrap, AliceAV, BobAV);
682
683 { /* Answer */
684 TOXAV_ERR_ANSWER rc;
685 toxav_answer(BobAV, 0, 0, 5000, &rc);
686
687 if (rc != TOXAV_ERR_ANSWER_OK) {
688 printf("toxav_answer failed: %d\n", rc);
689 exit(1);
690 }
691 }
692
693 iterate_tox(bootstrap, AliceAV, BobAV);
694
695 /* Start decode thread */
696 struct toxav_thread_data data = {
697 .AliceAV = AliceAV,
698 .BobAV = BobAV,
699 .sig = 0
700 };
701
702 pthread_t dect;
703 pthread_create(&dect, NULL, iterate_toxav, &data);
704 pthread_detach(dect);
705
706 CvCapture *capture = cvCreateFileCapture(vf_name);
707
708 if (!capture) {
709 printf("Failed to open video file: %s\n", vf_name);
710 exit(1);
711 }
712
713// toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL);
714
715 time_t start_time = time(NULL);
716
717 while (start_time + 90 > time(NULL)) {
718 IplImage *frame = cvQueryFrame(capture );
719
720 if (!frame)
721 break;
722
723 send_opencv_img(AliceAV, 0, frame);
724 iterate_tox(bootstrap, AliceAV, BobAV);
725 c_sleep(10);
726 }
727
728 cvReleaseCapture(&capture);
729
730 { /* Hangup */
731 TOXAV_ERR_CALL_CONTROL rc;
732 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
733
734 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
735 printf("toxav_call_control failed: %d\n", rc);
736 exit(1);
737 }
738 }
739
740 iterate_tox(bootstrap, AliceAV, BobAV);
741 assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
742
743 /* Stop decode thread */
744 printf("Stopping decode thread\n");
745 data.sig = -1;
746
747 while (data.sig != 1)
748 pthread_yield();
749
750 printf("Success!");
751 }
752
753
754 Tox *Alice = toxav_get_tox(AliceAV);
755 Tox *Bob = toxav_get_tox(BobAV);
756 toxav_kill(BobAV);
757 toxav_kill(AliceAV);
758 tox_kill(Bob);
759 tox_kill(Alice);
760 tox_kill(bootstrap);
761
762 printf("\nTest successful!\n");
763
764 Pa_Terminate();
765 return 0;
766}
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
index 0b4b869d..083f862f 100644
--- a/toxav/Makefile.inc
+++ b/toxav/Makefile.inc
@@ -1,38 +1,42 @@
1if BUILD_AV 1if BUILD_AV
2 2
3lib_LTLIBRARIES += libtoxav.la 3lib_LTLIBRARIES += libtoxav.la
4libtoxav_la_include_HEADERS = ../toxav/toxav.h 4 libtoxav_la_include_HEADERS = ../toxav/toxav.h
5libtoxav_la_includedir = $(includedir)/tox 5 libtoxav_la_includedir = $(includedir)/tox
6 6
7libtoxav_la_SOURCES = ../toxav/rtp.h \ 7libtoxav_la_SOURCES = ../toxav/rtp.h \
8 ../toxav/rtp.c \ 8 ../toxav/rtp.c \
9 ../toxav/msi.h \ 9 ../toxav/msi.h \
10 ../toxav/msi.c \ 10 ../toxav/msi.c \
11 ../toxav/group.h \ 11 ../toxav/group.h \
12 ../toxav/group.c \ 12 ../toxav/group.c \
13 ../toxav/codec.h \ 13 ../toxav/audio.h \
14 ../toxav/codec.c \ 14 ../toxav/audio.c \
15 ../toxav/toxav.h \ 15 ../toxav/video.h \
16 ../toxav/toxav.c 16 ../toxav/video.c \
17 17 ../toxav/bwcontroler.h \
18 ../toxav/bwcontroler.c \
19 ../toxav/toxav.h \
20 ../toxav/toxav.c \
21 ../toxav/toxav_old.c
18 22
19libtoxav_la_CFLAGS = -I../toxcore \ 23libtoxav_la_CFLAGS = -I../toxcore \
20 -I../toxav \ 24 -I../toxav \
21 $(LIBSODIUM_CFLAGS) \ 25 $(LIBSODIUM_CFLAGS) \
22 $(NACL_CFLAGS) \ 26 $(NACL_CFLAGS) \
23 $(AV_CFLAGS) \ 27 $(AV_CFLAGS) \
24 $(PTHREAD_CFLAGS) 28 $(PTHREAD_CFLAGS)
25 29
26libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ 30libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
27 $(LIBSODIUM_LDFLAGS) \ 31 $(LIBSODIUM_LDFLAGS) \
28 $(NACL_LDFLAGS) \ 32 $(NACL_LDFLAGS) \
29 $(EXTRA_LT_LDFLAGS) \ 33 $(EXTRA_LT_LDFLAGS) \
30 $(WINSOCK2_LIBS) 34 $(WINSOCK2_LIBS)
31 35
32libtoxav_la_LIBADD = libtoxcore.la \ 36libtoxav_la_LIBADD = libtoxcore.la \
33 $(LIBSODIUM_LIBS) \ 37 $(LIBSODIUM_LIBS) \
34 $(NACL_LIBS) \ 38 $(NACL_LIBS) \
35 $(PTHREAD_LIBS) \ 39 $(PTHREAD_LIBS) \
36 $(AV_LIBS) 40 $(AV_LIBS)
37 41
38endif \ No newline at end of file 42endif \ No newline at end of file
diff --git a/toxav/audio.c b/toxav/audio.c
new file mode 100644
index 00000000..ad543502
--- /dev/null
+++ b/toxav/audio.c
@@ -0,0 +1,439 @@
1/** audio.c
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <stdlib.h>
27
28#include "audio.h"
29#include "rtp.h"
30
31#include "../toxcore/logger.h"
32
33static struct JitterBuffer *jbuf_new(uint32_t capacity);
34static void jbuf_clear(struct JitterBuffer *q);
35static void jbuf_free(struct JitterBuffer *q);
36static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m);
37static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
38OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
39bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
40 int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
41bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels);
42
43
44
45ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
46{
47 ACSession *ac = calloc(sizeof(ACSession), 1);
48
49 if (!ac) {
50 LOGGER_WARNING("Allocation failed! Application might misbehave!");
51 return NULL;
52 }
53
54 if (create_recursive_mutex(ac->queue_mutex) != 0) {
55 LOGGER_WARNING("Failed to create recursive mutex!");
56 free(ac);
57 return NULL;
58 }
59
60 int status;
61 ac->decoder = opus_decoder_create(48000, 2, &status);
62
63 if (status != OPUS_OK) {
64 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
65 goto BASE_CLEANUP;
66 }
67
68 if (!(ac->j_buf = jbuf_new(3))) {
69 LOGGER_WARNING("Jitter buffer creaton failed!");
70 opus_decoder_destroy(ac->decoder);
71 goto BASE_CLEANUP;
72 }
73
74 /* Initialize encoders with default values */
75 ac->encoder = create_audio_encoder(48000, 48000, 2);
76
77 if (ac->encoder == NULL)
78 goto DECODER_CLEANUP;
79
80 ac->le_bit_rate = 48000;
81 ac->le_sample_rate = 48000;
82 ac->le_channel_count = 2;
83
84 ac->ld_channel_count = 2;
85 ac->ld_sample_rate = 48000;
86 ac->ldrts = 0; /* Make it possible to reconfigure straight away */
87
88 /* These need to be set in order to properly
89 * do error correction with opus */
90 ac->lp_frame_duration = 120;
91 ac->lp_sampling_rate = 48000;
92 ac->lp_channel_count = 1;
93
94 ac->av = av;
95 ac->friend_number = friend_number;
96 ac->acb.first = cb;
97 ac->acb.second = cb_data;
98
99 return ac;
100
101DECODER_CLEANUP:
102 opus_decoder_destroy(ac->decoder);
103 jbuf_free(ac->j_buf);
104BASE_CLEANUP:
105 pthread_mutex_destroy(ac->queue_mutex);
106 free(ac);
107 return NULL;
108}
109void ac_kill(ACSession *ac)
110{
111 if (!ac)
112 return;
113
114 opus_encoder_destroy(ac->encoder);
115 opus_decoder_destroy(ac->decoder);
116 jbuf_free(ac->j_buf);
117
118 pthread_mutex_destroy(ac->queue_mutex);
119
120 LOGGER_DEBUG("Terminated audio handler: %p", ac);
121 free(ac);
122}
123void ac_iterate(ACSession *ac)
124{
125 if (!ac)
126 return;
127
128 /* TODO fix this and jitter buffering */
129
130 /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */
131 int16_t tmp[5760 * 2];
132
133 struct RTPMessage *msg;
134 int rc = 0;
135
136 pthread_mutex_lock(ac->queue_mutex);
137
138 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
139 pthread_mutex_unlock(ac->queue_mutex);
140
141 if (rc == 2) {
142 LOGGER_DEBUG("OPUS correction");
143 int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000;
144 rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
145 } else {
146 /* Get values from packet and decode. */
147 /* NOTE: This didn't work very well
148 rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data));
149 if (rc != -1) {
150 cs->last_packet_sampling_rate = rc;
151 } else {
152 LOGGER_WARNING("Failed to load packet values!");
153 rtp_free_msg(msg);
154 continue;
155 }*/
156
157
158 /* Pick up sampling rate from packet */
159 memcpy(&ac->lp_sampling_rate, msg->data, 4);
160 ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate);
161
162 ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
163
164 /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
165 * it didn't work quite well.
166 */
167 if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
168 LOGGER_WARNING("Failed to reconfigure decoder!");
169 free(msg);
170 continue;
171 }
172
173 rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0);
174 free(msg);
175 }
176
177 if (rc < 0) {
178 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
179 } else if (ac->acb.first) {
180 ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate;
181
182 ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count,
183 ac->lp_sampling_rate, ac->acb.second);
184 }
185
186 return;
187 }
188
189 pthread_mutex_unlock(ac->queue_mutex);
190}
191int ac_queue_message(void *acp, struct RTPMessage *msg)
192{
193 if (!acp || !msg)
194 return -1;
195
196 if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
197 LOGGER_WARNING("Got dummy!");
198 free(msg);
199 return 0;
200 }
201
202 if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) {
203 LOGGER_WARNING("Invalid payload type!");
204 free(msg);
205 return -1;
206 }
207
208 ACSession *ac = acp;
209
210 pthread_mutex_lock(ac->queue_mutex);
211 int rc = jbuf_write(ac->j_buf, msg);
212 pthread_mutex_unlock(ac->queue_mutex);
213
214 if (rc == -1) {
215 LOGGER_WARNING("Could not queue the message!");
216 free(msg);
217 return -1;
218 }
219
220 return 0;
221}
222int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
223{
224 if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate,
225 sampling_rate, channels,
226 &ac->le_bit_rate,
227 &ac->le_sample_rate,
228 &ac->le_channel_count))
229 return -1;
230
231 return 0;
232}
233
234
235
236struct JitterBuffer {
237 struct RTPMessage **queue;
238 uint32_t size;
239 uint32_t capacity;
240 uint16_t bottom;
241 uint16_t top;
242};
243
244static struct JitterBuffer *jbuf_new(uint32_t capacity)
245{
246 unsigned int size = 1;
247
248 while (size <= (capacity * 4)) {
249 size *= 2;
250 }
251
252 struct JitterBuffer *q;
253
254 if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL;
255
256 if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) {
257 free(q);
258 return NULL;
259 }
260
261 q->size = size;
262 q->capacity = capacity;
263 return q;
264}
265static void jbuf_clear(struct JitterBuffer *q)
266{
267 for (; q->bottom != q->top; ++q->bottom) {
268 if (q->queue[q->bottom % q->size]) {
269 free(q->queue[q->bottom % q->size]);
270 q->queue[q->bottom % q->size] = NULL;
271 }
272 }
273}
274static void jbuf_free(struct JitterBuffer *q)
275{
276 if (!q) return;
277
278 jbuf_clear(q);
279 free(q->queue);
280 free(q);
281}
282static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m)
283{
284 uint16_t sequnum = m->header.sequnum;
285
286 unsigned int num = sequnum % q->size;
287
288 if ((uint32_t)(sequnum - q->bottom) > q->size) {
289 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
290
291 jbuf_clear(q);
292 q->bottom = sequnum - q->capacity;
293 q->queue[num] = m;
294 q->top = sequnum + 1;
295 return 0;
296 }
297
298 if (q->queue[num])
299 return -1;
300
301 q->queue[num] = m;
302
303 if ((sequnum - q->bottom) >= (q->top - q->bottom))
304 q->top = sequnum + 1;
305
306 return 0;
307}
308static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
309{
310 if (q->top == q->bottom) {
311 *success = 0;
312 return NULL;
313 }
314
315 unsigned int num = q->bottom % q->size;
316
317 if (q->queue[num]) {
318 struct RTPMessage *ret = q->queue[num];
319 q->queue[num] = NULL;
320 ++q->bottom;
321 *success = 1;
322 return ret;
323 }
324
325 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
326 ++q->bottom;
327 *success = 2;
328 return NULL;
329 }
330
331 *success = 0;
332 return NULL;
333}
334OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
335{
336 int status = OPUS_OK;
337 OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
338
339 if (status != OPUS_OK) {
340 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
341 return NULL;
342 }
343
344 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
345
346 if (status != OPUS_OK) {
347 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
348 goto FAILURE;
349 }
350
351 /* Enable in-band forward error correction in codec */
352 status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1));
353
354 if (status != OPUS_OK) {
355 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
356 goto FAILURE;
357 }
358
359 /* Make codec resistant to up to 10% packet loss
360 * NOTE This could also be adjusted on the fly, rather than hard-coded,
361 * with feedback from the receiving client.
362 */
363 status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10));
364
365 if (status != OPUS_OK) {
366 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
367 goto FAILURE;
368 }
369
370 /* Set algorithm to the highest complexity, maximizing compression */
371 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
372
373 if (status != OPUS_OK) {
374 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
375 goto FAILURE;
376 }
377
378 return rc;
379
380FAILURE:
381 opus_encoder_destroy(rc);
382 return NULL;
383}
384bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
385 int32_t *old_br, int32_t *old_sr, int32_t *old_ch)
386{
387 /* Values are checked in toxav.c */
388 if (*old_sr != new_sr || *old_ch != new_ch) {
389 OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
390
391 if (new_encoder == NULL)
392 return false;
393
394 opus_encoder_destroy(*e);
395 *e = new_encoder;
396 } else if (*old_br == new_br)
397 return true; /* Nothing changed */
398 else {
399 int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
400
401 if (status != OPUS_OK) {
402 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
403 return false;
404 }
405 }
406
407 *old_br = new_br;
408 *old_sr = new_sr;
409 *old_ch = new_ch;
410
411 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", new_br, new_sr, new_ch);
412 return true;
413}
414bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels)
415{
416 if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) {
417 if (current_time_monotonic() - ac->ldrts < 500)
418 return false;
419
420 int status;
421 OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status);
422
423 if (status != OPUS_OK) {
424 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
425 return false;
426 }
427
428 ac->ld_sample_rate = sampling_rate;
429 ac->ld_channel_count = channels;
430 ac->ldrts = current_time_monotonic();
431
432 opus_decoder_destroy(ac->decoder);
433 ac->decoder = new_dec;
434
435 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
436 }
437
438 return true;
439}
diff --git a/toxav/audio.h b/toxav/audio.h
new file mode 100644
index 00000000..b1db7448
--- /dev/null
+++ b/toxav/audio.h
@@ -0,0 +1,64 @@
1/** audio.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef AUDIO_H
23#define AUDIO_H
24
25#include <opus.h>
26#include <pthread.h>
27
28#include "toxav.h"
29
30#include "../toxcore/util.h"
31
32struct RTPMessage;
33
34typedef struct ACSession_s {
35 /* encoding */
36 OpusEncoder *encoder;
37 int32_t le_sample_rate; /* Last encoder sample rate */
38 int32_t le_channel_count; /* Last encoder channel count */
39 int32_t le_bit_rate; /* Last encoder bit rate */
40
41 /* decoding */
42 OpusDecoder *decoder;
43 int32_t lp_channel_count; /* Last packet channel count */
44 int32_t lp_sampling_rate; /* Last packet sample rate */
45 int32_t lp_frame_duration; /* Last packet frame duration */
46 int32_t ld_sample_rate; /* Last decoder sample rate */
47 int32_t ld_channel_count; /* Last decoder channel count */
48 uint64_t ldrts; /* Last decoder reconfiguration time stamp */
49 void *j_buf;
50
51 pthread_mutex_t queue_mutex[1];
52
53 ToxAV *av;
54 uint32_t friend_number;
55 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
56} ACSession;
57
58ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
59void ac_kill(ACSession *ac);
60void ac_iterate(ACSession *ac);
61int ac_queue_message(void *acp, struct RTPMessage *msg);
62int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
63
64#endif /* AUDIO_H */
diff --git a/toxav/bwcontroler.c b/toxav/bwcontroler.c
new file mode 100644
index 00000000..9dd15c93
--- /dev/null
+++ b/toxav/bwcontroler.c
@@ -0,0 +1,205 @@
1/** bwcontroler.c
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <assert.h>
27#include "bwcontroler.h"
28#include "../toxcore/logger.h"
29#include "../toxcore/util.h"
30
31#define BWC_PACKET_ID 196
32#define BWC_SEND_INTERVAL_MS 1000
33#define BWC_REFRESH_INTERVAL_MS 10000
34#define BWC_AVG_PKT_COUNT 20
35
36/**
37 *
38 */
39
40struct BWControler_s {
41 void (*mcb) (BWControler *, uint32_t, float, void *);
42 void *mcb_data;
43
44 Messenger *m;
45 uint32_t friend_number;
46
47 struct {
48 uint32_t lru; /* Last recv update time stamp */
49 uint32_t lsu; /* Last sent update time stamp */
50 uint32_t lfu; /* Last refresh time stamp */
51
52 uint32_t lost;
53 uint32_t recv;
54 } cycle;
55
56 struct {
57 uint32_t rb_s[BWC_AVG_PKT_COUNT];
58 RingBuffer *rb;
59 } rcvpkt; /* To calculate average received packet */
60};
61
62int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
63void send_update(BWControler *bwc);
64
65BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
66 void (*mcb) (BWControler *, uint32_t, float, void *),
67 void *udata)
68{
69 BWControler *retu = calloc(sizeof(struct BWControler_s), 1);
70
71 retu->mcb = mcb;
72 retu->mcb_data = udata;
73 retu->m = m;
74 retu->friend_number = friendnumber;
75 retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic();
76 retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
77
78 /* Fill with zeros */
79 int i = 0;
80
81 for (; i < BWC_AVG_PKT_COUNT; i ++)
82 rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
83
84 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
85
86 return retu;
87}
88void bwc_kill(BWControler *bwc)
89{
90 if (!bwc)
91 return;
92
93 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
94
95 rb_kill(bwc->rcvpkt.rb);
96 free(bwc);
97}
98void bwc_feed_avg(BWControler *bwc, uint32_t bytes)
99{
100 uint32_t *p;
101
102 rb_read(bwc->rcvpkt.rb, (void **) &p);
103 rb_write(bwc->rcvpkt.rb, p);
104
105 *p = bytes;
106}
107void bwc_add_lost(BWControler *bwc, uint32_t bytes)
108{
109 if (!bwc)
110 return;
111
112 if (!bytes) {
113 uint32_t *t_avg[BWC_AVG_PKT_COUNT], c = 1;
114
115 rb_data(bwc->rcvpkt.rb, (void **) t_avg);
116
117 int i = 0;
118
119 for (; i < BWC_AVG_PKT_COUNT; i ++) {
120 bytes += *(t_avg[i]);
121
122 if (*(t_avg[i]))
123 c++;
124 }
125
126 bytes /= c;
127 }
128
129 bwc->cycle.lost += bytes;
130 send_update(bwc);
131}
132void bwc_add_recv(BWControler *bwc, uint32_t bytes)
133{
134 if (!bwc || !bytes)
135 return;
136
137 bwc->cycle.recv += bytes;
138 send_update(bwc);
139}
140
141
142struct BWCMessage {
143 uint32_t lost;
144 uint32_t recv;
145};
146
147void send_update(BWControler *bwc)
148{
149 if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
150
151 bwc->cycle.lost /= 10;
152 bwc->cycle.recv /= 10;
153 bwc->cycle.lfu = current_time_monotonic();
154 } else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
155
156 if (bwc->cycle.lost) {
157 LOGGER_DEBUG ("%p Sent update rcv: %u lost: %u",
158 bwc, bwc->cycle.recv, bwc->cycle.lost);
159
160 uint8_t p_msg[sizeof(struct BWCMessage) + 1];
161 struct BWCMessage *b_msg = (struct BWCMessage *)(p_msg + 1);
162
163 p_msg[0] = BWC_PACKET_ID;
164 b_msg->lost = htonl(bwc->cycle.lost);
165 b_msg->recv = htonl(bwc->cycle.recv);
166
167 if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, p_msg, sizeof(p_msg)))
168 LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(p_msg), strerror(errno));
169 }
170
171 bwc->cycle.lsu = current_time_monotonic();
172 }
173}
174int on_update (BWControler *bwc, struct BWCMessage *msg)
175{
176 LOGGER_DEBUG ("%p Got update from peer", bwc);
177
178 /* Peer must respect time boundary */
179 if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) {
180 LOGGER_DEBUG("%p Rejecting extra update", bwc);
181 return -1;
182 }
183
184 bwc->cycle.lru = current_time_monotonic();
185
186 msg->recv = ntohl(msg->recv);
187 msg->lost = ntohl(msg->lost);
188
189 LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost);
190
191 if (msg->lost && bwc->mcb)
192 bwc->mcb(bwc, bwc->friend_number,
193 ((float) (msg->lost) / (msg->recv + msg->lost)),
194 bwc->mcb_data);
195
196 return 0;
197}
198int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
199{
200 if (length - 1 != sizeof(struct BWCMessage))
201 return -1;
202
203 /* NOTE the data is mutable */
204 return on_update(object, (struct BWCMessage *) (data + 1));
205}
diff --git a/toxav/bwcontroler.h b/toxav/bwcontroler.h
new file mode 100644
index 00000000..53b07d38
--- /dev/null
+++ b/toxav/bwcontroler.h
@@ -0,0 +1,37 @@
1/** bwcontroler.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef BWCONROLER_H
23#define BWCONROLER_H
24#include "../toxcore/Messenger.h"
25
26typedef struct BWControler_s BWControler;
27
28BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
29 void (*mcb) (BWControler *, uint32_t, float, void *),
30 void *udata);
31void bwc_kill(BWControler *bwc);
32
33void bwc_feed_avg(BWControler *bwc, uint32_t bytes);
34void bwc_add_lost(BWControler *bwc, uint32_t bytes);
35void bwc_add_recv(BWControler *bwc, uint32_t bytes);
36
37#endif /* BWCONROLER_H */
diff --git a/toxav/codec.c b/toxav/codec.c
deleted file mode 100644
index 8940aa25..00000000
--- a/toxav/codec.c
+++ /dev/null
@@ -1,705 +0,0 @@
1/** codec.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 "codec.h"
30#include "../toxcore/logger.h"
31#include "../toxcore/util.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <math.h>
36#include <assert.h>
37#include <time.h>
38
39#include "msi.h"
40#include "rtp.h"
41
42/* Good quality encode. */
43#define MAX_DECODE_TIME_US 0
44
45// TODO this has to be exchanged in msi
46#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
47#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/
48#define VIDEOFRAME_HEADER_SIZE 0x2
49
50/* FIXME: Might not be enough */
51#define VIDEO_DECODE_BUFFER_SIZE 20
52
53#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; }
54
55typedef ARRAY(uint8_t) Payload;
56
57typedef struct {
58 uint16_t size; /* Max size */
59 uint16_t start;
60 uint16_t end;
61 Payload **packets;
62} PayloadBuffer;
63
64static _Bool buffer_full(const PayloadBuffer *b)
65{
66 return (b->end + 1) % b->size == b->start;
67}
68
69static _Bool buffer_empty(const PayloadBuffer *b)
70{
71 return b->end == b->start;
72}
73
74static void buffer_write(PayloadBuffer *b, Payload *p)
75{
76 b->packets[b->end] = p;
77 b->end = (b->end + 1) % b->size;
78
79 if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */
80}
81
82static void buffer_read(PayloadBuffer *b, Payload **p)
83{
84 *p = b->packets[b->start];
85 b->start = (b->start + 1) % b->size;
86}
87
88static void buffer_clear(PayloadBuffer *b)
89{
90 while (!buffer_empty(b)) {
91 Payload *p;
92 buffer_read(b, &p);
93 free(p);
94 }
95}
96
97static PayloadBuffer *buffer_new(int size)
98{
99 PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1);
100
101 if (!buf) return NULL;
102
103 buf->size = size + 1; /* include empty elem */
104
105 if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) {
106 free(buf);
107 return NULL;
108 }
109
110 return buf;
111}
112
113static void buffer_free(PayloadBuffer *b)
114{
115 if (b) {
116 buffer_clear(b);
117 free(b->packets);
118 free(b);
119 }
120}
121
122/* JITTER BUFFER WORK */
123typedef struct _JitterBuffer {
124 RTPMessage **queue;
125 uint32_t size;
126 uint32_t capacity;
127 uint16_t bottom;
128 uint16_t top;
129} JitterBuffer;
130
131static JitterBuffer *jbuf_new(uint32_t capacity)
132{
133 unsigned int size = 1;
134
135 while (size <= (capacity * 4)) {
136 size *= 2;
137 }
138
139 JitterBuffer *q;
140
141 if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL;
142
143 if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
144 free(q);
145 return NULL;
146 }
147
148 q->size = size;
149 q->capacity = capacity;
150 return q;
151}
152
153static void jbuf_clear(JitterBuffer *q)
154{
155 for (; q->bottom != q->top; ++q->bottom) {
156 if (q->queue[q->bottom % q->size]) {
157 rtp_free_msg(NULL, q->queue[q->bottom % q->size]);
158 q->queue[q->bottom % q->size] = NULL;
159 }
160 }
161}
162
163static void jbuf_free(JitterBuffer *q)
164{
165 if (!q) return;
166
167 jbuf_clear(q);
168 free(q->queue);
169 free(q);
170}
171
172static int jbuf_write(JitterBuffer *q, RTPMessage *m)
173{
174 uint16_t sequnum = m->header->sequnum;
175
176 unsigned int num = sequnum % q->size;
177
178 if ((uint32_t)(sequnum - q->bottom) > q->size) {
179 jbuf_clear(q);
180 q->bottom = sequnum - q->capacity;
181 q->queue[num] = m;
182 q->top = sequnum + 1;
183 return 0;
184 }
185
186 if (q->queue[num])
187 return -1;
188
189 q->queue[num] = m;
190
191 if ((sequnum - q->bottom) >= (q->top - q->bottom))
192 q->top = sequnum + 1;
193
194 return 0;
195}
196
197/* Success is 0 when there is nothing to dequeue,
198 * 1 when there's a good packet,
199 * 2 when there's a lost packet */
200static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success)
201{
202 if (q->top == q->bottom) {
203 *success = 0;
204 return NULL;
205 }
206
207 unsigned int num = q->bottom % q->size;
208
209 if (q->queue[num]) {
210 RTPMessage *ret = q->queue[num];
211 q->queue[num] = NULL;
212 ++q->bottom;
213 *success = 1;
214 return ret;
215 }
216
217 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
218 ++q->bottom;
219 *success = 2;
220 return NULL;
221 }
222
223 *success = 0;
224 return NULL;
225}
226
227static int init_video_decoder(CSSession *cs)
228{
229 int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION);
230
231 if ( rc != VPX_CODEC_OK) {
232 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
233 return -1;
234 }
235
236 return 0;
237}
238
239static int init_audio_decoder(CSSession *cs)
240{
241 int rc;
242 cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc );
243
244 if ( rc != OPUS_OK ) {
245 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
246 return -1;
247 }
248
249 return 0;
250}
251
252static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate)
253{
254 vpx_codec_enc_cfg_t cfg;
255 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
256
257 if (rc != VPX_CODEC_OK) {
258 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
259 return -1;
260 }
261
262 cfg.rc_target_bitrate = video_bitrate;
263 cfg.g_w = max_width;
264 cfg.g_h = max_height;
265 cfg.g_pass = VPX_RC_ONE_PASS;
266 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
267 cfg.g_lag_in_frames = 0;
268 cfg.kf_min_dist = 0;
269 cfg.kf_max_dist = 48;
270 cfg.kf_mode = VPX_KF_AUTO;
271
272 rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
273
274 if ( rc != VPX_CODEC_OK) {
275 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
276 return -1;
277 }
278
279 rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
280
281 if ( rc != VPX_CODEC_OK) {
282 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
283 return -1;
284 }
285
286 cs->max_width = max_width;
287 cs->max_height = max_height;
288 cs->video_bitrate = video_bitrate;
289
290 return 0;
291}
292
293static int init_audio_encoder(CSSession *cs)
294{
295 int rc = OPUS_OK;
296 cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate,
297 cs->audio_encoder_channels, OPUS_APPLICATION_VOIP, &rc);
298
299 if ( rc != OPUS_OK ) {
300 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
301 return -1;
302 }
303
304 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate));
305
306 if ( rc != OPUS_OK ) {
307 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
308 return -1;
309 }
310
311 /* Enable in-band forward error correction in codec */
312 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_INBAND_FEC(1));
313
314 if ( rc != OPUS_OK ) {
315 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
316 return -1;
317 }
318
319 /* Make codec resistant to up to 10% packet loss */
320 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_PACKET_LOSS_PERC(10));
321
322 if ( rc != OPUS_OK ) {
323 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
324 return -1;
325 }
326
327 /* Set algorithm to the highest complexity, maximizing compression */
328 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
329
330 if ( rc != OPUS_OK ) {
331 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
332 return -1;
333 }
334
335 return 0;
336}
337
338/* PUBLIC */
339int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
340{
341 if (!cs || !length || length > cs->max_video_frame_size) {
342 LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
343 return cs_ErrorSplittingVideoPayload;
344 }
345
346 cs->split_video_frame[0] = cs->frameid_out++;
347 cs->split_video_frame[1] = 0;
348 cs->processing_video_frame = payload;
349 cs->processing_video_frame_size = length;
350
351 return ((length - 1) / cs->video_frame_piece_size) + 1;
352}
353
354const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
355{
356 if (!cs || !size) return NULL;
357
358 if (cs->processing_video_frame_size > cs->video_frame_piece_size) {
359 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
360 cs->processing_video_frame,
361 cs->video_frame_piece_size);
362
363 cs->processing_video_frame += cs->video_frame_piece_size;
364 cs->processing_video_frame_size -= cs->video_frame_piece_size;
365
366 *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE;
367 } else {
368 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
369 cs->processing_video_frame,
370 cs->processing_video_frame_size);
371
372 *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
373 }
374
375 cs->split_video_frame[1]++;
376
377 return cs->split_video_frame;
378}
379
380void cs_do(CSSession *cs)
381{
382 /* Codec session should always be protected by call mutex so no need to check for cs validity
383 */
384
385 if (!cs) return;
386
387 Payload *p;
388 int rc;
389
390 int success = 0;
391
392 pthread_mutex_lock(cs->queue_mutex);
393 RTPMessage *msg;
394
395 while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
396 pthread_mutex_unlock(cs->queue_mutex);
397
398 uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
399 int16_t tmp[fsize * cs->audio_decoder_channels];
400
401 if (success == 2) {
402 rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1);
403 } else {
404 rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0);
405 rtp_free_msg(NULL, msg);
406 }
407
408 if (rc < 0) {
409 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
410 } else if (cs->acb.first) {
411 /* Play */
412 cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second);
413 }
414
415 pthread_mutex_lock(cs->queue_mutex);
416 }
417
418 if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
419 /* Decode video */
420 buffer_read(cs->vbuf_raw, &p);
421
422 /* Leave space for (possibly) other thread to queue more data after we read it here */
423 pthread_mutex_unlock(cs->queue_mutex);
424
425 rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
426 free(p);
427
428 if (rc != VPX_CODEC_OK) {
429 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
430 } else {
431 vpx_codec_iter_t iter = NULL;
432 vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter);
433
434 /* Play decoded images */
435 for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
436 if (cs->vcb.first)
437 cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second);
438
439 vpx_img_free(dest);
440 }
441 }
442
443 return;
444 }
445
446 pthread_mutex_unlock(cs->queue_mutex);
447}
448
449int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height)
450{
451 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
452
453 if (cfg.g_w == width && cfg.g_h == height)
454 return 0;
455
456 if (width * height > cs->max_width * cs->max_height) {
457 vpx_codec_ctx_t v_encoder = cs->v_encoder;
458
459 if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) {
460 cs->v_encoder = v_encoder;
461 return cs_ErrorSettingVideoResolution;
462 }
463
464 vpx_codec_destroy(&v_encoder);
465 return 0;
466 }
467
468 LOGGER_DEBUG("New video resolution: %u %u", width, height);
469 cfg.g_w = width;
470 cfg.g_h = height;
471 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
472
473 if ( rc != VPX_CODEC_OK) {
474 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
475 return cs_ErrorSettingVideoResolution;
476 }
477
478 return 0;
479}
480
481int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
482{
483 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
484
485 if (cfg.rc_target_bitrate == video_bitrate)
486 return 0;
487
488 LOGGER_DEBUG("New video bitrate: %u", video_bitrate);
489 cfg.rc_target_bitrate = video_bitrate;
490 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
491
492 if ( rc != VPX_CODEC_OK) {
493 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
494 return cs_ErrorSettingVideoBitrate;
495 }
496
497 cs->video_bitrate = video_bitrate;
498 return 0;
499}
500
501CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
502{
503 CSSession *cs = calloc(sizeof(CSSession), 1);
504
505 if (!cs) {
506 LOGGER_WARNING("Allocation failed! Application might misbehave!");
507 return NULL;
508 }
509
510 if (create_recursive_mutex(cs->queue_mutex) != 0) {
511 LOGGER_WARNING("Failed to create recursive mutex!");
512 free(cs);
513 return NULL;
514 }
515
516 if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
517 LOGGER_WARNING("Jitter buffer creaton failed!");
518 goto error;
519 }
520
521 cs->audio_encoder_bitrate = cs_self->audio_bitrate;
522 cs->audio_encoder_sample_rate = cs_self->audio_sample_rate;
523 cs->audio_encoder_channels = cs_self->audio_channels;
524 cs->audio_encoder_frame_duration = cs_self->audio_frame_duration;
525
526 cs->audio_decoder_bitrate = cs_peer->audio_bitrate;
527 cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate;
528 cs->audio_decoder_channels = cs_peer->audio_channels;
529 cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
530
531
532 cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0;
533 cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0;
534
535 if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error;
536
537 if ((cs->support_video = has_video)) {
538 cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
539 cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
540
541 cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
542 cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0;
543 cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0;
544
545 if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error;
546
547 if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
548
549 if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) )
550 goto error;
551
552 if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
553 }
554
555 return cs;
556
557error:
558 LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
559
560 pthread_mutex_destroy(cs->queue_mutex);
561
562 if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
563
564 if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder);
565
566
567 if (has_video) {
568 if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder);
569
570 if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder);
571
572 buffer_free(cs->vbuf_raw);
573
574 free(cs->frame_buf);
575 free(cs->split_video_frame);
576 }
577
578 jbuf_free(cs->j_buf);
579 free(cs);
580
581 return NULL;
582}
583
584void cs_kill(CSSession *cs)
585{
586 if (!cs) return;
587
588 /* queue_message will not be called since it's unregistered before cs_kill is called */
589 pthread_mutex_destroy(cs->queue_mutex);
590
591
592 if ( cs->audio_encoder )
593 opus_encoder_destroy(cs->audio_encoder);
594
595 if ( cs->audio_decoder )
596 opus_decoder_destroy(cs->audio_decoder);
597
598 if ( cs->capabilities & cs_VideoDecoding )
599 vpx_codec_destroy(&cs->v_decoder);
600
601 if ( cs->capabilities & cs_VideoEncoding )
602 vpx_codec_destroy(&cs->v_encoder);
603
604 jbuf_free(cs->j_buf);
605 buffer_free(cs->vbuf_raw);
606 free(cs->frame_buf);
607 free(cs->split_video_frame);
608
609 LOGGER_DEBUG("Terminated codec state: %p", cs);
610 free(cs);
611}
612
613
614
615
616/* Called from RTP */
617void queue_message(RTPSession *session, RTPMessage *msg)
618{
619 /* This function is unregistered during call termination befor destroing
620 * Codec session so no need to check for validity of cs
621 */
622 CSSession *cs = session->cs;
623
624 if (!cs) return;
625
626 /* Audio */
627 if (session->payload_type == msi_TypeAudio % 128) {
628 pthread_mutex_lock(cs->queue_mutex);
629 int ret = jbuf_write(cs->j_buf, msg);
630 pthread_mutex_unlock(cs->queue_mutex);
631
632 if (ret == -1) {
633 rtp_free_msg(NULL, msg);
634 }
635 }
636 /* Video */
637 else {
638 uint8_t *packet = msg->data;
639 uint32_t packet_size = msg->length;
640
641 if (packet_size < VIDEOFRAME_HEADER_SIZE)
642 goto end;
643
644 uint8_t diff = packet[0] - cs->frameid_in;
645
646 if (diff != 0) {
647 if (diff < 225) { /* New frame */
648 /* Flush last frames' data and get ready for this frame */
649 Payload *p = malloc(sizeof(Payload) + cs->frame_size);
650
651 if (p) {
652 pthread_mutex_lock(cs->queue_mutex);
653
654 if (buffer_full(cs->vbuf_raw)) {
655 LOGGER_DEBUG("Dropped video frame");
656 Payload *tp;
657 buffer_read(cs->vbuf_raw, &tp);
658 free(tp);
659 } else {
660 p->size = cs->frame_size;
661 memcpy(p->data, cs->frame_buf, cs->frame_size);
662 }
663
664 buffer_write(cs->vbuf_raw, p);
665 pthread_mutex_unlock(cs->queue_mutex);
666 } else {
667 LOGGER_WARNING("Allocation failed! Program might misbehave!");
668 goto end;
669 }
670
671 cs->last_timestamp = msg->header->timestamp;
672 cs->frameid_in = packet[0];
673 memset(cs->frame_buf, 0, cs->frame_size);
674 cs->frame_size = 0;
675
676 } else { /* Old frame; drop */
677 LOGGER_DEBUG("Old packet: %u", packet[0]);
678 goto end;
679 }
680 }
681
682 uint8_t piece_number = packet[1];
683
684 uint32_t length_before_piece = ((piece_number - 1) * cs->video_frame_piece_size);
685 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
686
687 if (framebuf_new_length > cs->max_video_frame_size) {
688 goto end;
689 }
690
691 /* Otherwise it's part of the frame so just process */
692 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
693
694 memcpy(cs->frame_buf + length_before_piece,
695 packet + VIDEOFRAME_HEADER_SIZE,
696 packet_size - VIDEOFRAME_HEADER_SIZE);
697
698 if (framebuf_new_length > cs->frame_size) {
699 cs->frame_size = framebuf_new_length;
700 }
701
702end:
703 rtp_free_msg(NULL, msg);
704 }
705}
diff --git a/toxav/codec.h b/toxav/codec.h
deleted file mode 100644
index 6018e5df..00000000
--- a/toxav/codec.h
+++ /dev/null
@@ -1,176 +0,0 @@
1/** codec.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 _CODEC_H_
25#define _CODEC_H_
26
27#include "toxav.h"
28#include "rtp.h"
29
30#include <stdio.h>
31#include <math.h>
32#include <pthread.h>
33
34#include <vpx/vpx_decoder.h>
35#include <vpx/vpx_encoder.h>
36#include <vpx/vp8dx.h>
37#include <vpx/vp8cx.h>
38#include <vpx/vpx_image.h>
39#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
40#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
41
42/* Audio encoding/decoding */
43#include <opus.h>
44
45#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
46
47typedef void (*CSAudioCallback) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
48typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
49
50/**
51 * Codec capabilities
52 */
53typedef enum {
54 cs_AudioEncoding = 1 << 0,
55 cs_AudioDecoding = 1 << 1,
56 cs_VideoEncoding = 1 << 2,
57 cs_VideoDecoding = 1 << 3
58} CSCapabilities;
59
60/**
61 * Codec errors.
62 */
63typedef enum {
64 cs_ErrorSettingVideoResolution = -30,
65 cs_ErrorSettingVideoBitrate = -31,
66 cs_ErrorSplittingVideoPayload = -32,
67} CSError;
68
69/**
70 * Codec session - controling codec
71 */
72typedef struct _CSSession {
73
74 /* VIDEO
75 *
76 *
77 */
78 int support_video;
79
80 /* video encoding */
81 vpx_codec_ctx_t v_encoder;
82 uint32_t frame_counter;
83
84 /* video decoding */
85 vpx_codec_ctx_t v_decoder;
86 int max_width;
87 int max_height;
88 unsigned int video_bitrate;
89
90
91 /* Data handling */
92 uint8_t *frame_buf; /* buffer for split video payloads */
93 uint32_t frame_size; /* largest address written to in frame_buf for current input frame*/
94 uint8_t frameid_in, frameid_out; /* id of input and output video frame */
95 uint32_t last_timestamp; /* calculating cycles */
96
97 /* Limits */
98 uint32_t video_frame_piece_size;
99 uint32_t max_video_frame_size;
100
101 /* Reassembling */
102 uint8_t *split_video_frame;
103 const uint8_t *processing_video_frame;
104 uint16_t processing_video_frame_size;
105
106
107
108 /* AUDIO
109 *
110 *
111 */
112
113 /* audio encoding */
114 OpusEncoder *audio_encoder;
115 int audio_encoder_bitrate;
116 int audio_encoder_sample_rate;
117 int audio_encoder_frame_duration;
118 int audio_encoder_channels;
119
120 /* audio decoding */
121 OpusDecoder *audio_decoder;
122 int audio_decoder_bitrate;
123 int audio_decoder_sample_rate;
124 int audio_decoder_frame_duration;
125 int audio_decoder_channels;
126
127 struct _JitterBuffer *j_buf;
128
129
130 /* Voice activity detection */
131 uint32_t EVAD_tolerance; /* In frames */
132 uint32_t EVAD_tolerance_cr;
133
134
135
136 /* OTHER
137 *
138 *
139 */
140
141 uint64_t capabilities; /* supports*/
142
143 /* Callbacks */
144 PAIR(CSAudioCallback, void *) acb;
145 PAIR(CSVideoCallback, void *) vcb;
146
147 /* Buffering */
148 void *vbuf_raw; /* Un-decoded data */
149 pthread_mutex_t queue_mutex[1];
150
151 void *agent; /* Pointer to ToxAv */
152 int32_t call_idx;
153} CSSession;
154
155/* Make sure to be called BEFORE corresponding rtp_new */
156CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video);
157/* Make sure to be called AFTER corresponding rtp_kill */
158void cs_kill(CSSession *cs);
159
160int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length);
161const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size);
162
163/**
164 * Call playback callbacks
165 */
166void cs_do(CSSession *cs);
167
168
169/* Reconfigure video encoder; return 0 on success or -1 on failure. */
170int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height);
171int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate);
172
173
174/* Internal. Called from rtp_handle_message */
175void queue_message(RTPSession *session, RTPMessage *msg);
176#endif /* _CODEC_H_ */
diff --git a/toxav/group.c b/toxav/group.c
index 817ee6e6..190c2c3d 100644
--- a/toxav/group.c
+++ b/toxav/group.c
@@ -20,7 +20,7 @@
20 20
21#ifdef HAVE_CONFIG_H 21#ifdef HAVE_CONFIG_H
22#include "config.h" 22#include "config.h"
23#endif 23#endif /* HAVE_CONFIG_H */
24 24
25#include "group.h" 25#include "group.h"
26#include "../toxcore/util.h" 26#include "../toxcore/util.h"
@@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity)
54 54
55 Group_JitterBuffer *q; 55 Group_JitterBuffer *q;
56 56
57 if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL; 57 if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL;
58 58
59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) { 59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) {
60 free(q); 60 free(q);
@@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av)
190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels, 190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
191 OPUS_APPLICATION_AUDIO, &rc); 191 OPUS_APPLICATION_AUDIO, &rc);
192 192
193 if ( rc != OPUS_OK ) { 193 if (rc != OPUS_OK) {
194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); 194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
195 group_av->audio_encoder = NULL; 195 group_av->audio_encoder = NULL;
196 return -1; 196 return -1;
@@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av)
198 198
199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate)); 199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
200 200
201 if ( rc != OPUS_OK ) { 201 if (rc != OPUS_OK) {
202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
203 opus_encoder_destroy(group_av->audio_encoder); 203 opus_encoder_destroy(group_av->audio_encoder);
204 group_av->audio_encoder = NULL; 204 group_av->audio_encoder = NULL;
@@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av)
207 207
208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10)); 208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
209 209
210 if ( rc != OPUS_OK ) { 210 if (rc != OPUS_OK) {
211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
212 opus_encoder_destroy(group_av->audio_encoder); 212 opus_encoder_destroy(group_av->audio_encoder);
213 group_av->audio_encoder = NULL; 213 group_av->audio_encoder = NULL;
@@ -306,7 +306,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int g
306 int rc; 306 int rc;
307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc); 307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
308 308
309 if ( rc != OPUS_OK ) { 309 if (rc != OPUS_OK) {
310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); 310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
311 free(pk); 311 free(pk);
312 return -1; 312 return -1;
diff --git a/toxav/msi.c b/toxav/msi.c
index 97ba936c..c08c0135 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -1,6 +1,6 @@
1/** msi.c 1/** msi.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -32,1581 +32,795 @@
32#include <string.h> 32#include <string.h>
33#include <stdlib.h> 33#include <stdlib.h>
34#include <stdbool.h> 34#include <stdbool.h>
35#include <assert.h>
35 36
36#define MSI_MAXMSG_SIZE 256 37#define MSI_MAXMSG_SIZE 256
37 38
38/* Define default timeout for a request.
39 * There is no behavior specified by the msi on what will
40 * client do on timeout, but to call timeout callback.
41 */
42#define m_deftout 10000 /* in milliseconds */
43
44/** 39/**
45 * Protocol: 40 * Protocol:
46 * 41 *
47 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| 42 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
48 */ 43 */
49 44
50typedef uint8_t MSIRawCSettingsType[23];
51
52typedef enum { 45typedef enum {
53 IDRequest = 1, 46 IDRequest = 1,
54 IDResponse, 47 IDError,
55 IDReason, 48 IDCapabilities,
56 IDCallId,
57 IDCSettings,
58 49
59} MSIHeaderID; 50} MSIHeaderID;
60 51
61typedef enum {
62 TypeRequest,
63 TypeResponse,
64
65} MSIMessageType;
66 52
67typedef enum { 53typedef enum {
68 invite, 54 requ_init,
69 start, 55 requ_push,
70 cancel, 56 requ_pop,
71 reject,
72 end,
73
74} MSIRequest; 57} MSIRequest;
75 58
76typedef enum {
77 ringing,
78 starting,
79 ending,
80 error
81
82} MSIResponse;
83
84 59
85#define GENERIC_HEADER(header, val_type) \ 60#define GENERIC_HEADER(header, val_type) \
86typedef struct _MSIHeader##header { \ 61typedef struct { \
87val_type value; \ 62 val_type value; \
88_Bool exists; \ 63 bool exists; \
89} MSIHeader##header; 64} MSIHeader##header
90
91 65
92GENERIC_HEADER ( Request, MSIRequest )
93GENERIC_HEADER ( Response, MSIResponse )
94GENERIC_HEADER ( CallId, MSICallIDType )
95GENERIC_HEADER ( Reason, MSIReasonStrType )
96GENERIC_HEADER ( CSettings, MSIRawCSettingsType )
97 66
67GENERIC_HEADER (Request, MSIRequest);
68GENERIC_HEADER (Error, MSIError);
69GENERIC_HEADER (Capabilities, uint8_t);
98 70
99typedef struct _MSIMessage {
100
101 MSIHeaderRequest request;
102 MSIHeaderResponse response;
103 MSIHeaderReason reason;
104 MSIHeaderCallId callid;
105 MSIHeaderCSettings csettings;
106
107 int friend_id;
108 71
72typedef struct {
73 MSIHeaderRequest request;
74 MSIHeaderError error;
75 MSIHeaderCapabilities capabilities;
109} MSIMessage; 76} MSIMessage;
110 77
111 78
112static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) 79void msg_init (MSIMessage *dest, MSIRequest request);
113{ 80int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length);
114 if ( s->callbacks[i].first ) { 81uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length);
115 LOGGER_DEBUG("Invoking callback function: %d", i); 82static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg);
116 83int send_error (Messenger *m, uint32_t friend_number, MSIError error);
117 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second ); 84static int invoke_callback(MSICall *call, MSICallbackID cb);
118 } 85static MSICall *get_call (MSISession *session, uint32_t friend_number);
119} 86MSICall *new_call (MSISession *session, uint32_t friend_number);
120 87void kill_call (MSICall *call);
121/** 88void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
122 * Parse raw 'data' received from socket into MSIMessage struct. 89void handle_init (MSICall *call, const MSIMessage *msg);
123 * Every message has to have end value of 'end_byte' or _undefined_ behavior 90void handle_push (MSICall *call, const MSIMessage *msg);
124 * occures. The best practice is to check the end of the message at the handle_packet. 91void handle_pop (MSICall *call, const MSIMessage *msg);
125 */ 92void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
126static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length )
127{
128
129#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; }
130#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; }
131#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; }
132
133 if ( msg == NULL ) {
134 LOGGER_ERROR("Could not parse message: no storage!");
135 return -1;
136 }
137
138 if ( data[length - 1] ) { /* End byte must have value 0 */
139 LOGGER_ERROR("Invalid end byte");
140 return -1;
141 }
142
143 const uint8_t *it = data;
144 int size_constraint = length;
145
146 while ( *it ) {/* until end byte is hit */
147 switch (*it) {
148 case IDRequest:
149 FAIL_CONSTRAINT(size_constraint, 3);
150 FAIL_SIZE(it[1], 1);
151// FAIL_LIMITS(it[2], invite, end);
152 FAIL_LIMITS(it[2], end);
153 msg->request.value = it[2];
154 it += 3;
155 msg->request.exists = 1;
156 break;
157
158 case IDResponse:
159 FAIL_CONSTRAINT(size_constraint, 3);
160 FAIL_SIZE(it[1], 1);
161// FAIL_LIMITS(it[2], ringing, error);
162 FAIL_LIMITS(it[2], error);
163 msg->response.value = it[2];
164 it += 3;
165 msg->response.exists = 1;
166 break;
167
168 case IDCallId:
169 FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2);
170 FAIL_SIZE(it[1], sizeof(MSICallIDType));
171 memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType));
172 it += sizeof(MSICallIDType) + 2;
173 msg->callid.exists = 1;
174 break;
175
176 case IDReason:
177 FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2);
178 FAIL_SIZE(it[1], sizeof(MSIReasonStrType));
179 memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType));
180 it += sizeof(MSIReasonStrType) + 2;
181 msg->reason.exists = 1;
182 break;
183 93
184 case IDCSettings:
185 FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2);
186 FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType));
187 memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType));
188 it += sizeof(MSIRawCSettingsType) + 2;
189 msg->csettings.exists = 1;
190 break;
191
192 default:
193 LOGGER_ERROR("Invalid id byte");
194 return -1;
195 break;
196 }
197 }
198
199 return 0;
200}
201 94
202/** 95/**
203 * Create the message. 96 * Public functions
204 */ 97 */
205MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) 98void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id)
206{ 99{
207 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 100 if (!session)
208 101 return;
209 if ( retu == NULL ) {
210 LOGGER_WARNING("Allocation failed! Program might misbehave!");
211 return NULL;
212 }
213
214 if ( type == TypeRequest ) {
215 retu->request.exists = 1;
216 retu->request.value = type_value;
217
218 } else {
219 retu->response.exists = 1;
220 retu->response.value = type_value;
221 }
222 102
223 return retu; 103 pthread_mutex_lock(session->mutex);
104 session->callbacks[id] = callback;
105 pthread_mutex_unlock(session->mutex);
224} 106}
225 107MSISession *msi_new (Messenger *m)
226
227/**
228 * Parse data from handle_packet.
229 */
230MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
231{ 108{
232 if ( data == NULL ) { 109 if (m == NULL) {
233 LOGGER_WARNING("Tried to parse empty message!"); 110 LOGGER_ERROR("Could not init session on empty messenger!");
234 return NULL;
235 }
236
237 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
238
239 if ( retu == NULL ) {
240 LOGGER_WARNING("Allocation failed! Program might misbehave!");
241 return NULL; 111 return NULL;
242 } 112 }
243 113
244 if ( parse_raw_data ( retu, data, length ) == -1 ) { 114 MSISession *retu = calloc (sizeof (MSISession), 1);
245 115
246 free ( retu ); 116 if (retu == NULL) {
247 return NULL; 117 LOGGER_ERROR("Allocation failed! Program might misbehave!");
248 }
249
250 return retu;
251}
252
253
254/**
255 * Speaks for itself.
256 */
257uint8_t *format_output ( uint8_t *dest,
258 MSIHeaderID id,
259 const void *value,
260 uint8_t value_len,
261 uint16_t *length )
262{
263 if ( dest == NULL ) {
264 LOGGER_ERROR("No destination space!");
265 return NULL; 118 return NULL;
266 } 119 }
267 120
268 if (value == NULL || value_len == 0) { 121 if (create_recursive_mutex(retu->mutex) != 0) {
269 LOGGER_ERROR("Empty header value"); 122 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
123 free(retu);
270 return NULL; 124 return NULL;
271 } 125 }
272 126
273 *dest = id; 127 retu->messenger = m;
274 dest ++;
275 *dest = value_len;
276 dest ++;
277 128
278 memcpy(dest, value, value_len); 129 m_callback_msi_packet(m, handle_msi_packet, retu);
279 130
280 *length += (2 + value_len); 131 /* This is called when remote terminates session */
132 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
281 133
282 return dest + value_len; /* Set to next position ready to be written */ 134 LOGGER_DEBUG("New msi session: %p ", retu);
135 return retu;
283} 136}
284 137int msi_kill (MSISession *session)
285
286/**
287 * Parse MSIMessage to send.
288 */
289uint16_t parse_send ( MSIMessage *msg, uint8_t *dest )
290{ 138{
291 if (msg == NULL) { 139 if (session == NULL) {
292 LOGGER_ERROR("No message!"); 140 LOGGER_ERROR("Tried to terminate non-existing session");
293 return 0; 141 return -1;
294 }
295
296 if (dest == NULL ) {
297 LOGGER_ERROR("No destination!");
298 return 0;
299 } 142 }
300 143
301 uint8_t *it = dest; 144 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
302 uint16_t size = 0;
303 145
304 if (msg->request.exists) { 146 if (pthread_mutex_trylock(session->mutex) != 0) {
305 uint8_t cast = msg->request.value; 147 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
306 it = format_output(it, IDRequest, &cast, 1, &size); 148 return -1;
307 }
308
309 if (msg->response.exists) {
310 uint8_t cast = msg->response.value;
311 it = format_output(it, IDResponse, &cast, 1, &size);
312 } 149 }
313 150
314 if (msg->callid.exists) { 151 if (session->calls) {
315 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); 152 MSIMessage msg;
316 } 153 msg_init(&msg, requ_pop);
317 154
318 if (msg->reason.exists) { 155 MSICall *it = get_call(session, session->calls_head);
319 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
320 }
321 156
322 if (msg->csettings.exists) { 157 for (; it; it = it->next) {
323 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size); 158 send_message(session->messenger, it->friend_number, &msg);
159 kill_call(it); /* This will eventually free session->calls */
160 }
324 } 161 }
325 162
326 *it = 0; 163 pthread_mutex_unlock(session->mutex);
327 size ++; 164 pthread_mutex_destroy(session->mutex);
328
329 return size;
330}
331
332void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
333{
334 if ( !msg ) return;
335 165
336 msg->reason.exists = 1; 166 LOGGER_DEBUG("Terminated session: %p", session);
337 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType)); 167 free (session);
168 return 0;
338} 169}
339 170int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
340void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
341{ 171{
342 if ( !msg ) return; 172 if (!session)
173 return -1;
343 174
344 msg->callid.exists = 1; 175 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
345 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
346}
347 176
348void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value ) 177 if (pthread_mutex_trylock(session->mutex) != 0) {
349{ 178 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
350 if ( !msg ) return; 179 return -1;
351 180 }
352 msg->csettings.exists = 1;
353
354 msg->csettings.value[0] = value->call_type;
355 uint8_t *iter = msg->csettings.value + 1;
356
357 /* Video bitrate */
358 uint32_t lval = htonl(value->video_bitrate);
359 memcpy(iter, &lval, 4);
360 iter += 4;
361
362 /* Video max width */
363 uint16_t sval = htons(value->max_video_width);
364 memcpy(iter, &sval, 2);
365 iter += 2;
366
367 /* Video max height */
368 sval = htons(value->max_video_height);
369 memcpy(iter, &sval, 2);
370 iter += 2;
371
372 /* Audio bitrate */
373 lval = htonl(value->audio_bitrate);
374 memcpy(iter, &lval, 4);
375 iter += 4;
376
377 /* Audio frame duration */
378 sval = htons(value->audio_frame_duration);
379 memcpy(iter, &sval, 2);
380 iter += 2;
381
382 /* Audio sample rate */
383 lval = htonl(value->audio_sample_rate);
384 memcpy(iter, &lval, 4);
385 iter += 4;
386
387 /* Audio channels */
388 lval = htonl(value->audio_channels);
389 memcpy(iter, &lval, 4);
390}
391 181
392void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest ) 182 if (get_call(session, friend_number) != NULL) {
393{ 183 LOGGER_ERROR("Already in a call");
394 if ( !msg || !dest || !msg->csettings.exists ) return; 184 pthread_mutex_unlock(session->mutex);
185 return -1;
186 }
395 187
396 dest->call_type = msg->csettings.value[0]; 188 (*call) = new_call (session, friend_number);
397 uint8_t *iter = msg->csettings.value + 1;
398 189
399 memcpy(&dest->video_bitrate, iter, 4); 190 if (*call == NULL) {
400 iter += 4; 191 pthread_mutex_unlock(session->mutex);
401 dest->video_bitrate = ntohl(dest->video_bitrate); 192 return -1;
193 }
402 194
403 memcpy(&dest->max_video_width, iter, 2); 195 (*call)->self_capabilities = capabilities;
404 iter += 2;
405 dest->max_video_width = ntohs(dest->max_video_width);
406 196
407 memcpy(&dest->max_video_height, iter, 2); 197 MSIMessage msg;
408 iter += 2; 198 msg_init(&msg, requ_init);
409 dest->max_video_height = ntohs(dest->max_video_height);
410 199
411 memcpy(&dest->audio_bitrate, iter, 4); 200 msg.capabilities.exists = true;
412 iter += 4; 201 msg.capabilities.value = capabilities;
413 dest->audio_bitrate = ntohl(dest->audio_bitrate);
414 202
415 memcpy(&dest->audio_frame_duration, iter, 2); 203 send_message ((*call)->session->messenger, (*call)->friend_number, &msg);
416 iter += 2;
417 dest->audio_frame_duration = ntohs(dest->audio_frame_duration);
418 204
419 memcpy(&dest->audio_sample_rate, iter, 4); 205 (*call)->state = msi_CallRequesting;
420 iter += 4;
421 dest->audio_sample_rate = ntohl(dest->audio_sample_rate);
422 206
423 memcpy(&dest->audio_channels, iter, 4); 207 LOGGER_DEBUG("Invite sent");
424 dest->audio_channels = ntohl(dest->audio_channels); 208 pthread_mutex_unlock(session->mutex);
209 return 0;
425} 210}
426 211int msi_hangup (MSICall *call)
427typedef struct _Timer {
428 void (*func)(struct _Timer *);
429 uint64_t timeout;
430 MSISession *session;
431 int call_idx;
432 int id;
433
434} Timer;
435
436typedef struct _TimerHandler {
437 Timer **timers;
438
439 uint32_t max_capacity;
440 uint32_t size;
441} TimerHandler;
442
443
444static int timer_alloc (MSISession *session , void (*func)(Timer *), int call_idx, uint32_t timeout)
445{ 212{
446 static int timer_id; 213 if (!call || !call->session)
447 TimerHandler *timer_handler = session->timer_handler; 214 return -1;
448 215
449 uint32_t i = 0; 216 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number);
450 217
451 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++); 218 MSISession *session = call->session;
452 219
453 if (i == timer_handler->max_capacity) { 220 if (pthread_mutex_trylock(session->mutex) != 0) {
454 LOGGER_WARNING("Maximum capacity reached!"); 221 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
455 return -1; 222 return -1;
456 } 223 }
457 224
458 Timer *timer = timer_handler->timers[i] = calloc(sizeof(Timer), 1); 225 if (call->state == msi_CallInactive) {
459 226 LOGGER_ERROR("Call is in invalid state!");
460 if (timer == NULL) { 227 pthread_mutex_unlock(session->mutex);
461 LOGGER_ERROR("Failed to allocate timer!");
462 return -1; 228 return -1;
463 } 229 }
464 230
465 timer_handler->size ++; 231 MSIMessage msg;
466 232 msg_init(&msg, requ_pop);
467 timer->func = func;
468 timer->session = session;
469 timer->call_idx = call_idx;
470 timer->timeout = timeout + current_time_monotonic(); /* In ms */
471 ++timer_id;
472 timer->id = timer_id;
473
474 /* reorder */
475 if (i) {
476 int64_t j = i - 1;
477 233
478 for (; j >= 0 && timeout < timer_handler->timers[j]->timeout; j--) { 234 send_message (session->messenger, call->friend_number, &msg);
479 Timer *tmp = timer_handler->timers[j];
480 timer_handler->timers[j] = timer;
481 timer_handler->timers[j + 1] = tmp;
482 }
483 }
484 235
485 LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timer_handler->size); 236 kill_call(call);
486 return timer->id; 237 pthread_mutex_unlock(session->mutex);
238 return 0;
487} 239}
488 240int msi_answer (MSICall *call, uint8_t capabilities)
489static int timer_release ( TimerHandler *timers_container, int id)
490{ 241{
491 Timer **timed_events = timers_container->timers; 242 if (!call || !call->session)
243 return -1;
492 244
493 uint32_t i; 245 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
494 int rc = -1;
495 246
496 for (i = 0; i < timers_container->max_capacity; ++i) { 247 MSISession *session = call->session;
497 if (timed_events[i] && timed_events[i]->id == id) {
498 rc = i;
499 break;
500 }
501 }
502 248
503 if (rc == -1) { 249 if (pthread_mutex_trylock(session->mutex) != 0) {
504 LOGGER_WARNING("No event with id: %d", id); 250 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
505 return -1; 251 return -1;
506 } 252 }
507 253
508 free(timed_events[rc]); 254 if (call->state != msi_CallRequested) {
509 255 /* Though sending in invalid state will not cause anything wierd
510 timed_events[rc] = NULL; 256 * Its better to not do it like a maniac */
511 257 LOGGER_ERROR("Call is in invalid state!");
512 i = rc + 1; 258 pthread_mutex_unlock(session->mutex);
513 259 return -1;
514 for (; i < timers_container->max_capacity && timed_events[i]; i ++) {
515 timed_events[i - 1] = timed_events[i];
516 timed_events[i] = NULL;
517 } 260 }
518 261
519 timers_container->size--; 262 call->self_capabilities = capabilities;
520 263
521 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size); 264 MSIMessage msg;
522 return 0; 265 msg_init(&msg, requ_push);
523}
524 266
525/** 267 msg.capabilities.exists = true;
526 * Generate _random_ alphanumerical string. 268 msg.capabilities.value = capabilities;
527 */
528static void t_randomstr ( uint8_t *str, uint32_t size )
529{
530 if (str == NULL) {
531 LOGGER_DEBUG("Empty destination!");
532 return;
533 }
534 269
535 static const uint8_t _bytes[] = 270 send_message (session->messenger, call->friend_number, &msg);
536 "0123456789"
537 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
538 "abcdefghijklmnopqrstuvwxyz";
539 271
540 uint32_t _it = 0; 272 call->state = msi_CallActive;
273 pthread_mutex_unlock(session->mutex);
541 274
542 for ( ; _it < size; _it++ ) { 275 return 0;
543 str[_it] = _bytes[ random_int() % 61 ];
544 }
545} 276}
546 277int msi_change_capabilities(MSICall *call, uint8_t capabilities)
547/* TODO: it would be nice to actually have some sane error codes */
548typedef enum {
549 error_none,
550 error_deadcall, /* has call id but it's from old call */
551 error_id_mismatch, /* non-existing call */
552
553 error_no_callid, /* not having call id */
554 error_no_call, /* no call in session */
555 error_no_crypto_key, /* no crypto key */
556
557 error_busy
558
559} MSICallError; /* Error codes */
560
561
562/**
563 * Stringify error code.
564 */
565static const uint8_t *stringify_error ( MSICallError error_code )
566{ 278{
567 static const uint8_t *strings[] = { 279 if (!call || !call->session)
568 ( uint8_t *) "", 280 return -1;
569 ( uint8_t *) "Using dead call",
570 ( uint8_t *) "Call id not set to any call",
571 ( uint8_t *) "Call id not available",
572 ( uint8_t *) "No active call in session",
573 ( uint8_t *) "No Crypto-key set",
574 ( uint8_t *) "Callee busy"
575 };
576
577 return strings[error_code];
578}
579 281
580static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to ) 282 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
581{
582 msi_msg_set_callid ( msg, call->id );
583 283
584 uint8_t msg_string_final [MSI_MAXMSG_SIZE]; 284 MSISession *session = call->session;
585 uint16_t length = parse_send ( msg, msg_string_final );
586 285
587 if (!length) { 286 if (pthread_mutex_trylock(session->mutex) != 0) {
588 LOGGER_WARNING("Parsing message failed; nothing sent!"); 287 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
589 return -1; 288 return -1;
590 } 289 }
591 290
592 if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { 291 if (call->state != msi_CallActive) {
593 LOGGER_DEBUG("Sent message"); 292 LOGGER_ERROR("Call is in invalid state!");
594 return 0; 293 pthread_mutex_unlock(session->mutex);
595 }
596
597 return -1;
598}
599
600static int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to )
601{
602 MSIMessage *msg = msi_new_message ( TypeResponse, response );
603 int ret = send_message ( session, call, msg, to );
604 free ( msg );
605 return ret;
606}
607
608static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to )
609{
610 if (!call) {
611 LOGGER_WARNING("Cannot handle error on 'null' call");
612 return -1; 294 return -1;
613 } 295 }
614 296
615 LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); 297 call->self_capabilities = capabilities;
616 298
617 MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); 299 MSIMessage msg;
300 msg_init(&msg, requ_push);
618 301
619 msi_msg_set_reason ( msg_error, stringify_error(errid) ); 302 msg.capabilities.exists = true;
620 send_message ( session, call, msg_error, to ); 303 msg.capabilities.value = capabilities;
621 free ( msg_error );
622 304
623 return 0; 305 send_message (call->session->messenger, call->friend_number, &msg);
624}
625 306
626/** 307 pthread_mutex_unlock(session->mutex);
627 * Determine 'bigger' call id 308 return 0;
628 */
629static int call_id_bigger( const uint8_t *first, const uint8_t *second)
630{
631 return (memcmp(first, second, sizeof(MSICallIDType)) < 0);
632} 309}
633 310
634 311
635/** 312/**
636 * Set/change peer csettings 313 * Private functions
637 */ 314 */
638static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) 315void msg_init(MSIMessage *dest, MSIRequest request)
639{ 316{
640 if ( msg->csettings.exists ) { 317 memset(dest, 0, sizeof(*dest));
641 msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]); 318 dest->request.exists = true;
642 319 dest->request.value = request;
643 LOGGER_DEBUG("Peer: %d \n"
644 "Type: %u \n"
645 "Video bitrate: %u \n"
646 "Video height: %u \n"
647 "Video width: %u \n"
648 "Audio bitrate: %u \n"
649 "Audio framedur: %u \n"
650 "Audio sample rate: %u \n"
651 "Audio channels: %u \n", peer_id,
652 call->csettings_peer[peer_id].call_type,
653 call->csettings_peer[peer_id].video_bitrate,
654 call->csettings_peer[peer_id].max_video_height,
655 call->csettings_peer[peer_id].max_video_width,
656 call->csettings_peer[peer_id].audio_bitrate,
657 call->csettings_peer[peer_id].audio_frame_duration,
658 call->csettings_peer[peer_id].audio_sample_rate,
659 call->csettings_peer[peer_id].audio_channels );
660
661 return 0;
662 }
663
664 LOGGER_WARNING("No csettings header!");
665 return -1;
666} 320}
667 321int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length)
668
669/**
670 * Add peer to peer list.
671 */
672static void add_peer( MSICall *call, int peer_id )
673{ 322{
674 uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : 323 /* Parse raw data received from socket into MSIMessage struct */
675 realloc( call->peers, sizeof(uint32_t) * call->peer_count);
676
677 if (!peers) {
678 LOGGER_WARNING("Allocation failed! Program might misbehave!");
679 return;
680 }
681
682 call->peer_count ++;
683 call->peers = peers;
684 call->peers[call->peer_count - 1] = peer_id;
685
686 LOGGER_DEBUG("Added peer: %d", peer_id);
687}
688 324
325#define CHECK_SIZE(bytes, constraint, size) \
326 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
327 if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; }
689 328
690static MSICall *find_call ( MSISession *session, uint8_t *call_id ) 329#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
691{ 330 if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
692 if ( call_id == NULL ) return NULL;
693 331
694 int32_t i = 0; 332#define SET_UINT8(bytes, header) do { \
333 header.value = bytes[2]; \
334 header.exists = true; \
335 bytes += 3; \
336 } while(0)
695 337
696 for (; i < session->max_calls; i ++ ) 338#define SET_UINT16(bytes, header) do { \
697 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) { 339 memcpy(&header.value, bytes + 2, 2);\
698 return session->calls[i]; 340 header.exists = true; \
699 } 341 bytes += 4; \
342 } while(0)
700 343
701 return NULL;
702}
703 344
704static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout ) 345 assert(dest);
705{
706 346
707 if (peers == 0) { 347 if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
708 LOGGER_ERROR("No peers!"); 348 LOGGER_ERROR("Invalid end byte");
709 return NULL; 349 return -1;
710 } 350 }
711 351
712 int32_t call_idx = 0; 352 memset(dest, 0, sizeof(*dest));
713 353
714 for (; call_idx < session->max_calls; call_idx ++) { 354 const uint8_t *it = data;
715 if ( !session->calls[call_idx] ) { 355 int size_constraint = length;
716
717 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) {
718 LOGGER_WARNING("Allocation failed! Program might misbehave!");
719 return NULL;
720 }
721
722 break;
723 }
724 }
725
726 if ( call_idx == session->max_calls ) {
727 LOGGER_WARNING("Reached maximum amount of calls!");
728 return NULL;
729 }
730 356
357 while (*it) {/* until end byte is hit */
358 switch (*it) {
359 case IDRequest:
360 CHECK_SIZE(it, size_constraint, 1);
361 CHECK_ENUM_HIGH(it, requ_pop);
362 SET_UINT8(it, dest->request);
363 break;
731 364
732 MSICall *call = session->calls[call_idx]; 365 case IDError:
366 CHECK_SIZE(it, size_constraint, 1);
367 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
368 SET_UINT8(it, dest->error);
369 break;
733 370
734 call->call_idx = call_idx; 371 case IDCapabilities:
372 CHECK_SIZE(it, size_constraint, 1);
373 SET_UINT8(it, dest->capabilities);
374 break;
735 375
736 if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) { 376 default:
737 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 377 LOGGER_ERROR("Invalid id byte");
738 free(call); 378 return -1;
739 return NULL; 379 break;
380 }
740 } 381 }
741 382
742 call->session = session; 383 if (dest->request.exists == false) {
743 384 LOGGER_ERROR("Invalid request field!");
744 call->request_timer_id = 0;
745 call->ringing_timer_id = 0;
746
747 call->ringing_tout_ms = ringing_timeout;
748
749 LOGGER_DEBUG("Started new call with index: %u", call_idx);
750 return call;
751}
752
753static int terminate_call ( MSISession *session, MSICall *call )
754{
755 if ( !call ) {
756 LOGGER_WARNING("Tried to terminate non-existing call!");
757 return -1; 385 return -1;
758 } 386 }
759 387
760 /* Check event loop and cancel timed events if there are any
761 */
762 timer_release ( session->timer_handler, call->request_timer_id);
763 timer_release ( session->timer_handler, call->ringing_timer_id);
764
765 session->calls[call->call_idx] = NULL;
766
767 LOGGER_DEBUG("Terminated call id: %d", call->call_idx);
768
769 free ( call->csettings_peer );
770 free ( call->peers );
771 free ( call );
772
773 return 0; 388 return 0;
774}
775 389
776static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p) 390#undef CHECK_SIZE
391#undef CHECK_ENUM_HIGH
392#undef SET_UINT8
393#undef SET_UINT16
394}
395uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
777{ 396{
778 (void)messenger; 397 /* Parse a single header for sending */
779 MSISession *session = session_p; 398 assert(dest);
399 assert(value);
400 assert(value_len);
780 401
781 switch ( status ) { 402 *dest = id;
782 case 0: { /* Went offline */ 403 dest ++;
783 int32_t j = 0; 404 *dest = value_len;
784 405 dest ++;
785 for ( ; j < session->max_calls; j ++ ) {
786
787 if ( !session->calls[j] ) continue;
788 406
789 uint16_t i = 0; 407 memcpy(dest, value, value_len);
790 408
791 for ( ; i < session->calls[j]->peer_count; i ++ ) 409 *length += (2 + value_len);
792 if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
793 invoke_callback(session, j, msi_OnPeerTimeout);
794 terminate_call(session, session->calls[j]);
795 LOGGER_DEBUG("Remote: %d timed out!", friend_num);
796 return; /* TODO: On group calls change behaviour */
797 }
798 }
799 }
800 break;
801 410
802 default: 411 return dest + value_len; /* Set to next position ready to be written */
803 break;
804 }
805} 412}
806 413int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg)
807/**
808 * Function called at request timeout
809 */
810static void handle_timeout ( Timer *timer )
811{ 414{
812 /* TODO: Cancel might not arrive there; set up 415 /* Parse and send message */
813 * timers on these cancels and terminate call on 416 assert(m);
814 * their timeout
815 */
816 MSICall *call = timer->session->calls[timer->call_idx];
817 417
418 uint8_t parsed [MSI_MAXMSG_SIZE];
818 419
819 if (call) { 420 uint8_t *it = parsed;
820 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); 421 uint16_t size = 0;
821 422
822 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 423 if (msg->request.exists) {
823 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); 424 uint8_t cast = msg->request.value;
425 it = msg_parse_header_out(IDRequest, it, &cast,
426 sizeof(cast), &size);
427 } else {
428 LOGGER_DEBUG("Must have request field");
429 return -1;
824 } 430 }
825}
826
827
828/********** Request handlers **********/
829static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg )
830{
831 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1);
832 431
833 432 if (msg->error.exists) {
834 if (!msg->csettings.exists) {/**/ 433 uint8_t cast = msg->error.value;
835 LOGGER_WARNING("Peer sent invalid codec settings!"); 434 it = msg_parse_header_out(IDError, it, &cast,
836 send_error ( session, call, error_no_callid, msg->friend_id ); 435 sizeof(cast), &size);
837 return 0;
838 } 436 }
839 437
840 if ( call ) { 438 if (msg->capabilities.exists) {
841 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 439 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
842 if (call->state == msi_CallInviting) { 440 sizeof(msg->capabilities.value), &size);
843 /* The glare case. A calls B when at the same time
844 * B calls A. Who has advantage is set bey calculating
845 * 'bigger' Call id and then that call id is being used in
846 * future. User with 'bigger' Call id has the advantage
847 * as in he will wait the response from the other.
848 */
849 LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]);
850
851 if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */
852
853 /* Terminate call; peer will timeout(call) if call initialization fails */
854 terminate_call(session, call);
855
856 call = init_call ( session, 1, 0 );
857
858 if ( !call ) {
859 LOGGER_ERROR("Starting call");
860 return 0;
861 }
862
863 } else {
864 return 0; /* Wait for ringing from peer */
865 }
866 } else if (call->state == msi_CallActive) {
867 /* Request for media change; call callback and send starting response */
868 if (flush_peer_csettings(call, msg, 0) != 0) { /**/
869 LOGGER_WARNING("Peer sent invalid csetting!");
870 send_error ( session, call, error_no_callid, msg->friend_id );
871 return 0;
872 }
873
874 LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == msi_TypeAudio ? "audio" : "video");
875 send_reponse(session, call, starting, msg->friend_id);
876 invoke_callback(session, call->call_idx, msi_OnPeerCSChange);
877 return 1;
878 }
879 } else {
880 send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/
881 terminate_call(session, call);
882 return 0;
883 }
884 } else {
885 call = init_call ( session, 1, 0 );
886
887 if ( !call ) {
888 LOGGER_ERROR("Starting call");
889 return 0;
890 }
891 } 441 }
892 442
893 if ( !msg->callid.exists ) { 443 if (it == parsed) {
894 send_error ( session, call, error_no_callid, msg->friend_id ); 444 LOGGER_WARNING("Parsing message failed; empty message");
895 terminate_call(session, call); 445 return -1;
896 return 0;
897 } 446 }
898 447
899 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 448 *it = 0;
900 call->state = msi_CallStarting; 449 size ++;
901
902 add_peer( call, msg->friend_id);
903 flush_peer_csettings ( call, msg, 0 );
904 send_reponse(session, call, ringing, msg->friend_id);
905 invoke_callback(session, call->call_idx, msi_OnInvite);
906
907 return 1;
908}
909 450
910static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) 451 if (m_msi_packet(m, friend_number, parsed, size)) {
911{ 452 LOGGER_DEBUG("Sent message");
912 if ( !call ) {
913 LOGGER_WARNING("Session: %p Handling 'start' on no call");
914 return 0; 453 return 0;
915 } 454 }
916 455
917 (void)msg; 456 return -1;
918
919 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id );
920
921 call->state = msi_CallActive;
922 invoke_callback(session, call->call_idx, msi_OnStart);
923 return 1;
924} 457}
925 458int send_error (Messenger *m, uint32_t friend_number, MSIError error)
926static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg )
927{ 459{
928 if ( !call ) { 460 /* Send error message */
929 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 461 assert(m);
930 return 0;
931 }
932 462
933 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 463 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
934 464
935 invoke_callback(session, call->call_idx, msi_OnReject); 465 MSIMessage msg;
466 msg_init(&msg, requ_pop);
936 467
937 send_reponse(session, call, ending, msg->friend_id); 468 msg.error.exists = true;
938 terminate_call(session, call); 469 msg.error.value = error;
939 470
940 return 1; 471 send_message (m, friend_number, &msg);
472 return 0;
941} 473}
942 474int invoke_callback(MSICall *call, MSICallbackID cb)
943static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg )
944{ 475{
945 if ( !call ) { 476 assert(call);
946 LOGGER_WARNING("Session: %p Handling 'start' on no call");
947 return 0;
948 }
949
950 (void)msg;
951 477
952 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); 478 if (call->session->callbacks[cb]) {
479 LOGGER_DEBUG("Invoking callback function: %d", cb);
953 480
954 invoke_callback(session, call->call_idx, msi_OnCancel); 481 if (call->session->callbacks[cb] (call->session->av, call) != 0) {
955 terminate_call ( session, call ); 482 LOGGER_WARNING("Callback state handling failed, sending error");
956 483 goto FAILURE;
957 return 1; 484 }
958}
959 485
960static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
961{
962 if ( !call ) {
963 LOGGER_WARNING("Session: %p Handling 'start' on no call");
964 return 0; 486 return 0;
965 } 487 }
966 488
967 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); 489FAILURE:
490 /* If no callback present or error happened while handling,
491 * an error message will be sent to friend
492 */
968 493
969 invoke_callback(session, call->call_idx, msi_OnEnd); 494 if (call->error == msi_ENone)
970 send_reponse(session, call, ending, msg->friend_id); 495 call->error = msi_EHandle;
971 terminate_call ( session, call );
972 496
973 return 1; 497 return -1;
974} 498}
975 499static MSICall *get_call (MSISession *session, uint32_t friend_number)
976/********** Response handlers **********/
977static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
978{ 500{
979 if ( !call ) { 501 assert(session);
980 LOGGER_WARNING("Session: %p Handling 'start' on no call");
981 return 0;
982 }
983
984 (void)msg;
985 502
986 if ( call->ringing_timer_id ) { 503 if (session->calls == NULL || session->calls_tail < friend_number)
987 LOGGER_WARNING("Call already ringing"); 504 return NULL;
988 return 0;
989 }
990
991 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
992 505
993 call->ringing_timer_id = timer_alloc 506 return session->calls[friend_number];
994 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
995 invoke_callback(session, call->call_idx, msi_OnRinging);
996 return 1;
997} 507}
998static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) 508MSICall *new_call (MSISession *session, uint32_t friend_number)
999{ 509{
1000 if ( !call ) { 510 assert(session);
1001 LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call");
1002 return 0;
1003 }
1004
1005 if ( call->state == msi_CallActive ) { /* Change media */
1006 511
1007 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx ); 512 MSICall *rc = calloc(sizeof(MSICall), 1);
1008 513
1009 invoke_callback(session, call->call_idx, msi_OnSelfCSChange); 514 if (rc == NULL)
1010 515 return NULL;
1011 } else if ( call->state == msi_CallInviting ) {
1012 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
1013 516
1014 call->state = msi_CallActive; 517 rc->session = session;
518 rc->friend_number = friend_number;
1015 519
1016 MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); 520 if (session->calls == NULL) { /* Creating */
1017 send_message ( session, call, msg_start, msg->friend_id ); 521 session->calls = calloc (sizeof(MSICall *), friend_number + 1);
1018 free ( msg_start );
1019 522
523 if (session->calls == NULL) {
524 free(rc);
525 return NULL;
526 }
1020 527
1021 flush_peer_csettings ( call, msg, 0 ); 528 session->calls_tail = session->calls_head = friend_number;
1022 529
1023 /* This is here in case of glare */ 530 } else if (session->calls_tail < friend_number) { /* Appending */
1024 timer_release(session->timer_handler, call->ringing_timer_id); 531 void *tmp = realloc(session->calls, sizeof(MSICall *) * (friend_number + 1));
1025 invoke_callback(session, call->call_idx, msi_OnStart);
1026 } else {
1027 LOGGER_ERROR("Invalid call state");
1028 terminate_call(session, call );
1029 return 0;
1030 }
1031 532
1032 return 1; 533 if (tmp == NULL) {
1033} 534 free(rc);
1034static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg ) 535 return NULL;
1035{ 536 }
1036 if ( !call ) {
1037 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1038 return 0;
1039 }
1040
1041 (void)msg;
1042 537
1043 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); 538 session->calls = tmp;
1044 539
1045 invoke_callback(session, call->call_idx, msi_OnEnd); 540 /* Set fields in between to null */
1046 terminate_call ( session, call ); 541 uint32_t i = session->calls_tail + 1;
1047 542
1048 return 1; 543 for (; i < friend_number; i ++)
1049} 544 session->calls[i] = NULL;
1050static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
1051{
1052 if ( !call ) {
1053 LOGGER_WARNING("Handling 'error' on non-existing call!");
1054 return -1;
1055 }
1056 545
1057 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx ); 546 rc->prev = session->calls[session->calls_tail];
547 session->calls[session->calls_tail]->next = rc;
1058 548
1059 invoke_callback(session, call->call_idx, msi_OnEnd); 549 session->calls_tail = friend_number;
1060 550
1061 /* Handle error accordingly */ 551 } else if (session->calls_head > friend_number) { /* Inserting at front */
1062 if ( msg->reason.exists ) { 552 rc->next = session->calls[session->calls_head];
1063 /* TODO */ 553 session->calls[session->calls_head]->prev = rc;
554 session->calls_head = friend_number;
1064 } 555 }
1065 556
1066 terminate_call ( session, call ); 557 session->calls[friend_number] = rc;
1067 558 return rc;
1068 return 1;
1069} 559}
1070 560void kill_call (MSICall *call)
1071/**
1072 * BASIC call flow:
1073 *
1074 * ALICE BOB
1075 * | invite --> |
1076 * | |
1077 * | <-- ringing |
1078 * | |
1079 * | <-- starting |
1080 * | |
1081 * | start --> |
1082 * | |
1083 * | <-- MEDIA TRANS --> |
1084 * | |
1085 * | end --> |
1086 * | |
1087 * | <-- ending |
1088 *
1089 * Alice calls Bob by sending invite packet.
1090 * Bob recvs the packet and sends an ringing packet;
1091 * which notifies Alice that her invite is acknowledged.
1092 * Ringing screen shown on both sides.
1093 * Bob accepts the invite for a call by sending starting packet.
1094 * Alice recvs the starting packet and sends the started packet to
1095 * inform Bob that she recved the starting packet.
1096 * Now the media transmission is established ( i.e. RTP transmission ).
1097 * Alice hangs up and sends end packet.
1098 * Bob recves the end packet and sends ending packet
1099 * as the acknowledgement that the call is ending.
1100 *
1101 *
1102 */
1103static void msi_handle_packet ( Messenger *messenger, uint32_t source, const uint8_t *data, uint16_t length,
1104 void *object )
1105{ 561{
1106 LOGGER_DEBUG("Got msi message"); 562 /* Assume that session mutex is locked */
1107 /* Unused */ 563 if (call == NULL)
1108 (void)messenger;
1109
1110 MSISession *session = object;
1111 MSIMessage *msg;
1112
1113 if ( !length ) {
1114 LOGGER_WARNING("Length param negative");
1115 return; 564 return;
1116 }
1117 565
1118 msg = parse_recv ( data, length ); 566 LOGGER_DEBUG("Killing call: %p", call);
1119 567
1120 if ( !msg ) { 568 MSISession *session = call->session;
1121 LOGGER_WARNING("Error parsing message");
1122 return;
1123 } else {
1124 LOGGER_DEBUG("Successfully parsed message");
1125 }
1126 569
1127 msg->friend_id = source; 570 MSICall *prev = call->prev;
571 MSICall *next = call->next;
1128 572
1129 pthread_mutex_lock(session->mutex); 573 if (prev)
574 prev->next = next;
575 else if (next)
576 session->calls_head = next->friend_number;
577 else goto CLEAR_CONTAINER;
1130 578
1131 /* Find what call */ 579 if (next)
1132 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL; 580 next->prev = prev;
581 else if (prev)
582 session->calls_tail = prev->friend_number;
583 else goto CLEAR_CONTAINER;
1133 584
1134 /* Now handle message */ 585 session->calls[call->friend_number] = NULL;
586 free(call);
587 return;
1135 588
1136 if ( msg->request.exists ) { /* Handle request */ 589CLEAR_CONTAINER:
1137 590 session->calls_head = session->calls_tail = 0;
1138 switch (msg->request.value) { 591 free(session->calls);
1139 case invite: 592 free(call);
1140 handle_recv_invite ( session, call, msg ); 593 session->calls = NULL;
1141 break;
1142
1143 case start:
1144 handle_recv_start ( session, call, msg );
1145 break;
1146
1147 case cancel:
1148 handle_recv_cancel ( session, call, msg );
1149 break;
1150
1151 case reject:
1152 handle_recv_reject ( session, call, msg );
1153 break;
1154
1155 case end:
1156 handle_recv_end ( session, call, msg );
1157 break;
1158 }
1159
1160 } else if ( msg->response.exists ) { /* Handle response */
1161
1162 /* Got response so cancel timer */
1163 if ( call ) timer_release(session->timer_handler, call->request_timer_id);
1164
1165 switch (msg->response.value) {
1166 case ringing:
1167 handle_recv_ringing ( session, call, msg );
1168 break;
1169
1170 case starting:
1171 handle_recv_starting ( session, call, msg );
1172 break;
1173
1174 case ending:
1175 handle_recv_ending ( session, call, msg );
1176 break;
1177
1178 case error:
1179 handle_recv_error ( session, call, msg );
1180 break;
1181 }
1182
1183 } else {
1184 LOGGER_WARNING("Invalid message: no resp nor requ headers");
1185 }
1186
1187 free ( msg );
1188
1189 pthread_mutex_unlock(session->mutex);
1190} 594}
1191 595void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
1192
1193
1194/********** User functions **********/
1195void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1196{ 596{
1197 session->callbacks[id].first = callback; 597 (void)m;
1198 session->callbacks[id].second = userdata; 598 MSISession *session = data;
1199}
1200 599
600 switch (status) {
601 case 0: { /* Friend is now offline */
602 LOGGER_DEBUG("Friend %d is now offline", friend_number);
1201 603
1202MSISession *msi_new ( Messenger *messenger, int32_t max_calls ) 604 pthread_mutex_lock(session->mutex);
1203{ 605 MSICall *call = get_call(session, friend_number);
1204 if (messenger == NULL) {
1205 LOGGER_ERROR("Could not init session on empty messenger!");
1206 return NULL;
1207 }
1208 606
1209 if ( !max_calls ) { 607 if (call == NULL) {
1210 LOGGER_WARNING("Invalid max call treshold!"); 608 pthread_mutex_unlock(session->mutex);
1211 return NULL; 609 return;
1212 } 610 }
1213
1214 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
1215
1216 if (retu == NULL) {
1217 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1218 return NULL;
1219 }
1220
1221 if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) {
1222 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1223 goto error;
1224 }
1225
1226 retu->timer_handler = calloc(1, sizeof(TimerHandler));
1227
1228 if (retu->timer_handler == NULL) {
1229 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1230 goto error;
1231 }
1232
1233 /* Allocate space for timers */
1234 ((TimerHandler *)retu->timer_handler)->max_capacity = max_calls * 10;
1235
1236 if (!(((TimerHandler *)retu->timer_handler)->timers = calloc(max_calls * 10, sizeof(Timer *)))) {
1237 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1238 goto error;
1239 }
1240
1241 if (create_recursive_mutex(retu->mutex) != 0) {
1242 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1243 goto error;
1244 }
1245
1246 retu->messenger_handle = messenger;
1247 retu->agent_handler = NULL;
1248 retu->max_calls = max_calls;
1249 retu->frequ = 10000; /* default value? */
1250 retu->call_timeout = 30000; /* default value? */
1251
1252 m_callback_msi_packet(messenger, msi_handle_packet, retu );
1253
1254 /* This is called when remote terminates session */
1255 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu);
1256
1257 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls);
1258 return retu;
1259 611
1260error: 612 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
613 kill_call(call);
614 pthread_mutex_unlock(session->mutex);
615 }
616 break;
1261 617
1262 if (retu->timer_handler) { 618 default:
1263 free(((TimerHandler *)retu->timer_handler)->timers); 619 break;
1264 free(retu->timer_handler);
1265 } 620 }
1266
1267 free(retu->calls);
1268 free(retu);
1269 return NULL;
1270} 621}
1271 622void handle_init (MSICall *call, const MSIMessage *msg)
1272
1273int msi_kill ( MSISession *session )
1274{ 623{
1275 if (session == NULL) { 624 assert(call);
1276 LOGGER_ERROR("Tried to terminate non-existing session"); 625 LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
1277 return -1; 626
627 if (!msg->capabilities.exists) {
628 LOGGER_WARNING("Session: %p Invalid capabilities on 'init'");
629 call->error = msi_EInvalidMessage;
630 goto FAILURE;
1278 } 631 }
1279 632
1280 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); 633 switch (call->state) {
1281 pthread_mutex_lock(session->mutex); 634 case msi_CallInactive: {
635 /* Call requested */
636 call->peer_capabilities = msg->capabilities.value;
637 call->state = msi_CallRequested;
1282 638
1283 /* Cancel active calls */ 639 if (invoke_callback(call, msi_OnInvite) == -1)
1284 int32_t idx = 0; 640 goto FAILURE;
1285
1286 for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) {
1287 /* Cancel all? */
1288 uint16_t _it = 0;
1289 /*for ( ; _it < session->calls[idx]->peer_count; _it++ )
1290 * FIXME: will not work on multiple peers, must cancel call for all peers
1291 */
1292 MSICallState state = session->calls[idx]->state;
1293
1294 if (state == msi_CallInviting) {
1295 msi_cancel( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
1296 } else {
1297 msi_stopcall(session, idx);
1298 }
1299 } 641 }
642 break;
1300 643
1301 free(((TimerHandler *)session->timer_handler)->timers); 644 case msi_CallActive: {
1302 free(session->timer_handler); 645 /* If peer sent init while the call is already
646 * active it's probable that he is trying to
647 * re-call us while the call is not terminated
648 * on our side. We can assume that in this case
649 * we can automatically answer the re-call.
650 */
1303 651
1304 free ( session->calls ); 652 LOGGER_INFO("Friend is recalling us");
1305 pthread_mutex_unlock(session->mutex);
1306 pthread_mutex_destroy(session->mutex);
1307 653
1308 LOGGER_DEBUG("Terminated session: %p", session); 654 MSIMessage msg;
1309 free ( session ); 655 msg_init(&msg, requ_push);
1310 return 0;
1311}
1312
1313int msi_invite ( MSISession *session,
1314 int32_t *call_index,
1315 const MSICSettings *csettings,
1316 uint32_t rngsec,
1317 uint32_t friend_id )
1318{
1319 pthread_mutex_lock(session->mutex);
1320
1321 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
1322 656
657 msg.capabilities.exists = true;
658 msg.capabilities.value = call->self_capabilities;
1323 659
1324 int i = 0; 660 send_message (call->session->messenger, call->friend_number, &msg);
1325 661
1326 for (; i < session->max_calls; i ++) 662 /* If peer changed capabilities during re-call they will
1327 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) { 663 * be handled accordingly during the next step
1328 LOGGER_ERROR("Already in a call with friend %d", friend_id); 664 */
1329 pthread_mutex_unlock(session->mutex);
1330 return msi_ErrorAlreadyInCallWithPeer;
1331 } 665 }
666 break;
1332 667
1333 668 default: {
1334 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ 669 LOGGER_WARNING("Session: %p Invalid state on 'init'");
1335 670 call->error = msi_EInvalidState;
1336 if ( !call ) { 671 goto FAILURE;
1337 pthread_mutex_unlock(session->mutex); 672 }
1338 LOGGER_ERROR("Cannot handle more calls"); 673 break;
1339 return msi_ErrorReachedCallLimit;
1340 } 674 }
1341 675
1342 *call_index = call->call_idx; 676 return;
1343 677FAILURE:
1344 t_randomstr ( call->id, sizeof(call->id) ); 678 send_error(call->session->messenger, call->friend_number, call->error);
1345 679 kill_call(call);
1346 add_peer ( call, friend_id );
1347
1348 call->csettings_local = *csettings;
1349
1350 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1351
1352 msi_msg_set_csettings(msg_invite, csettings);
1353 send_message ( session, call, msg_invite, friend_id );
1354 free( msg_invite );
1355
1356 call->state = msi_CallInviting;
1357
1358 call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout );
1359
1360 LOGGER_DEBUG("Invite sent");
1361
1362 pthread_mutex_unlock(session->mutex);
1363
1364 return 0;
1365} 680}
1366 681void handle_push (MSICall *call, const MSIMessage *msg)
1367int msi_hangup ( MSISession *session, int32_t call_index )
1368{ 682{
1369 pthread_mutex_lock(session->mutex); 683 assert(call);
1370 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
1371 684
1372 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 685 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
1373 LOGGER_ERROR("Invalid call index!");
1374 pthread_mutex_unlock(session->mutex);
1375 return msi_ErrorNoCall;
1376 }
1377 686
1378 if ( session->calls[call_index]->state != msi_CallActive ) { 687 if (!msg->capabilities.exists) {
1379 LOGGER_ERROR("Call is not active!"); 688 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
1380 pthread_mutex_unlock(session->mutex); 689 call->error = msi_EInvalidMessage;
1381 return msi_ErrorInvalidState; 690 goto FAILURE;
1382 } 691 }
1383 692
1384 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 693 switch (call->state) {
1385 694 case msi_CallActive: {
1386 /* hangup for each peer */ 695 /* Only act if capabilities changed */
1387 int it = 0; 696 if (call->peer_capabilities != msg->capabilities.value) {
697 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
1388 698
1389 for ( ; it < session->calls[call_index]->peer_count; it ++ ) 699 call->peer_capabilities = msg->capabilities.value;
1390 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1391 700
1392 session->calls[call_index]->state = msi_CallOver; 701 if (invoke_callback(call, msi_OnCapabilities) == -1)
702 goto FAILURE;
703 }
704 }
705 break;
1393 706
1394 free ( msg_end ); 707 case msi_CallRequesting: {
708 LOGGER_INFO("Friend answered our call");
1395 709
1396 session->calls[call_index]->request_timer_id = 710 /* Call started */
1397 timer_alloc ( session, handle_timeout, call_index, m_deftout ); 711 call->peer_capabilities = msg->capabilities.value;
712 call->state = msi_CallActive;
1398 713
1399 pthread_mutex_unlock(session->mutex); 714 if (invoke_callback(call, msi_OnStart) == -1)
1400 return 0; 715 goto FAILURE;
1401}
1402 716
1403int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ) 717 }
1404{ 718 break;
1405 pthread_mutex_lock(session->mutex);
1406 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1407
1408 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1409 LOGGER_ERROR("Invalid call index!");
1410 pthread_mutex_unlock(session->mutex);
1411 return msi_ErrorNoCall;
1412 }
1413 719
1414 if ( session->calls[call_index]->state != msi_CallStarting ) { 720 /* Pushes during initialization state are ignored */
1415 LOGGER_ERROR("Call is in invalid state!"); 721 case msi_CallInactive:
1416 pthread_mutex_unlock(session->mutex); 722 case msi_CallRequested: {
1417 return msi_ErrorInvalidState; 723 LOGGER_WARNING("Ignoring invalid push");
724 }
725 break;
1418 } 726 }
1419 727
1420 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); 728 return;
1421
1422 session->calls[call_index]->csettings_local = *csettings;
1423
1424 msi_msg_set_csettings(msg_starting, csettings);
1425
1426 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
1427 free ( msg_starting );
1428
1429 session->calls[call_index]->state = msi_CallActive;
1430 729
1431 pthread_mutex_unlock(session->mutex); 730FAILURE:
1432 return 0; 731 send_error(call->session->messenger, call->friend_number, call->error);
732 kill_call(call);
1433} 733}
1434 734void handle_pop (MSICall *call, const MSIMessage *msg)
1435int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
1436{ 735{
1437 pthread_mutex_lock(session->mutex); 736 assert(call);
1438 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1439 737
1440 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 738 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
1441 LOGGER_ERROR("Invalid call index!");
1442 pthread_mutex_unlock(session->mutex);
1443 return msi_ErrorNoCall;
1444 }
1445
1446 if ( session->calls[call_index]->state != msi_CallInviting ) {
1447 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state);
1448 pthread_mutex_unlock(session->mutex);
1449 return msi_ErrorInvalidState;
1450 }
1451 739
1452 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); 740 /* callback errors are ignored */
1453 741
1454 /* FIXME */ 742 if (msg->error.exists) {
1455#if 0 743 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
744 call->error = msg->error.value;
745 invoke_callback(call, msi_OnError);
1456 746
1457 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 747 } else switch (call->state) {
1458 MSIReasonStrType reason_cast; 748 case msi_CallInactive: {
1459 memset(reason_cast, '\0', sizeof(MSIReasonStrType)); 749 LOGGER_ERROR("Handling what should be impossible case");
1460 memcpy(reason_cast, reason, strlen(reason)); 750 abort();
1461 msi_msg_set_reason(msg_cancel, reason_cast); 751 }
1462 } 752 break;
1463
1464#else
1465 (void)reason;
1466 753
1467#endif 754 case msi_CallActive: {
755 /* Hangup */
756 LOGGER_INFO("Friend hung up on us");
757 invoke_callback(call, msi_OnEnd);
758 }
759 break;
1468 760
1469 send_message ( session, session->calls[call_index], msg_cancel, peer ); 761 case msi_CallRequesting: {
1470 free ( msg_cancel ); 762 /* Reject */
763 LOGGER_INFO("Friend rejected our call");
764 invoke_callback(call, msi_OnEnd);
765 }
766 break;
1471 767
1472 terminate_call ( session, session->calls[call_index] ); 768 case msi_CallRequested: {
1473 pthread_mutex_unlock(session->mutex); 769 /* Cancel */
770 LOGGER_INFO("Friend canceled call invite");
771 invoke_callback(call, msi_OnEnd);
772 }
773 break;
774 }
1474 775
1475 return 0; 776 kill_call (call);
1476} 777}
1477 778void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
1478int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1479{ 779{
1480 pthread_mutex_lock(session->mutex); 780 LOGGER_DEBUG("Got msi message");
1481 LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1482
1483 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1484 LOGGER_ERROR("Invalid call index!");
1485 pthread_mutex_unlock(session->mutex);
1486 return msi_ErrorNoCall;
1487 }
1488
1489 if ( session->calls[call_index]->state != msi_CallStarting ) {
1490 LOGGER_ERROR("Call is in invalid state!");
1491 pthread_mutex_unlock(session->mutex);
1492 return msi_ErrorInvalidState;
1493 }
1494
1495 MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
1496
1497 /* FIXME */
1498#if 0
1499
1500 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1501 MSIReasonStrType reason_cast;
1502 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1503 memcpy(reason_cast, reason, strlen(reason));
1504 msi_msg_set_reason(msg_reject, reason_cast);
1505 }
1506
1507#else
1508 (void)reason;
1509
1510#endif
1511
1512 send_message ( session, session->calls[call_index], msg_reject,
1513 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
1514 free ( msg_reject );
1515
1516 session->calls[call_index]->state = msi_CallOver;
1517 session->calls[call_index]->request_timer_id =
1518 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1519
1520 pthread_mutex_unlock(session->mutex);
1521 return 0;
1522}
1523 781
1524int msi_stopcall ( MSISession *session, int32_t call_index ) 782 MSISession *session = object;
1525{ 783 MSIMessage msg;
1526 pthread_mutex_lock(session->mutex);
1527 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
1528 784
1529 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 785 if (msg_parse_in (&msg, data, length) == -1) {
1530 pthread_mutex_unlock(session->mutex); 786 LOGGER_WARNING("Error parsing message");
1531 return msi_ErrorNoCall; 787 send_error(m, friend_number, msi_EInvalidMessage);
788 return;
789 } else {
790 LOGGER_DEBUG("Successfully parsed message");
1532 } 791 }
1533 792
1534 /* just terminate it */
1535
1536 terminate_call ( session, session->calls[call_index] );
1537
1538 pthread_mutex_unlock(session->mutex);
1539 return 0;
1540}
1541
1542int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1543{
1544 pthread_mutex_lock(session->mutex); 793 pthread_mutex_lock(session->mutex);
794 MSICall *call = get_call(session, friend_number);
1545 795
1546 LOGGER_DEBUG("Changing media on call: %d", call_index); 796 if (call == NULL) {
1547 797 if (msg.request.value != requ_init) {
1548 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 798 send_error(m, friend_number, msi_EStrayMessage);
1549 LOGGER_ERROR("Invalid call index!"); 799 pthread_mutex_unlock(session->mutex);
1550 pthread_mutex_unlock(session->mutex); 800 return;
1551 return msi_ErrorNoCall; 801 }
1552 }
1553 802
1554 MSICall *call = session->calls[call_index]; 803 call = new_call(session, friend_number);
1555 804
1556 if ( call->state != msi_CallActive ) { 805 if (call == NULL) {
1557 LOGGER_ERROR("Call is not active!"); 806 send_error(m, friend_number, msi_ESystem);
1558 pthread_mutex_unlock(session->mutex); 807 pthread_mutex_unlock(session->mutex);
1559 return msi_ErrorInvalidState; 808 return;
1560 } 809 }
1561
1562 MSICSettings *local = &call->csettings_local;
1563
1564 if (
1565 local->call_type == csettings->call_type &&
1566 local->video_bitrate == csettings->video_bitrate &&
1567 local->max_video_width == csettings->max_video_width &&
1568 local->max_video_height == csettings->max_video_height &&
1569 local->audio_bitrate == csettings->audio_bitrate &&
1570 local->audio_frame_duration == csettings->audio_frame_duration &&
1571 local->audio_sample_rate == csettings->audio_sample_rate &&
1572 local->audio_channels == csettings->audio_channels ) {
1573 LOGGER_ERROR("Call is already set accordingly!");
1574 pthread_mutex_unlock(session->mutex);
1575 return -1;
1576 } 810 }
1577 811
1578 *local = *csettings; 812 switch (msg.request.value) {
1579 813 case requ_init:
1580 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite ); 814 handle_init(call, &msg);
1581 815 break;
1582 msi_msg_set_csettings ( msg_invite, local );
1583 send_message ( session, call, msg_invite, call->peers[0] );
1584 free ( msg_invite );
1585
1586 LOGGER_DEBUG("Request for media change sent");
1587
1588 pthread_mutex_unlock(session->mutex);
1589
1590 return 0;
1591}
1592
1593void msi_do(MSISession *session)
1594{
1595 pthread_mutex_lock(session->mutex);
1596
1597 TimerHandler *timer = session->timer_handler;
1598
1599 uint64_t time = current_time_monotonic();
1600
1601 while ( timer->timers[0] && timer->timers[0]->timeout < time ) {
1602 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1603 816
1604 int id = timer->timers[0]->id; 817 case requ_push:
1605 timer->timers[0]->func(timer->timers[0]); 818 handle_push(call, &msg);
819 break;
1606 820
1607 /* In case function has released timer */ 821 case requ_pop:
1608 if (timer->timers[0] && timer->timers[0]->id == id) 822 handle_pop(call, &msg); /* always kills the call */
1609 timer_release(timer, id); 823 break;
1610 } 824 }
1611 825
1612 pthread_mutex_unlock(session->mutex); 826 pthread_mutex_unlock(session->mutex);
diff --git a/toxav/msi.h b/toxav/msi.h
index 6a00def6..e69581d1 100644
--- a/toxav/msi.h
+++ b/toxav/msi.h
@@ -1,6 +1,6 @@
1/** msi.h 1/** msi.h
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -19,184 +19,133 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXMSI 22#ifndef MSI_H
23#define __TOXMSI 23#define MSI_H
24 24
25#include "codec.h" 25#include <inttypes.h>
26#include <pthread.h>
27
28#include "audio.h"
29#include "video.h"
26#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
27 31
28typedef uint8_t MSICallIDType[12]; 32/**
29typedef uint8_t MSIReasonStrType[255]; 33 * Error codes.
30typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); 34 */
35typedef enum {
36 msi_ENone,
37 msi_EInvalidMessage,
38 msi_EInvalidParam,
39 msi_EInvalidState,
40 msi_EStrayMessage,
41 msi_ESystem,
42 msi_EHandle,
43 msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing will not work */
44} MSIError;
31 45
32/** 46/**
33 * Call type identifier. Also used as rtp callback prefix. 47 * Supported capabilities
34 */ 48 */
35typedef enum { 49typedef enum {
36 msi_TypeAudio = 192, 50 msi_CapSAudio = 4, /* sending audio */
37 msi_TypeVideo 51 msi_CapSVideo = 8, /* sending video */
38} MSICallType; 52 msi_CapRAudio = 16, /* receiving audio */
53 msi_CapRVideo = 32, /* receiving video */
54} MSICapabilities;
39 55
40 56
41/** 57/**
42 * Call state identifiers. 58 * Call state identifiers.
43 */ 59 */
44typedef enum { 60typedef enum {
45 msi_CallInviting, /* when sending call invite */ 61 msi_CallInactive, /* Default */
46 msi_CallStarting, /* when getting call invite */
47 msi_CallActive, 62 msi_CallActive,
48 msi_CallHold, 63 msi_CallRequesting, /* when sending call invite */
49 msi_CallOver 64 msi_CallRequested, /* when getting call invite */
50
51} MSICallState; 65} MSICallState;
52 66
53
54/**
55 * Encoding settings.
56 */
57typedef struct _MSICodecSettings {
58 MSICallType call_type;
59
60 uint32_t video_bitrate; /* In kbits/s */
61 uint16_t max_video_width; /* In px */
62 uint16_t max_video_height; /* In px */
63
64 uint32_t audio_bitrate; /* In bits/s */
65 uint16_t audio_frame_duration; /* In ms */
66 uint32_t audio_sample_rate; /* In Hz */
67 uint32_t audio_channels;
68} MSICSettings;
69
70
71/** 67/**
72 * Callbacks ids that handle the states 68 * Callbacks ids that handle the states
73 */ 69 */
74typedef enum { 70typedef enum {
75 msi_OnInvite, /* Incoming call */ 71 msi_OnInvite, /* Incoming call */
76 msi_OnRinging, /* When peer is ready to accept/reject the call */
77 msi_OnStart, /* Call (RTP transmission) started */ 72 msi_OnStart, /* Call (RTP transmission) started */
78 msi_OnCancel, /* The side that initiated call canceled invite */
79 msi_OnReject, /* The side that was invited rejected the call */
80 msi_OnEnd, /* Call that was active ended */ 73 msi_OnEnd, /* Call that was active ended */
81 msi_OnRequestTimeout, /* When the requested action didn't get response in specified time */ 74 msi_OnError, /* On protocol error */
82 msi_OnPeerTimeout, /* Peer timed out; stop the call */ 75 msi_OnPeerTimeout, /* Peer timed out; stop the call */
83 msi_OnPeerCSChange, /* Peer requested Csettings change */ 76 msi_OnCapabilities, /* Peer requested capabilities change */
84 msi_OnSelfCSChange /* Csettings change confirmation */
85} MSICallbackID; 77} MSICallbackID;
86 78
87/** 79/**
88 * Errors 80 * The call struct. Please do not modify outside msi.c
89 */ 81 */
90typedef enum { 82typedef struct MSICall_s {
91 msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ 83 struct MSISession_s *session; /* Session pointer */
92 msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
93 msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
94 msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
95} MSIError;
96
97/**
98 * The call struct.
99 */
100typedef struct _MSICall { /* Call info structure */
101 struct _MSISession *session; /* Session pointer */
102
103 MSICallState state;
104
105 MSICSettings csettings_local; /* Local call settings */
106 MSICSettings *csettings_peer; /* Peers call settings */
107
108 MSICallIDType id; /* Random value identifying the call */
109
110 int ringing_tout_ms; /* Ringing timeout in ms */
111 84
112 int request_timer_id; /* Timer id for outgoing request/action */ 85 MSICallState state;
113 int ringing_timer_id; /* Timer id for ringing timeout */ 86 uint8_t peer_capabilities; /* Peer capabilities */
87 uint8_t self_capabilities; /* Self capabilities */
88 uint16_t peer_vfpsz; /* Video frame piece size */
89 uint32_t friend_number; /* Index of this call in MSISession */
90 MSIError error; /* Last error */
114 91
115 uint32_t *peers; 92 void *av_call; /* Pointer to av call handler */
116 uint16_t peer_count;
117 93
118 int32_t call_idx; /* Index of this call in MSISession */ 94 struct MSICall_s *next;
95 struct MSICall_s *prev;
119} MSICall; 96} MSICall;
120 97
121 98
122/** 99/**
123 * Control session struct 100 * Expected return on success is 0, if any other number is
101 * returned the call is considered errored and will be handled
102 * as such which means it will be terminated without any notice.
124 */ 103 */
125typedef struct _MSISession { 104typedef int msi_action_cb (void *av, MSICall *call);
126 105
106/**
107 * Control session struct. Please do not modify outside msi.c
108 */
109typedef struct MSISession_s {
127 /* Call handlers */ 110 /* Call handlers */
128 MSICall **calls; 111 MSICall **calls;
129 int32_t max_calls; 112 uint32_t calls_tail;
130 113 uint32_t calls_head;
131 void *agent_handler;
132 Messenger *messenger_handle;
133 114
134 uint32_t frequ; 115 void *av;
135 uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ 116 Messenger *messenger;
136 117
137 pthread_mutex_t mutex[1]; 118 pthread_mutex_t mutex[1];
138 119 msi_action_cb *callbacks[7];
139 void *timer_handler;
140 PAIR(MSICallbackType, void *) callbacks[10];
141} MSISession; 120} MSISession;
142 121
143/** 122/**
144 * Start the control session. 123 * Start the control session.
145 */ 124 */
146MSISession *msi_new ( Messenger *messenger, int32_t max_calls ); 125MSISession *msi_new(Messenger *m);
147
148/** 126/**
149 * Terminate control session. 127 * Terminate control session. NOTE: all calls will be freed
150 */ 128 */
151int msi_kill ( MSISession *session ); 129int msi_kill(MSISession *session);
152
153/** 130/**
154 * Callback setter. 131 * Callback setter.
155 */ 132 */
156void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); 133void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id);
157
158/**
159 * Send invite request to friend_id.
160 */
161int msi_invite ( MSISession *session,
162 int32_t *call_index,
163 const MSICSettings *csettings,
164 uint32_t rngsec,
165 uint32_t friend_id );
166
167/**
168 * Hangup active call.
169 */
170int msi_hangup ( MSISession *session, int32_t call_index );
171
172/** 134/**
173 * Answer active call request. 135 * Send invite request to friend_number.
174 */ 136 */
175int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ); 137int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
176
177/** 138/**
178 * Cancel request. 139 * Hangup call. NOTE: 'call' will be freed
179 */ 140 */
180int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ); 141int msi_hangup(MSICall *call);
181
182/** 142/**
183 * Reject incoming call. 143 * Answer call request.
184 */ 144 */
185int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); 145int msi_answer(MSICall *call, uint8_t capabilities);
186
187/**
188 * Terminate the call.
189 */
190int msi_stopcall ( MSISession *session, int32_t call_index );
191
192/**
193 * Change codec settings of the current call.
194 */
195int msi_change_csettings ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
196
197/** 146/**
198 * Main msi loop 147 * Change capabilities of the call.
199 */ 148 */
200void msi_do( MSISession *session ); 149int msi_change_capabilities(MSICall *call, uint8_t capabilities);
201 150
202#endif /* __TOXMSI */ 151#endif /* MSI_H */
diff --git a/toxav/rtp.c b/toxav/rtp.c
index 95d3bf3d..1813725a 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -1,6 +1,6 @@
1/** rtp.c 1/** rtp.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -24,505 +24,370 @@
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 25
26#include "rtp.h" 26#include "rtp.h"
27#include "bwcontroler.h"
27#include "../toxcore/logger.h" 28#include "../toxcore/logger.h"
28#include "../toxcore/util.h" 29#include "../toxcore/util.h"
30#include "../toxcore/Messenger.h"
29 31
30#include <stdlib.h> 32#include <stdlib.h>
31void queue_message(RTPSession *_session, RTPMessage *_msg); 33#include <assert.h>
32
33#define size_32 4
34
35#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
36#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
37#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
38#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
39#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
40#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
41
42#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
43#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
44#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
45#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
46#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
47#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
48
49/**
50 * Checks if message came in late.
51 */
52static int check_late_message (RTPSession *session, RTPMessage *msg)
53{
54 /*
55 * Check Sequence number. If this new msg has lesser number then the session->rsequnum
56 * it shows that the message came in late. Also check timestamp to be 100% certain.
57 *
58 */
59 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
60}
61
62
63/**
64 * Extracts header from payload.
65 */
66RTPHeader *extract_header ( const uint8_t *payload, int length )
67{
68 if ( !payload || !length ) {
69 LOGGER_WARNING("No payload to extract!");
70 return NULL;
71 }
72
73 RTPHeader *retu = calloc(1, sizeof (RTPHeader));
74
75 if ( !retu ) {
76 LOGGER_WARNING("Alloc failed! Program might misbehave!");
77 return NULL;
78 }
79
80 memcpy(&retu->sequnum, payload, sizeof(retu->sequnum));
81 retu->sequnum = ntohs(retu->sequnum);
82
83 const uint8_t *it = payload + 2;
84
85 retu->flags = *it;
86 ++it;
87
88 /* This indicates if the first 2 bits are valid.
89 * Now it may happen that this is out of order but
90 * it cuts down chances of parsing some invalid value
91 */
92
93 if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
94 /* Deallocate */
95 LOGGER_WARNING("Invalid version!");
96 free(retu);
97 return NULL;
98 }
99
100 /*
101 * Added a check for the size of the header little sooner so
102 * I don't need to parse the other stuff if it's bad
103 */
104 uint8_t cc = GET_FLAG_CSRCC ( retu );
105 int total = 12 /* Minimum header len */ + ( cc * 4 );
106
107 if ( length < total ) {
108 /* Deallocate */
109 LOGGER_WARNING("Length invalid!");
110 free(retu);
111 return NULL;
112 }
113
114 memset(retu->csrc, 0, 16 * sizeof (uint32_t));
115
116 retu->marker_payloadt = *it;
117 ++it;
118 retu->length = total;
119
120 34
121 memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
122 retu->timestamp = ntohl(retu->timestamp);
123 it += 4;
124 memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
125 retu->ssrc = ntohl(retu->ssrc);
126 35
127 uint8_t x; 36int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
128 37
129 for ( x = 0; x < cc; x++ ) {
130 it += 4;
131 memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
132 retu->csrc[x] = ntohl(retu->csrc[x]);
133 }
134 38
135 return retu; 39RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
136} 40 BWControler *bwc, void *cs,
137 41 int (*mcb) (void *, struct RTPMessage *))
138/**
139 * Extracts external header from payload. Must be called AFTER extract_header()!
140 */
141RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
142{ 42{
143 const uint8_t *it = payload; 43 assert(mcb);
44 assert(cs);
45 assert(m);
144 46
145 RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader)); 47 RTPSession *retu = calloc(1, sizeof(RTPSession));
146 48
147 if ( !retu ) { 49 if (!retu) {
148 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 50 LOGGER_WARNING("Alloc failed! Program might misbehave!");
149 return NULL; 51 return NULL;
150 } 52 }
151 53
152 uint16_t ext_length; 54 retu->ssrc = random_int();
153 memcpy(&ext_length, it, sizeof(ext_length)); 55 retu->payload_type = payload_type;
154 ext_length = ntohs(ext_length);
155 it += 2;
156 56
57 retu->m = m;
58 retu->friend_number = friendnumber;
157 59
158 if ( length < ( ext_length * sizeof(uint32_t) ) ) { 60 /* Also set payload type as prefix */
159 LOGGER_WARNING("Length invalid!");
160 free(retu);
161 return NULL;
162 }
163 61
164 retu->length = ext_length; 62 retu->bwc = bwc;
165 memcpy(&retu->type, it, sizeof(retu->type)); 63 retu->cs = cs;
166 retu->type = ntohs(retu->type); 64 retu->mcb = mcb;
167 it += 2;
168 65
169 if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) { 66 if (-1 == rtp_allow_receiving(retu)) {
170 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 67 LOGGER_WARNING("Failed to start rtp receiving mode");
171 free(retu); 68 free(retu);
172 return NULL; 69 return NULL;
173 } 70 }
174 71
175 uint16_t x;
176
177 for ( x = 0; x < ext_length; x++ ) {
178 it += 4;
179 memcpy(&(retu->table[x]), it, sizeof(retu->table[x]));
180 retu->table[x] = ntohl(retu->table[x]);
181 }
182
183 return retu; 72 return retu;
184} 73}
185 74void rtp_kill (RTPSession *session)
186/**
187 * Adds header to payload. Make sure _payload_ has enough space.
188 */
189uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
190{ 75{
191 uint8_t cc = GET_FLAG_CSRCC ( header ); 76 if (!session)
192 uint8_t *it = payload; 77 return;
193 uint16_t sequnum;
194 uint32_t timestamp;
195 uint32_t ssrc;
196 uint32_t csrc;
197
198
199 /* Add sequence number first */
200 sequnum = htons(header->sequnum);
201 memcpy(it, &sequnum, sizeof(sequnum));
202 it += 2;
203
204 *it = header->flags;
205 ++it;
206 *it = header->marker_payloadt;
207 ++it;
208
209 78
210 timestamp = htonl(header->timestamp); 79 LOGGER_DEBUG("Terminated RTP session: %p", session);
211 memcpy(it, &timestamp, sizeof(timestamp));
212 it += 4;
213 ssrc = htonl(header->ssrc);
214 memcpy(it, &ssrc, sizeof(ssrc));
215 80
216 uint8_t x; 81 rtp_stop_receiving (session);
82 free (session);
83}
84int rtp_allow_receiving(RTPSession *session)
85{
86 if (session == NULL)
87 return -1;
217 88
218 for ( x = 0; x < cc; x++ ) { 89 if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
219 it += 4; 90 handle_rtp_packet, session) == -1) {
220 csrc = htonl(header->csrc[x]); 91 LOGGER_WARNING("Failed to register rtp receive handler");
221 memcpy(it, &csrc, sizeof(csrc)); 92 return -1;
222 } 93 }
223 94
224 return it + 4; 95 LOGGER_DEBUG("Started receiving on session: %p", session);
96 return 0;
225} 97}
226 98int rtp_stop_receiving(RTPSession *session)
227/**
228 * Adds extension header to payload. Make sure _payload_ has enough space.
229 */
230uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
231{ 99{
232 uint8_t *it = payload; 100 if (session == NULL)
233 uint16_t length; 101 return -1;
234 uint16_t type;
235 uint32_t entry;
236
237 length = htons(header->length);
238 memcpy(it, &length, sizeof(length));
239 it += 2;
240 type = htons(header->type);
241 memcpy(it, &type, sizeof(type));
242 it -= 2; /* Return to 0 position */
243
244 if ( header->table ) {
245
246 uint16_t x;
247
248 for ( x = 0; x < header->length; x++ ) {
249 it += 4;
250 entry = htonl(header->table[x]);
251 memcpy(it, &entry, sizeof(entry));
252 }
253 }
254 102
255 return it + 4; 103 m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
256}
257 104
258/** 105 LOGGER_DEBUG("Stopped receiving on session: %p", session);
259 * Builds header from control session values. 106 return 0;
260 */ 107}
261RTPHeader *build_header ( RTPSession *session ) 108int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
262{ 109{
263 RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) ); 110 if (!session) {
264 111 LOGGER_WARNING("No session!");
265 if ( !retu ) { 112 return -1;
266 LOGGER_WARNING("Alloc failed! Program might misbehave!");
267 return NULL;
268 } 113 }
269 114
270 ADD_FLAG_VERSION ( retu, session->version ); 115 uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
271 ADD_FLAG_PADDING ( retu, session->padding ); 116 memset(rdata, 0, sizeof(rdata));
272 ADD_FLAG_EXTENSION ( retu, session->extension );
273 ADD_FLAG_CSRCC ( retu, session->cc );
274 ADD_SETTING_MARKER ( retu, session->marker );
275 ADD_SETTING_PAYLOAD ( retu, session->payload_type );
276 117
277 retu->sequnum = session->sequnum; 118 rdata[0] = session->payload_type;
278 retu->timestamp = current_time_monotonic(); /* milliseconds */
279 retu->ssrc = session->ssrc;
280 119
281 int i; 120 struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
282 121
283 for ( i = 0; i < session->cc; i++ ) 122 header->ve = 2;
284 retu->csrc[i] = session->csrc[i]; 123 header->pe = 0;
124 header->xe = 0;
125 header->cc = 0;
285 126
286 retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); 127 header->ma = 0;
128 header->pt = session->payload_type % 128;
287 129
288 return retu; 130 header->sequnum = htons(session->sequnum);
289} 131 header->timestamp = htonl(current_time_monotonic());
290 132 header->ssrc = htonl(session->ssrc);
291
292/**
293 * Parses data into RTPMessage struct. Stores headers separately from the payload data
294 * and so the length variable is set accordingly.
295 */
296RTPMessage *msg_parse ( const uint8_t *data, int length )
297{
298 RTPMessage *retu = calloc(1, sizeof (RTPMessage));
299
300 retu->header = extract_header ( data, length ); /* It allocates memory and all */
301 133
302 if ( !retu->header ) { 134 header->cpart = 0;
303 LOGGER_WARNING("Header failed to extract!"); 135 header->tlen = htons(length);
304 free(retu);
305 return NULL;
306 }
307 136
308 uint16_t from_pos = retu->header->length; 137 if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
309 retu->length = length - from_pos;
310 138
139 /**
140 * The lenght is lesser than the maximum allowed lenght (including header)
141 * Send the packet in single piece.
142 */
311 143
144 memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
312 145
313 if ( GET_FLAG_EXTENSION ( retu->header ) ) { 146 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
314 retu->ext_header = extract_ext_header ( data + from_pos, length ); 147 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
315
316 if ( retu->ext_header ) {
317 retu->length -= ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
318 from_pos += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
319 } else { /* Error */
320 LOGGER_WARNING("Ext Header failed to extract!");
321 rtp_free_msg(NULL, retu);
322 return NULL;
323 }
324 } else { 148 } else {
325 retu->ext_header = NULL;
326 }
327 149
328 if ( length - from_pos <= MAX_RTP_SIZE ) 150 /**
329 memcpy ( retu->data, data + from_pos, length - from_pos ); 151 * The lenght is greater than the maximum allowed lenght (including header)
330 else { 152 * Send the packet in multiple pieces.
331 LOGGER_WARNING("Invalid length!"); 153 */
332 rtp_free_msg(NULL, retu);
333 return NULL;
334 }
335 154
336 retu->next = NULL; 155 uint16_t sent = 0;
156 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
337 157
338 return retu; 158 while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
339} 159 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
340 160
341/** 161 if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
342 * Callback for networking core. 162 rdata, piece + sizeof(struct RTPHeader) + 1))
343 */ 163 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
344int rtp_handle_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object ) 164 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
345{
346 RTPSession *session = object;
347 RTPMessage *msg;
348 165
349 if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */ 166 sent += piece;
350 LOGGER_WARNING("No session or invalid length of received buffer!"); 167 header->cpart = htons(sent);
351 return -1; 168 }
352 }
353 169
354 msg = msg_parse ( data + 1, length - 1 ); 170 /* Send remaining */
171 piece = length - sent;
355 172
356 if ( !msg ) { 173 if (piece) {
357 LOGGER_WARNING("Could not parse message!"); 174 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
358 return -1;
359 }
360 175
361 /* Check if message came in late */ 176 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
362 if ( check_late_message(session, msg) < 0 ) { /* Not late */ 177 piece + sizeof(struct RTPHeader) + 1))
363 session->rsequnum = msg->header->sequnum; 178 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
364 session->timestamp = msg->header->timestamp; 179 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
180 }
365 } 181 }
366 182
367 queue_message(session, msg); 183 session->sequnum ++;
368
369 return 0; 184 return 0;
370} 185}
371 186
372/**
373 * Allocate message and store data there
374 */
375RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
376{
377 if ( !session ) {
378 LOGGER_WARNING("No session!");
379 return NULL;
380 }
381
382 uint8_t *from_pos;
383 RTPMessage *retu = calloc(1, sizeof (RTPMessage));
384
385 if ( !retu ) {
386 LOGGER_WARNING("Alloc failed! Program might misbehave!");
387 return NULL;
388 }
389 187
390 /* Sets header values and copies the extension header in retu */ 188bool chloss (const RTPSession *session, const struct RTPHeader *header)
391 retu->header = build_header ( session ); /* It allocates memory and all */ 189{
392 retu->ext_header = session->ext_header; 190 if (ntohl(header->timestamp) < session->rtimestamp) {
191 uint16_t hosq, lost = 0;
393 192
193 hosq = ntohs(header->sequnum);
394 194
395 uint32_t total_length = length + retu->header->length + 1; 195 lost = (hosq > session->rsequnum) ?
196 (session->rsequnum + 65535) - hosq :
197 session->rsequnum - hosq;
396 198
397 retu->data[0] = session->prefix; 199 puts ("Lost packet");
398 200
399 if ( retu->ext_header ) { 201 while (lost --)
400 total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); 202 bwc_add_lost(session->bwc , 0);
401 203
402 from_pos = add_header ( retu->header, retu->data + 1 ); 204 return true;
403 from_pos = add_ext_header ( retu->ext_header, from_pos + 1 );
404 } else {
405 from_pos = add_header ( retu->header, retu->data + 1 );
406 } 205 }
407 206
408 /* 207 return false;
409 * Parses the extension header into the message
410 * Of course if any
411 */
412
413 /* Appends data on to retu->data */
414 memcpy ( from_pos, data, length );
415
416 retu->length = total_length;
417
418 retu->next = NULL;
419
420 return retu;
421} 208}
422 209struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
423
424
425int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length )
426{ 210{
427 RTPMessage *msg = rtp_new_message (session, data, length); 211 assert(allocate_len >= data_length);
428 212
429 if ( !msg ) return -1; 213 struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
430 214
431 int ret = send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length); 215 msg->len = data_length - sizeof(struct RTPHeader);
216 memcpy(&msg->header, data, data_length);
432 217
433 if ( 0 != ret) { 218 msg->header.sequnum = ntohs(msg->header.sequnum);
434 LOGGER_WARNING("Failed to send full packet (len: %d)! error: %i", length, ret); 219 msg->header.timestamp = ntohl(msg->header.timestamp);
435 rtp_free_msg ( session, msg ); 220 msg->header.ssrc = ntohl(msg->header.ssrc);
436 return rtp_ErrorSending;
437 }
438 221
439 /* Set sequ number */ 222 msg->header.cpart = ntohs(msg->header.cpart);
440 session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; 223 msg->header.tlen = ntohs(msg->header.tlen);
441 rtp_free_msg ( session, msg );
442 224
443 return 0; 225 return msg;
444} 226}
445 227int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
446void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
447{ 228{
448 if ( !session ) { 229 (void) m;
449 if ( msg->ext_header ) { 230 (void) friendnumber;
450 free ( msg->ext_header->table );
451 free ( msg->ext_header );
452 }
453 } else {
454 if ( msg->ext_header && session->ext_header != msg->ext_header ) {
455 free ( msg->ext_header->table );
456 free ( msg->ext_header );
457 }
458 }
459 231
460 free ( msg->header ); 232 RTPSession *session = object;
461 free ( msg );
462}
463
464RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
465{
466 RTPSession *retu = calloc(1, sizeof(RTPSession));
467 233
468 if ( !retu ) { 234 data ++;
469 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 235 length--;
470 return NULL;
471 }
472 236
473 if ( -1 == m_callback_rtp_packet(messenger, friend_num, payload_type, rtp_handle_packet, retu)) { 237 if (!session || length < sizeof (struct RTPHeader)) {
474 LOGGER_ERROR("Error setting custom register handler for rtp session"); 238 LOGGER_WARNING("No session or invalid length of received buffer!");
475 free(retu); 239 return -1;
476 return NULL;
477 } 240 }
478 241
479 LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); 242 const struct RTPHeader *header = (struct RTPHeader *) data;
480
481 retu->version = RTP_VERSION; /* It's always 2 */
482 retu->padding = 0; /* If some additional data is needed about the packet */
483 retu->extension = 0; /* If extension to header is needed */
484 retu->cc = 1; /* Amount of contributors */
485 retu->csrc = NULL; /* Container */
486 retu->ssrc = random_int();
487 retu->marker = 0;
488 retu->payload_type = payload_type % 128;
489
490 retu->dest = friend_num;
491
492 retu->rsequnum = retu->sequnum = 0;
493
494 retu->ext_header = NULL; /* When needed allocate */
495 243
244 if (header->pt != session->payload_type % 128) {
245 LOGGER_WARNING("Invalid payload type with the session");
246 return -1;
247 }
496 248
497 if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) { 249 if (ntohs(header->cpart) >= ntohs(header->tlen)) {
498 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 250 /* Never allow this case to happen */
499 free(retu); 251 return -1;
500 return NULL;
501 } 252 }
502 253
503 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ 254 bwc_feed_avg(session->bwc, length);
504 255
505 /* Also set payload type as prefix */ 256 if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
506 retu->prefix = payload_type; 257 /* The message is sent in single part */
507 258
508 /* 259 /* Only allow messages which have arrived in order;
509 * 260 * drop late messages
510 */ 261 */
511 return retu; 262 if (chloss(session, header)) {
512} 263 return 0;
264 } else {
265 /* Message is not late; pick up the latest parameters */
266 session->rsequnum = ntohs(header->sequnum);
267 session->rtimestamp = ntohl(header->timestamp);
268 }
513 269
514void rtp_kill ( RTPSession *session, Messenger *messenger ) 270 bwc_add_recv(session->bwc, length);
515{
516 if ( !session ) return;
517 271
518 m_callback_rtp_packet(messenger, session->dest, session->prefix, NULL, NULL); 272 /* Invoke processing of active multiparted message */
273 if (session->mp) {
274 if (session->mcb)
275 session->mcb (session->cs, session->mp);
276 else
277 free(session->mp);
519 278
520 free ( session->ext_header ); 279 session->mp = NULL;
521 free ( session->csrc ); 280 }
522 281
523 LOGGER_DEBUG("Terminated RTP session: %p", session); 282 /* The message came in the allowed time;
283 * process it only if handler for the session is present.
284 */
524 285
525 /* And finally free session */ 286 if (!session->mcb)
526 free ( session ); 287 return 0;
527 288
289 return session->mcb (session->cs, new_message(length, data, length));
290 } else {
291 /* The message is sent in multiple parts */
292
293 if (session->mp) {
294 /* There are 2 possible situations in this case:
295 * 1) being that we got the part of already processing message.
296 * 2) being that we got the part of a new/old message.
297 *
298 * We handle them differently as we only allow a single multiparted
299 * processing message
300 */
301
302 if (session->mp->header.sequnum == ntohs(header->sequnum) &&
303 session->mp->header.timestamp == ntohl(header->timestamp)) {
304 /* First case */
305
306 /* Make sure we have enough allocated memory */
307 if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
308 session->mp->header.tlen <= ntohs(header->cpart)) {
309 /* There happened to be some corruption on the stream;
310 * continue wihtout this part
311 */
312 return 0;
313 }
314
315 memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
316 length - sizeof(struct RTPHeader));
317
318 session->mp->len += length - sizeof(struct RTPHeader);
319
320 bwc_add_recv(session->bwc, length);
321
322 if (session->mp->len == session->mp->header.tlen) {
323 /* Received a full message; now push it for the further
324 * processing.
325 */
326 if (session->mcb)
327 session->mcb (session->cs, session->mp);
328 else
329 free(session->mp);
330
331 session->mp = NULL;
332 }
333 } else {
334 /* Second case */
335
336 if (session->mp->header.timestamp > ntohl(header->timestamp))
337 /* The received message part is from the old message;
338 * discard it.
339 */
340 return 0;
341
342 /* Measure missing parts of the old message */
343 bwc_add_lost(session->bwc,
344 (session->mp->header.tlen - session->mp->len) +
345
346 /* Must account sizes of rtp headers too */
347 ((session->mp->header.tlen - session->mp->len) /
348 MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
349
350 /* Push the previous message for processing */
351 if (session->mcb)
352 session->mcb (session->cs, session->mp);
353 else
354 free(session->mp);
355
356 session->mp = NULL;
357 goto NEW_MULTIPARTED;
358 }
359 } else {
360 /* In this case threat the message as if it was received in order
361 */
362
363 /* This is also a point for new multiparted messages */
364NEW_MULTIPARTED:
365
366 /* Only allow messages which have arrived in order;
367 * drop late messages
368 */
369 if (chloss(session, header)) {
370 return 0;
371 } else {
372 /* Message is not late; pick up the latest parameters */
373 session->rsequnum = ntohs(header->sequnum);
374 session->rtimestamp = ntohl(header->timestamp);
375 }
376
377 bwc_add_recv(session->bwc, length);
378
379 /* Again, only store message if handler is present
380 */
381 if (session->mcb) {
382 session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
383
384 /* Reposition data if necessary */
385 if (ntohs(header->cpart));
386
387 memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
388 }
389 }
390 }
391
392 return 0;
528} 393}
diff --git a/toxav/rtp.h b/toxav/rtp.h
index c98840ac..0393ac27 100644
--- a/toxav/rtp.h
+++ b/toxav/rtp.h
@@ -1,6 +1,6 @@
1/** rtp.h 1/** rtp.h
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -19,109 +19,91 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXRTP 22#ifndef RTP_H
23#define __TOXRTP 23#define RTP_H
24
25#define RTP_VERSION 2
26#include <inttypes.h>
27// #include <pthread.h>
28 24
25#include "bwcontroler.h"
29#include "../toxcore/Messenger.h" 26#include "../toxcore/Messenger.h"
30 27#include "stdbool.h"
31#define MAX_SEQU_NUM 65535
32#define MAX_RTP_SIZE 65535
33
34typedef enum {
35 rtp_ErrorSending = -40
36} RTPError;
37/**
38 * Standard rtp header
39 */
40typedef struct _RTPHeader {
41 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
42 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
43 uint16_t sequnum; /* Sequence Number */
44 uint32_t timestamp; /* Timestamp */
45 uint32_t ssrc; /* SSRC */
46 uint32_t csrc[16]; /* CSRC's table */
47 uint32_t length; /* Length of the header in payload string. */
48
49} RTPHeader;
50 28
51/** 29/**
52 * Standard rtp extension header. 30 * Payload type identifier. Also used as rtp callback prefix.
53 */ 31 */
54typedef struct _RTPExtHeader { 32enum {
55 uint16_t type; /* Extension profile */ 33 rtp_TypeAudio = 192,
56 uint16_t length; /* Number of extensions */ 34 rtp_TypeVideo,
57 uint32_t *table; /* Extension's table */ 35};
58 36
59} RTPExtHeader; 37struct RTPHeader {
60 38 /* Standard RTP header */
61/** 39#ifndef WORDS_BIGENDIAN
62 * Standard rtp message. 40 uint16_t cc: 4; /* Contributing sources count */
63 */ 41 uint16_t xe: 1; /* Extra header */
64typedef struct _RTPMessage { 42 uint16_t pe: 1; /* Padding */
65 RTPHeader *header; 43 uint16_t ve: 2; /* Version */
66 RTPExtHeader *ext_header; 44
67 45 uint16_t pt: 7; /* Payload type */
68 uint8_t data[MAX_RTP_SIZE]; 46 uint16_t ma: 1; /* Marker */
69 uint32_t length; 47#else
70 48 uint16_t ve: 2; /* Version */
71 struct _RTPMessage *next; 49 uint16_t pe: 1; /* Padding */
72} RTPMessage; 50 uint16_t xe: 1; /* Extra header */
51 uint16_t cc: 4; /* Contributing sources count */
52
53 uint16_t ma: 1; /* Marker */
54 uint16_t pt: 7; /* Payload type */
55#endif
56
57 uint16_t sequnum;
58 uint32_t timestamp;
59 uint32_t ssrc;
60 uint32_t csrc[16];
61
62 /* Non-standard TOX-specific fields */
63 uint16_t cpart;/* Data offset of the current part */
64 uint16_t tlen; /* Total message lenght */
65} __attribute__ ((packed));
66
67/* Check alignment */
68typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ];
69
70struct RTPMessage {
71 uint16_t len;
72
73 struct RTPHeader header;
74 uint8_t data[];
75} __attribute__ ((packed));
76
77/* Check alignment */
78typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ];
73 79
74/** 80/**
75 * RTP control session. 81 * RTP control session.
76 */ 82 */
77typedef struct _RTPSession { 83typedef struct {
78 uint8_t version; 84 uint8_t payload_type;
79 uint8_t padding; 85 uint16_t sequnum; /* Sending sequence number */
80 uint8_t extension; 86 uint16_t rsequnum; /* Receiving sequence number */
81 uint8_t cc; 87 uint32_t rtimestamp;
82 uint8_t marker; 88 uint32_t ssrc;
83 uint8_t payload_type;
84 uint16_t sequnum; /* Set when sending */
85 uint16_t rsequnum; /* Check when recving msg */
86 uint32_t timestamp;
87 uint32_t ssrc;
88 uint32_t *csrc;
89
90 /* If some additional data must be sent via message
91 * apply it here. Only by allocating this member you will be
92 * automatically placing it within a message.
93 */
94 RTPExtHeader *ext_header;
95
96 /* Msg prefix for core to know when recving */
97 uint8_t prefix;
98
99 int dest;
100
101 struct _CSSession *cs;
102 89
103} RTPSession; 90 struct RTPMessage *mp; /* Expected parted message */
104
105/**
106 * Must be called before calling any other rtp function.
107 */
108RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num );
109
110/**
111 * Terminate the session.
112 */
113void rtp_kill ( RTPSession *session, Messenger *messenger );
114 91
115/** 92 Messenger *m;
116 * Sends msg to _RTPSession::dest 93 uint32_t friend_number;
117 */
118int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
119 94
120/** 95 BWControler *bwc;
121 * Dealloc msg. 96 void *cs;
122 */ 97 int (*mcb) (void *, struct RTPMessage *msg);
123void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); 98} RTPSession;
124 99
125 100
101RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num,
102 BWControler *bwc, void *cs,
103 int (*mcb) (void *, struct RTPMessage *));
104void rtp_kill (RTPSession *session);
105int rtp_allow_receiving (RTPSession *session);
106int rtp_stop_receiving (RTPSession *session);
107int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length);
126 108
127#endif /* __TOXRTP */ 109#endif /* RTP_H */
diff --git a/toxav/toxav.c b/toxav/toxav.c
index a51ec5e3..9eda3412 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -1,6 +1,6 @@
1/** toxav.c 1/** toxav.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -23,15 +23,10 @@
23#include "config.h" 23#include "config.h"
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 25
26#define TOX_DEFINED
27typedef struct Messenger Tox;
28
29#define _GNU_SOURCE /* implicit declaration warning */
30
31#include "codec.h"
32#include "msi.h" 26#include "msi.h"
33#include "group.h" 27#include "rtp.h"
34 28
29#include "../toxcore/Messenger.h"
35#include "../toxcore/logger.h" 30#include "../toxcore/logger.h"
36#include "../toxcore/util.h" 31#include "../toxcore/util.h"
37 32
@@ -39,659 +34,1235 @@ typedef struct Messenger Tox;
39#include <stdlib.h> 34#include <stdlib.h>
40#include <string.h> 35#include <string.h>
41 36
42/* Assume 24 fps*/
43#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) 37#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
44 38
45/* true if invalid call index */ 39typedef struct ToxAVCall_s {
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) 40 ToxAV *av;
47 41
48const ToxAvCSettings av_DefaultSettings = { 42 pthread_mutex_t mutex_audio[1];
49 av_TypeAudio, 43 PAIR(RTPSession *, ACSession *) audio;
50 44
51 500, 45 pthread_mutex_t mutex_video[1];
52 1280, 46 PAIR(RTPSession *, VCSession *) video;
53 720,
54 47
55 32000, 48 BWControler *bwc;
56 20, 49
57 48000, 50 bool active;
58 1 51 MSICall *msi_call;
59}; 52 uint32_t friend_number;
53
54 uint32_t audio_bit_rate; /* Sending audio bit rate */
55 uint32_t video_bit_rate; /* Sending video bit rate */
60 56
61static const uint32_t jbuf_capacity = 6; 57 /** Required for monitoring changes in states */
62static const uint8_t audio_index = 0, video_index = 1; 58 uint8_t previous_self_capabilities;
63 59
64typedef struct _ToxAvCall {
65 pthread_mutex_t mutex[1]; 60 pthread_mutex_t mutex[1];
66 pthread_mutex_t mutex_encoding_audio[1]; 61
67 pthread_mutex_t mutex_encoding_video[1]; 62 struct ToxAVCall_s *prev;
68 pthread_mutex_t mutex_do[1]; 63 struct ToxAVCall_s *next;
69 RTPSession *crtps[2]; /** Audio is first and video is second */ 64} ToxAVCall;
70 CSSession *cs; 65
71 _Bool active; 66struct ToxAV {
72} ToxAvCall; 67 Messenger *m;
73 68 MSISession *msi;
74struct _ToxAv { 69
75 Messenger *messenger; 70 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
76 MSISession *msi_session; /** Main msi session */ 71 ToxAVCall **calls;
77 ToxAvCall *calls; /** Per-call params */ 72 uint32_t calls_tail;
78 uint32_t max_calls; 73 uint32_t calls_head;
79 74 pthread_mutex_t mutex[1];
80 PAIR(ToxAvAudioCallback, void *) acb; 75
81 PAIR(ToxAvVideoCallback, void *) vcb; 76 PAIR(toxav_call_cb *, void *) ccb; /* Call callback */
82 77 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
83 /* Decode time measure */ 78 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
84 int32_t dectmsscount; /** Measure count */ 79 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
85 int32_t dectmsstotal; /** Last cycle total */ 80 PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */
86 int32_t avgdectms; /** Average decoding time in ms */ 81
82 /** Decode time measures */
83 int32_t dmssc; /** Measure count */
84 int32_t dmsst; /** Last cycle total */
85 int32_t dmssa; /** Average decoding time in ms */
86
87 uint32_t interval; /** Calculated interval */
87}; 88};
88 89
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) 90void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data);
91
92int callback_invite(void *toxav_inst, MSICall *call);
93int callback_start(void *toxav_inst, MSICall *call);
94int callback_end(void *toxav_inst, MSICall *call);
95int callback_error(void *toxav_inst, MSICall *call);
96int callback_capabilites(void *toxav_inst, MSICall *call);
97
98bool audio_bit_rate_invalid(uint32_t bit_rate);
99bool video_bit_rate_invalid(uint32_t bit_rate);
100bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
101ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error);
102ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
103ToxAVCall *call_remove(ToxAVCall *call);
104bool call_prepare_transmission(ToxAVCall *call);
105void call_kill_transmission(ToxAVCall *call);
106
107uint32_t toxav_version_major(void)
90{ 108{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 109 return 0;
92 return (const MSICSettings *) from;
93} 110}
94 111uint32_t toxav_version_minor(void)
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{ 112{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 113 return 0;
98 return (const ToxAvCSettings *) from; 114}
99 115uint32_t toxav_version_patch(void)
116{
117 return 0;
100} 118}
119bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
120{
121 (void)major;
122 (void)minor;
123 (void)patch;
101 124
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls) 125 return 1;
126}
127ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error)
103{ 128{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1); 129 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK;
130 ToxAV *av = NULL;
131
132 if (tox == NULL) {
133 rc = TOXAV_ERR_NEW_NULL;
134 goto END;
135 }
136
137 if (((Messenger *)tox)->msi_packet) {
138 rc = TOXAV_ERR_NEW_MULTIPLE;
139 goto END;
140 }
141
142 av = calloc (sizeof(ToxAV), 1);
105 143
106 if (av == NULL) { 144 if (av == NULL) {
107 LOGGER_WARNING("Allocation failed!"); 145 LOGGER_WARNING("Allocation failed!");
108 return NULL; 146 rc = TOXAV_ERR_NEW_MALLOC;
147 goto END;
148 }
149
150 if (create_recursive_mutex(av->mutex) != 0) {
151 LOGGER_WARNING("Mutex creation failed!");
152 rc = TOXAV_ERR_NEW_MALLOC;
153 goto END;
109 } 154 }
110 155
111 av->messenger = (Messenger *)messenger; 156 av->m = (Messenger *)tox;
112 av->msi_session = msi_new(av->messenger, max_calls); 157 av->msi = msi_new(av->m);
113 av->msi_session->agent_handler = av;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls);
115 av->max_calls = max_calls;
116 158
117 unsigned int i; 159 if (av->msi == NULL) {
160 pthread_mutex_destroy(av->mutex);
161 rc = TOXAV_ERR_NEW_MALLOC;
162 goto END;
163 }
118 164
119 for (i = 0; i < max_calls; ++i) { 165 av->interval = 200;
120 if (create_recursive_mutex(av->calls[i].mutex) != 0 ) { 166 av->msi->av = av;
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i);
122 msi_kill(av->msi_session);
123 167
124 free(av->calls); 168 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
125 free(av); 169 msi_register_callback(av->msi, callback_start, msi_OnStart);
126 return NULL; 170 msi_register_callback(av->msi, callback_end, msi_OnEnd);
127 } 171 msi_register_callback(av->msi, callback_error, msi_OnError);
172 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
173 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
174
175END:
176
177 if (error)
178 *error = rc;
179
180 if (rc != TOXAV_ERR_NEW_OK) {
181 free(av);
182 av = NULL;
128 } 183 }
129 184
130 return av; 185 return av;
131} 186}
132 187void toxav_kill(ToxAV *av)
133void toxav_kill ( ToxAv *av )
134{ 188{
135 uint32_t i; 189 if (av == NULL)
190 return;
136 191
137 for (i = 0; i < av->max_calls; i ++) { 192 pthread_mutex_lock(av->mutex);
138 if ( av->calls[i].crtps[audio_index] )
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
140 193
194 /* To avoid possible deadlocks */
195 while (av->msi && msi_kill(av->msi) != 0) {
196 pthread_mutex_unlock(av->mutex);
197 pthread_mutex_lock(av->mutex);
198 }
141 199
142 if ( av->calls[i].crtps[video_index] ) 200 /* Msi kill will hang up all calls so just clean these calls */
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); 201 if (av->calls) {
144 202 ToxAVCall *it = call_get(av, av->calls_head);
145 if ( av->calls[i].cs )
146 cs_kill(av->calls[i].cs);
147 203
148 pthread_mutex_destroy(av->calls[i].mutex); 204 while (it) {
205 call_kill_transmission(it);
206 it = call_remove(it); /* This will eventually free av->calls */
207 }
149 } 208 }
150 209
151 msi_kill(av->msi_session); 210 pthread_mutex_unlock(av->mutex);
211 pthread_mutex_destroy(av->mutex);
152 212
153 free(av->calls);
154 free(av); 213 free(av);
155} 214}
156 215Tox *toxav_get_tox(const ToxAV *av)
157uint32_t toxav_do_interval(ToxAv *av) 216{
217 return (Tox *) av->m;
218}
219uint32_t toxav_iteration_interval(const ToxAV *av)
158{ 220{
159 int i = 0; 221 /* If no call is active interval is 200 */
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ 222 return av->calls ? av->interval : 200;
223}
224void toxav_iterate(ToxAV *av)
225{
226 pthread_mutex_lock(av->mutex);
161 227
162 for (; i < av->max_calls; i ++) { 228 if (av->calls == NULL) {
163 pthread_mutex_lock(av->calls[i].mutex); 229 pthread_mutex_unlock(av->mutex);
230 return;
231 }
164 232
165 if (av->calls[i].active) { 233 uint64_t start = current_time_monotonic();
166 /* This should work. Video payload will always come in greater intervals */ 234 int32_t rc = 500;
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
168 }
169 235
170 pthread_mutex_unlock(av->calls[i].mutex); 236 ToxAVCall *i = av->calls[av->calls_head];
171 }
172 237
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms; 238 for (; i; i = i->next) {
174} 239 if (i->active) {
240 pthread_mutex_lock(i->mutex);
241 pthread_mutex_unlock(av->mutex);
175 242
176void toxav_do(ToxAv *av) 243 ac_iterate(i->audio.second);
177{ 244 vc_iterate(i->video.second);
178 msi_do(av->msi_session);
179 245
180 uint64_t start = current_time_monotonic(); 246 if (i->msi_call->self_capabilities & msi_CapRAudio &&
247 i->msi_call->peer_capabilities & msi_CapSAudio)
248 rc = MIN(i->audio.second->lp_frame_duration, rc);
181 249
182 uint32_t i = 0; 250 if (i->msi_call->self_capabilities & msi_CapRVideo &&
251 i->msi_call->peer_capabilities & msi_CapSVideo)
252 rc = MIN(i->video.second->lcfd, (uint32_t) rc);
183 253
184 for (; i < av->max_calls; i ++) { 254 uint32_t fid = i->friend_number;
185 pthread_mutex_lock(av->calls[i].mutex);
186 255
187 if (av->calls[i].active) { 256 pthread_mutex_unlock(i->mutex);
188 pthread_mutex_lock(av->calls[i].mutex_do); 257 pthread_mutex_lock(av->mutex);
189 pthread_mutex_unlock(av->calls[i].mutex); 258
190 cs_do(av->calls[i].cs); 259 /* In case this call is popped from container stop iteration */
191 pthread_mutex_unlock(av->calls[i].mutex_do); 260 if (call_get(av, fid) != i)
192 } else { 261 break;
193 pthread_mutex_unlock(av->calls[i].mutex);
194 } 262 }
195 } 263 }
196 264
197 uint64_t end = current_time_monotonic(); 265 pthread_mutex_unlock(av->mutex);
198 266
199 /* TODO maybe use variable for sizes */ 267 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa);
200 av->dectmsstotal += end - start; 268 av->dmsst += current_time_monotonic() - start;
201 269
202 if (++av->dectmsscount == 3) { 270 if (++av->dmssc == 3) {
203 av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; 271 av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */;
204 av->dectmsscount = 0; 272 av->dmssc = 0;
205 av->dectmsstotal = 0; 273 av->dmsst = 0;
206 } 274 }
207} 275}
208 276bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) 277 TOXAV_ERR_CALL *error)
210{ 278{
211 msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); 279 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
212}
213 280
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) 281 pthread_mutex_lock(av->mutex);
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219 282
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) 283 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
221{ 284 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) {
222 av->vcb.first = cb; 285 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
223 av->vcb.second = userdata; 286 goto END;
224} 287 }
225 288
226int toxav_call (ToxAv *av, 289 ToxAVCall *call = call_new(av, friend_number, error);
227 int32_t *call_index,
228 int user,
229 const ToxAvCSettings *csettings,
230 int ringing_seconds )
231{
232 return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user);
233}
234 290
235int toxav_hangup ( ToxAv *av, int32_t call_index ) 291 if (call == NULL) {
236{ 292 rc = TOXAV_ERR_CALL_MALLOC;
237 return msi_hangup(av->msi_session, call_index); 293 goto END;
238} 294 }
239 295
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 296 call->audio_bit_rate = audio_bit_rate;
241{ 297 call->video_bit_rate = video_bit_rate;
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
243}
244 298
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 299 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
246{
247 return msi_reject(av->msi_session, call_index, reason);
248}
249 300
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 301 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
251{ 302 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
252 return msi_cancel(av->msi_session, call_index, peer_id, reason);
253}
254 303
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 304 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
256{ 305 call_remove(call);
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); 306 rc = TOXAV_ERR_CALL_SYNC;
258} 307 goto END;
308 }
259 309
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 310 call->msi_call->av_call = call;
311
312END:
313 pthread_mutex_unlock(av->mutex);
314
315 if (error)
316 *error = rc;
317
318 return rc == TOXAV_ERR_CALL_OK;
319}
320void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data)
261{ 321{
262 return msi_stopcall(av->msi_session, call_index); 322 pthread_mutex_lock(av->mutex);
323 av->ccb.first = function;
324 av->ccb.second = user_data;
325 pthread_mutex_unlock(av->mutex);
263} 326}
264 327bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) 328 TOXAV_ERR_ANSWER *error)
266{ 329{
267 if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 330 pthread_mutex_lock(av->mutex);
268 !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) {
269 LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
270 return av_ErrorNoCall;
271 }
272
273 ToxAvCall *call = &av->calls[call_index];
274 331
275 pthread_mutex_lock(call->mutex); 332 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
276 333
277 if (call->active) { 334 if (m_friend_exists(av->m, friend_number) == 0) {
278 pthread_mutex_unlock(call->mutex); 335 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n"); 336 goto END;
280 return av_ErrorAlreadyInCallWithPeer;
281 } 337 }
282 338
283 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0 339 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) { 340 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))
285 pthread_mutex_unlock(call->mutex); 341 ) {
286 LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n"); 342 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
287 return av_ErrorUnknown; 343 goto END;
288 } 344 }
289 345
290 const ToxAvCSettings *c_peer = toxavcsettings_cast 346 ToxAVCall *call = call_get(av, friend_number);
291 (&av->msi_session->calls[call_index]->csettings_peer[0]);
292 const ToxAvCSettings *c_self = toxavcsettings_cast
293 (&av->msi_session->calls[call_index]->csettings_local);
294 347
295 LOGGER_DEBUG( 348 if (call == NULL) {
296 "Type: %u(s) %u(p)\n" 349 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
297 "Video bitrate: %u(s) %u(p)\n" 350 goto END;
298 "Video height: %u(s) %u(p)\n" 351 }
299 "Video width: %u(s) %u(p)\n"
300 "Audio bitrate: %u(s) %u(p)\n"
301 "Audio framedur: %u(s) %u(p)\n"
302 "Audio sample rate: %u(s) %u(p)\n"
303 "Audio channels: %u(s) %u(p)\n",
304 c_self->call_type, c_peer->call_type,
305 c_self->video_bitrate, c_peer->video_bitrate,
306 c_self->max_video_height, c_peer->max_video_height,
307 c_self->max_video_width, c_peer->max_video_width,
308 c_self->audio_bitrate, c_peer->audio_bitrate,
309 c_self->audio_frame_duration, c_peer->audio_frame_duration,
310 c_self->audio_sample_rate, c_peer->audio_sample_rate,
311 c_self->audio_channels, c_peer->audio_channels );
312 352
313 if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { 353 if (!call_prepare_transmission(call)) {
314 LOGGER_ERROR("Error while starting Codec State!\n"); 354 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
315 pthread_mutex_unlock(call->mutex); 355 goto END;
316 return av_ErrorInitializingCodecs;
317 } 356 }
318 357
319 call->cs->agent = av; 358 call->audio_bit_rate = audio_bit_rate;
320 call->cs->call_idx = call_index; 359 call->video_bit_rate = video_bit_rate;
321 360
322 call->cs->acb.first = av->acb.first; 361 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
323 call->cs->acb.second = av->acb.second;
324 362
325 call->cs->vcb.first = av->vcb.first; 363 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
326 call->cs->vcb.second = av->vcb.second; 364 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
327 365
366 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
367 rc = TOXAV_ERR_ANSWER_SYNC;
328 368
329 call->crtps[audio_index] = 369END:
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); 370 pthread_mutex_unlock(av->mutex);
331 371
332 if ( !call->crtps[audio_index] ) { 372 if (error)
333 LOGGER_ERROR("Error while starting audio RTP session!\n"); 373 *error = rc;
334 goto error; 374
375 return rc == TOXAV_ERR_ANSWER_OK;
376}
377void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data)
378{
379 pthread_mutex_lock(av->mutex);
380 av->scb.first = function;
381 av->scb.second = user_data;
382 pthread_mutex_unlock(av->mutex);
383}
384bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error)
385{
386 pthread_mutex_lock(av->mutex);
387 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
388
389 if (m_friend_exists(av->m, friend_number) == 0) {
390 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
391 goto END;
335 } 392 }
336 393
337 call->crtps[audio_index]->cs = call->cs; 394 ToxAVCall *call = call_get(av, friend_number);
338 395
339 if ( support_video ) { 396 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
340 call->crtps[video_index] = 397 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
341 rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); 398 goto END;
399 }
342 400
343 if ( !call->crtps[video_index] ) { 401 switch (control) {
344 LOGGER_ERROR("Error while starting video RTP session!\n"); 402 case TOXAV_CALL_CONTROL_RESUME: {
345 goto error; 403 /* Only act if paused and had media transfer active before */
404 if (call->msi_call->self_capabilities == 0 &&
405 call->previous_self_capabilities) {
406
407 if (msi_change_capabilities(call->msi_call,
408 call->previous_self_capabilities) == -1) {
409 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
410 goto END;
411 }
412
413 rtp_allow_receiving(call->audio.first);
414 rtp_allow_receiving(call->video.first);
415 } else {
416 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
417 goto END;
418 }
346 } 419 }
420 break;
421
422 case TOXAV_CALL_CONTROL_PAUSE: {
423 /* Only act if not already paused */
424 if (call->msi_call->self_capabilities) {
425 call->previous_self_capabilities = call->msi_call->self_capabilities;
426
427 if (msi_change_capabilities(call->msi_call, 0) == -1) {
428 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
429 goto END;
430 }
431
432 rtp_stop_receiving(call->audio.first);
433 rtp_stop_receiving(call->video.first);
434 } else {
435 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
436 goto END;
437 }
438 }
439 break;
440
441 case TOXAV_CALL_CONTROL_CANCEL: {
442 /* Hang up */
443 pthread_mutex_lock(call->mutex);
444
445 if (msi_hangup(call->msi_call) != 0) {
446 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
447 pthread_mutex_unlock(call->mutex);
448 goto END;
449 }
450
451 call->msi_call = NULL;
452 pthread_mutex_unlock(call->mutex);
347 453
348 call->crtps[video_index]->cs = call->cs; 454 /* No mather the case, terminate the call */
455 call_kill_transmission(call);
456 call_remove(call);
457 }
458 break;
459
460 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
461 if (call->msi_call->self_capabilities & msi_CapRAudio) {
462 if (msi_change_capabilities(call->msi_call, call->
463 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
464 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
465 goto END;
466 }
467
468 rtp_stop_receiving(call->audio.first);
469 } else {
470 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
471 goto END;
472 }
473 }
474 break;
475
476 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
477 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
478 if (msi_change_capabilities(call->msi_call, call->
479 msi_call->self_capabilities | msi_CapRAudio) == -1) {
480 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
481 goto END;
482 }
483
484 rtp_allow_receiving(call->audio.first);
485 } else {
486 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
487 goto END;
488 }
489 }
490 break;
491
492 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
493 if (call->msi_call->self_capabilities & msi_CapRVideo) {
494 if (msi_change_capabilities(call->msi_call, call->
495 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
496 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
497 goto END;
498 }
499
500 rtp_stop_receiving(call->video.first);
501 } else {
502 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
503 goto END;
504 }
505 }
506 break;
507
508 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
509 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
510 if (msi_change_capabilities(call->msi_call, call->
511 msi_call->self_capabilities | msi_CapRVideo) == -1) {
512 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
513 goto END;
514 }
515
516 rtp_allow_receiving(call->audio.first);
517 } else {
518 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
519 goto END;
520 }
521 }
522 break;
349 } 523 }
350 524
351 call->active = 1; 525END:
352 pthread_mutex_unlock(call->mutex); 526 pthread_mutex_unlock(av->mutex);
353 return av_ErrorNone;
354error:
355 rtp_kill(call->crtps[audio_index], av->messenger);
356 call->crtps[audio_index] = NULL;
357 rtp_kill(call->crtps[video_index], av->messenger);
358 call->crtps[video_index] = NULL;
359 cs_kill(call->cs);
360 call->cs = NULL;
361 call->active = 0;
362 pthread_mutex_destroy(call->mutex_encoding_audio);
363 pthread_mutex_destroy(call->mutex_encoding_video);
364 pthread_mutex_destroy(call->mutex_do);
365 527
366 pthread_mutex_unlock(call->mutex); 528 if (error)
367 return av_ErrorCreatingRtpSessions; 529 *error = rc;
368}
369 530
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) 531 return rc == TOXAV_ERR_CALL_CONTROL_OK;
532}
533bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate,
534 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error)
371{ 535{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 536 TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK;
373 LOGGER_WARNING("Invalid call index: %d", call_index); 537 ToxAVCall *call;
374 return av_ErrorNoCall; 538
539 if (m_friend_exists(av->m, friend_number) == 0) {
540 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
541 goto END;
375 } 542 }
376 543
377 ToxAvCall *call = &av->calls[call_index]; 544 if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) {
545 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE;
546 goto END;
547 }
378 548
379 pthread_mutex_lock(call->mutex); 549 if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) {
550 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE;
551 goto END;
552 }
553
554 pthread_mutex_lock(av->mutex);
555 call = call_get(av, friend_number);
380 556
381 if (!call->active) { 557 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
382 pthread_mutex_unlock(call->mutex); 558 pthread_mutex_unlock(av->mutex);
383 LOGGER_WARNING("Action on inactive call: %d", call_index); 559 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
384 return av_ErrorInvalidState; 560 goto END;
385 } 561 }
386 562
387 call->active = 0; 563 if (audio_bit_rate >= 0) {
564 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
388 565
389 pthread_mutex_lock(call->mutex_encoding_audio); 566 if (call->audio_bit_rate == audio_bit_rate) {
390 pthread_mutex_unlock(call->mutex_encoding_audio); 567 LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate);
391 pthread_mutex_lock(call->mutex_encoding_video); 568 } else if (audio_bit_rate == 0) {
392 pthread_mutex_unlock(call->mutex_encoding_video); 569 LOGGER_DEBUG("Turned off audio sending");
393 pthread_mutex_lock(call->mutex_do);
394 pthread_mutex_unlock(call->mutex_do);
395 570
396 rtp_kill(call->crtps[audio_index], av->messenger); 571 if (msi_change_capabilities(call->msi_call, call->msi_call->
397 call->crtps[audio_index] = NULL; 572 self_capabilities ^ msi_CapSAudio) != 0) {
398 rtp_kill(call->crtps[video_index], av->messenger); 573 pthread_mutex_unlock(av->mutex);
399 call->crtps[video_index] = NULL; 574 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
400 cs_kill(call->cs); 575 goto END;
401 call->cs = NULL; 576 }
402 577
403 pthread_mutex_destroy(call->mutex_encoding_audio); 578 /* Audio sending is turned off; notify peer */
404 pthread_mutex_destroy(call->mutex_encoding_video); 579 call->audio_bit_rate = 0;
405 pthread_mutex_destroy(call->mutex_do); 580 } else {
581 pthread_mutex_lock(call->mutex);
582
583 if (call->audio_bit_rate == 0) {
584 LOGGER_DEBUG("Turned on audio sending");
585
586 /* The audio has been turned off before this */
587 if (msi_change_capabilities(call->msi_call, call->
588 msi_call->self_capabilities | msi_CapSAudio) != 0) {
589 pthread_mutex_unlock(call->mutex);
590 pthread_mutex_unlock(av->mutex);
591 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
592 goto END;
593 }
594 } else
595 LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate);
596
597 call->audio_bit_rate = audio_bit_rate;
598 pthread_mutex_unlock(call->mutex);
599 }
600 }
406 601
407 pthread_mutex_unlock(call->mutex); 602 if (video_bit_rate >= 0) {
603 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
408 604
409 return av_ErrorNone; 605 if (call->video_bit_rate == video_bit_rate) {
410} 606 LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate);
607 } else if (video_bit_rate == 0) {
608 LOGGER_DEBUG("Turned off video sending");
411 609
412static int toxav_send_rtp_payload(ToxAv *av, 610 /* Video sending is turned off; notify peer */
413 ToxAvCall *call, 611 if (msi_change_capabilities(call->msi_call, call->msi_call->
414 ToxAvCallType type, 612 self_capabilities ^ msi_CapSVideo) != 0) {
415 const uint8_t *payload, 613 pthread_mutex_unlock(av->mutex);
416 unsigned int length) 614 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
615 goto END;
616 }
617
618 call->video_bit_rate = 0;
619 } else {
620 pthread_mutex_lock(call->mutex);
621
622 if (call->video_bit_rate == 0) {
623 LOGGER_DEBUG("Turned on video sending");
624
625 /* The video has been turned off before this */
626 if (msi_change_capabilities(call->msi_call, call->
627 msi_call->self_capabilities | msi_CapSVideo) != 0) {
628 pthread_mutex_unlock(call->mutex);
629 pthread_mutex_unlock(av->mutex);
630 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
631 goto END;
632 }
633 } else
634 LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate);
635
636 call->video_bit_rate = video_bit_rate;
637 pthread_mutex_unlock(call->mutex);
638 }
639 }
640
641 pthread_mutex_unlock(av->mutex);
642END:
643
644 if (error)
645 *error = rc;
646
647 return rc == TOXAV_ERR_BIT_RATE_SET_OK;
648}
649void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data)
417{ 650{
418 if (call->crtps[type - av_TypeAudio]) { 651 pthread_mutex_lock(av->mutex);
652 av->bcb.first = function;
653 av->bcb.second = user_data;
654 pthread_mutex_unlock(av->mutex);
655}
656bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
657 uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error)
658{
659 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
660 ToxAVCall *call;
661
662 if (m_friend_exists(av->m, friend_number) == 0) {
663 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
664 goto END;
665 }
666
667 if (pthread_mutex_trylock(av->mutex) != 0) {
668 rc = TOXAV_ERR_SEND_FRAME_SYNC;
669 goto END;
670 }
419 671
420 /* Audio */ 672 call = call_get(av, friend_number);
421 if (type == av_TypeAudio)
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
423 673
424 /* Video */ 674 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
425 int parts = cs_split_video_payload(call->cs, payload, length); 675 pthread_mutex_unlock(av->mutex);
676 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
677 goto END;
678 }
426 679
427 if (parts < 0) return parts; 680 if (call->audio_bit_rate == 0 ||
681 !(call->msi_call->self_capabilities & msi_CapSAudio) ||
682 !(call->msi_call->peer_capabilities & msi_CapRAudio)) {
683 pthread_mutex_unlock(av->mutex);
684 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
685 goto END;
686 }
428 687
429 uint16_t part_size; 688 pthread_mutex_lock(call->mutex_audio);
430 const uint8_t *iter; 689 pthread_mutex_unlock(av->mutex);
431 690
432 int i; 691 if (pcm == NULL) {
692 pthread_mutex_unlock(call->mutex_audio);
693 rc = TOXAV_ERR_SEND_FRAME_NULL;
694 goto END;
695 }
433 696
434 for (i = 0; i < parts; i++) { 697 if (channels > 2) {
435 iter = cs_get_split_video_frame(call->cs, &part_size); 698 pthread_mutex_unlock(call->mutex_audio);
699 rc = TOXAV_ERR_SEND_FRAME_INVALID;
700 goto END;
701 }
436 702
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) 703 { /* Encode and send */
438 return av_ErrorSendingPayload; 704 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
705 pthread_mutex_unlock(call->mutex_audio);
706 rc = TOXAV_ERR_SEND_FRAME_INVALID;
707 goto END;
439 } 708 }
440 709
441 return av_ErrorNone; 710 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
442 711
443 } else return av_ErrorNoRtpSession; 712 sampling_rate = htonl(sampling_rate);
444} 713 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
714 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
715 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
716
717 if (vrc < 0) {
718 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
719 pthread_mutex_unlock(call->mutex_audio);
720 rc = TOXAV_ERR_SEND_FRAME_INVALID;
721 goto END;
722 }
445 723
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 724 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) {
725 LOGGER_WARNING("Failed to send audio packet");
726 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
727 }
728 }
729
730
731 pthread_mutex_unlock(call->mutex_audio);
732
733END:
734
735 if (error)
736 *error = rc;
737
738 return rc == TOXAV_ERR_SEND_FRAME_OK;
739}
740bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
741 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
447{ 742{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 743 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
449 LOGGER_WARNING("Invalid call index: %d", call_index); 744 ToxAVCall *call;
450 return av_ErrorNoCall; 745
746 if (m_friend_exists(av->m, friend_number) == 0) {
747 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
748 goto END;
451 } 749 }
452 750
751 if (pthread_mutex_trylock(av->mutex) != 0) {
752 rc = TOXAV_ERR_SEND_FRAME_SYNC;
753 goto END;
754 }
453 755
454 ToxAvCall *call = &av->calls[call_index]; 756 call = call_get(av, friend_number);
455 pthread_mutex_lock(call->mutex);
456 757
457 if (!call->active) { 758 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
458 pthread_mutex_unlock(call->mutex); 759 pthread_mutex_unlock(av->mutex);
459 LOGGER_WARNING("Action on inactive call: %d", call_index); 760 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
460 return av_ErrorInvalidState; 761 goto END;
461 } 762 }
462 763
463 if (!(call->cs->capabilities & cs_VideoEncoding)) { 764 if (call->video_bit_rate == 0 ||
464 pthread_mutex_unlock(call->mutex); 765 !(call->msi_call->self_capabilities & msi_CapSVideo) ||
465 LOGGER_WARNING("Call doesn't support encoding video: %d", call_index); 766 !(call->msi_call->peer_capabilities & msi_CapRVideo)) {
466 return av_ErrorInvalidState; 767 pthread_mutex_unlock(av->mutex);
768 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
769 goto END;
467 } 770 }
468 771
469 if (cs_set_video_encoder_resolution(call->cs, input->w, input->h) < 0) { 772 pthread_mutex_lock(call->mutex_video);
470 pthread_mutex_unlock(call->mutex); 773 pthread_mutex_unlock(av->mutex);
471 return av_ErrorSettingVideoResolution; 774
775 if (y == NULL || u == NULL || v == NULL) {
776 pthread_mutex_unlock(call->mutex_video);
777 rc = TOXAV_ERR_SEND_FRAME_NULL;
778 goto END;
472 } 779 }
473 780
474 pthread_mutex_lock(call->mutex_encoding_video); 781 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0) {
475 pthread_mutex_unlock(call->mutex); 782 pthread_mutex_unlock(call->mutex_video);
783 rc = TOXAV_ERR_SEND_FRAME_INVALID;
784 goto END;
785 }
786
787 { /* Encode */
788 vpx_image_t img;
789 img.w = img.h = img.d_w = img.d_h = 0;
790 vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
791
792 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
793 * http://fourcc.org/yuv.php#IYUV
794 */
795 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
796 memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
797 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
476 798
477 int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 799 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
800 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
478 801
479 if ( rc != VPX_CODEC_OK) { 802 vpx_img_free(&img);
480 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); 803
481 pthread_mutex_unlock(call->mutex_encoding_video); 804 if (vrc != VPX_CODEC_OK) {
482 return av_ErrorEncodingVideo; 805 pthread_mutex_unlock(call->mutex_video);
806 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
807 rc = TOXAV_ERR_SEND_FRAME_INVALID;
808 goto END;
809 }
483 } 810 }
484 811
485 ++call->cs->frame_counter; 812 ++call->video.second->frame_counter;
486 813
487 vpx_codec_iter_t iter = NULL; 814 { /* Send frames */
488 const vpx_codec_cx_pkt_t *pkt; 815 vpx_codec_iter_t iter = NULL;
489 int copied = 0; 816 const vpx_codec_cx_pkt_t *pkt;
490 817
491 while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { 818 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) {
492 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 819 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT &&
493 if ( copied + pkt->data.frame.sz > dest_max ) { 820 rtp_send_data(call->video.first, pkt->data.frame.buf, pkt->data.frame.sz) < 0) {
494 pthread_mutex_unlock(call->mutex_encoding_video);
495 return av_ErrorPacketTooLarge;
496 }
497 821
498 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); 822 pthread_mutex_unlock(call->mutex_video);
499 copied += pkt->data.frame.sz; 823 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
824 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
825 goto END;
826 }
500 } 827 }
501 } 828 }
502 829
503 pthread_mutex_unlock(call->mutex_encoding_video); 830 pthread_mutex_unlock(call->mutex_video);
504 return copied; 831
832END:
833
834 if (error)
835 *error = rc;
836
837 return rc == TOXAV_ERR_SEND_FRAME_OK;
838}
839void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data)
840{
841 pthread_mutex_lock(av->mutex);
842 av->acb.first = function;
843 av->acb.second = user_data;
844 pthread_mutex_unlock(av->mutex);
845}
846void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data)
847{
848 pthread_mutex_lock(av->mutex);
849 av->vcb.first = function;
850 av->vcb.second = user_data;
851 pthread_mutex_unlock(av->mutex);
505} 852}
506 853
507int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 854
855/*******************************************************************************
856 *
857 * :: Internal
858 *
859 ******************************************************************************/
860void callback_bwc(BWControler *bwc, uint32_t friend_number, float loss, void *user_data)
508{ 861{
862 /* Callback which is called when the internal measure mechanism reported packet loss.
863 * We report suggested lowered bitrate to an app. If app is sending both audio and video,
864 * we will report lowered bitrate for video only because in that case video probably
865 * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
866 * The application may choose to disable video totally if the stream is too bad.
867 */
509 868
510 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 869 ToxAVCall *call = user_data;
511 LOGGER_WARNING("Invalid call index: %d", call_index); 870 assert(call);
512 return av_ErrorNoCall;
513 }
514 871
515 ToxAvCall *call = &av->calls[call_index]; 872 LOGGER_DEBUG("Reported loss of %f%%", loss * 100);
516 pthread_mutex_lock(call->mutex);
517 873
874 if (loss < .01f)
875 return;
518 876
519 if (!call->active) { 877 pthread_mutex_lock(call->av->mutex);
520 pthread_mutex_unlock(call->mutex); 878
521 LOGGER_WARNING("Action on inactive call: %d", call_index); 879 if (!call->av->bcb.first) {
522 return av_ErrorInvalidState; 880 pthread_mutex_unlock(call->av->mutex);
881 LOGGER_WARNING("No callback to report loss on");
882 return;
523 } 883 }
524 884
525 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); 885 if (call->video_bit_rate)
526 pthread_mutex_unlock(call->mutex); 886 (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate,
887 call->video_bit_rate - (call->video_bit_rate * loss),
888 call->av->bcb.second);
889 else if (call->audio_bit_rate)
890 (*call->av->bcb.first) (call->av, friend_number,
891 call->audio_bit_rate - (call->audio_bit_rate * loss),
892 0, call->av->bcb.second);
527 893
528 return rc; 894 pthread_mutex_unlock(call->av->mutex);
529} 895}
530 896int callback_invite(void *toxav_inst, MSICall *call)
531int toxav_prepare_audio_frame ( ToxAv *av,
532 int32_t call_index,
533 uint8_t *dest,
534 int dest_max,
535 const int16_t *frame,
536 int frame_size)
537{ 897{
538 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 898 ToxAV *toxav = toxav_inst;
539 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 899 pthread_mutex_lock(toxav->mutex);
540 return av_ErrorNoCall;
541 }
542 900
543 ToxAvCall *call = &av->calls[call_index]; 901 ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL);
544 pthread_mutex_lock(call->mutex);
545 902
546 if (!call->active) { 903 if (av_call == NULL) {
547 pthread_mutex_unlock(call->mutex); 904 LOGGER_WARNING("Failed to initialize call...");
548 LOGGER_WARNING("Action on inactive call: %d", call_index); 905 pthread_mutex_unlock(toxav->mutex);
549 return av_ErrorInvalidState; 906 return -1;
550 } 907 }
551 908
552 pthread_mutex_lock(call->mutex_encoding_audio); 909 call->av_call = av_call;
553 pthread_mutex_unlock(call->mutex); 910 av_call->msi_call = call;
554 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max);
555 pthread_mutex_unlock(call->mutex_encoding_audio);
556 911
557 if (rc < 0) { 912 if (toxav->ccb.first)
558 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); 913 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
559 return av_ErrorEncodingAudio; 914 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
915 else {
916 /* No handler to capture the call request, send failure */
917 pthread_mutex_unlock(toxav->mutex);
918 return -1;
560 } 919 }
561 920
562 return rc; 921 pthread_mutex_unlock(toxav->mutex);
922 return 0;
563} 923}
564 924int callback_start(void *toxav_inst, MSICall *call)
565int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
566{ 925{
567 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 926 ToxAV *toxav = toxav_inst;
568 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 927 pthread_mutex_lock(toxav->mutex);
569 return av_ErrorNoCall;
570 }
571 928
572 ToxAvCall *call = &av->calls[call_index]; 929 ToxAVCall *av_call = call_get(toxav, call->friend_number);
573 pthread_mutex_lock(call->mutex);
574 930
931 if (av_call == NULL) {
932 /* Should this ever happen? */
933 pthread_mutex_unlock(toxav->mutex);
934 return -1;
935 }
575 936
576 if (!call->active) { 937 if (!call_prepare_transmission(av_call)) {
577 pthread_mutex_unlock(call->mutex); 938 callback_error(toxav_inst, call);
578 LOGGER_WARNING("Action on inactive call: %d", call_index); 939 pthread_mutex_unlock(toxav->mutex);
579 return av_ErrorInvalidState; 940 return -1;
580 } 941 }
581 942
582 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); 943 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
583 pthread_mutex_unlock(call->mutex); 944 callback_error(toxav_inst, call);
584 return rc; 945 pthread_mutex_unlock(toxav->mutex);
585} 946 return -1;
947 }
586 948
587int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) 949 pthread_mutex_unlock(toxav->mutex);
950 return 0;
951}
952int callback_end(void *toxav_inst, MSICall *call)
588{ 953{
589 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 954 ToxAV *toxav = toxav_inst;
590 !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) 955 pthread_mutex_lock(toxav->mutex);
591 return av_ErrorNoCall;
592 956
593 *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); 957 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
594 return av_ErrorNone;
595}
596 958
597int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) 959 if (call->av_call) {
598{ 960 call_kill_transmission(call->av_call);
599 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] 961 call_remove(call->av_call);
600 || av->msi_session->calls[call_index]->peer_count <= peer ) 962 }
601 return av_ErrorNoCall;
602 963
603 return av->msi_session->calls[call_index]->peers[peer]; 964 pthread_mutex_unlock(toxav->mutex);
965 return 0;
604} 966}
605 967int callback_error(void *toxav_inst, MSICall *call)
606ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
607{ 968{
608 if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) 969 ToxAV *toxav = toxav_inst;
609 return av_CallNonExistent; 970 pthread_mutex_lock(toxav->mutex);
610 971
611 return av->msi_session->calls[call_index]->state; 972 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR);
612 973
974 if (call->av_call) {
975 call_kill_transmission(call->av_call);
976 call_remove(call->av_call);
977 }
978
979 pthread_mutex_unlock(toxav->mutex);
980 return 0;
613} 981}
982int callback_capabilites(void *toxav_inst, MSICall *call)
983{
984 ToxAV *toxav = toxav_inst;
985 pthread_mutex_lock(toxav->mutex);
614 986
615int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 987 if (call->peer_capabilities & msi_CapSAudio)
988 rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first);
989 else
990 rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first);
991
992 if (call->peer_capabilities & msi_CapSVideo)
993 rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first);
994 else
995 rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first);
996
997 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
998
999 pthread_mutex_unlock(toxav->mutex);
1000 return 0;
1001}
1002bool audio_bit_rate_invalid(uint32_t bit_rate)
616{ 1003{
617 return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; 1004 /* Opus RFC 6716 section-2.1.1 dictates the following:
618 /* 0 is error here */ 1005 * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
1006 */
1007 return bit_rate < 6 || bit_rate > 510;
619} 1008}
620 1009bool video_bit_rate_invalid(uint32_t bit_rate)
621Tox *toxav_get_tox(ToxAv *av)
622{ 1010{
623 return (Tox *)av->messenger; 1011 (void) bit_rate;
1012 /* TODO: If anyone knows the answer to this one please fill it up */
1013 return false;
624} 1014}
1015bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1016{
1017 if (av->scb.first)
1018 av->scb.first(av, friend_number, state, av->scb.second);
1019 else
1020 return false;
625 1021
626int toxav_get_active_count(ToxAv *av) 1022 return true;
1023}
1024ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
627{ 1025{
628 if (!av) return -1; 1026 /* Assumes mutex locked */
1027 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1028 ToxAVCall *call = NULL;
629 1029
630 int rc = 0, i = 0; 1030 if (m_friend_exists(av->m, friend_number) == 0) {
1031 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1032 goto END;
1033 }
631 1034
632 for (; i < av->max_calls; i++) { 1035 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
633 pthread_mutex_lock(av->calls[i].mutex); 1036 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1037 goto END;
1038 }
1039
1040 if (call_get(av, friend_number) != NULL) {
1041 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1042 goto END;
1043 }
634 1044
635 if (av->calls[i].active) rc++;
636 1045
637 pthread_mutex_unlock(av->calls[i].mutex); 1046 call = calloc(sizeof(ToxAVCall), 1);
1047
1048 if (call == NULL) {
1049 rc = TOXAV_ERR_CALL_MALLOC;
1050 goto END;
638 } 1051 }
639 1052
640 return rc; 1053 call->av = av;
641} 1054 call->friend_number = friend_number;
642 1055
643/* Create a new toxav group. 1056 if (av->calls == NULL) { /* Creating */
644 * 1057 av->calls = calloc (sizeof(ToxAVCall *), friend_number + 1);
645 * return group number on success.
646 * return -1 on failure.
647 *
648 * Audio data callback format:
649 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
650 *
651 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
652 */
653int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int,
654 uint8_t, unsigned int, void *), void *userdata)
655{
656 Messenger *m = tox;
657 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
658}
659 1058
660/* Join a AV group (you need to have been invited first.) 1059 if (av->calls == NULL) {
661 * 1060 free(call);
662 * returns group number on success 1061 call = NULL;
663 * returns -1 on failure. 1062 rc = TOXAV_ERR_CALL_MALLOC;
664 * 1063 goto END;
665 * Audio data callback format (same as the one for toxav_add_av_groupchat()): 1064 }
666 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) 1065
667 * 1066 av->calls_tail = av->calls_head = friend_number;
668 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1067
669 */ 1068 } else if (av->calls_tail < friend_number) { /* Appending */
670int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 1069 void *tmp = realloc(av->calls, sizeof(ToxAVCall *) * (friend_number + 1));
671 void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), 1070
672 void *userdata) 1071 if (tmp == NULL) {
1072 free(call);
1073 call = NULL;
1074 rc = TOXAV_ERR_CALL_MALLOC;
1075 goto END;
1076 }
1077
1078 av->calls = tmp;
1079
1080 /* Set fields in between to null */
1081 uint32_t i = av->calls_tail + 1;
1082
1083 for (; i < friend_number; i ++)
1084 av->calls[i] = NULL;
1085
1086 call->prev = av->calls[av->calls_tail];
1087 av->calls[av->calls_tail]->next = call;
1088
1089 av->calls_tail = friend_number;
1090
1091 } else if (av->calls_head > friend_number) { /* Inserting at front */
1092 call->next = av->calls[av->calls_head];
1093 av->calls[av->calls_head]->prev = call;
1094 av->calls_head = friend_number;
1095 }
1096
1097 av->calls[friend_number] = call;
1098
1099END:
1100
1101 if (error)
1102 *error = rc;
1103
1104 return call;
1105}
1106ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
673{ 1107{
674 Messenger *m = tox; 1108 /* Assumes mutex locked */
675 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); 1109 if (av->calls == NULL || av->calls_tail < friend_number)
1110 return NULL;
1111
1112 return av->calls[friend_number];
676} 1113}
1114ToxAVCall *call_remove(ToxAVCall *call)
1115{
1116 if (call == NULL)
1117 return NULL;
677 1118
678/* Send audio to the group chat. 1119 uint32_t friend_number = call->friend_number;
679 * 1120 ToxAV *av = call->av;
680 * return 0 on success. 1121
681 * return -1 on failure. 1122 ToxAVCall *prev = call->prev;
682 * 1123 ToxAVCall *next = call->next;
683 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1124
684 * 1125 /* Set av call in msi to NULL in order to know if call if ToxAVCall is
685 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) 1126 * removed from the msi call.
686 * Valid number of channels are 1 or 2. 1127 */
687 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. 1128 if (call->msi_call) {
688 * 1129 call->msi_call->av_call = NULL;
689 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 1130 }
690 */ 1131
691int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 1132 free(call);
692 unsigned int sample_rate) 1133
1134 if (prev)
1135 prev->next = next;
1136 else if (next)
1137 av->calls_head = next->friend_number;
1138 else goto CLEAR;
1139
1140 if (next)
1141 next->prev = prev;
1142 else if (prev)
1143 av->calls_tail = prev->friend_number;
1144 else goto CLEAR;
1145
1146 av->calls[friend_number] = NULL;
1147 return next;
1148
1149CLEAR:
1150 av->calls_head = av->calls_tail = 0;
1151 free(av->calls);
1152 av->calls = NULL;
1153
1154 return NULL;
1155}
1156bool call_prepare_transmission(ToxAVCall *call)
693{ 1157{
694 Messenger *m = tox; 1158 /* Assumes mutex locked */
695 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); 1159
1160 if (call == NULL)
1161 return false;
1162
1163 ToxAV *av = call->av;
1164
1165 if (!av->acb.first && !av->vcb.first)
1166 /* It makes no sense to have CSession without callbacks */
1167 return false;
1168
1169 if (call->active) {
1170 LOGGER_WARNING("Call already active!\n");
1171 return true;
1172 }
1173
1174 if (create_recursive_mutex(call->mutex_audio) != 0)
1175 return false;
1176
1177 if (create_recursive_mutex(call->mutex_video) != 0)
1178 goto FAILURE_3;
1179
1180 if (create_recursive_mutex(call->mutex) != 0)
1181 goto FAILURE_2;
1182
1183 /* Prepare bwc */
1184 call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call);
1185
1186 { /* Prepare audio */
1187 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1188
1189 if (!call->audio.second) {
1190 LOGGER_ERROR("Failed to create audio codec session");
1191 goto FAILURE;
1192 }
1193
1194 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc,
1195 call->audio.second, ac_queue_message);
1196
1197 if (!call->audio.first) {
1198 LOGGER_ERROR("Failed to create audio rtp session");;
1199 goto FAILURE;
1200 }
1201 }
1202 { /* Prepare video */
1203 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second);
1204
1205 if (!call->video.second) {
1206 LOGGER_ERROR("Failed to create video codec session");
1207 goto FAILURE;
1208 }
1209
1210 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc,
1211 call->video.second, vc_queue_message);
1212
1213 if (!call->video.first) {
1214 LOGGER_ERROR("Failed to create video rtp session");
1215 goto FAILURE;
1216 }
1217 }
1218
1219 call->active = 1;
1220 return true;
1221
1222FAILURE:
1223 bwc_kill(call->bwc);
1224 rtp_kill(call->audio.first);
1225 ac_kill(call->audio.second);
1226 call->audio.first = NULL;
1227 call->audio.second = NULL;
1228 rtp_kill(call->video.first);
1229 vc_kill(call->video.second);
1230 call->video.first = NULL;
1231 call->video.second = NULL;
1232 pthread_mutex_destroy(call->mutex);
1233FAILURE_2:
1234 pthread_mutex_destroy(call->mutex_video);
1235FAILURE_3:
1236 pthread_mutex_destroy(call->mutex_audio);
1237 return false;
696} 1238}
1239void call_kill_transmission(ToxAVCall *call)
1240{
1241 if (call == NULL || call->active == 0)
1242 return;
697 1243
1244 call->active = 0;
1245
1246 pthread_mutex_lock(call->mutex_audio);
1247 pthread_mutex_unlock(call->mutex_audio);
1248 pthread_mutex_lock(call->mutex_video);
1249 pthread_mutex_unlock(call->mutex_video);
1250 pthread_mutex_lock(call->mutex);
1251 pthread_mutex_unlock(call->mutex);
1252
1253 bwc_kill(call->bwc);
1254
1255 rtp_kill(call->audio.first);
1256 ac_kill(call->audio.second);
1257 call->audio.first = NULL;
1258 call->audio.second = NULL;
1259
1260 rtp_kill(call->video.first);
1261 vc_kill(call->video.second);
1262 call->video.first = NULL;
1263 call->video.second = NULL;
1264
1265 pthread_mutex_destroy(call->mutex_audio);
1266 pthread_mutex_destroy(call->mutex_video);
1267 pthread_mutex_destroy(call->mutex);
1268}
diff --git a/toxav/toxav.h b/toxav/toxav.h
index 7285f45c..08a6d265 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -1,285 +1,698 @@
1/** toxav.h 1/* toxav.h
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
7 * Tox is free software: you can redistribute it and/or modify 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 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 9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version. 10 * (at your option) any later version.
11 * 11 *
12 * Tox is distributed in the hope that it will be useful, 12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details. 15 * GNU General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU General Public License 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/>. 18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 * 19 *
20 */ 20 */
21 21
22#ifndef TOXAV_H
23#define TOXAV_H
22 24
23#ifndef __TOXAV 25#include <stdbool.h>
24#define __TOXAV 26#include <stddef.h>
25#include <inttypes.h> 27#include <stdint.h>
26 28
27#ifdef __cplusplus 29#ifdef __cplusplus
28extern "C" { 30extern "C" {
29#endif 31#endif
30 32
31typedef struct _ToxAv ToxAv; 33/** \page av Public audio/video API for Tox clients.
32 34 *
33/* vpx_image_t */ 35 * This API can handle multiple calls. Each call has its state, in very rare
34#include <vpx/vpx_image.h> 36 * occasions the library can change the state of the call without apps knowledge.
35 37 *
36typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); 38 */
37typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data); 39/** \subsection events Events and callbacks
38typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data); 40 *
39 41 * As in Core API, events are handled by callbacks. One callback can be
42 * registered per event. All events have a callback function type named
43 * `toxav_{event}_cb` and a function to register it named `toxav_callback_{event}`.
44 * Passing a NULL callback will result in no callback being registered for that
45 * event. Only one callback per event can be registered, so if a client needs
46 * multiple event listeners, it needs to implement the dispatch functionality
47 * itself. Unlike Core API, lack of some event handlers will cause the the
48 * library to drop calls before they are started. Hanging up call from a
49 * callback causes undefined behaviour.
50 *
51 */
52/** \subsection threading Threading implications
53 *
54 * Unlike the Core API, this API is fully thread-safe. The library will ensure
55 * the proper synchronization of parallel calls.
56 *
57 * A common way to run ToxAV (multiple or single instance) is to have a thread,
58 * separate from tox instance thread, running a simple toxav_iterate loop,
59 * sleeping for toxav_iteration_interval * milliseconds on each iteration.
60 *
61 * An important thing to note is that events are triggered from both tox and
62 * toxav thread (see above). Audio and video receive frame events are triggered
63 * from toxav thread while all the other events are triggered from tox thread.
64 *
65 * Tox thread has priority with mutex mechanisms. Any api function can
66 * fail if mutexes are held by tox thread in which case they will set SYNC
67 * error code.
68 */
69/**
70 * External Tox type.
71 */
40#ifndef TOX_DEFINED 72#ifndef TOX_DEFINED
41#define TOX_DEFINED 73#define TOX_DEFINED
42typedef struct Tox Tox; 74typedef struct Tox Tox;
43#endif 75#endif /* TOX_DEFINED */
44
45#define RTP_PAYLOAD_SIZE 65535
46
47 76
48/** 77/**
49 * Callbacks ids that handle the call states. 78 * ToxAV.
50 */ 79 */
51typedef enum {
52 av_OnInvite, /* Incoming call */
53 av_OnRinging, /* When peer is ready to accept/reject the call */
54 av_OnStart, /* Call (RTP transmission) started */
55 av_OnCancel, /* The side that initiated call canceled invite */
56 av_OnReject, /* The side that was invited rejected the call */
57 av_OnEnd, /* Call that was active ended */
58 av_OnRequestTimeout, /* When the requested action didn't get response in specified time */
59 av_OnPeerTimeout, /* Peer timed out; stop the call */
60 av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */
61 av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */
62} ToxAvCallbackID;
63
64
65/** 80/**
66 * Call type identifier. 81 * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
82 * instance, and Tox instance can have only one ToxAV instance. One must make
83 * sure to close ToxAV instance prior closing Tox instance otherwise undefined
84 * behaviour occurs. Upon closing of ToxAV instance, all active calls will be
85 * forcibly terminated without notifying peers.
86 *
67 */ 87 */
68typedef enum { 88#ifndef TOXAV_DEFINED
69 av_TypeAudio = 192, 89#define TOXAV_DEFINED
70 av_TypeVideo 90typedef struct ToxAV ToxAV;
71} ToxAvCallType; 91#endif /* TOXAV_DEFINED */
72
73 92
74typedef enum {
75 av_CallNonExistent = -1,
76 av_CallInviting, /* when sending call invite */
77 av_CallStarting, /* when getting call invite */
78 av_CallActive,
79 av_CallHold,
80 av_CallHungUp
81} ToxAvCallState;
82 93
94/*******************************************************************************
95 *
96 * :: API version
97 *
98 ******************************************************************************/
83/** 99/**
84 * Error indicators. Values under -20 are reserved for toxcore. 100 * The major version number. Incremented when the API or ABI changes in an
101 * incompatible way.
85 */ 102 */
86typedef enum { 103#define TOXAV_VERSION_MAJOR 0u
87 av_ErrorNone = 0,
88 av_ErrorUnknown = -1, /* Unknown error */
89 av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
90 av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
91 av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
92 av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
93 av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */
94 av_ErrorSettingVideoResolution = -31, /* Error setting resolution */
95 av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */
96 av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */
97 av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */
98 av_ErrorEncodingAudio = -35, /* opus_encode failed */
99 av_ErrorSendingPayload = -40, /* Sending lossy packet failed */
100 av_ErrorCreatingRtpSessions = -41, /* One of the rtp sessions failed to initialize */
101 av_ErrorNoRtpSession = -50, /* Trying to perform rtp action on invalid session */
102 av_ErrorInvalidCodecState = -51, /* Codec state not initialized */
103 av_ErrorPacketTooLarge = -52, /* Split packet exceeds it's limit */
104} ToxAvError;
105
106 104
107/** 105/**
108 * Locally supported capabilities. 106 * The minor version number. Incremented when functionality is added without
107 * breaking the API or ABI. Set to 0 when the major version number is
108 * incremented.
109 */ 109 */
110typedef enum { 110#define TOXAV_VERSION_MINOR 0u
111 av_AudioEncoding = 1 << 0,
112 av_AudioDecoding = 1 << 1,
113 av_VideoEncoding = 1 << 2,
114 av_VideoDecoding = 1 << 3
115} ToxAvCapabilities;
116
117 111
118/** 112/**
119 * Encoding settings. 113 * The patch or revision number. Incremented when bugfixes are applied without
114 * changing any functionality or API or ABI.
120 */ 115 */
121typedef struct _ToxAvCSettings { 116#define TOXAV_VERSION_PATCH 0u
122 ToxAvCallType call_type;
123
124 uint32_t video_bitrate; /* In kbits/s */
125 uint16_t max_video_width; /* In px */
126 uint16_t max_video_height; /* In px */
127
128 uint32_t audio_bitrate; /* In bits/s */
129 uint16_t audio_frame_duration; /* In ms */
130 uint32_t audio_sample_rate; /* In Hz */
131 uint32_t audio_channels;
132} ToxAvCSettings;
133
134extern const ToxAvCSettings av_DefaultSettings;
135 117
136/** 118/**
137 * Start new A/V session. There can only be one session at the time. 119 * A macro to check at preprocessing time whether the client code is compatible
120 * with the installed version of ToxAV.
138 */ 121 */
139ToxAv *toxav_new(Tox *messenger, int32_t max_calls); 122#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
123 (TOXAV_VERSION_MAJOR == MAJOR && \
124 (TOXAV_VERSION_MINOR > MINOR || \
125 (TOXAV_VERSION_MINOR == MINOR && \
126 TOXAV_VERSION_PATCH >= PATCH)))
140 127
141/** 128/**
142 * Remove A/V session. 129 * A macro to make compilation fail if the client code is not compatible with
130 * the installed version of ToxAV.
143 */ 131 */
144void toxav_kill(ToxAv *av); 132#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
133 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
145 134
146/** 135/**
147 * Returns the interval in milliseconds when the next toxav_do() should be called. 136 * A convenience macro to call toxav_version_is_compatible with the currently
148 * If no call is active at the moment returns 200. 137 * compiling API version.
149 */ 138 */
150uint32_t toxav_do_interval(ToxAv *av); 139#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
140 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
151 141
152/** 142/**
153 * Main loop for the session. Best called right after tox_do(); 143 * Return the major version number of the library. Can be used to display the
144 * ToxAV library version or to check whether the client is compatible with the
145 * dynamically linked version of ToxAV.
154 */ 146 */
155void toxav_do(ToxAv *av); 147uint32_t toxav_version_major(void);
156 148
157/** 149/**
158 * Register callback for call state. 150 * Return the minor version number of the library.
159 */ 151 */
160void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); 152uint32_t toxav_version_minor(void);
161 153
162/** 154/**
163 * Register callback for audio data. 155 * Return the patch number of the library.
164 */ 156 */
165void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); 157uint32_t toxav_version_patch(void);
166 158
167/** 159/**
168 * Register callback for video data. 160 * Return whether the compiled library version is compatible with the passed
161 * version numbers.
169 */ 162 */
170void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); 163bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
164
165
166/*******************************************************************************
167 *
168 * :: Creation and destruction
169 *
170 ******************************************************************************/
171typedef enum TOXAV_ERR_NEW {
172 /**
173 * The function returned successfully.
174 */
175 TOXAV_ERR_NEW_OK,
176 /**
177 * One of the arguments to the function was NULL when it was not expected.
178 */
179 TOXAV_ERR_NEW_NULL,
180 /**
181 * Memory allocation failure while trying to allocate structures required for
182 * the A/V session.
183 */
184 TOXAV_ERR_NEW_MALLOC,
185 /**
186 * Attempted to create a second session for the same Tox instance.
187 */
188 TOXAV_ERR_NEW_MULTIPLE,
189} TOXAV_ERR_NEW;
171 190
172/** 191/**
173 * Call user. Use its friend_id. 192 * Start new A/V session. There can only be only one session per Tox instance.
174 */ 193 */
175int toxav_call(ToxAv *av, 194ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
176 int32_t *call_index,
177 int friend_id,
178 const ToxAvCSettings *csettings,
179 int ringing_seconds);
180 195
181/** 196/**
182 * Hangup active call. 197 * Releases all resources associated with the A/V session.
198 *
199 * If any calls were ongoing, these will be forcibly terminated without
200 * notifying peers. After calling this function, no other functions may be
201 * called and the av pointer becomes invalid.
183 */ 202 */
184int toxav_hangup(ToxAv *av, int32_t call_index); 203void toxav_kill(ToxAV *toxAV);
185 204
186/** 205/**
187 * Answer incoming call. Pass the csettings that you will use. 206 * Returns the Tox instance the A/V object was created for.
188 */ 207 */
189int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); 208Tox *toxav_get_tox(const ToxAV *toxAV);
209
190 210
211/*******************************************************************************
212 *
213 * :: A/V event loop
214 *
215 ******************************************************************************/
191/** 216/**
192 * Reject incoming call. 217 * Returns the interval in milliseconds when the next toxav_iterate call should
218 * be. If no call is active at the moment, this function returns 200.
193 */ 219 */
194int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); 220uint32_t toxav_iteration_interval(const ToxAV *toxAV);
195 221
196/** 222/**
197 * Cancel outgoing request. 223 * Main loop for the session. This function needs to be called in intervals of
224 * toxav_iteration_interval() milliseconds. It is best called in the separate
225 * thread from tox_iterate.
198 */ 226 */
199int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); 227void toxav_iterate(ToxAV *toxAV);
228
229
230/*******************************************************************************
231 *
232 * :: Call setup
233 *
234 ******************************************************************************/
235typedef enum TOXAV_ERR_CALL {
236 /**
237 * The function returned successfully.
238 */
239 TOXAV_ERR_CALL_OK,
240 /**
241 * A resource allocation error occurred while trying to create the structures
242 * required for the call.
243 */
244 TOXAV_ERR_CALL_MALLOC,
245 /**
246 * Synchronization error occurred.
247 */
248 TOXAV_ERR_CALL_SYNC,
249 /**
250 * The friend number did not designate a valid friend.
251 */
252 TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
253 /**
254 * The friend was valid, but not currently connected.
255 */
256 TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED,
257 /**
258 * Attempted to call a friend while already in an audio or video call with
259 * them.
260 */
261 TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL,
262 /**
263 * Audio or video bit rate is invalid.
264 */
265 TOXAV_ERR_CALL_INVALID_BIT_RATE,
266} TOXAV_ERR_CALL;
200 267
201/** 268/**
202 * Notify peer that we are changing codec settings. 269 * Call a friend. This will start ringing the friend.
270 *
271 * It is the client's responsibility to stop ringing after a certain timeout,
272 * if such behaviour is desired. If the client does not stop ringing, the
273 * library will not stop until the friend is disconnected. Audio and video
274 * receiving are both enabled by default.
275 *
276 * @param friend_number The friend number of the friend that should be called.
277 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
278 * audio sending.
279 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
280 * video sending.
203 */ 281 */
204int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); 282bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
283 uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
205 284
206/** 285/**
207 * Terminate transmission. Note that transmission will be 286 * The function type for the call callback.
208 * terminated without informing remote peer. Usually called when we can't inform peer. 287 *
288 * @param friend_number The friend number from which the call is incoming.
289 * @param audio_enabled True if friend is sending audio.
290 * @param video_enabled True if friend is sending video.
209 */ 291 */
210int toxav_stop_call(ToxAv *av, int32_t call_index); 292typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled,
293 bool video_enabled, void *user_data);
211 294
212/** 295/**
213 * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. 296 * Set the callback for the `call` event. Pass NULL to unset.
214 * Also, it must be called when call is started 297 *
215 */ 298 */
216int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); 299void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
300
301typedef enum TOXAV_ERR_ANSWER {
302 /**
303 * The function returned successfully.
304 */
305 TOXAV_ERR_ANSWER_OK,
306 /**
307 * Synchronization error occurred.
308 */
309 TOXAV_ERR_ANSWER_SYNC,
310 /**
311 * Failed to initialize codecs for call session. Note that codec initiation
312 * will fail if there is no receive callback registered for either audio or
313 * video.
314 */
315 TOXAV_ERR_ANSWER_CODEC_INITIALIZATION,
316 /**
317 * The friend number did not designate a valid friend.
318 */
319 TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND,
320 /**
321 * The friend was valid, but they are not currently trying to initiate a call.
322 * This is also returned if this client is already in a call with the friend.
323 */
324 TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING,
325 /**
326 * Audio or video bit rate is invalid.
327 */
328 TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
329} TOXAV_ERR_ANSWER;
217 330
218/** 331/**
219 * Clears transmission data. Call this at the end of the transmission. 332 * Accept an incoming call.
333 *
334 * If answering fails for any reason, the call will still be pending and it is
335 * possible to try and answer it later. Audio and video receiving are both
336 * enabled by default.
337 *
338 * @param friend_number The friend number of the friend that is calling.
339 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
340 * audio sending.
341 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
342 * video sending.
220 */ 343 */
221int toxav_kill_transmission(ToxAv *av, int32_t call_index); 344bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
345 TOXAV_ERR_ANSWER *error);
346
347
348/*******************************************************************************
349 *
350 * :: Call state graph
351 *
352 ******************************************************************************/
353enum TOXAV_FRIEND_CALL_STATE {
354 /**
355 * Set by the AV core if an error occurred on the remote end or if friend
356 * timed out. This is the final state after which no more state
357 * transitions can occur for the call. This call state will never be triggered
358 * in combination with other call states.
359 */
360 TOXAV_FRIEND_CALL_STATE_ERROR = 1,
361 /**
362 * The call has finished. This is the final state after which no more state
363 * transitions can occur for the call. This call state will never be
364 * triggered in combination with other call states.
365 */
366 TOXAV_FRIEND_CALL_STATE_FINISHED = 2,
367 /**
368 * The flag that marks that friend is sending audio.
369 */
370 TOXAV_FRIEND_CALL_STATE_SENDING_A = 4,
371 /**
372 * The flag that marks that friend is sending video.
373 */
374 TOXAV_FRIEND_CALL_STATE_SENDING_V = 8,
375 /**
376 * The flag that marks that friend is receiving audio.
377 */
378 TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16,
379 /**
380 * The flag that marks that friend is receiving video.
381 */
382 TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
383};
222 384
223/** 385/**
224 * Encode video frame. 386 * The function type for the call_state callback.
387 *
388 * @param friend_number The friend number for which the call state changed.
389 * @param state The bitmask of the new call state which is guaranteed to be
390 * different than the previous state. The state is set to 0 when the call is
391 * paused. The bitmask represents all the activities currently performed by the
392 * friend.
225 */ 393 */
226int toxav_prepare_video_frame ( ToxAv *av, 394typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
227 int32_t call_index,
228 uint8_t *dest,
229 int dest_max,
230 vpx_image_t *input);
231 395
232/** 396/**
233 * Send encoded video packet. 397 * Set the callback for the `call_state` event. Pass NULL to unset.
398 *
234 */ 399 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); 400void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
401
402/*******************************************************************************
403 *
404 * :: Call control
405 *
406 ******************************************************************************/
407typedef enum TOXAV_CALL_CONTROL {
408 /**
409 * Resume a previously paused call. Only valid if the pause was caused by this
410 * client, if not, this control is ignored. Not valid before the call is accepted.
411 */
412 TOXAV_CALL_CONTROL_RESUME,
413 /**
414 * Put a call on hold. Not valid before the call is accepted.
415 */
416 TOXAV_CALL_CONTROL_PAUSE,
417 /**
418 * Reject a call if it was not answered, yet. Cancel a call after it was
419 * answered.
420 */
421 TOXAV_CALL_CONTROL_CANCEL,
422 /**
423 * Request that the friend stops sending audio. Regardless of the friend's
424 * compliance, this will cause the audio_receive_frame event to stop being
425 * triggered on receiving an audio frame from the friend.
426 */
427 TOXAV_CALL_CONTROL_MUTE_AUDIO,
428 /**
429 * Calling this control will notify client to start sending audio again.
430 */
431 TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
432 /**
433 * Request that the friend stops sending video. Regardless of the friend's
434 * compliance, this will cause the video_receive_frame event to stop being
435 * triggered on receiving a video frame from the friend.
436 */
437 TOXAV_CALL_CONTROL_HIDE_VIDEO,
438 /**
439 * Calling this control will notify client to start sending video again.
440 */
441 TOXAV_CALL_CONTROL_SHOW_VIDEO,
442} TOXAV_CALL_CONTROL;
443
444typedef enum TOXAV_ERR_CALL_CONTROL {
445 /**
446 * The function returned successfully.
447 */
448 TOXAV_ERR_CALL_CONTROL_OK,
449 /**
450 * Synchronization error occurred.
451 */
452 TOXAV_ERR_CALL_CONTROL_SYNC,
453 /**
454 * The friend_number passed did not designate a valid friend.
455 */
456 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
457 /**
458 * This client is currently not in a call with the friend. Before the call is
459 * answered, only CANCEL is a valid control.
460 */
461 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL,
462 /**
463 * Happens if user tried to pause an already paused call or if trying to
464 * resume a call that is not paused.
465 */
466 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
467} TOXAV_ERR_CALL_CONTROL;
236 468
237/** 469/**
238 * Encode audio frame. 470 * Sends a call control command to a friend.
471 *
472 * @param friend_number The friend number of the friend this client is in a call
473 * with.
474 * @param control The control command to send.
475 *
476 * @return true on success.
239 */ 477 */
240int toxav_prepare_audio_frame ( ToxAv *av, 478bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control,
241 int32_t call_index, 479 TOXAV_ERR_CALL_CONTROL *error);
242 uint8_t *dest, 480
243 int dest_max, 481
244 const int16_t *frame, 482/*******************************************************************************
245 int frame_size); 483 *
484 * :: Controlling bit rates
485 *
486 ******************************************************************************/
487typedef enum TOXAV_ERR_BIT_RATE_SET {
488 /**
489 * The function returned successfully.
490 */
491 TOXAV_ERR_BIT_RATE_SET_OK,
492 /**
493 * Synchronization error occurred.
494 */
495 TOXAV_ERR_BIT_RATE_SET_SYNC,
496 /**
497 * The audio bit rate passed was not one of the supported values.
498 */
499 TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
500 /**
501 * The video bit rate passed was not one of the supported values.
502 */
503 TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
504 /**
505 * The friend_number passed did not designate a valid friend.
506 */
507 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
508 /**
509 * This client is currently not in a call with the friend.
510 */
511 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
512} TOXAV_ERR_BIT_RATE_SET;
246 513
247/** 514/**
248 * Send encoded audio frame. 515 * Set the bit rate to be used in subsequent audio/video frames.
516 *
517 * @param friend_number The friend number of the friend for which to set the
518 * bit rate.
519 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
520 * audio sending. Set to -1 to leave unchanged.
521 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
522 * video sending. Set to -1 to leave unchanged.
523 *
249 */ 524 */
250int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); 525bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
526 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
251 527
252/** 528/**
253 * Get codec settings from the peer. These were exchanged during call initialization 529 * The function type for the bit_rate_status callback. The event is triggered
254 * or when peer send us new csettings. 530 * when the network becomes too saturated for current bit rates at which
531 * point core suggests new bit rates.
532 *
533 * @param friend_number The friend number of the friend for which to set the
534 * bit rate.
535 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
536 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
255 */ 537 */
256int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); 538typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
539 uint32_t video_bit_rate, void *user_data);
257 540
258/** 541/**
259 * Get friend id of peer participating in conversation. 542 * Set the callback for the `bit_rate_status` event. Pass NULL to unset.
543 *
260 */ 544 */
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); 545void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
546
547
548/*******************************************************************************
549 *
550 * :: A/V sending
551 *
552 ******************************************************************************/
553typedef enum TOXAV_ERR_SEND_FRAME {
554 /**
555 * The function returned successfully.
556 */
557 TOXAV_ERR_SEND_FRAME_OK,
558 /**
559 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
560 * data pointer was NULL.
561 */
562 TOXAV_ERR_SEND_FRAME_NULL,
563 /**
564 * The friend_number passed did not designate a valid friend.
565 */
566 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND,
567 /**
568 * This client is currently not in a call with the friend.
569 */
570 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL,
571 /**
572 * Synchronization error occurred.
573 */
574 TOXAV_ERR_SEND_FRAME_SYNC,
575 /**
576 * One of the frame parameters was invalid. E.g. the resolution may be too
577 * small or too large, or the audio sampling rate may be unsupported.
578 */
579 TOXAV_ERR_SEND_FRAME_INVALID,
580 /**
581 * Either friend turned off audio or video receiving or we turned off sending
582 * for the said payload.
583 */
584 TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED,
585 /**
586 * Failed to push frame through rtp interface.
587 */
588 TOXAV_ERR_SEND_FRAME_RTP_FAILED,
589} TOXAV_ERR_SEND_FRAME;
262 590
263/** 591/**
264 * Get current call state. 592 * Send an audio frame to a friend.
593 *
594 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
595 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
596 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
597 * this means the expected format is LRLRLR... with samples for left and right
598 * alternating.
599 *
600 * @param friend_number The friend number of the friend to which to send an
601 * audio frame.
602 * @param pcm An array of audio samples. The size of this array must be
603 * sample_count * channels.
604 * @param sample_count Number of samples in this frame. Valid numbers here are
605 * ((sample rate) * (audio length) / 1000), where audio length can be
606 * 2.5, 5, 10, 20, 40 or 60 millseconds.
607 * @param channels Number of audio channels. Supported values are 1 and 2.
608 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
609 * rates are 8000, 12000, 16000, 24000, or 48000.
610 */
611bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
612 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
613 TOXAV_ERR_SEND_FRAME *error);
614
615/**
616 * Send a video frame to a friend.
617 *
618 * Y - plane should be of size: height * width
619 * U - plane should be of size: (height/2) * (width/2)
620 * V - plane should be of size: (height/2) * (width/2)
621 *
622 * @param friend_number The friend number of the friend to which to send a video
623 * frame.
624 * @param width Width of the frame in pixels.
625 * @param height Height of the frame in pixels.
626 * @param y Y (Luminance) plane data.
627 * @param u U (Chroma) plane data.
628 * @param v V (Chroma) plane data.
265 */ 629 */
266ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); 630bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
631 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
632 TOXAV_ERR_SEND_FRAME *error);
267 633
634
635/*******************************************************************************
636 *
637 * :: A/V receiving
638 *
639 ******************************************************************************/
268/** 640/**
269 * Is certain capability supported. Used to determine if encoding/decoding is ready. 641 * The function type for the audio_receive_frame callback. The callback can be
642 * called multiple times per single iteration depending on the amount of queued
643 * frames in the buffer. The received format is the same as in send function.
644 *
645 * @param friend_number The friend number of the friend who sent an audio frame.
646 * @param pcm An array of audio samples (sample_count * channels elements).
647 * @param sample_count The number of audio samples per channel in the PCM array.
648 * @param channels Number of audio channels.
649 * @param sampling_rate Sampling rate used in this frame.
650 *
270 */ 651 */
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); 652typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
653 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
654 void *user_data);
272 655
273/** 656/**
274 * Returns tox reference. 657 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
658 *
275 */ 659 */
276Tox *toxav_get_tox (ToxAv *av); 660void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
277 661
278/** 662/**
279 * Returns number of active calls or -1 on error. 663 * The function type for the video_receive_frame callback.
664 *
665 * @param friend_number The friend number of the friend who sent a video frame.
666 * @param width Width of the frame in pixels.
667 * @param height Height of the frame in pixels.
668 * @param y
669 * @param u
670 * @param v Plane data.
671 * The size of plane data is derived from width and height where
672 * Y = MAX(width, abs(ystride)) * height,
673 * U = MAX(width/2, abs(ustride)) * (height/2) and
674 * V = MAX(width/2, abs(vstride)) * (height/2).
675 * @param ystride
676 * @param ustride
677 * @param vstride Strides data. Strides represent padding for each plane
678 * that may or may not be present. You must handle strides in
679 * your image processing code. Strides are negative if the
680 * image is bottom-up hence why you MUST abs() it when
681 * calculating plane buffer size.
682 */
683typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
684 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
685 int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
686
687/**
688 * Set the callback for the `video_receive_frame` event. Pass NULL to unset.
689 *
280 */ 690 */
281int toxav_get_active_count (ToxAv *av); 691void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
282 692
693/**
694 * NOTE Compatibility with old toxav group calls TODO remove
695 */
283/* Create a new toxav group. 696/* Create a new toxav group.
284 * 697 *
285 * return group number on success. 698 * return group number on success.
@@ -290,7 +703,7 @@ int toxav_get_active_count (ToxAv *av);
290 * 703 *
291 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 704 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
292 */ 705 */
293int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, 706int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t,
294 unsigned int, void *), void *userdata); 707 unsigned int, void *), void *userdata);
295 708
296/* Join a AV group (you need to have been invited first.) 709/* Join a AV group (you need to have been invited first.)
@@ -304,7 +717,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, con
304 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 717 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
305 */ 718 */
306int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 719int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
307 void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); 720 void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
308 721
309/* Send audio to the group chat. 722/* Send audio to the group chat.
310 * 723 *
@@ -325,5 +738,4 @@ int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsign
325#ifdef __cplusplus 738#ifdef __cplusplus
326} 739}
327#endif 740#endif
328 741#endif /* TOXAV_H */
329#endif /* __TOXAV */
diff --git a/toxav/toxav_old.c b/toxav/toxav_old.c
new file mode 100644
index 00000000..7d7e5e7b
--- /dev/null
+++ b/toxav/toxav_old.c
@@ -0,0 +1,81 @@
1/* toxav_old.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21/**
22 * This file contains the group chats code for the backwards compatibility.
23 */
24
25#include "toxav.h"
26#include "group.h"
27
28/* Create a new toxav group.
29 *
30 * return group number on success.
31 * return -1 on failure.
32 *
33 * Audio data callback format:
34 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
35 *
36 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
37 */
38int toxav_add_av_groupchat(struct Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int,
39 uint8_t, unsigned int, void *), void *userdata)
40{
41 Messenger *m = (Messenger *)tox;
42 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
43}
44
45/* Join a AV group (you need to have been invited first.)
46 *
47 * returns group number on success
48 * returns -1 on failure.
49 *
50 * Audio data callback format (same as the one for toxav_add_av_groupchat()):
51 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
52 *
53 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
54 */
55int toxav_join_av_groupchat(struct Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
56 void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
57 void *userdata)
58{
59 Messenger *m = (Messenger *)tox;
60 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata);
61}
62
63/* Send audio to the group chat.
64 *
65 * return 0 on success.
66 * return -1 on failure.
67 *
68 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
69 *
70 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
71 * Valid number of channels are 1 or 2.
72 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
73 *
74 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
75 */
76int toxav_group_send_audio(struct Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
77 unsigned int sample_rate)
78{
79 Messenger *m = (Messenger *)tox;
80 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate);
81} \ No newline at end of file
diff --git a/toxav/video.c b/toxav/video.c
new file mode 100644
index 00000000..8a832201
--- /dev/null
+++ b/toxav/video.c
@@ -0,0 +1,264 @@
1/** video.c
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <stdlib.h>
27#include <assert.h>
28
29#include "video.h"
30#include "msi.h"
31#include "rtp.h"
32
33#include "../toxcore/logger.h"
34#include "../toxcore/network.h"
35
36#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
37#define VIDEO_DECODE_BUFFER_SIZE 20
38
39VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
40{
41 VCSession *vc = calloc(sizeof(VCSession), 1);
42
43 if (!vc) {
44 LOGGER_WARNING("Allocation failed! Application might misbehave!");
45 return NULL;
46 }
47
48 if (create_recursive_mutex(vc->queue_mutex) != 0) {
49 LOGGER_WARNING("Failed to create recursive mutex!");
50 free(vc);
51 return NULL;
52 }
53
54 if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)))
55 goto BASE_CLEANUP;
56
57 int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0);
58
59 if (rc != VPX_CODEC_OK) {
60 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
61 goto BASE_CLEANUP;
62 }
63
64 /* Set encoder to some initial values
65 */
66 vpx_codec_enc_cfg_t cfg;
67 rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
68
69 if (rc != VPX_CODEC_OK) {
70 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
71 goto BASE_CLEANUP_1;
72 }
73
74 cfg.rc_target_bitrate = 500000;
75 cfg.g_w = 800;
76 cfg.g_h = 600;
77 cfg.g_pass = VPX_RC_ONE_PASS;
78 /* FIXME If we set error resilience the app will crash due to bug in vp8.
79 Perhaps vp9 has solved it?*/
80// cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
81 cfg.g_lag_in_frames = 0;
82 cfg.kf_min_dist = 0;
83 cfg.kf_max_dist = 48;
84 cfg.kf_mode = VPX_KF_AUTO;
85
86 rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
87
88 if (rc != VPX_CODEC_OK) {
89 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
90 goto BASE_CLEANUP_1;
91 }
92
93 rc = vpx_codec_control(vc->encoder, VP8E_SET_CPUUSED, 8);
94
95 if (rc != VPX_CODEC_OK) {
96 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
97 vpx_codec_destroy(vc->encoder);
98 goto BASE_CLEANUP_1;
99 }
100
101 vc->linfts = current_time_monotonic();
102 vc->lcfd = 60;
103 vc->vcb.first = cb;
104 vc->vcb.second = cb_data;
105 vc->friend_number = friend_number;
106 vc->av = av;
107
108 return vc;
109
110BASE_CLEANUP_1:
111 vpx_codec_destroy(vc->decoder);
112BASE_CLEANUP:
113 pthread_mutex_destroy(vc->queue_mutex);
114 rb_kill(vc->vbuf_raw);
115 free(vc);
116 return NULL;
117}
118void vc_kill(VCSession *vc)
119{
120 if (!vc)
121 return;
122
123 vpx_codec_destroy(vc->encoder);
124 vpx_codec_destroy(vc->decoder);
125
126 void *p;
127
128 while (rb_read(vc->vbuf_raw, (void **)&p))
129 free(p);
130
131 rb_kill(vc->vbuf_raw);
132
133 pthread_mutex_destroy(vc->queue_mutex);
134
135 LOGGER_DEBUG("Terminated video handler: %p", vc);
136 free(vc);
137}
138void vc_iterate(VCSession *vc)
139{
140 if (!vc)
141 return;
142
143 struct RTPMessage *p;
144 int rc;
145
146 pthread_mutex_lock(vc->queue_mutex);
147
148 if (rb_read(vc->vbuf_raw, (void **)&p)) {
149 pthread_mutex_unlock(vc->queue_mutex);
150
151 rc = vpx_codec_decode(vc->decoder, p->data, p->len, NULL, MAX_DECODE_TIME_US);
152 free(p);
153
154 if (rc != VPX_CODEC_OK)
155 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
156 else {
157 vpx_codec_iter_t iter = NULL;
158 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
159
160 /* Play decoded images */
161 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
162 if (vc->vcb.first)
163 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
164 (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
165 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
166
167 vpx_img_free(dest);
168 }
169 }
170
171 return;
172 }
173
174 pthread_mutex_unlock(vc->queue_mutex);
175}
176int vc_queue_message(void *vcp, struct RTPMessage *msg)
177{
178 /* This function does the reconstruction of video packets.
179 * See more info about video splitting in docs
180 */
181 if (!vcp || !msg)
182 return -1;
183
184 if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
185 LOGGER_WARNING("Got dummy!");
186 free(msg);
187 return 0;
188 }
189
190 if (msg->header.pt != rtp_TypeVideo % 128) {
191 LOGGER_WARNING("Invalid payload type!");
192 free(msg);
193 return -1;
194 }
195
196 VCSession *vc = vcp;
197
198 pthread_mutex_lock(vc->queue_mutex);
199 free(rb_write(vc->vbuf_raw, msg));
200 {
201 /* Calculate time took for peer to send us this frame */
202 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
203 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
204 vc->linfts = current_time_monotonic();
205 }
206 pthread_mutex_unlock(vc->queue_mutex);
207
208 return 0;
209}
210int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height)
211{
212 if (!vc)
213 return -1;
214
215 vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc;
216 int rc;
217
218 if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height)
219 return 0; /* Nothing changed */
220
221 if (cfg.g_w == width && cfg.g_h == height) {
222 /* Only bit rate changed */
223 cfg.rc_target_bitrate = bit_rate;
224
225 rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
226
227 if (rc != VPX_CODEC_OK) {
228 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
229 return -1;
230 }
231 } else {
232 /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
233 * reconfiguring encoder to use resolutions greater than initially set.
234 */
235
236 LOGGER_DEBUG("Have to reinitialize vpx encoder on session %p", vc);
237
238 cfg.rc_target_bitrate = bit_rate;
239 cfg.g_w = width;
240 cfg.g_h = height;
241
242 vpx_codec_ctx_t new_c;
243
244 rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
245
246 if (rc != VPX_CODEC_OK) {
247 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
248 return -1;
249 }
250
251 rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8);
252
253 if (rc != VPX_CODEC_OK) {
254 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
255 vpx_codec_destroy(&new_c);
256 return -1;
257 }
258
259 vpx_codec_destroy(vc->encoder);
260 memcpy(vc->encoder, &new_c, sizeof(new_c));
261 }
262
263 return 0;
264}
diff --git a/toxav/video.h b/toxav/video.h
new file mode 100644
index 00000000..fb836a35
--- /dev/null
+++ b/toxav/video.h
@@ -0,0 +1,67 @@
1/** video.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#ifndef VIDEO_H
23#define VIDEO_H
24
25#include <vpx/vpx_decoder.h>
26#include <vpx/vpx_encoder.h>
27#include <vpx/vp8dx.h>
28#include <vpx/vp8cx.h>
29#include <vpx/vpx_image.h>
30#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
31#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
32
33#include <pthread.h>
34
35#include "toxav.h"
36
37#include "../toxcore/util.h"
38
39struct RTPMessage;
40
41typedef struct VCSession_s {
42 /* encoding */
43 vpx_codec_ctx_t encoder[1];
44 uint32_t frame_counter;
45
46 /* decoding */
47 vpx_codec_ctx_t decoder[1];
48 void *vbuf_raw; /* Un-decoded data */
49
50 uint64_t linfts; /* Last received frame time stamp */
51 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
52
53 ToxAV *av;
54 uint32_t friend_number;
55
56 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
57
58 pthread_mutex_t queue_mutex[1];
59} VCSession;
60
61VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data);
62void vc_kill(VCSession *vc);
63void vc_iterate(VCSession *vc);
64int vc_queue_message(void *vcp, struct RTPMessage *msg);
65int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height);
66
67#endif /* VIDEO_H */
diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c
index 2ff2ac13..6d45077a 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -1264,7 +1264,7 @@ int file_seek(const Messenger *m, int32_t friendnumber, uint32_t filenumber, uin
1264 if (ft->status != FILESTATUS_NOT_ACCEPTED) 1264 if (ft->status != FILESTATUS_NOT_ACCEPTED)
1265 return -5; 1265 return -5;
1266 1266
1267 if (position > ft->size) { 1267 if (position >= ft->size) {
1268 return -6; 1268 return -6;
1269 } 1269 }
1270 1270
@@ -1569,7 +1569,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
1569 return -1; 1569 return -1;
1570 } 1570 }
1571 1571
1572 /* seek can only be sent by the receiver to seek before resuming broken tranfers. */ 1572 /* seek can only be sent by the receiver to seek before resuming broken transfers. */
1573 if (ft->status != FILESTATUS_NOT_ACCEPTED || !receive_send) { 1573 if (ft->status != FILESTATUS_NOT_ACCEPTED || !receive_send) {
1574 return -1; 1574 return -1;
1575 } 1575 }
@@ -1577,7 +1577,7 @@ static int handle_filecontrol(Messenger *m, int32_t friendnumber, uint8_t receiv
1577 memcpy(&position, data, sizeof(position)); 1577 memcpy(&position, data, sizeof(position));
1578 net_to_host((uint8_t *) &position, sizeof(position)); 1578 net_to_host((uint8_t *) &position, sizeof(position));
1579 1579
1580 if (position > ft->size) { 1580 if (position >= ft->size) {
1581 return -1; 1581 return -1;
1582 } 1582 }
1583 1583
@@ -2128,6 +2128,11 @@ static int handle_packet(void *object, int i, uint8_t *temp, uint16_t len)
2128 file_data = data + 1; 2128 file_data = data + 1;
2129 } 2129 }
2130 2130
2131 /* Prevent more data than the filesize from being passed to clients. */
2132 if ((ft->transferred + file_data_length) > ft->size) {
2133 file_data_length = ft->size - ft->transferred;
2134 }
2135
2131 if (m->file_filedata) 2136 if (m->file_filedata)
2132 (*m->file_filedata)(m, i, real_filenumber, position, file_data, file_data_length, m->file_filedata_userdata); 2137 (*m->file_filedata)(m, i, real_filenumber, position, file_data, file_data_length, m->file_filedata_userdata);
2133 2138
@@ -2240,7 +2245,7 @@ static void connection_status_cb(Messenger *m)
2240} 2245}
2241 2246
2242 2247
2243#ifdef LOGGING 2248#ifdef TOX_LOGGER
2244#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL 2249#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
2245static time_t lastdump = 0; 2250static time_t lastdump = 0;
2246static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1]; 2251static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1];
@@ -2316,7 +2321,7 @@ void do_messenger(Messenger *m)
2316 do_friends(m); 2321 do_friends(m);
2317 connection_status_cb(m); 2322 connection_status_cb(m);
2318 2323
2319#ifdef LOGGING 2324#ifdef TOX_LOGGER
2320 2325
2321 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { 2326 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
2322 2327
@@ -2415,7 +2420,7 @@ void do_messenger(Messenger *m)
2415 } 2420 }
2416 } 2421 }
2417 2422
2418#endif /* LOGGING */ 2423#endif /* TOX_LOGGER */
2419} 2424}
2420 2425
2421/* new messenger format for load/save, more robust and forward compatible */ 2426/* new messenger format for load/save, more robust and forward compatible */
diff --git a/toxcore/assoc.c b/toxcore/assoc.c
index 44c4cc30..932adc76 100644
--- a/toxcore/assoc.c
+++ b/toxcore/assoc.c
@@ -878,9 +878,9 @@ void Assoc_self_client_id_changed(Assoc *assoc, const uint8_t *id)
878 } 878 }
879} 879}
880 880
881#ifdef LOGGING 881#ifdef TOX_LOGGER
882static char *idpart2str(uint8_t *id, size_t len); 882static char *idpart2str(uint8_t *id, size_t len);
883#endif /* LOGGING */ 883#endif /* TOX_LOGGER */
884 884
885/* refresh buckets */ 885/* refresh buckets */
886void do_Assoc(Assoc *assoc, DHT *dht) 886void do_Assoc(Assoc *assoc, DHT *dht)
@@ -974,7 +974,7 @@ void kill_Assoc(Assoc *assoc)
974 } 974 }
975} 975}
976 976
977#ifdef LOGGING 977#ifdef TOX_LOGGER
978 978
979static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1]; 979static char buffer[crypto_box_PUBLICKEYBYTES * 2 + 1];
980static char *idpart2str(uint8_t *id, size_t len) 980static char *idpart2str(uint8_t *id, size_t len)
@@ -1028,4 +1028,4 @@ void Assoc_status(const Assoc *assoc)
1028 } 1028 }
1029} 1029}
1030 1030
1031#endif /* LOGGING */ 1031#endif /* TOX_LOGGER */
diff --git a/toxcore/assoc.h b/toxcore/assoc.h
index 1b4e1ff9..65a2745d 100644
--- a/toxcore/assoc.h
+++ b/toxcore/assoc.h
@@ -97,8 +97,8 @@ void do_Assoc(Assoc *assoc, DHT *dht);
97/* destroy */ 97/* destroy */
98void kill_Assoc(Assoc *assoc); 98void kill_Assoc(Assoc *assoc);
99 99
100#ifdef LOGGING 100#ifdef TOX_LOGGER
101void Assoc_status(const Assoc *assoc); 101void Assoc_status(const Assoc *assoc);
102#endif /* LOGGING */ 102#endif /* TOX_LOGGER */
103 103
104#endif /* !__ASSOC_H__ */ 104#endif /* !__ASSOC_H__ */
diff --git a/toxcore/logger.c b/toxcore/logger.c
index e8aef7e0..f19f76b1 100644
--- a/toxcore/logger.c
+++ b/toxcore/logger.c
@@ -44,7 +44,7 @@
44#endif 44#endif
45 45
46 46
47struct logger { 47struct Logger {
48 FILE *log_file; 48 FILE *log_file;
49 LOG_LEVEL level; 49 LOG_LEVEL level;
50 uint64_t start_time; /* Time when lib loaded */ 50 uint64_t start_time; /* Time when lib loaded */
@@ -87,7 +87,7 @@ char *strtime(char *dest, size_t max_len)
87 */ 87 */
88Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id) 88Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
89{ 89{
90#ifndef LOGGING /* Disabled */ 90#ifndef TOX_LOGGER /* Disabled */
91 return NULL; 91 return NULL;
92#endif 92#endif
93 93
@@ -96,7 +96,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
96 if (!retu) 96 if (!retu)
97 return NULL; 97 return NULL;
98 98
99 if ( pthread_mutex_init(retu->mutex, NULL) != 0 ) { 99 if (pthread_mutex_init(retu->mutex, NULL) != 0) {
100 free(retu); 100 free(retu);
101 return NULL; 101 return NULL;
102 } 102 }
@@ -110,7 +110,7 @@ Logger *logger_new (const char *file_name, LOG_LEVEL level, const char *id)
110 110
111 if (!(retu->tstr = calloc(16, sizeof (char))) || 111 if (!(retu->tstr = calloc(16, sizeof (char))) ||
112 !(retu->posstr = calloc(300, sizeof (char))) || 112 !(retu->posstr = calloc(300, sizeof (char))) ||
113 !(retu->msg = calloc(4096, sizeof (char))) ) 113 !(retu->msg = calloc(4096, sizeof (char))))
114 goto FAILURE; 114 goto FAILURE;
115 115
116 if (id) { 116 if (id) {
@@ -147,7 +147,7 @@ FAILURE:
147 147
148void logger_kill(Logger *log) 148void logger_kill(Logger *log)
149{ 149{
150#ifndef LOGGING /* Disabled */ 150#ifndef TOX_LOGGER /* Disabled */
151 return; 151 return;
152#endif 152#endif
153 153
@@ -160,7 +160,7 @@ void logger_kill(Logger *log)
160 free(log->posstr); 160 free(log->posstr);
161 free(log->msg); 161 free(log->msg);
162 162
163 if (fclose(log->log_file) != 0 ) 163 if (fclose(log->log_file) != 0)
164 perror("Could not close log file"); 164 perror("Could not close log file");
165 165
166 pthread_mutex_unlock(log->mutex); 166 pthread_mutex_unlock(log->mutex);
@@ -177,7 +177,7 @@ void logger_kill_global(void)
177 177
178void logger_set_global(Logger *log) 178void logger_set_global(Logger *log)
179{ 179{
180#ifndef LOGGING /* Disabled */ 180#ifndef TOX_LOGGER /* Disabled */
181 return; 181 return;
182#endif 182#endif
183 183
@@ -186,7 +186,7 @@ void logger_set_global(Logger *log)
186 186
187Logger *logger_get_global(void) 187Logger *logger_get_global(void)
188{ 188{
189#ifndef LOGGING /* Disabled */ 189#ifndef TOX_LOGGER /* Disabled */
190 return NULL; 190 return NULL;
191#endif 191#endif
192 192
@@ -195,17 +195,17 @@ Logger *logger_get_global(void)
195 195
196void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...) 196void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, const char *format, ...)
197{ 197{
198#ifndef LOGGING /* Disabled */ 198#ifndef TOX_LOGGER /* Disabled */
199 return; 199 return;
200#endif 200#endif
201 201
202 static const char *logger_format = 202 static const char *logger_format =
203 "%s " /* Logger id string */ 203 "%s " /* Logger id string */
204 "%-16s" /* Time string of format: %m:%d %H:%M:%S */ 204 "%-16s" /* Time string of format: %m:%d %H:%M:%S */
205 "%u " /* Thread id */ 205 "%-12u " /* Thread id */
206 "%-5s " /* Logger lever string */ 206 "%-5s " /* Logger lever string */
207 "%-20s " /* File:line string */ 207 "%-20s " /* File:line string */
208 "- %s" /* Output message */ 208 "- %s" /* Output message */
209 WIN_CR "\n"; /* Every new print new line */ 209 WIN_CR "\n"; /* Every new print new line */
210 210
211 211
diff --git a/toxcore/logger.h b/toxcore/logger.h
index 0513b32c..4d3e3b54 100644
--- a/toxcore/logger.h
+++ b/toxcore/logger.h
@@ -43,7 +43,7 @@ typedef enum {
43 LOG_ERROR 43 LOG_ERROR
44} LOG_LEVEL; 44} LOG_LEVEL;
45 45
46typedef struct logger Logger; 46typedef struct Logger Logger;
47 47
48/** 48/**
49 * Set 'level' as the lowest printable level. If id == NULL, random number is used. 49 * Set 'level' as the lowest printable level. If id == NULL, random number is used.
@@ -66,21 +66,22 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con
66 66
67 67
68/* To do some checks or similar only when logging, use this */ 68/* To do some checks or similar only when logging, use this */
69#ifdef LOGGING 69#ifdef TOX_LOGGER
70# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0) 70# define LOGGER_SCOPE(__SCOPE_DO__) do { __SCOPE_DO__ } while(0)
71# define LOGGER_WRITE(log, level, format, ...) \ 71# define LOGGER_WRITE(log, level, format, ...) \
72 logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__ ) 72 logger_write(log, level, __FILE__, __LINE__, format, ##__VA_ARGS__)
73#else 73#else
74/* # warning "Logging disabled" */
74# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0) 75# define LOGGER_SCOPE(__SCOPE_DO__) do {} while(0)
75# define LOGGER_WRITE(log, level, format, ...) do {} while(0) 76# define LOGGER_WRITE(log, level, format, ...) do {} while(0)
76#endif /* LOGGING */ 77#endif /* TOX_LOGGER */
77 78
78/* To log with an logger */ 79/* To log with an logger */
79#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__ ) 80#define LOGGER_TRACE_(log, format, ...) LOGGER_WRITE(log, LOG_TRACE, format, ##__VA_ARGS__)
80#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__ ) 81#define LOGGER_DEBUG_(log, format, ...) LOGGER_WRITE(log, LOG_DEBUG, format, ##__VA_ARGS__)
81#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__ ) 82#define LOGGER_INFO_(log, format, ...) LOGGER_WRITE(log, LOG_INFO, format, ##__VA_ARGS__)
82#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__ ) 83#define LOGGER_WARNING_(log, format, ...) LOGGER_WRITE(log, LOG_WARNING, format, ##__VA_ARGS__)
83#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__ ) 84#define LOGGER_ERROR_(log, format, ...) LOGGER_WRITE(log, LOG_ERROR, format, ##__VA_ARGS__)
84 85
85/* To log with the global logger */ 86/* To log with the global logger */
86#define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__) 87#define LOGGER_TRACE(format, ...) LOGGER_TRACE_(NULL, format, ##__VA_ARGS__)
diff --git a/toxcore/network.c b/toxcore/network.c
index 22ee4202..965e65f9 100644
--- a/toxcore/network.c
+++ b/toxcore/network.c
@@ -266,7 +266,7 @@ uint64_t current_time_monotonic(void)
266} 266}
267 267
268/* In case no logging */ 268/* In case no logging */
269#ifndef LOGGING 269#ifndef TOX_LOGGER
270#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__) 270#define loglogdata(__message__, __buffer__, __buflen__, __ip_port__, __res__)
271#else 271#else
272#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0 272#define data_0(__buflen__, __buffer__) __buflen__ > 4 ? ntohl(*(uint32_t *)&__buffer__[1]) : 0
@@ -287,7 +287,7 @@ uint64_t current_time_monotonic(void)
287 __buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \ 287 __buffer__[0], __message__, (size_t)__res__, (!__res__ ? '!' : '>'), __buflen__, \
288 ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__)); 288 ip_ntoa(&((__ip_port__).ip)), ntohs((__ip_port__).port), 0, "OK", data_0(__buflen__, __buffer__), data_1(__buflen__, __buffer__));
289 289
290#endif /* LOGGING */ 290#endif /* TOX_LOGGER */
291 291
292/* Basic network functions: 292/* Basic network functions:
293 * Function to send packet(data) of length length to ip_port. 293 * Function to send packet(data) of length length to ip_port.
@@ -615,9 +615,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
615 } 615 }
616 616
617 if (ip.family == AF_INET6) { 617 if (ip.family == AF_INET6) {
618#ifdef LOGGING 618#ifdef TOX_LOGGER
619 int is_dualstack = 619 int is_dualstack =
620#endif /* LOGGING */ 620#endif /* TOX_LOGGER */
621 set_socket_dualstack(temp->sock); 621 set_socket_dualstack(temp->sock);
622 LOGGER_DEBUG( "Dual-stack socket: %s", 622 LOGGER_DEBUG( "Dual-stack socket: %s",
623 is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" ); 623 is_dualstack ? "enabled" : "Failed to enable, won't be able to receive from/send to IPv4 addresses" );
@@ -628,9 +628,9 @@ Networking_Core *new_networking_ex(IP ip, uint16_t port_from, uint16_t port_to,
628 mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02; 628 mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02;
629 mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; 629 mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01;
630 mreq.ipv6mr_interface = 0; 630 mreq.ipv6mr_interface = 0;
631#ifdef LOGGING 631#ifdef TOX_LOGGER
632 int res = 632 int res =
633#endif /* LOGGING */ 633#endif /* TOX_LOGGER */
634 setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)); 634 setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
635 635
636 LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" : 636 LOGGER_DEBUG(res < 0 ? "Failed to activate local multicast membership. (%u, %s)" :
diff --git a/toxcore/onion_client.h b/toxcore/onion_client.h
index ad28ac51..416d593a 100644
--- a/toxcore/onion_client.h
+++ b/toxcore/onion_client.h
@@ -29,7 +29,7 @@
29#include "ping_array.h" 29#include "ping_array.h"
30 30
31#define MAX_ONION_CLIENTS 8 31#define MAX_ONION_CLIENTS 8
32#define ONION_NODE_PING_INTERVAL 20 32#define ONION_NODE_PING_INTERVAL 15
33#define ONION_NODE_TIMEOUT (ONION_NODE_PING_INTERVAL * 3) 33#define ONION_NODE_TIMEOUT (ONION_NODE_PING_INTERVAL * 3)
34 34
35/* The interval in seconds at which to tell our friends where we are */ 35/* The interval in seconds at which to tell our friends where we are */
diff --git a/toxcore/util.c b/toxcore/util.c
index c320cdc4..28d8721c 100644
--- a/toxcore/util.c
+++ b/toxcore/util.c
@@ -192,3 +192,87 @@ int create_recursive_mutex(pthread_mutex_t *mutex)
192 192
193 return 0; 193 return 0;
194} 194}
195
196
197struct RingBuffer {
198 uint16_t size; /* Max size */
199 uint16_t start;
200 uint16_t end;
201 void **data;
202};
203
204bool rb_full(const RingBuffer *b)
205{
206 return (b->end + 1) % b->size == b->start;
207}
208bool rb_empty(const RingBuffer *b)
209{
210 return b->end == b->start;
211}
212void *rb_write(RingBuffer *b, void *p)
213{
214 void *rc = NULL;
215
216 if ((b->end + 1) % b->size == b->start) /* full */
217 rc = b->data[b->start];
218
219 b->data[b->end] = p;
220 b->end = (b->end + 1) % b->size;
221
222 if (b->end == b->start)
223 b->start = (b->start + 1) % b->size;
224
225 return rc;
226}
227bool rb_read(RingBuffer *b, void **p)
228{
229 if (b->end == b->start) { /* Empty */
230 *p = NULL;
231 return false;
232 }
233
234 *p = b->data[b->start];
235 b->start = (b->start + 1) % b->size;
236 return true;
237}
238RingBuffer *rb_new(int size)
239{
240 RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
241
242 if (!buf) return NULL;
243
244 buf->size = size + 1; /* include empty elem */
245
246 if (!(buf->data = calloc(buf->size, sizeof(void *)))) {
247 free(buf);
248 return NULL;
249 }
250
251 return buf;
252}
253void rb_kill(RingBuffer *b)
254{
255 if (b) {
256 free(b->data);
257 free(b);
258 }
259}
260uint16_t rb_size(const RingBuffer *b)
261{
262 if (rb_empty(b))
263 return 0;
264
265 return
266 b->end > b->start ?
267 b->end - b->start :
268 (b->size - b->start) + b->end;
269}
270uint16_t rb_data(const RingBuffer *b, void **dest)
271{
272 uint16_t i = 0;
273
274 for (; i < rb_size(b); i++)
275 dest[i] = b->data[(b->start + i) % b->size];
276
277 return i;
278}
diff --git a/toxcore/util.h b/toxcore/util.h
index fde9f03c..bdbf7d3b 100644
--- a/toxcore/util.h
+++ b/toxcore/util.h
@@ -30,6 +30,7 @@
30#include <pthread.h> 30#include <pthread.h>
31 31
32#define MIN(a,b) (((a)<(b))?(a):(b)) 32#define MIN(a,b) (((a)<(b))?(a):(b))
33#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
33 34
34void unix_time_update(); 35void unix_time_update();
35uint64_t unix_time(); 36uint64_t unix_time();
@@ -54,6 +55,18 @@ typedef int (*load_state_callback_func)(void *outer, const uint8_t *data, uint32
54int load_state(load_state_callback_func load_state_callback, void *outer, 55int load_state(load_state_callback_func load_state_callback, void *outer,
55 const uint8_t *data, uint32_t length, uint16_t cookie_inner); 56 const uint8_t *data, uint32_t length, uint16_t cookie_inner);
56 57
58/* Returns -1 if failed or 0 if success */
57int create_recursive_mutex(pthread_mutex_t *mutex); 59int create_recursive_mutex(pthread_mutex_t *mutex);
58 60
61/* Ring buffer */
62typedef struct RingBuffer RingBuffer;
63bool rb_full(const RingBuffer *b);
64bool rb_empty(const RingBuffer *b);
65void *rb_write(RingBuffer *b, void *p);
66bool rb_read(RingBuffer *b, void **p);
67RingBuffer *rb_new(int size);
68void rb_kill(RingBuffer *b);
69uint16_t rb_size(const RingBuffer *b);
70uint16_t rb_data(const RingBuffer *b, void **dest);
71
59#endif /* __UTIL_H__ */ 72#endif /* __UTIL_H__ */
diff --git a/toxdns/toxdns.c b/toxdns/toxdns.c
index f7df5df3..1fbf4a00 100644
--- a/toxdns/toxdns.c
+++ b/toxdns/toxdns.c
@@ -44,7 +44,7 @@ uint8_t i = 0; \
44 } \ 44 } \
45 } \ 45 } \
46} \ 46} \
47 47
48typedef struct { 48typedef struct {
49 uint8_t temp_pk[crypto_box_PUBLICKEYBYTES]; 49 uint8_t temp_pk[crypto_box_PUBLICKEYBYTES];
50 uint8_t temp_sk[crypto_box_SECRETKEYBYTES]; 50 uint8_t temp_sk[crypto_box_SECRETKEYBYTES];